BigW Consortium Gitlab

project_team.rb 6.41 KB
Newer Older
1
class ProjectTeam
2 3 4 5
  attr_accessor :project

  def initialize(project)
    @project = project
6 7 8 9 10 11 12 13
  end

  # Shortcut to add users
  #
  # Use:
  #   @team << [@user, :master]
  #   @team << [@users, :master]
  #
14
  def <<(args)
15
    users, access, current_user = *args
16 17

    if users.respond_to?(:each)
18
      add_users(users, access, current_user)
19
    else
20
      add_user(users, access, current_user)
21
    end
22 23
  end

24
  def find_member(user_id)
25
    member = project.members.find_by(user_id: user_id)
26 27 28

    # If user is not in project members
    # we should check for group membership
29
    if group && !member
30
      member = group.members.find_by(user_id: user_id)
31 32
    end

33
    member
34 35
  end

Adam Niedzielski committed
36
  def add_users(users, access, current_user = nil, expires_at = nil)
37
    ProjectMember.add_users_into_projects(
38
      [project.id],
39 40
      users,
      access,
Adam Niedzielski committed
41 42
      current_user,
      expires_at
43 44 45
    )
  end

46 47 48 49
  def add_user(user, access, current_user = nil)
    add_users([user], access, current_user)
  end

50 51
  # Remove all users from project team
  def truncate
52
    ProjectMember.truncate_team(project)
53 54 55
  end

  def members
56
    @members ||= fetch_members
57
  end
58
  alias_method :users, :members
59 60

  def guests
61
    @guests ||= fetch_members(:guests)
62 63 64
  end

  def reporters
65
    @reporters ||= fetch_members(:reporters)
66 67 68
  end

  def developers
69
    @developers ||= fetch_members(:developers)
70 71 72
  end

  def masters
73
    @masters ||= fetch_members(:masters)
74
  end
75

76
  def import(source_project, current_user = nil)
77 78
    target_project = project

79
    source_members = source_project.project_members.to_a
80
    target_user_ids = target_project.project_members.pluck(:user_id)
81

82
    source_members.reject! do |member|
83
      # Skip if user already present in team
84
      !member.invite? && target_user_ids.include?(member.user_id)
85 86
    end

87 88 89 90
    source_members.map! do |member|
      new_member = member.dup
      new_member.id = nil
      new_member.source = target_project
91
      new_member.created_by = current_user
92
      new_member
93 94
    end

95
    ProjectMember.transaction do
96 97
      source_members.each do |member|
        member.save
98 99 100 101 102 103 104
      end
    end

    true
  rescue
    false
  end
105

106
  def guest?(user)
107
    max_member_access(user.id) == Gitlab::Access::GUEST
108 109 110
  end

  def reporter?(user)
111
    max_member_access(user.id) == Gitlab::Access::REPORTER
112 113 114
  end

  def developer?(user)
115
    max_member_access(user.id) == Gitlab::Access::DEVELOPER
116 117 118
  end

  def master?(user)
119
    max_member_access(user.id) == Gitlab::Access::MASTER
120 121
  end

122 123 124 125 126 127 128 129
  def member?(user, min_member_access = nil)
    member = !!find_member(user.id)

    if min_member_access
      member && max_member_access(user.id) >= min_member_access
    else
      member
    end
130 131
  end

132 133 134 135
  def human_max_access(user_id)
    Gitlab::Access.options_with_owner.key(max_member_access(user_id))
  end

136 137
  # Determine the maximum access level for a group of users in bulk.
  #
Stan Hu committed
138
  # Returns a Hash mapping user ID -> maximum access level.
139 140 141
  def max_member_access_for_user_ids(user_ids)
    user_ids = user_ids.uniq
    key = "max_member_access:#{project.id}"
Stan Hu committed
142
    RequestStore.store[key] ||= {}
143
    access = RequestStore.store[key]
144

145 146
    # Lookup only the IDs we need
    user_ids = user_ids - access.keys
147

148
    if user_ids.present?
Stan Hu committed
149
      user_ids.each { |id| access[id] = Gitlab::Access::NO_ACCESS }
150

Stan Hu committed
151
      member_access = project.members.access_for_user_ids(user_ids)
152 153 154
      merge_max!(access, member_access)

      if group
Stan Hu committed
155
        group_access = group.members.access_for_user_ids(user_ids)
156 157 158
        merge_max!(access, group_access)
      end

159 160
      # Each group produces a list of maximum access level per user. We take the
      # max of the values produced by each group.
161
      if project.invited_groups.any? && project.allowed_to_share_with_group?
162 163 164 165
        project.project_group_links.each do |group_link|
          invited_access = max_invited_level_for_users(group_link, user_ids)
          merge_max!(access, invited_access)
        end
166
      end
167 168
    end

169 170 171 172 173
    access
  end

  def max_member_access(user_id)
    max_member_access_for_user_ids([user_id])[user_id]
174 175
  end

176
  private
177

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
  # For a given group, return the maximum access level for the user. This is the min of
  # the invited access level of the group and the access level of the user within the group.
  # For example, if the group has been given DEVELOPER access but the member has MASTER access,
  # the user should receive only DEVELOPER access.
  def max_invited_level_for_users(group_link, user_ids)
    invited_group = group_link.group
    capped_access_level = group_link.group_access
    access = invited_group.group_members.access_for_user_ids(user_ids)

    # If the user is not in the list, assume he/she does not have access
    missing_users = user_ids - access.keys
    missing_users.each { |id| access[id] = Gitlab::Access::NO_ACCESS }

    # Cap the maximum access by the invited level access
    access.each { |key, value| access[key] = [value, capped_access_level].min }
193 194
  end

195
  def fetch_members(level = nil)
196 197
    project_members = project.members
    group_members = group ? group.members : []
198 199 200
    invited_members = []

    if project.invited_groups.any? && project.allowed_to_share_with_group?
201
      project.project_group_links.includes(group: [:group_members]).each do |group_link|
202
        invited_group = group_link.group
203
        im = invited_group.members
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

        if level
          int_level = GroupMember.access_level_roles[level.to_s.singularize.titleize]

          # Skip group members if we ask for masters
          # but max group access is developers
          next if int_level > group_link.group_access

          # If we ask for developers and max
          # group access is developers we need to provide
          # both group master, developers as devs
          if int_level == group_link.group_access
            im.where("access_level >= ?)", group_link.group_access)
          else
            im.send(level)
          end
        end

        invited_members << im
      end

      invited_members = invited_members.flatten.compact
    end
227 228 229

    if level
      project_members = project_members.send(level)
230
      group_members = group_members.send(level) if group
231 232
    end

233
    user_ids = project_members.pluck(:user_id)
234
    user_ids.push(*invited_members.map(&:user_id)) if invited_members.any?
235
    user_ids.push(*group_members.pluck(:user_id)) if group
236 237

    User.where(id: user_ids)
238
  end
239 240 241 242

  def group
    project.group
  end
243 244 245 246

  def merge_max!(first_hash, second_hash)
    first_hash.merge!(second_hash) { |_key, old, new| old > new ? old : new }
  end
247
end