diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-20 09:55:51 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-20 09:55:51 +0000 |
commit | e8d2c2579383897a1dd7f9debd359abe8ae8373d (patch) | |
tree | c42be41678c2586d49a75cabce89322082698334 /app/services/members | |
parent | fc845b37ec3a90aaa719975f607740c22ba6a113 (diff) | |
download | gitlab-ce-e8d2c2579383897a1dd7f9debd359abe8ae8373d.tar.gz |
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.rb | 172 | ||||
-rw-r--r-- | app/services/members/groups/creator_service.rb | 17 | ||||
-rw-r--r-- | app/services/members/invite_service.rb | 2 | ||||
-rw-r--r-- | app/services/members/projects/creator_service.rb | 22 |
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 |