mirror of https://github.com/docusealco/docuseal
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
5.6 KiB
175 lines
5.6 KiB
# frozen_string_literal: true
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: users
|
|
#
|
|
# id :bigint not null, primary key
|
|
# archived_at :datetime
|
|
# consumed_timestep :integer
|
|
# current_sign_in_at :datetime
|
|
# current_sign_in_ip :string
|
|
# email :string not null
|
|
# encrypted_password :string not null
|
|
# failed_attempts :integer default(0), not null
|
|
# first_name :string
|
|
# last_name :string
|
|
# last_sign_in_at :datetime
|
|
# last_sign_in_ip :string
|
|
# locked_at :datetime
|
|
# otp_required_for_login :boolean default(FALSE), not null
|
|
# otp_secret :string
|
|
# provider :string
|
|
# remember_created_at :datetime
|
|
# reset_password_sent_at :datetime
|
|
# reset_password_token :string
|
|
# role :string not null
|
|
# sign_in_count :integer default(0), not null
|
|
# uid :string
|
|
# unlock_token :string
|
|
# uuid :string not null
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# account_id :bigint not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_users_on_account_id (account_id)
|
|
# index_users_on_email (email) UNIQUE
|
|
# index_users_on_provider_and_uid (provider,uid) UNIQUE
|
|
# index_users_on_reset_password_token (reset_password_token) UNIQUE
|
|
# index_users_on_unlock_token (unlock_token) UNIQUE
|
|
# index_users_on_uuid (uuid) UNIQUE
|
|
#
|
|
# Foreign Keys
|
|
#
|
|
# fk_rails_... (account_id => accounts.id)
|
|
#
|
|
class User < ApplicationRecord
|
|
ROLES = [
|
|
ADMIN_ROLE = 'admin'
|
|
].freeze
|
|
|
|
EMAIL_REGEXP = /[^@;,<>\s]+@[^@;,<>\s]+/
|
|
|
|
FULL_EMAIL_REGEXP =
|
|
/\A[a-z0-9][\.']?(?:(?:[a-z0-9_-]+[\.\+'])*[a-z0-9_-]+)*@(?:[a-z0-9]+[\.-])*[a-z0-9]+\.[a-z]{2,}\z/i
|
|
|
|
has_one_attached :signature
|
|
has_one_attached :initials
|
|
|
|
belongs_to :account
|
|
has_one :access_token, dependent: :destroy
|
|
has_many :access_tokens, dependent: :destroy
|
|
has_many :templates, dependent: :destroy, foreign_key: :author_id, inverse_of: :author
|
|
has_many :template_folders, dependent: :destroy, foreign_key: :author_id, inverse_of: :author
|
|
has_many :user_configs, dependent: :destroy
|
|
has_many :encrypted_configs, dependent: :destroy, class_name: 'EncryptedUserConfig'
|
|
has_many :email_messages, dependent: :destroy, foreign_key: :author_id, inverse_of: :author
|
|
|
|
devise :two_factor_authenticatable, :recoverable, :rememberable, :validatable, :trackable, :lockable, :omniauthable, omniauth_providers: %i[saml google_oauth2 microsoft_graph]
|
|
|
|
attribute :role, :string, default: ADMIN_ROLE
|
|
attribute :uuid, :string, default: -> { SecureRandom.uuid }
|
|
|
|
scope :active, -> { where(archived_at: nil) }
|
|
scope :archived, -> { where.not(archived_at: nil) }
|
|
scope :admins, -> { where(role: ADMIN_ROLE) }
|
|
|
|
validates :email, format: { with: /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\z/ }
|
|
|
|
def access_token
|
|
super || build_access_token.tap(&:save!)
|
|
end
|
|
|
|
def active_for_authentication?
|
|
super && !archived_at? && !account.archived_at?
|
|
end
|
|
|
|
def remember_me
|
|
true
|
|
end
|
|
|
|
def sidekiq?
|
|
return true if Rails.env.development?
|
|
|
|
role == 'admin'
|
|
end
|
|
|
|
def self.sign_in_after_reset_password
|
|
if PasswordsController::Current.user.present?
|
|
!PasswordsController::Current.user.otp_required_for_login
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
def initials
|
|
[first_name&.first, last_name&.first].compact_blank.join.upcase
|
|
end
|
|
|
|
def full_name
|
|
[first_name, last_name].compact_blank.join(' ')
|
|
end
|
|
|
|
def friendly_name
|
|
if full_name.present?
|
|
%("#{full_name.delete('"')}" <#{email}>)
|
|
else
|
|
email
|
|
end
|
|
end
|
|
|
|
# Omniauth callback method
|
|
def self.from_omniauth(auth)
|
|
# Find existing user by provider and uid
|
|
user = User.find_by(provider: auth.provider, uid: auth.uid)
|
|
|
|
if user
|
|
# Update user info from provider if needed
|
|
user.update(
|
|
email: auth.info.email,
|
|
first_name: auth.info.first_name || auth.info.name&.split&.first,
|
|
last_name: auth.info.last_name || auth.info.name&.split&.last
|
|
) if auth.info.email.present?
|
|
return user
|
|
end
|
|
|
|
# Try to find user by email if no provider/uid match
|
|
existing_user = User.find_by(email: auth.info.email) if auth.info.email.present?
|
|
|
|
if existing_user
|
|
# Link existing account with omniauth provider
|
|
existing_user.update(
|
|
provider: auth.provider,
|
|
uid: auth.uid
|
|
)
|
|
return existing_user
|
|
end
|
|
|
|
# Create new user from omniauth data
|
|
# For multitenant setups, we need to determine the account
|
|
account = if Docuseal.multitenant?
|
|
# In multitenant mode, create account from domain or use default logic
|
|
Account.find_or_create_by(name: auth.info.email&.split('@')&.last || 'SSO Account')
|
|
else
|
|
# In single-tenant mode, use the first account or create one
|
|
Account.first || Account.create!(name: 'Default Account')
|
|
end
|
|
|
|
User.create!(
|
|
email: auth.info.email,
|
|
first_name: auth.info.first_name || auth.info.name&.split&.first,
|
|
last_name: auth.info.last_name || auth.info.name&.split&.last,
|
|
provider: auth.provider,
|
|
uid: auth.uid,
|
|
account: account,
|
|
# Skip password validation for omniauth users
|
|
password: Devise.friendly_token[0, 20]
|
|
)
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
Rails.logger.error "Failed to create user from omniauth: #{e.message}"
|
|
nil
|
|
end
|
|
end
|