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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-20 12:55:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-20 12:55:51 +0300
commite8d2c2579383897a1dd7f9debd359abe8ae8373d (patch)
treec42be41678c2586d49a75cabce89322082698334 /app/services/members
parentfc845b37ec3a90aaa719975f607740c22ba6a113 (diff)
Add latest changes from gitlab-org/gitlab@14-1-stable-eev14.1.0-rc42
Diffstat (limited to 'app/services/members')
-rw-r--r--app/services/members/creator_service.rb172
-rw-r--r--app/services/members/groups/creator_service.rb17
-rw-r--r--app/services/members/invite_service.rb2
-rw-r--r--app/services/members/projects/creator_service.rb22
4 files changed, 212 insertions, 1 deletions
diff --git a/app/services/members/creator_service.rb b/app/services/members/creator_service.rb
new file mode 100644
index 00000000000..f6972f81162
--- /dev/null
+++ b/app/services/members/creator_service.rb
@@ -0,0 +1,172 @@
+# frozen_string_literal: true
+
+module Members
+ # This class serves as more of an app-wide way we add/create members
+ # All roads to add members should take this path.
+ class CreatorService
+ class << self
+ def parsed_access_level(access_level)
+ access_levels.fetch(access_level) { access_level.to_i }
+ end
+
+ def access_levels
+ raise NotImplementedError
+ end
+
+ def add_users(source, users, access_level, current_user: nil, expires_at: nil)
+ return [] unless users.present?
+
+ emails, users, existing_members = parse_users_list(source, users)
+
+ Member.transaction do
+ (emails + users).map! do |user|
+ new(source,
+ user,
+ access_level,
+ existing_members: existing_members,
+ current_user: current_user,
+ expires_at: expires_at)
+ .execute
+ end
+ end
+ end
+
+ private
+
+ def parse_users_list(source, list)
+ emails = []
+ user_ids = []
+ users = []
+ existing_members = {}
+
+ list.each do |item|
+ case item
+ when User
+ users << item
+ when Integer
+ user_ids << item
+ when /\A\d+\Z/
+ user_ids << item.to_i
+ when Devise.email_regexp
+ emails << item
+ end
+ end
+
+ if user_ids.present?
+ users.concat(User.id_in(user_ids))
+ # the below will automatically discard invalid user_ids
+ existing_members = source.members_and_requesters.where(user_id: user_ids).index_by(&:user_id) # rubocop:todo CodeReuse/ActiveRecord
+ end
+
+ [emails, users, existing_members]
+ end
+ end
+
+ def initialize(source, user, access_level, **args)
+ @source = source
+ @user = user
+ @access_level = self.class.parsed_access_level(access_level)
+ @args = args
+ end
+
+ def execute
+ find_or_build_member
+ update_member
+
+ member
+ end
+
+ private
+
+ attr_reader :source, :user, :access_level, :member, :args
+
+ def update_member
+ return unless can_update_member?
+
+ member.attributes = member_attributes
+
+ if member.request?
+ approve_request
+ else
+ member.save
+ end
+ end
+
+ def can_update_member?
+ # There is no current user for bulk actions, in which case anything is allowed
+ !current_user # inheriting classes will add more logic
+ end
+
+ # Populates the attributes of a member.
+ #
+ # This logic resides in a separate method so that EE can extend this logic,
+ # without having to patch the `add_user` method directly.
+ def member_attributes
+ {
+ created_by: member.created_by || current_user,
+ access_level: access_level,
+ expires_at: args[:expires_at]
+ }
+ end
+
+ def approve_request
+ ::Members::ApproveAccessRequestService.new(current_user,
+ access_level: access_level)
+ .execute(
+ member,
+ skip_authorization: ldap,
+ skip_log_audit_event: ldap
+ )
+ end
+
+ def current_user
+ args[:current_user]
+ end
+
+ def find_or_build_member
+ @user = parse_user_param
+
+ @member = if user.is_a?(User)
+ find_or_initialize_member_by_user
+ else
+ source.members.build(invite_email: user)
+ end
+ end
+
+ # This method is used to find users that have been entered into the "Add members" field.
+ # These can be the User objects directly, their IDs, their emails, or new emails to be invited.
+ def parse_user_param
+ case user
+ when User
+ user
+ when Integer
+ # might not return anything - this needs enhancement
+ User.find_by(id: user) # rubocop:todo CodeReuse/ActiveRecord
+ else
+ # must be an email or at least we'll consider it one
+ User.find_by_any_email(user) || user
+ end
+ end
+
+ def find_or_initialize_member_by_user
+ if existing_members
+ # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/334062
+ # i'm not so sure this is needed as the parse_users_list looks at members_and_requesters...
+ # so it is like we could just do a find or initialize by here and be fine
+ existing_members[user.id] || source.members.build(user_id: user.id)
+ else
+ source.members_and_requesters.find_or_initialize_by(user_id: user.id) # rubocop:todo CodeReuse/ActiveRecord
+ end
+ end
+
+ def existing_members
+ args[:existing_members]
+ end
+
+ def ldap
+ args[:ldap] || false
+ end
+ end
+end
+
+Members::CreatorService.prepend_mod_with('Members::CreatorService')
diff --git a/app/services/members/groups/creator_service.rb b/app/services/members/groups/creator_service.rb
new file mode 100644
index 00000000000..df4d3f59d3b
--- /dev/null
+++ b/app/services/members/groups/creator_service.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Members
+ module Groups
+ class CreatorService < Members::CreatorService
+ def self.access_levels
+ Gitlab::Access.sym_options_with_owner
+ end
+
+ private
+
+ def can_update_member?
+ super || current_user.can?(:update_group_member, member)
+ end
+ end
+ end
+end
diff --git a/app/services/members/invite_service.rb b/app/services/members/invite_service.rb
index 48010f9c8e7..6298943977b 100644
--- a/app/services/members/invite_service.rb
+++ b/app/services/members/invite_service.rb
@@ -21,7 +21,7 @@ module Members
def validate_invites!
super
- # we need the below due to add_users hitting Member#parse_users_list and ignoring invalid emails
+ # we need the below due to add_users hitting Members::CreatorService.parse_users_list and ignoring invalid emails
# ideally we wouldn't need this, but we can't really change the add_users method
valid, invalid = invites.partition { |email| Member.valid_email?(email) }
@invites = valid
diff --git a/app/services/members/projects/creator_service.rb b/app/services/members/projects/creator_service.rb
new file mode 100644
index 00000000000..2e974177075
--- /dev/null
+++ b/app/services/members/projects/creator_service.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Members
+ module Projects
+ class CreatorService < Members::CreatorService
+ def self.access_levels
+ Gitlab::Access.sym_options
+ end
+
+ private
+
+ def can_update_member?
+ super || current_user.can?(:update_project_member, member) || adding_a_new_owner?
+ end
+
+ def adding_a_new_owner?
+ # this condition is reached during testing setup a lot due to use of `.add_user`
+ member.owner? && member.new_record?
+ end
+ end
+ end
+end