summaryrefslogtreecommitdiff
path: root/app/models/user.rb
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-02-27 12:30:26 +0000
committerFilipa Lacerda <filipa@gitlab.com>2017-02-27 12:30:26 +0000
commit729d2ea04d24c068519515a4df6d4c38f25cd229 (patch)
tree57048bfd961acd44b0f278daf215b6e141fbd021 /app/models/user.rb
parentf95d46c9d22603445fc7b8247df1120eaed67cd1 (diff)
parentc425f366bfa84efab92b5d5e1d0721f16a2890bc (diff)
downloadgitlab-ce-729d2ea04d24c068519515a4df6d4c38f25cd229.tar.gz
Merge branch 'master' into ci-tables-ui-improvementsci-tables-ui-improvements
* master: (196 commits) Add quotes to coverage pattern Update CHANGELOG.md Bump omniauth to 1.4.2 Bump Hashie to 3.5.5 to eliminate warning noise use single backticks for inline code Add performance query regression fix for !9088 affecting #27267 Fix spec API: Return 400 for all validation erros in the mebers API Adds confirmation for cancel button Add newline Corrected indentation on the template string Adds backoff algo from EE to CE We don't need these checks anymore Raise error when no content is provided Address review Update API v3 in line with v4 Fix new offenses Fix spec Fix specs Rename commit_file, commit_dir and remove_file and update specs ...
Diffstat (limited to 'app/models/user.rb')
-rw-r--r--app/models/user.rb63
1 files changed, 58 insertions, 5 deletions
diff --git a/app/models/user.rb b/app/models/user.rb
index f614eb66e1f..40264401b53 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -81,7 +81,6 @@ class User < ActiveRecord::Base
has_many :authorized_projects, through: :project_authorizations, source: :project
has_many :snippets, dependent: :destroy, foreign_key: :author_id
- has_many :issues, dependent: :destroy, foreign_key: :author_id
has_many :notes, dependent: :destroy, foreign_key: :author_id
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
has_many :events, dependent: :destroy, foreign_key: :author_id
@@ -99,12 +98,17 @@ class User < ActiveRecord::Base
has_many :assigned_issues, dependent: :nullify, foreign_key: :assignee_id, class_name: "Issue"
has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest"
+ # Issues that a user owns are expected to be moved to the "ghost" user before
+ # the user is destroyed. If the user owns any issues during deletion, this
+ # should be treated as an exceptional condition.
+ has_many :issues, dependent: :restrict_with_exception, foreign_key: :author_id
+
#
# Validations
#
# Note: devise :validatable above adds validations for :email and :password
validates :name, presence: true
- validates_confirmation_of :email
+ validates :email, confirmation: true
validates :notification_email, presence: true
validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
@@ -120,6 +124,7 @@ class User < ActiveRecord::Base
validate :unique_email, if: ->(user) { user.email_changed? }
validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
validate :owns_public_email, if: ->(user) { user.public_email_changed? }
+ validate :ghost_users_must_be_blocked
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :generate_password, on: :create
@@ -334,9 +339,15 @@ class User < ActiveRecord::Base
def reference_pattern
%r{
#{Regexp.escape(reference_prefix)}
- (?<user>#{Gitlab::Regex::NAMESPACE_REF_REGEX_STR})
+ (?<user>#{Gitlab::Regex::FULL_NAMESPACE_REGEX_STR})
}x
end
+
+ # Return (create if necessary) the ghost user. The ghost user
+ # owns records previously belonging to deleted users.
+ def ghost
+ User.find_by_ghost(true) || create_ghost_user
+ end
end
#
@@ -435,6 +446,12 @@ class User < ActiveRecord::Base
errors.add(:public_email, "is not an email you own") unless all_emails.include?(public_email)
end
+ def ghost_users_must_be_blocked
+ if ghost? && !blocked?
+ errors.add(:ghost, 'cannot be enabled for a user who is not blocked.')
+ end
+ end
+
def update_emails_with_primary_email
primary_email_record = emails.find_by(email: email)
if primary_email_record
@@ -580,8 +597,8 @@ class User < ActiveRecord::Base
if project.repository.branch_exists?(event.branch_name)
merge_requests = MergeRequest.where("created_at >= ?", event.created_at).
- where(source_project_id: project.id,
- source_branch: event.branch_name)
+ where(source_project_id: project.id,
+ source_branch: event.branch_name)
merge_requests.empty?
end
end
@@ -999,4 +1016,40 @@ class User < ActiveRecord::Base
super
end
end
+
+ def self.create_ghost_user
+ # Since we only want a single ghost user in an instance, we use an
+ # exclusive lease to ensure than this block is never run concurrently.
+ lease_key = "ghost_user_creation"
+ lease = Gitlab::ExclusiveLease.new(lease_key, timeout: 1.minute.to_i)
+
+ until uuid = lease.try_obtain
+ # Keep trying until we obtain the lease. To prevent hammering Redis too
+ # much we'll wait for a bit between retries.
+ sleep(1)
+ end
+
+ # Recheck if a ghost user is already present. One might have been
+ # added between the time we last checked (first line of this method)
+ # and the time we acquired the lock.
+ ghost_user = User.find_by_ghost(true)
+ return ghost_user if ghost_user.present?
+
+ uniquify = Uniquify.new
+
+ username = uniquify.string("ghost") { |s| User.find_by_username(s) }
+
+ email = uniquify.string(-> (n) { "ghost#{n}@example.com" }) do |s|
+ User.find_by_email(s)
+ end
+
+ bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.'
+
+ User.create(
+ username: username, password: Devise.friendly_token, bio: bio,
+ email: email, name: "Ghost User", state: :blocked, ghost: true
+ )
+ ensure
+ Gitlab::ExclusiveLease.cancel(lease_key, uuid)
+ end
end