class User < ApplicationRecord has_secure_password has_many :sessions, dependent: :destroy normalizes :email_address, with: ->(e) { e.strip.downcase } enum :role, { admin: 0, user: 1, viewer: 2 }, default: :user generates_token_for :password_reset, expires_in: 1.hour do updated_at end validates :email_address, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP } validates :role, presence: true before_validation :set_first_user_as_admin, on: :create def self.from_oidc(auth_hash) # Extract user info from OIDC auth hash email = auth_hash.dig('info', 'email') return nil unless email user = find_or_initialize_by(email_address: email) # Map OIDC groups to role for new users or update existing user's role if auth_hash.dig('extra', 'raw_info', 'groups') user.role = map_oidc_groups_to_role(auth_hash.dig('extra', 'raw_info', 'groups')) end # For OIDC users, set a random password if they don't have one if user.new_record? && !user.password_digest? user.password = SecureRandom.hex(32) # OIDC users won't use this end # Save the user (skip password validation for OIDC users) user.save!(validate: false) if user.changed? user end def admin? role == 'admin' end def viewer? role == 'viewer' end private def set_first_user_as_admin return if User.any? self.role = 'admin' end def self.map_oidc_groups_to_role(groups) groups = Array(groups) # Check admin groups first admin_groups = ENV['OIDC_ADMIN_GROUPS']&.split(',')&.map(&:strip) return 'admin' if admin_groups && (admin_groups & groups).any? # Check user groups user_groups = ENV['OIDC_USER_GROUPS']&.split(',')&.map(&:strip) return 'user' if user_groups && (user_groups & groups).any? # Check viewer groups viewer_groups = ENV['OIDC_VIEWER_GROUPS']&.split(',')&.map(&:strip) return 'viewer' if viewer_groups && (viewer_groups & groups).any? # Default to user if no group matches 'user' end end