Files
sure/app/models/sso_identity_block.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

62 lines
1.7 KiB
Ruby

# frozen_string_literal: true
class SsoIdentityBlock < ApplicationRecord
include Encryptable
class BlockedIdentity < StandardError; end
encrypts :identity_label if encryption_ready?
before_validation :redact_identity_label_unless_encrypted
validates :provider, :uid_digest, :identity_label, presence: true
validates :uid_digest, uniqueness: { scope: :provider }
class << self
def blocked?(provider:, uid:)
exists?(provider: provider, uid_digest: digest(uid))
end
def block_all!(identities, identity_label:)
identities.find_each do |identity|
with_identity_lock(provider: identity.provider, uid: identity.uid) do
find_or_create_by!(
provider: identity.provider,
uid_digest: digest(identity.uid)
) { |block| block.identity_label = identity_label }
end
end
end
def with_identity_lock(provider:, uid:)
transaction do
lock_key = advisory_lock_key(provider: provider, uid: uid)
connection.execute(sanitize_sql_array([ "SELECT pg_advisory_xact_lock(?)", lock_key ]))
yield
end
end
def digest(uid)
OpenSSL::HMAC.hexdigest("SHA256", digest_key, uid.to_s)
end
private
def digest_key
Rails.application.key_generator.generate_key("sso_identity_block_uid", 32)
end
def advisory_lock_key(provider:, uid:)
OpenSSL::HMAC.digest("SHA256", digest_key, "#{provider}\0#{uid}").unpack1("q>")
end
end
private
def redact_identity_label_unless_encrypted
return if self.class.encryption_ready?
self.identity_label = "Removed identity #{uid_digest.to_s.first(12)}"
end
end