1# frozen_string_literal: true
2
3require 'carrierwave/orm/activerecord'
4
5class User < ApplicationRecord
6  extend Gitlab::ConfigHelper
7
8  include Gitlab::ConfigHelper
9  include Gitlab::SQL::Pattern
10  include AfterCommitQueue
11  include Avatarable
12  include Referable
13  include Sortable
14  include CaseSensitivity
15  include TokenAuthenticatable
16  include FeatureGate
17  include CreatedAtFilterable
18  include BulkMemberAccessLoad
19  include BlocksJsonSerialization
20  include WithUploads
21  include OptionallySearch
22  include FromUnion
23  include BatchDestroyDependentAssociations
24  include HasUniqueInternalUsers
25  include IgnorableColumns
26  include UpdateHighestRole
27  include HasUserType
28  include Gitlab::Auth::Otp::Fortinet
29  include RestrictedSignup
30  include StripAttribute
31
32  DEFAULT_NOTIFICATION_LEVEL = :participating
33
34  INSTANCE_ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT = 10
35
36  BLOCKED_PENDING_APPROVAL_STATE = 'blocked_pending_approval'
37
38  COUNT_CACHE_VALIDITY_PERIOD = 24.hours
39
40  MAX_USERNAME_LENGTH = 255
41  MIN_USERNAME_LENGTH = 2
42
43  SECONDARY_EMAIL_ATTRIBUTES = [
44    :commit_email,
45    :notification_email,
46    :public_email
47  ].freeze
48
49  add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) }
50  add_authentication_token_field :feed_token
51  add_authentication_token_field :static_object_token
52
53  default_value_for :admin, false
54  default_value_for(:external) { Gitlab::CurrentSettings.user_default_external }
55  default_value_for :can_create_group, gitlab_config.default_can_create_group
56  default_value_for :can_create_team, false
57  default_value_for :hide_no_ssh_key, false
58  default_value_for :hide_no_password, false
59  default_value_for :project_view, :files
60  default_value_for :notified_of_own_activity, false
61  default_value_for :preferred_language, I18n.default_locale
62  default_value_for :theme_id, gitlab_config.default_theme
63
64  attr_encrypted :otp_secret,
65    key:       Gitlab::Application.secrets.otp_key_base,
66    mode:      :per_attribute_iv_and_salt,
67    insecure_mode: true,
68    algorithm: 'aes-256-cbc'
69
70  devise :two_factor_authenticatable,
71         otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base
72
73  devise :two_factor_backupable, otp_number_of_backup_codes: 10
74  serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiveRecordSerialize
75
76  devise :lockable, :recoverable, :rememberable, :trackable,
77         :validatable, :omniauthable, :confirmable, :registerable
78
79  include AdminChangedPasswordNotifier
80
81  # This module adds async behaviour to Devise emails
82  # and should be added after Devise modules are initialized.
83  include AsyncDeviseEmail
84
85  MINIMUM_INACTIVE_DAYS = 90
86
87  # Override Devise::Models::Trackable#update_tracked_fields!
88  # to limit database writes to at most once every hour
89  # rubocop: disable CodeReuse/ServiceClass
90  def update_tracked_fields!(request)
91    return if Gitlab::Database.read_only?
92
93    update_tracked_fields(request)
94
95    Gitlab::ExclusiveLease.throttle(id) do
96      ::Ability.forgetting(/admin/) do
97        Users::UpdateService.new(self, user: self).execute(validate: false)
98      end
99    end
100  end
101  # rubocop: enable CodeReuse/ServiceClass
102
103  attr_accessor :force_random_password
104
105  # Virtual attribute for authenticating by either username or email
106  attr_accessor :login
107
108  # Virtual attribute for impersonator
109  attr_accessor :impersonator
110
111  #
112  # Relations
113  #
114
115  # Namespace for personal projects
116  has_one :namespace,
117          -> { where(type: Namespaces::UserNamespace.sti_name) },
118          dependent: :destroy, # rubocop:disable Cop/ActiveRecordDependent
119          foreign_key: :owner_id,
120          inverse_of: :owner,
121          autosave: true # rubocop:disable Cop/ActiveRecordDependent
122
123  # Profile
124  has_many :keys, -> { regular_keys }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
125  has_many :expired_today_and_unnotified_keys, -> { expired_today_and_not_notified }, class_name: 'Key'
126  has_many :expiring_soon_and_unnotified_keys, -> { expiring_soon_and_not_notified }, class_name: 'Key'
127  has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :nullify # rubocop:disable Cop/ActiveRecordDependent
128  has_many :group_deploy_keys
129  has_many :gpg_keys
130
131  has_many :emails
132  has_many :personal_access_tokens, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
133  has_many :identities, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent
134  has_many :u2f_registrations, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
135  has_many :webauthn_registrations
136  has_many :chat_names, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
137  has_one :user_synced_attributes_metadata, autosave: true
138  has_one :aws_role, class_name: 'Aws::Role'
139
140  # Followers
141  has_many :followed_users, foreign_key: :follower_id, class_name: 'Users::UserFollowUser'
142  has_many :followees, through: :followed_users
143
144  has_many :following_users, foreign_key: :followee_id, class_name: 'Users::UserFollowUser'
145  has_many :followers, through: :following_users
146
147  # Groups
148  has_many :members
149  has_many :group_members, -> { where(requested_at: nil).where("access_level >= ?", Gitlab::Access::GUEST) }, class_name: 'GroupMember'
150  has_many :groups, through: :group_members
151  has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
152  has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
153  has_many :developer_groups, -> { where(members: { access_level: ::Gitlab::Access::DEVELOPER }) }, through: :group_members, source: :group
154  has_many :owned_or_maintainers_groups,
155           -> { where(members: { access_level: [Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
156           through: :group_members,
157           source: :group
158  alias_attribute :masters_groups, :maintainers_groups
159  has_many :reporter_developer_maintainer_owned_groups,
160           -> { where(members: { access_level: [Gitlab::Access::REPORTER, Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
161           through: :group_members,
162           source: :group
163  has_many :minimal_access_group_members, -> { where(access_level: [Gitlab::Access::MINIMAL_ACCESS]) }, class_name: 'GroupMember'
164  has_many :minimal_access_groups, through: :minimal_access_group_members, source: :group
165
166  # Projects
167  has_many :groups_projects,          through: :groups, source: :projects
168  has_many :personal_projects,        through: :namespace, source: :projects
169  has_many :project_members, -> { where(requested_at: nil) }
170  has_many :projects,                 through: :project_members
171  has_many :created_projects,         foreign_key: :creator_id, class_name: 'Project'
172  has_many :users_star_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
173  has_many :starred_projects, through: :users_star_projects, source: :project
174  has_many :project_authorizations, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
175  has_many :authorized_projects, through: :project_authorizations, source: :project
176
177  has_many :user_interacted_projects
178  has_many :project_interactions, through: :user_interacted_projects, source: :project, class_name: 'Project'
179
180  has_many :snippets,                 dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
181  has_many :notes,                    dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
182  has_many :issues,                   dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
183  has_many :merge_requests,           dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
184  has_many :events,                   dependent: :delete_all, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
185  has_many :releases,                 dependent: :nullify, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
186  has_many :subscriptions,            dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
187  has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
188  has_one  :abuse_report,             dependent: :destroy, foreign_key: :user_id # rubocop:disable Cop/ActiveRecordDependent
189  has_many :reported_abuse_reports,   dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" # rubocop:disable Cop/ActiveRecordDependent
190  has_many :spam_logs,                dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
191  has_many :builds,                   class_name: 'Ci::Build'
192  has_many :pipelines,                class_name: 'Ci::Pipeline'
193  has_many :todos
194  has_many :notification_settings
195  has_many :award_emoji,              dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
196  has_many :triggers,                 class_name: 'Ci::Trigger', foreign_key: :owner_id
197
198  has_many :issue_assignees, inverse_of: :assignee
199  has_many :merge_request_assignees, inverse_of: :assignee
200  has_many :merge_request_reviewers, inverse_of: :reviewer
201  has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue
202  has_many :assigned_merge_requests, class_name: "MergeRequest", through: :merge_request_assignees, source: :merge_request
203  has_many :created_custom_emoji, class_name: 'CustomEmoji', inverse_of: :creator
204
205  has_many :bulk_imports
206
207  has_many :custom_attributes, class_name: 'UserCustomAttribute'
208  has_many :callouts, class_name: 'Users::Callout'
209  has_many :group_callouts, class_name: 'Users::GroupCallout'
210  has_many :term_agreements
211  belongs_to :accepted_term, class_name: 'ApplicationSetting::Term'
212
213  has_many :metrics_users_starred_dashboards, class_name: 'Metrics::UsersStarredDashboard', inverse_of: :user
214
215  has_one :status, class_name: 'UserStatus'
216  has_one :user_preference
217  has_one :user_detail
218  has_one :user_highest_role
219  has_one :user_canonical_email
220  has_one :credit_card_validation, class_name: '::Users::CreditCardValidation'
221  has_one :atlassian_identity, class_name: 'Atlassian::Identity'
222  has_one :banned_user, class_name: '::Users::BannedUser'
223
224  has_many :reviews, foreign_key: :author_id, inverse_of: :author
225
226  has_many :in_product_marketing_emails, class_name: '::Users::InProductMarketingEmail'
227
228  has_many :timelogs
229
230  #
231  # Validations
232  #
233  # Note: devise :validatable above adds validations for :email and :password
234  validates :name, presence: true, length: { maximum: 255 }
235  validates :first_name, length: { maximum: 127 }
236  validates :last_name, length: { maximum: 127 }
237  validates :email, confirmation: true
238  validates :notification_email, devise_email: true, allow_blank: true
239  validates :public_email, uniqueness: true, devise_email: true, allow_blank: true
240  validates :commit_email, devise_email: true, allow_blank: true, unless: ->(user) { user.commit_email == Gitlab::PrivateCommitEmail::TOKEN }
241  validates :projects_limit,
242    presence: true,
243    numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Gitlab::Database::MAX_INT_VALUE }
244  validates :username, presence: true
245
246  validates :namespace, presence: true
247  validate :namespace_move_dir_allowed, if: :username_changed?
248
249  validate :unique_email, if: :email_changed?
250  validate :notification_email_verified, if: :notification_email_changed?
251  validate :public_email_verified, if: :public_email_changed?
252  validate :commit_email_verified, if: :commit_email_changed?
253  validate :signup_email_valid?, on: :create, if: ->(user) { !user.created_by_id }
254  validate :check_username_format, if: :username_changed?
255
256  validates :theme_id, allow_nil: true, inclusion: { in: Gitlab::Themes.valid_ids,
257    message: _("%{placeholder} is not a valid theme") % { placeholder: '%{value}' } }
258
259  validates :color_scheme_id, allow_nil: true, inclusion: { in: Gitlab::ColorSchemes.valid_ids,
260    message: _("%{placeholder} is not a valid color scheme") % { placeholder: '%{value}' } }
261
262  validates :website_url, allow_blank: true, url: true, if: :website_url_changed?
263
264  before_validation :sanitize_attrs
265  before_save :default_private_profile_to_false
266  before_save :ensure_incoming_email_token
267  before_save :ensure_user_rights_and_limits, if: ->(user) { user.new_record? || user.external_changed? }
268  before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) }
269  before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? }
270  before_validation :ensure_namespace_correct
271  before_save :ensure_namespace_correct # in case validation is skipped
272  after_validation :set_username_errors
273  after_update :username_changed_hook, if: :saved_change_to_username?
274  after_destroy :post_destroy_hook
275  after_destroy :remove_key_cache
276  after_create :add_primary_email_to_emails!, if: :confirmed?
277  after_commit(on: :update) do
278    if previous_changes.key?('email')
279      # Add the old primary email to Emails if not added already - this should be removed
280      # after the background migration for MR https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70872/ has completed,
281      # as the primary email is now added to Emails upon confirmation
282      # Issue to remove that: https://gitlab.com/gitlab-org/gitlab/-/issues/344134
283      previous_confirmed_at = previous_changes.key?('confirmed_at') ? previous_changes['confirmed_at'][0] : confirmed_at
284      previous_email = previous_changes[:email][0]
285      if previous_confirmed_at && !emails.exists?(email: previous_email)
286        # rubocop: disable CodeReuse/ServiceClass
287        Emails::CreateService.new(self, user: self, email: previous_email).execute(confirmed_at: previous_confirmed_at)
288        # rubocop: enable CodeReuse/ServiceClass
289      end
290
291      update_invalid_gpg_signatures
292    end
293  end
294
295  after_initialize :set_projects_limit
296
297  # User's Layout preference
298  enum layout: { fixed: 0, fluid: 1 }
299
300  # User's Dashboard preference
301  enum dashboard: { projects: 0, stars: 1, project_activity: 2, starred_project_activity: 3, groups: 4, todos: 5, issues: 6, merge_requests: 7, operations: 8, followed_user_activity: 9 }
302
303  # User's Project preference
304  enum project_view: { readme: 0, activity: 1, files: 2 }
305
306  # User's role
307  enum role: { software_developer: 0, development_team_lead: 1, devops_engineer: 2, systems_administrator: 3, security_analyst: 4, data_analyst: 5, product_manager: 6, product_designer: 7, other: 8 }, _suffix: true
308
309  delegate  :notes_filter_for,
310            :set_notes_filter,
311            :first_day_of_week, :first_day_of_week=,
312            :timezone, :timezone=,
313            :time_display_relative, :time_display_relative=,
314            :time_format_in_24h, :time_format_in_24h=,
315            :show_whitespace_in_diffs, :show_whitespace_in_diffs=,
316            :view_diffs_file_by_file, :view_diffs_file_by_file=,
317            :tab_width, :tab_width=,
318            :sourcegraph_enabled, :sourcegraph_enabled=,
319            :gitpod_enabled, :gitpod_enabled=,
320            :setup_for_company, :setup_for_company=,
321            :render_whitespace_in_code, :render_whitespace_in_code=,
322            :markdown_surround_selection, :markdown_surround_selection=,
323            to: :user_preference
324
325  delegate :path, to: :namespace, allow_nil: true, prefix: true
326  delegate :job_title, :job_title=, to: :user_detail, allow_nil: true
327  delegate :other_role, :other_role=, to: :user_detail, allow_nil: true
328  delegate :bio, :bio=, to: :user_detail, allow_nil: true
329  delegate :webauthn_xid, :webauthn_xid=, to: :user_detail, allow_nil: true
330  delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true
331  delegate :pronunciation, :pronunciation=, to: :user_detail, allow_nil: true
332  delegate :registration_objective, :registration_objective=, to: :user_detail, allow_nil: true
333
334  accepts_nested_attributes_for :user_preference, update_only: true
335  accepts_nested_attributes_for :user_detail, update_only: true
336  accepts_nested_attributes_for :credit_card_validation, update_only: true, allow_destroy: true
337
338  state_machine :state, initial: :active do
339    event :block do
340      transition active: :blocked
341      transition deactivated: :blocked
342      transition ldap_blocked: :blocked
343      transition blocked_pending_approval: :blocked
344    end
345
346    event :ldap_block do
347      transition active: :ldap_blocked
348      transition deactivated: :ldap_blocked
349    end
350
351    event :activate do
352      transition deactivated: :active
353      transition blocked: :active
354      transition ldap_blocked: :active
355      transition blocked_pending_approval: :active
356      transition banned: :active
357    end
358
359    event :block_pending_approval do
360      transition active: :blocked_pending_approval
361    end
362
363    event :ban do
364      transition active: :banned
365    end
366
367    event :unban do
368      transition banned: :active
369    end
370
371    event :deactivate do
372      # Any additional changes to this event should be also
373      # reflected in app/workers/users/deactivate_dormant_users_worker.rb
374      transition active: :deactivated
375    end
376
377    state :blocked, :ldap_blocked, :blocked_pending_approval, :banned do
378      def blocked?
379        true
380      end
381    end
382
383    before_transition do
384      !Gitlab::Database.read_only?
385    end
386
387    # rubocop: disable CodeReuse/ServiceClass
388    # Ideally we should not call a service object here but user.block
389    # is also bcalled by Users::MigrateToGhostUserService which references
390    # this state transition object in order to do a rollback.
391    # For this reason the tradeoff is to disable this cop.
392    after_transition any => :blocked do |user|
393      user.run_after_commit do
394        Ci::DropPipelineService.new.execute_async_for_all(user.pipelines, :user_blocked, user)
395        Ci::DisableUserPipelineSchedulesService.new.execute(user)
396      end
397    end
398
399    after_transition any => :deactivated do |user|
400      next unless Gitlab::CurrentSettings.user_deactivation_emails_enabled
401
402      NotificationService.new.user_deactivated(user.name, user.notification_email_or_default)
403    end
404    # rubocop: enable CodeReuse/ServiceClass
405
406    after_transition active: :banned do |user|
407      user.create_banned_user
408    end
409
410    after_transition banned: :active do |user|
411      user.banned_user&.destroy
412    end
413  end
414
415  # Scopes
416  scope :admins, -> { where(admin: true) }
417  scope :instance_access_request_approvers_to_be_notified, -> { admins.active.order_recent_sign_in.limit(INSTANCE_ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT) }
418  scope :blocked, -> { with_states(:blocked, :ldap_blocked) }
419  scope :blocked_pending_approval, -> { with_states(:blocked_pending_approval) }
420  scope :banned, -> { with_states(:banned) }
421  scope :external, -> { where(external: true) }
422  scope :non_external, -> { where(external: false) }
423  scope :confirmed, -> { where.not(confirmed_at: nil) }
424  scope :active, -> { with_state(:active).non_internal }
425  scope :active_without_ghosts, -> { with_state(:active).without_ghosts }
426  scope :deactivated, -> { with_state(:deactivated).non_internal }
427  scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) }
428  scope :by_username, -> (usernames) { iwhere(username: Array(usernames).map(&:to_s)) }
429  scope :by_name, -> (names) { iwhere(name: Array(names)) }
430  scope :by_user_email, -> (emails) { iwhere(email: Array(emails)) }
431  scope :by_emails, -> (emails) { joins(:emails).where(emails: { email: Array(emails).map(&:downcase) }) }
432  scope :for_todos, -> (todos) { where(id: todos.select(:user_id).distinct) }
433  scope :with_emails, -> { preload(:emails) }
434  scope :with_dashboard, -> (dashboard) { where(dashboard: dashboard) }
435  scope :with_public_profile, -> { where(private_profile: false) }
436  scope :with_expiring_and_not_notified_personal_access_tokens, ->(at) do
437    where('EXISTS (?)',
438          ::PersonalAccessToken
439            .where('personal_access_tokens.user_id = users.id')
440            .without_impersonation
441            .expiring_and_not_notified(at).select(1))
442  end
443  scope :with_personal_access_tokens_expired_today, -> do
444    where('EXISTS (?)',
445          ::PersonalAccessToken
446            .select(1)
447            .where('personal_access_tokens.user_id = users.id')
448            .without_impersonation
449            .expired_today_and_not_notified)
450  end
451
452  scope :with_ssh_key_expiring_soon, -> do
453    includes(:expiring_soon_and_unnotified_keys)
454      .where('EXISTS (?)',
455         ::Key
456         .select(1)
457         .where('keys.user_id = users.id')
458         .expiring_soon_and_not_notified)
459  end
460  scope :order_recent_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'DESC')) }
461  scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) }
462  scope :order_recent_last_activity, -> { reorder(Gitlab::Database.nulls_last_order('last_activity_on', 'DESC')) }
463  scope :order_oldest_last_activity, -> { reorder(Gitlab::Database.nulls_first_order('last_activity_on', 'ASC')) }
464  scope :by_id_and_login, ->(id, login) { where(id: id).where('username = LOWER(:login) OR email = LOWER(:login)', login: login) }
465  scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) }
466  scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil) }
467  scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
468  scope :get_ids_by_username, -> (username) { where(username: username).pluck(:id) }
469
470  strip_attributes! :name
471
472  def preferred_language
473    read_attribute('preferred_language') ||
474      I18n.default_locale.to_s.presence_in(Gitlab::I18n.available_locales) ||
475      default_preferred_language
476  end
477
478  def active_for_authentication?
479    return false unless super
480
481    check_ldap_if_ldap_blocked!
482
483    can?(:log_in)
484  end
485
486  # The messages for these keys are defined in `devise.en.yml`
487  def inactive_message
488    if blocked_pending_approval?
489      :blocked_pending_approval
490    elsif blocked?
491      :blocked
492    elsif internal?
493      :forbidden
494    else
495      super
496    end
497  end
498
499  def self.with_visible_profile(user)
500    return with_public_profile if user.nil?
501
502    if user.admin?
503      all
504    else
505      with_public_profile.or(where(id: user.id))
506    end
507  end
508
509  # Limits the users to those that have TODOs, optionally in the given state.
510  #
511  # user - The user to get the todos for.
512  #
513  # with_todos - If we should limit the result set to users that are the
514  #              authors of todos.
515  #
516  # todo_state - An optional state to require the todos to be in.
517  def self.limit_to_todo_authors(user: nil, with_todos: false, todo_state: nil)
518    if user && with_todos
519      where(id: Todo.where(user: user, state: todo_state).select(:author_id))
520    else
521      all
522    end
523  end
524
525  # Returns a relation that optionally includes the given user.
526  #
527  # user_id - The ID of the user to include.
528  def self.union_with_user(user_id = nil)
529    if user_id.present?
530      # We use "unscoped" here so that any inner conditions are not repeated for
531      # the outer query, which would be redundant.
532      User.unscoped.from_union([all, User.unscoped.where(id: user_id)])
533    else
534      all
535    end
536  end
537
538  def self.with_two_factor
539    with_u2f_registrations = <<-SQL
540      EXISTS (
541        SELECT *
542        FROM u2f_registrations AS u2f
543        WHERE u2f.user_id = users.id
544      ) OR users.otp_required_for_login = ?
545      OR
546      EXISTS (
547        SELECT *
548        FROM webauthn_registrations AS webauthn
549        WHERE webauthn.user_id = users.id
550      )
551    SQL
552
553    where(with_u2f_registrations, true)
554  end
555
556  def self.without_two_factor
557    joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id
558           LEFT OUTER JOIN webauthn_registrations AS webauthn ON webauthn.user_id = users.id")
559      .where("u2f.id IS NULL AND webauthn.id IS NULL AND users.otp_required_for_login = ?", false)
560  end
561
562  #
563  # Class methods
564  #
565  class << self
566    # Devise method overridden to allow support for dynamic password lengths
567    def password_length
568      Gitlab::CurrentSettings.minimum_password_length..Devise.password_length.max
569    end
570
571    # Generate a random password that conforms to the current password length settings
572    def random_password
573      Devise.friendly_token(password_length.max)
574    end
575
576    # Devise method overridden to allow sign in with email or username
577    def find_for_database_authentication(warden_conditions)
578      conditions = warden_conditions.dup
579      if login = conditions.delete(:login)
580        where(conditions).find_by("lower(username) = :value OR lower(email) = :value", value: login.downcase.strip)
581      else
582        find_by(conditions)
583      end
584    end
585
586    def sort_by_attribute(method)
587      order_method = method || 'id_desc'
588
589      case order_method.to_s
590      when 'recent_sign_in' then order_recent_sign_in
591      when 'oldest_sign_in' then order_oldest_sign_in
592      when 'last_activity_on_desc' then order_recent_last_activity
593      when 'last_activity_on_asc' then order_oldest_last_activity
594      else
595        order_by(order_method)
596      end
597    end
598
599    # Find a User by their primary email or any associated secondary email
600    def find_by_any_email(email, confirmed: false)
601      return unless email
602
603      by_any_email(email, confirmed: confirmed).take
604    end
605
606    # Returns a relation containing all the users for the given email addresses
607    #
608    # @param emails [String, Array<String>] email addresses to check
609    # @param confirmed [Boolean] Only return users where the email is confirmed
610    def by_any_email(emails, confirmed: false)
611      from_users = by_user_email(emails)
612      from_users = from_users.confirmed if confirmed
613
614      from_emails = by_emails(emails)
615      from_emails = from_emails.confirmed.merge(Email.confirmed) if confirmed
616
617      items = [from_users, from_emails]
618
619      user_ids = Gitlab::PrivateCommitEmail.user_ids_for_emails(Array(emails).map(&:downcase))
620      items << where(id: user_ids) if user_ids.present?
621
622      from_union(items)
623    end
624
625    def find_by_private_commit_email(email)
626      user_id = Gitlab::PrivateCommitEmail.user_id_for_email(email)
627
628      find_by(id: user_id)
629    end
630
631    def filter_items(filter_name)
632      case filter_name
633      when 'admins'
634        admins
635      when 'blocked'
636        blocked
637      when 'blocked_pending_approval'
638        blocked_pending_approval
639      when 'banned'
640        banned
641      when 'two_factor_disabled'
642        without_two_factor
643      when 'two_factor_enabled'
644        with_two_factor
645      when 'wop'
646        without_projects
647      when 'external'
648        external
649      when 'deactivated'
650        deactivated
651      else
652        active_without_ghosts
653      end
654    end
655
656    # Searches users matching the given query.
657    #
658    # This method uses ILIKE on PostgreSQL.
659    #
660    # query - The search query as a String
661    #
662    # Returns an ActiveRecord::Relation.
663    def search(query, **options)
664      query = query&.delete_prefix('@')
665      return none if query.blank?
666
667      query = query.downcase
668
669      order = <<~SQL
670        CASE
671          WHEN users.name = :query THEN 0
672          WHEN users.username = :query THEN 1
673          WHEN users.email = :query THEN 2
674          ELSE 3
675        END
676      SQL
677
678      sanitized_order_sql = Arel.sql(sanitize_sql_array([order, query: query]))
679
680      search_with_secondary_emails(query).reorder(sanitized_order_sql, :name)
681    end
682
683    # Limits the result set to users _not_ in the given query/list of IDs.
684    #
685    # users - The list of users to ignore. This can be an
686    #         `ActiveRecord::Relation`, or an Array.
687    def where_not_in(users = nil)
688      users ? where.not(id: users) : all
689    end
690
691    def reorder_by_name
692      reorder(:name)
693    end
694
695    def search_without_secondary_emails(query)
696      return none if query.blank?
697
698      query = query.downcase
699
700      where(
701        fuzzy_arel_match(:name, query, lower_exact_match: true)
702          .or(fuzzy_arel_match(:username, query, lower_exact_match: true))
703          .or(arel_table[:email].eq(query))
704      )
705    end
706
707    # searches user by given pattern
708    # it compares name, email, username fields and user's secondary emails with given pattern
709    # This method uses ILIKE on PostgreSQL.
710
711    def search_with_secondary_emails(query)
712      return none if query.blank?
713
714      query = query.downcase
715
716      email_table = Email.arel_table
717      matched_by_email_user_id = email_table
718        .project(email_table[:user_id])
719        .where(email_table[:email].eq(query))
720        .take(1) # at most 1 record as there is a unique constraint
721
722      where(
723        fuzzy_arel_match(:name, query)
724          .or(fuzzy_arel_match(:username, query))
725          .or(arel_table[:email].eq(query))
726          .or(arel_table[:id].eq(matched_by_email_user_id))
727      )
728    end
729
730    def by_login(login)
731      return unless login
732
733      if login.include?('@')
734        unscoped.iwhere(email: login).take
735      else
736        unscoped.iwhere(username: login).take
737      end
738    end
739
740    def find_by_username(username)
741      by_username(username).take
742    end
743
744    def find_by_username!(username)
745      by_username(username).take!
746    end
747
748    # Returns a user for the given SSH key.
749    def find_by_ssh_key_id(key_id)
750      find_by('EXISTS (?)', Key.select(1).where('keys.user_id = users.id').where(id: key_id))
751    end
752
753    def find_by_full_path(path, follow_redirects: false)
754      namespace = Namespace.user_namespaces.find_by_full_path(path, follow_redirects: follow_redirects)
755      namespace&.owner
756    end
757
758    def reference_prefix
759      '@'
760    end
761
762    # Pattern used to extract `@user` user references from text
763    def reference_pattern
764      @reference_pattern ||=
765        %r{
766          (?<!\w)
767          #{Regexp.escape(reference_prefix)}
768          (?<user>#{Gitlab::PathRegex::FULL_NAMESPACE_FORMAT_REGEX})
769        }x
770    end
771
772    # Return (create if necessary) the ghost user. The ghost user
773    # owns records previously belonging to deleted users.
774    def ghost
775      email = 'ghost%s@example.com'
776      unique_internal(where(user_type: :ghost), 'ghost', email) do |u|
777        u.bio = _('This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.')
778        u.name = 'Ghost User'
779      end
780    end
781
782    def alert_bot
783      email_pattern = "alert%s@#{Settings.gitlab.host}"
784
785      unique_internal(where(user_type: :alert_bot), 'alert-bot', email_pattern) do |u|
786        u.bio = 'The GitLab alert bot'
787        u.name = 'GitLab Alert Bot'
788        u.avatar = bot_avatar(image: 'alert-bot.png')
789      end
790    end
791
792    def migration_bot
793      email_pattern = "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}"
794
795      unique_internal(where(user_type: :migration_bot), 'migration-bot', email_pattern) do |u|
796        u.bio = 'The GitLab migration bot'
797        u.name = 'GitLab Migration Bot'
798        u.confirmed_at = Time.zone.now
799      end
800    end
801
802    def security_bot
803      email_pattern = "security-bot%s@#{Settings.gitlab.host}"
804
805      unique_internal(where(user_type: :security_bot), 'GitLab-Security-Bot', email_pattern) do |u|
806        u.bio = 'System bot that monitors detected vulnerabilities for solutions and creates merge requests with the fixes.'
807        u.name = 'GitLab Security Bot'
808        u.website_url = Gitlab::Routing.url_helpers.help_page_url('user/application_security/security_bot/index.md')
809        u.avatar = bot_avatar(image: 'security-bot.png')
810        u.confirmed_at = Time.zone.now
811      end
812    end
813
814    def support_bot
815      email_pattern = "support%s@#{Settings.gitlab.host}"
816
817      unique_internal(where(user_type: :support_bot), 'support-bot', email_pattern) do |u|
818        u.bio = 'The GitLab support bot used for Service Desk'
819        u.name = 'GitLab Support Bot'
820        u.avatar = bot_avatar(image: 'support-bot.png')
821        u.confirmed_at = Time.zone.now
822      end
823    end
824
825    def automation_bot
826      email_pattern = "automation%s@#{Settings.gitlab.host}"
827
828      unique_internal(where(user_type: :automation_bot), 'automation-bot', email_pattern) do |u|
829        u.bio = 'The GitLab automation bot used for automated workflows and tasks'
830        u.name = 'GitLab Automation Bot'
831        u.avatar = bot_avatar(image: 'support-bot.png') # todo: add an avatar for automation-bot
832      end
833    end
834
835    # Return true if there is only single non-internal user in the deployment,
836    # ghost user is ignored.
837    def single_user?
838      User.non_internal.limit(2).count == 1
839    end
840
841    def single_user
842      User.non_internal.first if single_user?
843    end
844  end
845
846  #
847  # Instance methods
848  #
849
850  def full_path
851    username
852  end
853
854  def to_param
855    username
856  end
857
858  def to_reference(_from = nil, target_project: nil, full: nil)
859    "#{self.class.reference_prefix}#{username}"
860  end
861
862  def skip_confirmation=(bool)
863    skip_confirmation! if bool
864  end
865
866  def skip_reconfirmation=(bool)
867    skip_reconfirmation! if bool
868  end
869
870  def generate_reset_token
871    @reset_token, enc = Devise.token_generator.generate(self.class, :reset_password_token)
872
873    self.reset_password_token   = enc
874    self.reset_password_sent_at = Time.current.utc
875
876    @reset_token
877  end
878
879  def recently_sent_password_reset?
880    reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago
881  end
882
883  def remember_me!
884    super if ::Gitlab::Database.read_write?
885  end
886
887  def forget_me!
888    super if ::Gitlab::Database.read_write?
889  end
890
891  def disable_two_factor!
892    transaction do
893      update(
894        otp_required_for_login:      false,
895        encrypted_otp_secret:        nil,
896        encrypted_otp_secret_iv:     nil,
897        encrypted_otp_secret_salt:   nil,
898        otp_grace_period_started_at: nil,
899        otp_backup_codes:            nil
900      )
901      self.u2f_registrations.destroy_all # rubocop: disable Cop/DestroyAll
902      self.webauthn_registrations.destroy_all # rubocop: disable Cop/DestroyAll
903    end
904  end
905
906  def two_factor_enabled?
907    two_factor_otp_enabled? || two_factor_webauthn_u2f_enabled?
908  end
909
910  def two_factor_otp_enabled?
911    otp_required_for_login? ||
912    forti_authenticator_enabled?(self) ||
913    forti_token_cloud_enabled?(self)
914  end
915
916  def two_factor_u2f_enabled?
917    return false if Feature.enabled?(:webauthn, default_enabled: :yaml)
918
919    if u2f_registrations.loaded?
920      u2f_registrations.any?
921    else
922      u2f_registrations.exists?
923    end
924  end
925
926  def two_factor_webauthn_u2f_enabled?
927    two_factor_u2f_enabled? || two_factor_webauthn_enabled?
928  end
929
930  def two_factor_webauthn_enabled?
931    return false unless Feature.enabled?(:webauthn, default_enabled: :yaml)
932
933    (webauthn_registrations.loaded? && webauthn_registrations.any?) || (!webauthn_registrations.loaded? && webauthn_registrations.exists?)
934  end
935
936  def namespace_move_dir_allowed
937    if namespace&.any_project_has_container_registry_tags?
938      errors.add(:username, _('cannot be changed if a personal project has container registry tags.'))
939    end
940  end
941
942  # will_save_change_to_attribute? is used by Devise to check if it is necessary
943  # to clear any existing reset_password_tokens before updating an authentication_key
944  # and login in our case is a virtual attribute to allow login by username or email.
945  def will_save_change_to_login?
946    will_save_change_to_username? || will_save_change_to_email?
947  end
948
949  def unique_email
950    return if errors.added?(:email, _('has already been taken'))
951
952    if !emails.exists?(email: email) && Email.exists?(email: email)
953      errors.add(:email, _('has already been taken'))
954    end
955  end
956
957  def commit_email_or_default
958    if self.commit_email == Gitlab::PrivateCommitEmail::TOKEN
959      return private_commit_email
960    end
961
962    # The commit email is the same as the primary email if undefined
963    self.commit_email.presence || self.email
964  end
965
966  def notification_email_or_default
967    # The notification email is the same as the primary email if undefined
968    self.notification_email.presence || self.email
969  end
970
971  def private_commit_email
972    Gitlab::PrivateCommitEmail.for_user(self)
973  end
974
975  # see if the new email is already a verified secondary email
976  def check_for_verified_email
977    skip_reconfirmation! if emails.confirmed.where(email: self.email).any?
978  end
979
980  def update_invalid_gpg_signatures
981    gpg_keys.each(&:update_invalid_gpg_signatures)
982  end
983
984  # Returns the groups a user has access to, either through a membership or a project authorization
985  def authorized_groups
986    Group.unscoped do
987      authorized_groups_with_shared_membership
988    end
989  end
990
991  # Returns the groups a user is a member of, either directly or through a parent group
992  def membership_groups
993    groups.self_and_descendants
994  end
995
996  # Returns a relation of groups the user has access to, including their parent
997  # and child groups (recursively).
998  def all_expanded_groups
999    return groups if groups.empty?
1000
1001    Gitlab::ObjectHierarchy.new(groups).all_objects
1002  end
1003
1004  def expanded_groups_requiring_two_factor_authentication
1005    all_expanded_groups.where(require_two_factor_authentication: true)
1006  end
1007
1008  def source_groups_of_two_factor_authentication_requirement
1009    Gitlab::ObjectHierarchy.new(expanded_groups_requiring_two_factor_authentication)
1010      .all_objects
1011      .where(id: groups)
1012  end
1013
1014  # rubocop: disable CodeReuse/ServiceClass
1015  def refresh_authorized_projects(source: nil)
1016    Users::RefreshAuthorizedProjectsService.new(self, source: source).execute
1017  end
1018  # rubocop: enable CodeReuse/ServiceClass
1019
1020  def remove_project_authorizations(project_ids, per_batch = 1000)
1021    project_ids.each_slice(per_batch) do |project_ids_batch|
1022      project_authorizations.where(project_id: project_ids_batch).delete_all
1023    end
1024  end
1025
1026  def authorized_projects(min_access_level = nil)
1027    # We're overriding an association, so explicitly call super with no
1028    # arguments or it would be passed as `force_reload` to the association
1029    projects = super()
1030
1031    if min_access_level
1032      projects = projects
1033        .where('project_authorizations.access_level >= ?', min_access_level)
1034    end
1035
1036    projects
1037  end
1038
1039  def authorized_project?(project, min_access_level = nil)
1040    authorized_projects(min_access_level).exists?({ id: project.id })
1041  end
1042
1043  # Typically used in conjunction with projects table to get projects
1044  # a user has been given access to.
1045  # The param `related_project_column` is the column to compare to the
1046  # project_authorizations. By default is projects.id
1047  #
1048  # Example use:
1049  # `Project.where('EXISTS(?)', user.authorizations_for_projects)`
1050  def authorizations_for_projects(min_access_level: nil, related_project_column: 'projects.id')
1051    authorizations = project_authorizations
1052                      .select(1)
1053                      .where("project_authorizations.project_id = #{related_project_column}")
1054
1055    return authorizations unless min_access_level.present?
1056
1057    authorizations.where('project_authorizations.access_level >= ?', min_access_level)
1058  end
1059
1060  # Returns the projects this user has reporter (or greater) access to, limited
1061  # to at most the given projects.
1062  #
1063  # This method is useful when you have a list of projects and want to
1064  # efficiently check to which of these projects the user has at least reporter
1065  # access.
1066  def projects_with_reporter_access_limited_to(projects)
1067    authorized_projects(Gitlab::Access::REPORTER).where(id: projects)
1068  end
1069
1070  def owned_projects
1071    @owned_projects ||= Project.from_union(
1072      [
1073        Project.where(namespace: namespace),
1074        Project.joins(:project_authorizations)
1075          .where.not('projects.namespace_id' => namespace.id)
1076          .where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER })
1077      ],
1078      remove_duplicates: false
1079    )
1080  end
1081
1082  # Returns projects which user can admin issues on (for example to move an issue to that project).
1083  #
1084  # This logic is duplicated from `Ability#project_abilities` into a SQL form.
1085  def projects_where_can_admin_issues
1086    authorized_projects(Gitlab::Access::REPORTER).non_archived.with_issues_enabled
1087  end
1088
1089  # rubocop: disable CodeReuse/ServiceClass
1090  def require_ssh_key?
1091    count = Users::KeysCountService.new(self).count
1092
1093    count == 0 && Gitlab::ProtocolAccess.allowed?('ssh')
1094  end
1095  # rubocop: enable CodeReuse/ServiceClass
1096
1097  def require_password_creation_for_web?
1098    allow_password_authentication_for_web? && password_automatically_set?
1099  end
1100
1101  def require_password_creation_for_git?
1102    allow_password_authentication_for_git? && password_automatically_set?
1103  end
1104
1105  def require_personal_access_token_creation_for_git_auth?
1106    return false if allow_password_authentication_for_git? || password_based_omniauth_user?
1107
1108    PersonalAccessTokensFinder.new(user: self, impersonation: false, state: 'active').execute.none?
1109  end
1110
1111  def require_extra_setup_for_git_auth?
1112    require_password_creation_for_git? || require_personal_access_token_creation_for_git_auth?
1113  end
1114
1115  def allow_password_authentication?
1116    allow_password_authentication_for_web? || allow_password_authentication_for_git?
1117  end
1118
1119  def allow_password_authentication_for_web?
1120    Gitlab::CurrentSettings.password_authentication_enabled_for_web? && !ldap_user?
1121  end
1122
1123  def allow_password_authentication_for_git?
1124    Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !password_based_omniauth_user?
1125  end
1126
1127  # method overriden in EE
1128  def password_based_login_forbidden?
1129    false
1130  end
1131
1132  def can_change_username?
1133    gitlab_config.username_changing_enabled
1134  end
1135
1136  def can_create_project?
1137    projects_limit_left > 0
1138  end
1139
1140  def can_create_group?
1141    can?(:create_group)
1142  end
1143
1144  def can_select_namespace?
1145    several_namespaces? || admin
1146  end
1147
1148  def can?(action, subject = :global)
1149    Ability.allowed?(self, action, subject)
1150  end
1151
1152  def confirm_deletion_with_password?
1153    !password_automatically_set? && allow_password_authentication?
1154  end
1155
1156  def first_name
1157    read_attribute(:first_name) || begin
1158      name.split(' ').first unless name.blank?
1159    end
1160  end
1161
1162  def last_name
1163    read_attribute(:last_name) || begin
1164      name.split(' ').drop(1).join(' ') unless name.blank?
1165    end
1166  end
1167
1168  def projects_limit_left
1169    projects_limit - personal_projects_count
1170  end
1171
1172  # rubocop: disable CodeReuse/ServiceClass
1173  def recent_push(project = nil)
1174    service = Users::LastPushEventService.new(self)
1175
1176    if project
1177      service.last_event_for_project(project)
1178    else
1179      service.last_event_for_user
1180    end
1181  end
1182  # rubocop: enable CodeReuse/ServiceClass
1183
1184  def several_namespaces?
1185    union_sql = ::Gitlab::SQL::Union.new(
1186      [owned_groups,
1187       maintainers_groups,
1188       groups_with_developer_maintainer_project_access]).to_sql
1189
1190    ::Group.from("(#{union_sql}) #{::Group.table_name}").any?
1191  end
1192
1193  def namespace_id
1194    namespace.try :id
1195  end
1196
1197  def name_with_username
1198    "#{name} (#{username})"
1199  end
1200
1201  def already_forked?(project)
1202    !!fork_of(project)
1203  end
1204
1205  def fork_of(project)
1206    namespace.find_fork_of(project)
1207  end
1208
1209  def password_based_omniauth_user?
1210    ldap_user? || crowd_user?
1211  end
1212
1213  def crowd_user?
1214    if identities.loaded?
1215      identities.find { |identity| identity.provider == 'crowd' && identity.extern_uid.present? }
1216    else
1217      identities.with_any_extern_uid('crowd').exists?
1218    end
1219  end
1220
1221  def ldap_user?
1222    if identities.loaded?
1223      identities.find { |identity| Gitlab::Auth::OAuth::Provider.ldap_provider?(identity.provider) && !identity.extern_uid.nil? }
1224    else
1225      identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"])
1226    end
1227  end
1228
1229  def ldap_identity
1230    @ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
1231  end
1232
1233  def matches_identity?(provider, extern_uid)
1234    identities.with_extern_uid(provider, extern_uid).exists?
1235  end
1236
1237  def project_deploy_keys
1238    @project_deploy_keys ||= DeployKey.in_projects(authorized_projects.select(:id)).distinct(:id)
1239  end
1240
1241  def highest_role
1242    user_highest_role&.highest_access_level || Gitlab::Access::NO_ACCESS
1243  end
1244
1245  def credit_card_validated_at
1246    credit_card_validation&.credit_card_validated_at
1247  end
1248
1249  def accessible_deploy_keys
1250    DeployKey.from_union([
1251      DeployKey.where(id: project_deploy_keys.select(:deploy_key_id)),
1252      DeployKey.are_public
1253    ])
1254  end
1255
1256  def created_by
1257    User.find_by(id: created_by_id) if created_by_id
1258  end
1259
1260  def sanitize_attrs
1261    sanitize_links
1262    sanitize_name
1263  end
1264
1265  def sanitize_links
1266    %i[skype linkedin twitter].each do |attr|
1267      value = self[attr]
1268      self[attr] = Sanitize.clean(value) if value.present?
1269    end
1270  end
1271
1272  def sanitize_name
1273    return unless self.name
1274
1275    self.name = self.name.gsub(%r{</?[^>]*>}, '')
1276  end
1277
1278  def unset_secondary_emails_matching_deleted_email!(deleted_email)
1279    secondary_email_attribute_changed = false
1280    SECONDARY_EMAIL_ATTRIBUTES.each do |attribute|
1281      if read_attribute(attribute) == deleted_email
1282        self.write_attribute(attribute, nil)
1283        secondary_email_attribute_changed = true
1284      end
1285    end
1286    save if secondary_email_attribute_changed
1287  end
1288
1289  def admin_unsubscribe!
1290    update_column :admin_email_unsubscribed_at, Time.current
1291  end
1292
1293  def set_projects_limit
1294    # `User.select(:id)` raises
1295    # `ActiveModel::MissingAttributeError: missing attribute: projects_limit`
1296    # without this safeguard!
1297    return unless has_attribute?(:projects_limit) && projects_limit.nil?
1298
1299    self.projects_limit = Gitlab::CurrentSettings.default_projects_limit
1300  end
1301
1302  def requires_ldap_check?
1303    if !Gitlab.config.ldap.enabled
1304      false
1305    elsif ldap_user?
1306      !last_credential_check_at || (last_credential_check_at + ldap_sync_time) < Time.current
1307    else
1308      false
1309    end
1310  end
1311
1312  def ldap_sync_time
1313    # This number resides in this method so it can be redefined in EE.
1314    1.hour
1315  end
1316
1317  def try_obtain_ldap_lease
1318    # After obtaining this lease LDAP checks will be blocked for 600 seconds
1319    # (10 minutes) for this user.
1320    lease = Gitlab::ExclusiveLease.new("user_ldap_check:#{id}", timeout: 600)
1321    lease.try_obtain
1322  end
1323
1324  def solo_owned_groups
1325    @solo_owned_groups ||= owned_groups.includes(:owners).select do |group|
1326      group.owners == [self]
1327    end
1328  end
1329
1330  def with_defaults
1331    User.defaults.each do |k, v|
1332      public_send("#{k}=", v) # rubocop:disable GitlabSecurity/PublicSend
1333    end
1334
1335    self
1336  end
1337
1338  def can_leave_project?(project)
1339    project.namespace != namespace &&
1340      project.project_member(self)
1341  end
1342
1343  def full_website_url
1344    return "http://#{website_url}" if website_url !~ %r{\Ahttps?://}
1345
1346    website_url
1347  end
1348
1349  def short_website_url
1350    website_url.sub(%r{\Ahttps?://}, '')
1351  end
1352
1353  def all_ssh_keys
1354    keys.map(&:publishable_key)
1355  end
1356
1357  def temp_oauth_email?
1358    email.start_with?('temp-email-for-oauth')
1359  end
1360
1361  # rubocop: disable CodeReuse/ServiceClass
1362  def avatar_url(size: nil, scale: 2, **args)
1363    GravatarService.new.execute(email, size, scale, username: username)
1364  end
1365  # rubocop: enable CodeReuse/ServiceClass
1366
1367  def primary_email_verified?
1368    confirmed? && !temp_oauth_email?
1369  end
1370
1371  def accept_pending_invitations!
1372    pending_invitations.select do |member|
1373      member.accept_invite!(self)
1374    end
1375  end
1376
1377  def pending_invitations
1378    Member.where(invite_email: verified_emails).invite
1379  end
1380
1381  def all_emails(include_private_email: true)
1382    all_emails = []
1383    all_emails << email unless temp_oauth_email?
1384    all_emails << private_commit_email if include_private_email
1385    all_emails.concat(emails.map(&:email))
1386    all_emails.uniq
1387  end
1388
1389  def verified_emails(include_private_email: true)
1390    verified_emails = []
1391    verified_emails << email if primary_email_verified?
1392    verified_emails << private_commit_email if include_private_email
1393    verified_emails.concat(emails.confirmed.pluck(:email))
1394    verified_emails.uniq
1395  end
1396
1397  def public_verified_emails
1398    strong_memoize(:public_verified_emails) do
1399      emails = verified_emails(include_private_email: false)
1400      emails << email unless temp_oauth_email?
1401      emails.uniq
1402    end
1403  end
1404
1405  def any_email?(check_email)
1406    downcased = check_email.downcase
1407
1408    # handle the outdated private commit email case
1409    return true if persisted? &&
1410        id == Gitlab::PrivateCommitEmail.user_id_for_email(downcased)
1411
1412    all_emails.include?(check_email.downcase)
1413  end
1414
1415  def verified_email?(check_email)
1416    downcased = check_email.downcase
1417
1418    # handle the outdated private commit email case
1419    return true if persisted? &&
1420        id == Gitlab::PrivateCommitEmail.user_id_for_email(downcased)
1421
1422    verified_emails.include?(check_email.downcase)
1423  end
1424
1425  def hook_attrs
1426    {
1427      id: id,
1428      name: name,
1429      username: username,
1430      avatar_url: avatar_url(only_path: false),
1431      email: public_email.presence || _('[REDACTED]')
1432    }
1433  end
1434
1435  def ensure_namespace_correct
1436    if namespace
1437      namespace.path = username if username_changed?
1438      namespace.name = name if name_changed?
1439    else
1440      # TODO: we should no longer need the `type` parameter once we can make the
1441      #       the `has_one :namespace` association use the correct class.
1442      #       issue https://gitlab.com/gitlab-org/gitlab/-/issues/341070
1443      namespace = build_namespace(path: username, name: name, type: ::Namespaces::UserNamespace.sti_name)
1444      namespace.build_namespace_settings
1445    end
1446  end
1447
1448  def set_username_errors
1449    namespace_path_errors = self.errors.delete(:"namespace.path")
1450
1451    return unless namespace_path_errors&.any?
1452
1453    if namespace_path_errors.include?('has already been taken') && !User.exists?(username: username)
1454      self.errors.add(:base, :username_exists_as_a_different_namespace)
1455    else
1456      namespace_path_errors.each do |msg|
1457        self.errors.add(:username, msg)
1458      end
1459    end
1460  end
1461
1462  def username_changed_hook
1463    system_hook_service.execute_hooks_for(self, :rename)
1464  end
1465
1466  def post_destroy_hook
1467    log_info("User \"#{name}\" (#{email})  was removed")
1468
1469    system_hook_service.execute_hooks_for(self, :destroy)
1470  end
1471
1472  # rubocop: disable CodeReuse/ServiceClass
1473  def remove_key_cache
1474    Users::KeysCountService.new(self).delete_cache
1475  end
1476  # rubocop: enable CodeReuse/ServiceClass
1477
1478  def delete_async(deleted_by:, params: {})
1479    block if params[:hard_delete]
1480    DeleteUserWorker.perform_async(deleted_by.id, id, params.to_h)
1481  end
1482
1483  # rubocop: disable CodeReuse/ServiceClass
1484  def notification_service
1485    NotificationService.new
1486  end
1487  # rubocop: enable CodeReuse/ServiceClass
1488
1489  def log_info(message)
1490    Gitlab::AppLogger.info message
1491  end
1492
1493  # rubocop: disable CodeReuse/ServiceClass
1494  def system_hook_service
1495    SystemHooksService.new
1496  end
1497  # rubocop: enable CodeReuse/ServiceClass
1498
1499  def starred?(project)
1500    starred_projects.exists?(project.id)
1501  end
1502
1503  def toggle_star(project)
1504    UsersStarProject.transaction do
1505      user_star_project = users_star_projects
1506          .where(project: project, user: self).lock(true).first
1507
1508      if user_star_project
1509        user_star_project.destroy
1510      else
1511        UsersStarProject.create!(project: project, user: self)
1512      end
1513    end
1514  end
1515
1516  def following?(user)
1517    self.followees.exists?(user.id)
1518  end
1519
1520  def follow(user)
1521    return false if self.id == user.id
1522
1523    begin
1524      followee = Users::UserFollowUser.create(follower_id: self.id, followee_id: user.id)
1525      self.followees.reset if followee.persisted?
1526    rescue ActiveRecord::RecordNotUnique
1527      false
1528    end
1529  end
1530
1531  def unfollow(user)
1532    if Users::UserFollowUser.where(follower_id: self.id, followee_id: user.id).delete_all > 0
1533      self.followees.reset
1534    else
1535      false
1536    end
1537  end
1538
1539  def manageable_namespaces
1540    @manageable_namespaces ||= [namespace] + manageable_groups
1541  end
1542
1543  def manageable_groups(include_groups_with_developer_maintainer_access: false)
1544    owned_and_maintainer_group_hierarchy = if Feature.enabled?(:linear_user_manageable_groups, self, default_enabled: :yaml)
1545                                             owned_or_maintainers_groups.self_and_descendants
1546                                           else
1547                                             Gitlab::ObjectHierarchy.new(owned_or_maintainers_groups).base_and_descendants
1548                                           end
1549
1550    if include_groups_with_developer_maintainer_access
1551      union_sql = ::Gitlab::SQL::Union.new(
1552        [owned_and_maintainer_group_hierarchy,
1553         groups_with_developer_maintainer_project_access]).to_sql
1554
1555      ::Group.from("(#{union_sql}) #{::Group.table_name}")
1556    else
1557      owned_and_maintainer_group_hierarchy
1558    end
1559  end
1560
1561  def manageable_groups_with_routes(include_groups_with_developer_maintainer_access: false)
1562    manageable_groups(include_groups_with_developer_maintainer_access: include_groups_with_developer_maintainer_access)
1563      .eager_load(:route)
1564      .order('routes.path')
1565  end
1566
1567  def namespaces(owned_only: false)
1568    user_groups = owned_only ? owned_groups : groups
1569    personal_namespace = Namespace.where(id: namespace.id)
1570
1571    Namespace.from_union([user_groups, personal_namespace])
1572  end
1573
1574  def oauth_authorized_tokens
1575    Doorkeeper::AccessToken.where(resource_owner_id: id, revoked_at: nil)
1576  end
1577
1578  # Returns the projects a user contributed to in the last year.
1579  #
1580  # This method relies on a subquery as this performs significantly better
1581  # compared to a JOIN when coupled with, for example,
1582  # `Project.visible_to_user`. That is, consider the following code:
1583  #
1584  #     some_user.contributed_projects.visible_to_user(other_user)
1585  #
1586  # If this method were to use a JOIN the resulting query would take roughly 200
1587  # ms on a database with a similar size to GitLab.com's database. On the other
1588  # hand, using a subquery means we can get the exact same data in about 40 ms.
1589  def contributed_projects
1590    events = Event.select(:project_id)
1591      .contributions.where(author_id: self)
1592      .where("created_at > ?", Time.current - 1.year)
1593      .distinct
1594      .reorder(nil)
1595
1596    Project.where(id: events)
1597  end
1598
1599  def can_be_removed?
1600    !solo_owned_groups.present?
1601  end
1602
1603  def can_remove_self?
1604    true
1605  end
1606
1607  def ci_owned_runners
1608    @ci_owned_runners ||= begin
1609      project_runners = Ci::RunnerProject
1610        .where(project: authorized_projects(Gitlab::Access::MAINTAINER))
1611        .joins(:runner)
1612        .select('ci_runners.*')
1613
1614      group_runners = Ci::RunnerNamespace
1615        .where(namespace_id: owned_groups.self_and_descendant_ids)
1616        .joins(:runner)
1617        .select('ci_runners.*')
1618
1619      Ci::Runner.from_union([project_runners, group_runners]).allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336436')
1620    end
1621  end
1622
1623  def owns_runner?(runner)
1624    ::Gitlab::Database.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336436') do
1625      ci_owned_runners.exists?(runner.id)
1626    end
1627  end
1628
1629  def notification_email_for(notification_group)
1630    # Return group-specific email address if present, otherwise return global notification email address
1631    notification_group&.notification_email_for(self) || notification_email_or_default
1632  end
1633
1634  def notification_settings_for(source, inherit: false)
1635    if notification_settings.loaded?
1636      notification_settings.find do |notification|
1637        notification.source_type == source.class.base_class.name &&
1638          notification.source_id == source.id
1639      end
1640    else
1641      notification_settings.find_or_initialize_by(source: source) do |ns|
1642        next unless source.is_a?(Group) && inherit
1643
1644        # If we're here it means we're trying to create a NotificationSetting for a group that doesn't have one.
1645        # Find the closest parent with a notification_setting that's not Global level, or that has an email set.
1646        ancestor_ns = source
1647                        .notification_settings(hierarchy_order: :asc)
1648                        .where(user: self)
1649                        .find_by('level != ? OR notification_email IS NOT NULL', NotificationSetting.levels[:global])
1650        # Use it to seed the settings
1651        ns.assign_attributes(ancestor_ns&.slice(*NotificationSetting.allowed_fields))
1652        ns.source = source
1653        ns.user = self
1654      end
1655    end
1656  end
1657
1658  def notification_settings_for_groups(groups)
1659    ids = groups.is_a?(ActiveRecord::Relation) ? groups.select(:id) : groups.map(&:id)
1660    notification_settings.for_groups.where(source_id: ids)
1661  end
1662
1663  # Lazy load global notification setting
1664  # Initializes User setting with Participating level if setting not persisted
1665  def global_notification_setting
1666    return @global_notification_setting if defined?(@global_notification_setting)
1667
1668    @global_notification_setting = notification_settings.find_or_initialize_by(source: nil)
1669    @global_notification_setting.update(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
1670
1671    @global_notification_setting
1672  end
1673
1674  def assigned_open_merge_requests_count(force: false)
1675    Rails.cache.fetch(['users', id, 'assigned_open_merge_requests_count'], force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
1676      MergeRequestsFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count
1677    end
1678  end
1679
1680  def review_requested_open_merge_requests_count(force: false)
1681    Rails.cache.fetch(['users', id, 'review_requested_open_merge_requests_count'], force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
1682      MergeRequestsFinder.new(self, reviewer_id: id, state: 'opened', non_archived: true).execute.count
1683    end
1684  end
1685
1686  def assigned_open_issues_count(force: false)
1687    Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
1688      IssuesFinder.new(self, assignee_id: self.id, state: 'opened', non_archived: true).execute.count
1689    end
1690  end
1691
1692  def todos_done_count(force: false)
1693    Rails.cache.fetch(['users', id, 'todos_done_count'], force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
1694      TodosFinder.new(self, state: :done).execute.count
1695    end
1696  end
1697
1698  def todos_pending_count(force: false)
1699    Rails.cache.fetch(['users', id, 'todos_pending_count'], force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
1700      TodosFinder.new(self, state: :pending).execute.count
1701    end
1702  end
1703
1704  def personal_projects_count(force: false)
1705    Rails.cache.fetch(['users', id, 'personal_projects_count'], force: force, expires_in: 24.hours, raw: true) do
1706      personal_projects.count
1707    end.to_i
1708  end
1709
1710  def update_todos_count_cache
1711    todos_done_count(force: true)
1712    todos_pending_count(force: true)
1713  end
1714
1715  def invalidate_cache_counts
1716    invalidate_issue_cache_counts
1717    invalidate_merge_request_cache_counts
1718    invalidate_todos_cache_counts
1719    invalidate_personal_projects_count
1720  end
1721
1722  def invalidate_issue_cache_counts
1723    Rails.cache.delete(['users', id, 'assigned_open_issues_count'])
1724  end
1725
1726  def invalidate_merge_request_cache_counts
1727    Rails.cache.delete(['users', id, 'assigned_open_merge_requests_count'])
1728    Rails.cache.delete(['users', id, 'review_requested_open_merge_requests_count'])
1729  end
1730
1731  def invalidate_todos_cache_counts
1732    Rails.cache.delete(['users', id, 'todos_done_count'])
1733    Rails.cache.delete(['users', id, 'todos_pending_count'])
1734  end
1735
1736  def invalidate_personal_projects_count
1737    Rails.cache.delete(['users', id, 'personal_projects_count'])
1738  end
1739
1740  # This is copied from Devise::Models::Lockable#valid_for_authentication?, as our auth
1741  # flow means we don't call that automatically (and can't conveniently do so).
1742  #
1743  # See:
1744  #   <https://github.com/plataformatec/devise/blob/v4.7.1/lib/devise/models/lockable.rb#L104>
1745  #
1746  # rubocop: disable CodeReuse/ServiceClass
1747  def increment_failed_attempts!
1748    return if ::Gitlab::Database.read_only?
1749
1750    increment_failed_attempts
1751
1752    if attempts_exceeded?
1753      lock_access! unless access_locked?
1754    else
1755      Users::UpdateService.new(self, user: self).execute(validate: false)
1756    end
1757  end
1758  # rubocop: enable CodeReuse/ServiceClass
1759
1760  def access_level
1761    if admin?
1762      :admin
1763    else
1764      :regular
1765    end
1766  end
1767
1768  def access_level=(new_level)
1769    new_level = new_level.to_s
1770    return unless %w(admin regular).include?(new_level)
1771
1772    self.admin = (new_level == 'admin')
1773  end
1774
1775  def can_read_all_resources?
1776    can?(:read_all_resources)
1777  end
1778
1779  def can_admin_all_resources?
1780    can?(:admin_all_resources)
1781  end
1782
1783  def update_two_factor_requirement
1784    periods = expanded_groups_requiring_two_factor_authentication.pluck(:two_factor_grace_period)
1785
1786    self.require_two_factor_authentication_from_group = periods.any?
1787    self.two_factor_grace_period = periods.min || User.column_defaults['two_factor_grace_period']
1788
1789    save
1790  end
1791
1792  # each existing user needs to have a `feed_token`.
1793  # we do this on read since migrating all existing users is not a feasible
1794  # solution.
1795  def feed_token
1796    ensure_feed_token! unless Gitlab::CurrentSettings.disable_feed_token
1797  end
1798
1799  # Each existing user needs to have a `static_object_token`.
1800  # We do this on read since migrating all existing users is not a feasible
1801  # solution.
1802  def static_object_token
1803    ensure_static_object_token!
1804  end
1805
1806  def enabled_static_object_token
1807    static_object_token if Gitlab::CurrentSettings.static_objects_external_storage_enabled?
1808  end
1809
1810  def enabled_incoming_email_token
1811    incoming_email_token if Gitlab::IncomingEmail.supports_issue_creation?
1812  end
1813
1814  def sync_attribute?(attribute)
1815    return true if ldap_user? && attribute == :email
1816
1817    attributes = Gitlab.config.omniauth.sync_profile_attributes
1818
1819    if attributes.is_a?(Array)
1820      attributes.include?(attribute.to_s)
1821    else
1822      attributes
1823    end
1824  end
1825
1826  def read_only_attribute?(attribute)
1827    user_synced_attributes_metadata&.read_only?(attribute)
1828  end
1829
1830  # override, from Devise
1831  def lock_access!
1832    Gitlab::AppLogger.info("Account Locked: username=#{username}")
1833    super
1834  end
1835
1836  # Determine the maximum access level for a group of projects in bulk.
1837  #
1838  # Returns a Hash mapping project ID -> maximum access level.
1839  def max_member_access_for_project_ids(project_ids)
1840    max_member_access_for_resource_ids(Project, project_ids) do |project_ids|
1841      project_authorizations.where(project: project_ids)
1842                            .group(:project_id)
1843                            .maximum(:access_level)
1844    end
1845  end
1846
1847  def max_member_access_for_project(project_id)
1848    max_member_access_for_project_ids([project_id])[project_id]
1849  end
1850
1851  # Determine the maximum access level for a group of groups in bulk.
1852  #
1853  # Returns a Hash mapping project ID -> maximum access level.
1854  def max_member_access_for_group_ids(group_ids)
1855    max_member_access_for_resource_ids(Group, group_ids) do |group_ids|
1856      group_members.where(source: group_ids).group(:source_id).maximum(:access_level)
1857    end
1858  end
1859
1860  def max_member_access_for_group(group_id)
1861    max_member_access_for_group_ids([group_id])[group_id]
1862  end
1863
1864  def terms_accepted?
1865    return true if project_bot?
1866
1867    accepted_term_id.present?
1868  end
1869
1870  def required_terms_not_accepted?
1871    Gitlab::CurrentSettings.current_application_settings.enforce_terms? &&
1872      !terms_accepted?
1873  end
1874
1875  def requires_usage_stats_consent?
1876    self.admin? && 7.days.ago > self.created_at && !has_current_license? && User.single_user? && !consented_usage_stats?
1877  end
1878
1879  # Avoid migrations only building user preference object when needed.
1880  def user_preference
1881    super.presence || build_user_preference
1882  end
1883
1884  def user_detail
1885    super.presence || build_user_detail
1886  end
1887
1888  def pending_todo_for(target)
1889    todos.find_by(target: target, state: :pending)
1890  end
1891
1892  def password_expired?
1893    !!(password_expires_at && password_expires_at < Time.current)
1894  end
1895
1896  def password_expired_if_applicable?
1897    return false if bot?
1898    return false unless password_expired?
1899    return false if password_automatically_set?
1900    return false unless allow_password_authentication?
1901
1902    true
1903  end
1904
1905  def can_be_deactivated?
1906    active? && no_recent_activity? && !internal?
1907  end
1908
1909  def last_active_at
1910    last_activity = last_activity_on&.to_time&.in_time_zone
1911    last_sign_in = current_sign_in_at
1912
1913    [last_activity, last_sign_in].compact.max
1914  end
1915
1916  REQUIRES_ROLE_VALUE = 99
1917
1918  def role_required?
1919    role_before_type_cast == REQUIRES_ROLE_VALUE
1920  end
1921
1922  def set_role_required!
1923    update_column(:role, REQUIRES_ROLE_VALUE)
1924  end
1925
1926  def dismissed_callout?(feature_name:, ignore_dismissal_earlier_than: nil)
1927    callout = callouts_by_feature_name[feature_name]
1928
1929    callout_dismissed?(callout, ignore_dismissal_earlier_than)
1930  end
1931
1932  def dismissed_callout_for_group?(feature_name:, group:, ignore_dismissal_earlier_than: nil)
1933    source_feature_name = "#{feature_name}_#{group.id}"
1934    callout = group_callouts_by_feature_name[source_feature_name]
1935
1936    callout_dismissed?(callout, ignore_dismissal_earlier_than)
1937  end
1938
1939  # Load the current highest access by looking directly at the user's memberships
1940  def current_highest_access_level
1941    members.non_request.maximum(:access_level)
1942  end
1943
1944  def confirmation_required_on_sign_in?
1945    !confirmed? && !confirmation_period_valid?
1946  end
1947
1948  def impersonated?
1949    impersonator.present?
1950  end
1951
1952  def created_recently?
1953    created_at > Devise.confirm_within.ago
1954  end
1955
1956  def find_or_initialize_callout(feature_name)
1957    callouts.find_or_initialize_by(feature_name: ::Users::Callout.feature_names[feature_name])
1958  end
1959
1960  def find_or_initialize_group_callout(feature_name, group_id)
1961    group_callouts
1962      .find_or_initialize_by(feature_name: ::Users::GroupCallout.feature_names[feature_name], group_id: group_id)
1963  end
1964
1965  def can_trigger_notifications?
1966    confirmed? && !blocked? && !ghost?
1967  end
1968
1969  # This attribute hosts a Ci::JobToken::Scope object which is set when
1970  # the user is authenticated successfully via CI_JOB_TOKEN.
1971  def ci_job_token_scope
1972    Gitlab::SafeRequestStore[ci_job_token_scope_cache_key]
1973  end
1974
1975  def set_ci_job_token_scope!(job)
1976    Gitlab::SafeRequestStore[ci_job_token_scope_cache_key] = Ci::JobToken::Scope.new(job.project)
1977  end
1978
1979  def from_ci_job_token?
1980    ci_job_token_scope.present?
1981  end
1982
1983  # override from Devise::Confirmable
1984  #
1985  # Add the primary email to user.emails (or confirm it if it was already
1986  # present) when the primary email is confirmed.
1987  def confirm(*args)
1988    saved = super(*args)
1989    return false unless saved
1990
1991    email_to_confirm = self.emails.find_by(email: self.email)
1992
1993    if email_to_confirm.present?
1994      email_to_confirm.confirm(*args)
1995    else
1996      add_primary_email_to_emails!
1997    end
1998
1999    saved
2000  end
2001
2002  def user_project
2003    strong_memoize(:user_project) do
2004      personal_projects.find_by(path: username, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
2005    end
2006  end
2007
2008  def user_readme
2009    strong_memoize(:user_readme) do
2010      user_project&.repository&.readme
2011    end
2012  end
2013
2014  protected
2015
2016  # override, from Devise::Validatable
2017  def password_required?
2018    return false if internal? || project_bot?
2019
2020    super
2021  end
2022
2023  # override from Devise::Confirmable
2024  def confirmation_period_valid?
2025    return false if Feature.disabled?(:soft_email_confirmation)
2026
2027    super
2028  end
2029
2030  # This is copied from Devise::Models::TwoFactorAuthenticatable#consume_otp!
2031  #
2032  # An OTP cannot be used more than once in a given timestep
2033  # Storing timestep of last valid OTP is sufficient to satisfy this requirement
2034  #
2035  # See:
2036  #   <https://github.com/tinfoil/devise-two-factor/blob/master/lib/devise_two_factor/models/two_factor_authenticatable.rb#L66>
2037  #
2038  def consume_otp!
2039    if self.consumed_timestep != current_otp_timestep
2040      self.consumed_timestep = current_otp_timestep
2041      return Gitlab::Database.read_only? ? true : save(validate: false)
2042    end
2043
2044    false
2045  end
2046
2047  private
2048
2049  # To enable JiHu repository to modify the default language options
2050  def default_preferred_language
2051    'en'
2052  end
2053
2054  # rubocop: disable CodeReuse/ServiceClass
2055  def add_primary_email_to_emails!
2056    Emails::CreateService.new(self, user: self, email: self.email).execute(confirmed_at: self.confirmed_at)
2057  end
2058  # rubocop: enable CodeReuse/ServiceClass
2059
2060  def notification_email_verified
2061    return if notification_email.blank? || temp_oauth_email?
2062
2063    errors.add(:notification_email, _("must be an email you have verified")) unless verified_emails.include?(notification_email_or_default)
2064  end
2065
2066  def public_email_verified
2067    return if public_email.blank?
2068
2069    errors.add(:public_email, _("must be an email you have verified")) unless verified_emails.include?(public_email)
2070  end
2071
2072  def commit_email_verified
2073    return if commit_email.blank?
2074
2075    errors.add(:commit_email, _("must be an email you have verified")) unless verified_emails.include?(commit_email_or_default)
2076  end
2077
2078  def callout_dismissed?(callout, ignore_dismissal_earlier_than)
2079    return false unless callout
2080    return callout.dismissed_after?(ignore_dismissal_earlier_than) if ignore_dismissal_earlier_than
2081
2082    true
2083  end
2084
2085  def callouts_by_feature_name
2086    @callouts_by_feature_name ||= callouts.index_by(&:feature_name)
2087  end
2088
2089  def group_callouts_by_feature_name
2090    @group_callouts_by_feature_name ||= group_callouts.index_by(&:source_feature_name)
2091  end
2092
2093  def authorized_groups_without_shared_membership
2094    Group.from_union([
2095      groups.select(Namespace.arel_table[Arel.star]),
2096      authorized_projects.joins(:namespace).select(Namespace.arel_table[Arel.star])
2097    ])
2098  end
2099
2100  def authorized_groups_with_shared_membership
2101    cte = Gitlab::SQL::CTE.new(:direct_groups, authorized_groups_without_shared_membership)
2102    cte_alias = cte.table.alias(Group.table_name)
2103
2104    Group
2105      .with(cte.to_arel)
2106      .from_union([
2107        Group.from(cte_alias),
2108        Group.joins(:shared_with_group_links)
2109             .where(group_group_links: { shared_with_group_id: Group.from(cte_alias) })
2110    ])
2111  end
2112
2113  def default_private_profile_to_false
2114    return unless private_profile_changed? && private_profile.nil?
2115
2116    self.private_profile = false
2117  end
2118
2119  def has_current_license?
2120    false
2121  end
2122
2123  def consented_usage_stats?
2124    # Bypass the cache here because it's possible the admin enabled the
2125    # usage ping, and we don't want to annoy the user again if they
2126    # already set the value. This is a bit of hack, but the alternative
2127    # would be to put in a more complex cache invalidation step. Since
2128    # this call only gets called in the uncommon situation where the
2129    # user is an admin and the only user in the instance, this shouldn't
2130    # cause too much load on the system.
2131    ApplicationSetting.current_without_cache&.usage_stats_set_by_user_id == self.id
2132  end
2133
2134  def ensure_user_rights_and_limits
2135    if external?
2136      self.can_create_group = false
2137      self.projects_limit   = 0
2138    else
2139      # Only revert these back to the default if they weren't specifically changed in this update.
2140      self.can_create_group = gitlab_config.default_can_create_group unless can_create_group_changed?
2141      self.projects_limit = Gitlab::CurrentSettings.default_projects_limit unless projects_limit_changed?
2142    end
2143  end
2144
2145  def signup_email_valid?
2146    error = validate_admin_signup_restrictions(email)
2147
2148    errors.add(:email, error) if error
2149  end
2150
2151  def signup_email_invalid_message
2152    _('is not allowed for sign-up.')
2153  end
2154
2155  def check_username_format
2156    return if username.blank? || Mime::EXTENSION_LOOKUP.keys.none? { |type| username.end_with?(".#{type}") }
2157
2158    errors.add(:username, _('ending with a reserved file extension is not allowed.'))
2159  end
2160
2161  def groups_with_developer_maintainer_project_access
2162    project_creation_levels = [::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS]
2163
2164    if ::Gitlab::CurrentSettings.default_project_creation == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS
2165      project_creation_levels << nil
2166    end
2167
2168    developer_groups.self_and_descendants.where(project_creation_level: project_creation_levels)
2169  end
2170
2171  def no_recent_activity?
2172    last_active_at.to_i <= MINIMUM_INACTIVE_DAYS.days.ago.to_i
2173  end
2174
2175  def update_highest_role?
2176    return false unless persisted?
2177
2178    (previous_changes.keys & %w(state user_type)).any?
2179  end
2180
2181  def update_highest_role_attribute
2182    id
2183  end
2184
2185  def ci_job_token_scope_cache_key
2186    "users:#{id}:ci:job_token_scope"
2187  end
2188
2189  # An `ldap_blocked` user will be unblocked if LDAP indicates they are allowed.
2190  def check_ldap_if_ldap_blocked!
2191    return unless ::Gitlab::Auth::Ldap::Config.enabled? && ldap_blocked?
2192
2193    ::Gitlab::Auth::Ldap::Access.allowed?(self)
2194  end
2195end
2196
2197User.prepend_mod_with('User')
2198