Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/user.rb')
-rw-r--r--app/models/user.rb56
1 files changed, 43 insertions, 13 deletions
diff --git a/app/models/user.rb b/app/models/user.rb
index c36898aaf70..c9873975cc9 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -151,7 +151,7 @@ class User < MainClusterwide::ApplicationRecord
# Namespace for personal projects
has_one :namespace,
-> { where(type: Namespaces::UserNamespace.sti_name) },
- required: true,
+ required: false,
dependent: :destroy, # rubocop:disable Cop/ActiveRecordDependent
foreign_key: :owner_id,
inverse_of: :owner,
@@ -270,7 +270,8 @@ class User < MainClusterwide::ApplicationRecord
belongs_to :accepted_term, class_name: 'ApplicationSetting::Term'
has_many :organization_users, class_name: 'Organizations::OrganizationUser', inverse_of: :user
- has_many :organizations, through: :organization_users, class_name: 'Organizations::Organization', inverse_of: :users
+ has_many :organizations, through: :organization_users, class_name: 'Organizations::Organization', inverse_of: :users,
+ disable_joins: true
has_one :status, class_name: 'UserStatus'
has_one :user_preference
@@ -284,8 +285,6 @@ class User < MainClusterwide::ApplicationRecord
has_many :reviews, foreign_key: :author_id, inverse_of: :author
- has_many :in_product_marketing_emails, class_name: '::Users::InProductMarketingEmail'
-
has_many :timelogs
has_many :resource_label_events, dependent: :nullify # rubocop:disable Cop/ActiveRecordDependent
@@ -304,6 +303,10 @@ class User < MainClusterwide::ApplicationRecord
# Validations
#
# Note: devise :validatable above adds validations for :email and :password
+ validates :username,
+ presence: true,
+ exclusion: { in: Gitlab::PathRegex::TOP_LEVEL_ROUTES, message: N_('%{value} is a reserved name') }
+ validates :username, uniqueness: true, unless: :namespace
validates :name, presence: true, length: { maximum: 255 }
validates :first_name, length: { maximum: 127 }
validates :last_name, length: { maximum: 127 }
@@ -314,10 +317,9 @@ class User < MainClusterwide::ApplicationRecord
validates :projects_limit,
presence: true,
numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Gitlab::Database::MAX_INT_VALUE }
- validates :username, presence: true
validate :check_password_weakness, if: :encrypted_password_changed?
- validates :namespace, presence: true
+ validates :namespace, presence: true, unless: :optional_namespace?
validate :namespace_move_dir_allowed, if: :username_changed?, unless: :new_record?
validate :unique_email, if: :email_changed?
@@ -591,6 +593,8 @@ class User < MainClusterwide::ApplicationRecord
scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) }
scope :order_recent_last_activity, -> { reorder(arel_table[:last_activity_on].desc.nulls_last, arel_table[:id].asc) }
scope :order_oldest_last_activity, -> { reorder(arel_table[:last_activity_on].asc.nulls_first, arel_table[:id].desc) }
+ scope :ordered_by_id_desc, -> { reorder(arel_table[:id].desc) }
+
scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', Gitlab::CurrentSettings.deactivate_dormant_users_period.day.ago.to_date) }
scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil).where('created_at <= ?', MINIMUM_DAYS_CREATED.day.ago.to_date) }
scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
@@ -847,6 +851,25 @@ class User < MainClusterwide::ApplicationRecord
scope.reorder(order)
end
+ # This should be kept in sync with the frontend filtering in
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/5d34e3488faa3982d30d7207773991c1e0b6368a/app/assets/javascripts/gfm_auto_complete.js#L68 and
+ # https://gitlab.com/gitlab-org/gitlab/-/blob/5d34e3488faa3982d30d7207773991c1e0b6368a/app/assets/javascripts/gfm_auto_complete.js#L1053
+ def gfm_autocomplete_search(query)
+ where(
+ "REPLACE(users.name, ' ', '') ILIKE :pattern OR users.username ILIKE :pattern",
+ pattern: "%#{sanitize_sql_like(query)}%"
+ ).order(
+ Arel.sql(sanitize_sql(
+ [
+ "CASE WHEN starts_with(REPLACE(users.name, ' ', ''), :pattern) OR starts_with(users.username, :pattern) THEN 1 ELSE 2 END",
+ { pattern: query }
+ ]
+ )),
+ :username,
+ :id
+ )
+ end
+
# Limits the result set to users _not_ in the given query/list of IDs.
#
# users - The list of users to ignore. This can be an
@@ -1302,7 +1325,13 @@ class User < MainClusterwide::ApplicationRecord
end
def can_create_project?
- projects_limit_left > 0
+ projects_limit_left > 0 && allow_user_to_create_group_and_project?
+ end
+
+ def allow_user_to_create_group_and_project?
+ return true if Gitlab::CurrentSettings.allow_project_creation_for_guest_and_below
+
+ highest_role > Gitlab::Access::GUEST
end
def can_create_group?
@@ -1596,12 +1625,6 @@ class User < MainClusterwide::ApplicationRecord
if namespace
namespace.path = username if username_changed?
namespace.name = name if name_changed?
- elsif Feature.disabled?(:create_personal_ns_outside_model, Feature.current_request)
- # TODO: we should no longer need the `type` parameter once we can make the
- # the `has_one :namespace` association use the correct class.
- # issue https://gitlab.com/gitlab-org/gitlab/-/issues/341070
- namespace = build_namespace(path: username, name: name, type: ::Namespaces::UserNamespace.sti_name)
- namespace.build_namespace_settings
end
end
@@ -1623,6 +1646,9 @@ class User < MainClusterwide::ApplicationRecord
self.errors.add(:base, :username_exists_as_a_different_namespace)
else
namespace_path_errors.each do |msg|
+ # Already handled by username validation.
+ next if msg.ends_with?('is a reserved name')
+
self.errors.add(:username, msg)
end
end
@@ -2300,6 +2326,10 @@ class User < MainClusterwide::ApplicationRecord
private
+ def optional_namespace?
+ Feature.enabled?(:optional_personal_namespace, self)
+ end
+
def block_or_ban
user_scores = Abuse::UserTrustScore.new(self)
if user_scores.spammer? && account_age_in_days < 7