diff options
Diffstat (limited to 'app/models/user.rb')
-rw-r--r-- | app/models/user.rb | 149 |
1 files changed, 105 insertions, 44 deletions
diff --git a/app/models/user.rb b/app/models/user.rb index a39da30220a..a587723053f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -48,7 +48,7 @@ class User < ApplicationRecord add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) } add_authentication_token_field :feed_token - add_authentication_token_field :static_object_token + add_authentication_token_field :static_object_token, encrypted: :optional default_value_for :admin, false default_value_for(:external) { Gitlab::CurrentSettings.user_default_external } @@ -81,6 +81,7 @@ class User < ApplicationRecord # This module adds async behaviour to Devise emails # and should be added after Devise modules are initialized. include AsyncDeviseEmail + include ForcedEmailConfirmation MINIMUM_INACTIVE_DAYS = 90 @@ -250,7 +251,7 @@ class User < ApplicationRecord validate :notification_email_verified, if: :notification_email_changed? validate :public_email_verified, if: :public_email_changed? validate :commit_email_verified, if: :commit_email_changed? - validate :signup_email_valid?, on: :create, if: ->(user) { !user.created_by_id } + validate :email_allowed_by_restrictions?, if: ->(user) { user.new_record? ? !user.created_by_id : user.email_changed? } validate :check_username_format, if: :username_changed? validates :theme_id, allow_nil: true, inclusion: { in: Gitlab::Themes.valid_ids, @@ -330,6 +331,7 @@ class User < ApplicationRecord delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true delegate :pronunciation, :pronunciation=, to: :user_detail, allow_nil: true delegate :registration_objective, :registration_objective=, to: :user_detail, allow_nil: true + delegate :requires_credit_card_verification, :requires_credit_card_verification=, to: :user_detail, allow_nil: true accepts_nested_attributes_for :user_preference, update_only: true accepts_nested_attributes_for :user_detail, update_only: true @@ -465,7 +467,7 @@ class User < ApplicationRecord scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) } scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil) } scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) } - scope :get_ids_by_username, -> (username) { where(username: username).pluck(:id) } + scope :by_ids_or_usernames, -> (ids, usernames) { where(username: usernames).or(where(id: ids)) } strip_attributes! :name @@ -536,27 +538,15 @@ class User < ApplicationRecord end def self.with_two_factor - with_u2f_registrations = <<-SQL - EXISTS ( - SELECT * - FROM u2f_registrations AS u2f - WHERE u2f.user_id = users.id - ) OR users.otp_required_for_login = ? - OR - EXISTS ( - SELECT * - FROM webauthn_registrations AS webauthn - WHERE webauthn.user_id = users.id - ) - SQL - - where(with_u2f_registrations, true) + where(otp_required_for_login: true) + .or(where_exists(U2fRegistration.where(U2fRegistration.arel_table[:user_id].eq(arel_table[:id])))) + .or(where_exists(WebauthnRegistration.where(WebauthnRegistration.arel_table[:user_id].eq(arel_table[:id])))) end def self.without_two_factor - joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id - LEFT OUTER JOIN webauthn_registrations AS webauthn ON webauthn.user_id = users.id") - .where("u2f.id IS NULL AND webauthn.id IS NULL AND users.otp_required_for_login = ?", false) + where + .missing(:u2f_registrations, :webauthn_registrations) + .where(otp_required_for_login: false) end # @@ -720,13 +710,19 @@ class User < ApplicationRecord .take(1) # at most 1 record as there is a unique constraint where( - fuzzy_arel_match(:name, query) - .or(fuzzy_arel_match(:username, query)) + fuzzy_arel_match(:name, query, use_minimum_char_limit: user_search_minimum_char_limit) + .or(fuzzy_arel_match(:username, query, use_minimum_char_limit: user_search_minimum_char_limit)) .or(arel_table[:email].eq(query)) .or(arel_table[:id].eq(matched_by_email_user_id)) ) end + # This method is overridden in JiHu. + # https://gitlab.com/gitlab-org/gitlab/-/issues/348509 + def user_search_minimum_char_limit + true + end + def by_login(login) return unless login @@ -841,6 +837,10 @@ class User < ApplicationRecord def single_user User.non_internal.first if single_user? end + + def get_ids_by_ids_or_usernames(ids, usernames) + by_ids_or_usernames(ids, usernames).pluck(:id) + end end # @@ -1337,7 +1337,7 @@ class User < ApplicationRecord def can_leave_project?(project) project.namespace != namespace && - project.project_member(self) + project.member(self) end def full_website_url @@ -1536,8 +1536,8 @@ class User < ApplicationRecord end end - def manageable_namespaces - @manageable_namespaces ||= [namespace] + manageable_groups + def forkable_namespaces + @forkable_namespaces ||= [namespace] + manageable_groups(include_groups_with_developer_maintainer_access: true) end def manageable_groups(include_groups_with_developer_maintainer_access: false) @@ -1606,23 +1606,32 @@ class User < ApplicationRecord def ci_owned_runners @ci_owned_runners ||= begin - project_runners = Ci::RunnerProject - .where(project: authorized_projects(Gitlab::Access::MAINTAINER)) - .joins(:runner) - .select('ci_runners.*') - - group_runners = Ci::RunnerNamespace - .where(namespace_id: owned_groups.self_and_descendant_ids) - .joins(:runner) - .select('ci_runners.*') - - Ci::Runner.from_union([project_runners, group_runners]).allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336436') + if ci_owned_runners_cross_joins_fix_enabled? + Ci::Runner + .from_union([ci_owned_project_runners_from_project_members, + ci_owned_project_runners_from_group_members, + ci_owned_group_runners]) + else + Ci::Runner + .from_union([ci_legacy_owned_project_runners, ci_legacy_owned_group_runners]) + .allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336436') + end end end def owns_runner?(runner) - ::Gitlab::Database.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336436') do + if ci_owned_runners_cross_joins_fix_enabled? ci_owned_runners.exists?(runner.id) + else + ::Gitlab::Database.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336436') do + ci_owned_runners.exists?(runner.id) + end + end + end + + def ci_owned_runners_cross_joins_fix_enabled? + strong_memoize(:ci_owned_runners_cross_joins_fix_enabled) do + Feature.enabled?(:ci_owned_runners_cross_joins_fix, self, default_enabled: :yaml) end end @@ -1902,6 +1911,10 @@ class User < ApplicationRecord true end + def can_log_in_with_non_expired_password? + can?(:log_in) && !password_expired_if_applicable? + end + def can_be_deactivated? active? && no_recent_activity? && !internal? end @@ -1980,18 +1993,22 @@ class User < ApplicationRecord ci_job_token_scope.present? end - # override from Devise::Confirmable + # override from Devise::Models::Confirmable # # Add the primary email to user.emails (or confirm it if it was already # present) when the primary email is confirmed. - def confirm(*args) - saved = super(*args) + def confirm(args = {}) + saved = super(args) return false unless saved email_to_confirm = self.emails.find_by(email: self.email) if email_to_confirm.present? - email_to_confirm.confirm(*args) + if skip_confirmation_period_expiry_check + email_to_confirm.force_confirm(args) + else + email_to_confirm.confirm(args) + end else add_primary_email_to_emails! end @@ -2142,14 +2159,14 @@ class User < ApplicationRecord end end - def signup_email_valid? + def email_allowed_by_restrictions? error = validate_admin_signup_restrictions(email) errors.add(:email, error) if error end def signup_email_invalid_message - _('is not allowed for sign-up.') + self.new_record? ? _('is not allowed for sign-up.') : _('is not allowed.') end def check_username_format @@ -2192,6 +2209,50 @@ class User < ApplicationRecord ::Gitlab::Auth::Ldap::Access.allowed?(self) end + + def ci_legacy_owned_project_runners + Ci::RunnerProject + .select('ci_runners.*') + .joins(:runner) + .where(project: authorized_projects(Gitlab::Access::MAINTAINER)) + end + + def ci_legacy_owned_group_runners + Ci::RunnerNamespace + .select('ci_runners.*') + .joins(:runner) + .where(namespace_id: owned_groups.self_and_descendant_ids) + end + + def ci_owned_project_runners_from_project_members + Ci::RunnerProject + .select('ci_runners.*') + .joins(:runner) + .where(project: project_members.where('access_level >= ?', Gitlab::Access::MAINTAINER).pluck(:source_id)) + end + + def ci_owned_project_runners_from_group_members + Ci::RunnerProject + .select('ci_runners.*') + .joins(:runner) + .joins('JOIN ci_project_mirrors ON ci_project_mirrors.project_id = ci_runner_projects.project_id') + .joins('JOIN ci_namespace_mirrors ON ci_namespace_mirrors.namespace_id = ci_project_mirrors.namespace_id') + .merge(ci_namespace_mirrors_for_group_members(Gitlab::Access::MAINTAINER)) + end + + def ci_owned_group_runners + Ci::RunnerNamespace + .select('ci_runners.*') + .joins(:runner) + .joins('JOIN ci_namespace_mirrors ON ci_namespace_mirrors.namespace_id = ci_runner_namespaces.namespace_id') + .merge(ci_namespace_mirrors_for_group_members(Gitlab::Access::OWNER)) + end + + def ci_namespace_mirrors_for_group_members(level) + Ci::NamespaceMirror.contains_any_of_namespaces( + group_members.where('access_level >= ?', level).pluck(:source_id) + ) + end end User.prepend_mod_with('User') |