Files
sure/app/models/sso_audit_log.rb
T
Josh ca66346dc5 feat: add safe admin user removal (#3131)
* feat: add safe admin user removal

* fix: address user removal review findings

* fix: close remaining user removal review gaps

* fix: handle deleted users during session creation

* fix: fail closed when session creation fails

* fix: reject token issuance for inactive users
2026-08-22 21:54:32 +02:00

139 lines
3.6 KiB
Ruby

# frozen_string_literal: true
class SsoAuditLog < ApplicationRecord
belongs_to :user, optional: true
# Event types for SSO audit logging
EVENT_TYPES = %w[
login
login_failed
logout
logout_idp
link
unlink
jit_account_created
identity_unblocked
user_removed
].freeze
validates :event_type, presence: true, inclusion: { in: EVENT_TYPES }
scope :recent, -> { order(created_at: :desc) }
scope :for_user, ->(user) { where(user: user) }
scope :by_event, ->(event) { where(event_type: event) }
class << self
# Log a successful SSO login
def log_login!(user:, provider:, request:, metadata: {})
create!(
user: user,
event_type: "login",
provider: provider,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: metadata
)
end
# Log a failed SSO login attempt
def log_login_failed!(provider:, request:, reason:, metadata: {})
create!(
user: nil,
event_type: "login_failed",
provider: provider,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: metadata.merge(reason: reason)
)
end
# Log a logout (local only)
def log_logout!(user:, request:, metadata: {})
create!(
user: user,
event_type: "logout",
provider: nil,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: metadata
)
end
# Log a federated logout (to IdP)
def log_logout_idp!(user:, provider:, request:, metadata: {})
create!(
user: user,
event_type: "logout_idp",
provider: provider,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: metadata
)
end
# Log an account link (existing user links SSO identity)
def log_link!(user:, provider:, request:, metadata: {})
create!(
user: user,
event_type: "link",
provider: provider,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: metadata
)
end
# Log an account unlink (user disconnects SSO identity)
def log_unlink!(user:, provider:, request:, metadata: {})
create!(
user: user,
event_type: "unlink",
provider: provider,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: metadata
)
end
# Log JIT account creation via SSO
def log_jit_account_created!(user:, provider:, request:, metadata: {})
create!(
user: user,
event_type: "jit_account_created",
provider: provider,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: metadata
)
end
def log_user_removed!(user:, actor:, request:)
create!(
user: user,
event_type: "user_removed",
provider: nil,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: {
actor_user_id: actor.id,
target_user_id: user.id
}
)
end
def log_identity_unblocked!(block:, actor:, request:)
create!(
user: nil,
event_type: "identity_unblocked",
provider: block.provider,
ip_address: request.remote_ip,
user_agent: request.user_agent&.truncate(500),
metadata: {
actor_user_id: actor.id,
identity_block_id: block.id
}
)
end
end
end