Class: Members::CreatorService

Inherits:
Object
  • Object
show all
Defined in:
app/services/members/creator_service.rb

Overview

This class serves as more of an app-wide way we add/create members All roads to add members should take this path.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(invitee:, builder: StandardMemberBuilder, **args) ⇒ CreatorService

Returns a new instance of CreatorService.



170
171
172
173
174
175
# File 'app/services/members/creator_service.rb', line 170

def initialize(invitee:, builder: StandardMemberBuilder, **args)
  @invitee = invitee
  @builder = builder
  @args = args
  @access_level = self.class.parsed_access_level(args[:access_level])
end

Class Method Details

.access_levelsObject



16
17
18
# File 'app/services/members/creator_service.rb', line 16

def access_levels
  Gitlab::Access.sym_options_with_owner
end

.add_member(source, invitee, access_level, **args) ⇒ Object

NOTE: immediately_sync_authorizations can be expensive to run in the foreground. This should only be used in rare cases where asynchronous authorization does not work (e.g. user is created and used immediately in the same request).



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/services/members/creator_service.rb', line 85

def add_member(source, invitee, access_level, **args)
  # We delete the immediately_sync_authorizations option as we don't want to support that for adding multiple
  # members
  sync_immediately = args.delete(:immediately_sync_authorizations)

  result = add_members(source, [invitee], access_level, **args).first

  # Some code paths really need the member to exist straight away as the user will be used straight away. Async
  # refresh will almost always lead to bugs for these cases.
  if sync_immediately && source.is_a?(Project)
    ::AuthorizedProjectUpdate::ProjectRecalculatePerUserService
      .new(source, invitee)
      .execute
  end

  result
end

.add_members(sources, invitees, access_level, **args) ⇒ Object

Add members to sources with passed access option

access can be an integer representing a access code or symbol like :maintainer representing role

Ex.

add_members(
  sources,
  user_ids,
  Member::MAINTAINER
)

add_members(
  sources,
  user_ids,
  :maintainer
)

Project::ActiveRecord_Relation] - Can’t be an array of source ids because we don’t know the type of source.

Parameters:

  • sources (Group, Project, Array<Group>, Array<Project>, Group::ActiveRecord_Relation, )

    ources [Group, Project, Array<Group>, Array<Project>, Group::ActiveRecord_Relation,



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'app/services/members/creator_service.rb', line 41

def add_members(sources, invitees, access_level, **args)
  return [] unless invitees.present?

  sources = Array.wrap(sources) if sources.is_a?(ApplicationRecord) # For single source

  Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.temporary_ignore_tables_in_transaction(
    %w[users user_preferences user_details emails identities], url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424276'
  ) do
    Member.transaction do
      sources.flat_map do |source|
        # If this user is attempting to manage Owner members and doesn't have permission, do not allow
        current_user = args[:current_user]
        next [] if managing_owners?(current_user, access_level) && cannot_manage_owners?(source, current_user)

        emails, users, existing_members, users_by_emails = parse_users_list(source, invitees)

        common_arguments = {
          source: source,
          access_level: access_level,
          existing_members: existing_members,
          users_by_emails: users_by_emails
        }.merge(parsed_args(args))

        build_members(emails, users, common_arguments)
      end
    end
  end
end

.build_members(emails, users, common_arguments) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
# File 'app/services/members/creator_service.rb', line 70

def build_members(emails, users, common_arguments)
  members = emails.map do |email|
    new(invitee: email, builder: InviteMemberBuilder, **common_arguments).execute
  end

  members += users.map do |user|
    new(invitee: user, **common_arguments).execute
  end

  members
end

.cannot_manage_owners?(source, current_user) ⇒ Boolean

Returns:

  • (Boolean)


8
9
10
# File 'app/services/members/creator_service.rb', line 8

def cannot_manage_owners?(source, current_user)
  source.max_member_access_for_user(current_user) < Gitlab::Access::OWNER
end

.parsed_access_level(access_level) ⇒ Object



12
13
14
# File 'app/services/members/creator_service.rb', line 12

def parsed_access_level(access_level)
  access_levels.fetch(access_level) { access_level.to_i }
end

Instance Method Details

#executeObject



179
180
181
182
183
184
# File 'app/services/members/creator_service.rb', line 179

def execute
  find_or_build_member
  commit_member

  member
end