7f73f440f9
When getting the max member access for a group of users, we stored the results in RequestStore. However, this will only return results for project members, so anyone who wasn't a member of the project would be checked once at the start, and then once for each comment they made. These queries are generally quite fast, but no query is faster!
207 lines
4.5 KiB
Ruby
207 lines
4.5 KiB
Ruby
class ProjectTeam
|
|
attr_accessor :project
|
|
|
|
def initialize(project)
|
|
@project = project
|
|
end
|
|
|
|
# Shortcut to add users
|
|
#
|
|
# Use:
|
|
# @team << [@user, :master]
|
|
# @team << [@users, :master]
|
|
#
|
|
def <<(args)
|
|
users, access, current_user = *args
|
|
|
|
if users.respond_to?(:each)
|
|
add_users(users, access, current_user: current_user)
|
|
else
|
|
add_user(users, access, current_user: current_user)
|
|
end
|
|
end
|
|
|
|
def add_guest(user, current_user: nil)
|
|
self << [user, :guest, current_user]
|
|
end
|
|
|
|
def add_reporter(user, current_user: nil)
|
|
self << [user, :reporter, current_user]
|
|
end
|
|
|
|
def add_developer(user, current_user: nil)
|
|
self << [user, :developer, current_user]
|
|
end
|
|
|
|
def add_master(user, current_user: nil)
|
|
self << [user, :master, current_user]
|
|
end
|
|
|
|
def find_member(user_id)
|
|
member = project.members.find_by(user_id: user_id)
|
|
|
|
# If user is not in project members
|
|
# we should check for group membership
|
|
if group && !member
|
|
member = group.members.find_by(user_id: user_id)
|
|
end
|
|
|
|
member
|
|
end
|
|
|
|
def add_users(users, access_level, current_user: nil, expires_at: nil)
|
|
ProjectMember.add_users(
|
|
project,
|
|
users,
|
|
access_level,
|
|
current_user: current_user,
|
|
expires_at: expires_at
|
|
)
|
|
end
|
|
|
|
def add_user(user, access_level, current_user: nil, expires_at: nil)
|
|
ProjectMember.add_user(
|
|
project,
|
|
user,
|
|
access_level,
|
|
current_user: current_user,
|
|
expires_at: expires_at
|
|
)
|
|
end
|
|
|
|
# Remove all users from project team
|
|
def truncate
|
|
ProjectMember.truncate_team(project)
|
|
end
|
|
|
|
def members
|
|
@members ||= fetch_members
|
|
end
|
|
alias_method :users, :members
|
|
|
|
def guests
|
|
@guests ||= fetch_members(Gitlab::Access::GUEST)
|
|
end
|
|
|
|
def reporters
|
|
@reporters ||= fetch_members(Gitlab::Access::REPORTER)
|
|
end
|
|
|
|
def developers
|
|
@developers ||= fetch_members(Gitlab::Access::DEVELOPER)
|
|
end
|
|
|
|
def masters
|
|
@masters ||= fetch_members(Gitlab::Access::MASTER)
|
|
end
|
|
|
|
def import(source_project, current_user = nil)
|
|
target_project = project
|
|
|
|
source_members = source_project.project_members.to_a
|
|
target_user_ids = target_project.project_members.pluck(:user_id)
|
|
|
|
source_members.reject! do |member|
|
|
# Skip if user already present in team
|
|
!member.invite? && target_user_ids.include?(member.user_id)
|
|
end
|
|
|
|
source_members.map! do |member|
|
|
new_member = member.dup
|
|
new_member.id = nil
|
|
new_member.source = target_project
|
|
new_member.created_by = current_user
|
|
new_member
|
|
end
|
|
|
|
ProjectMember.transaction do
|
|
source_members.each do |member|
|
|
member.save
|
|
end
|
|
end
|
|
|
|
true
|
|
rescue
|
|
false
|
|
end
|
|
|
|
def guest?(user)
|
|
max_member_access(user.id) == Gitlab::Access::GUEST
|
|
end
|
|
|
|
def reporter?(user)
|
|
max_member_access(user.id) == Gitlab::Access::REPORTER
|
|
end
|
|
|
|
def developer?(user)
|
|
max_member_access(user.id) == Gitlab::Access::DEVELOPER
|
|
end
|
|
|
|
def master?(user)
|
|
max_member_access(user.id) == Gitlab::Access::MASTER
|
|
end
|
|
|
|
# Checks if `user` is authorized for this project, with at least the
|
|
# `min_access_level` (if given).
|
|
def member?(user, min_access_level = Gitlab::Access::GUEST)
|
|
return false unless user
|
|
|
|
user.authorized_project?(project, min_access_level)
|
|
end
|
|
|
|
def human_max_access(user_id)
|
|
Gitlab::Access.options_with_owner.key(max_member_access(user_id))
|
|
end
|
|
|
|
# Determine the maximum access level for a group of users in bulk.
|
|
#
|
|
# Returns a Hash mapping user ID -> maximum access level.
|
|
def max_member_access_for_user_ids(user_ids)
|
|
user_ids = user_ids.uniq
|
|
key = "max_member_access:#{project.id}"
|
|
|
|
access = {}
|
|
|
|
if RequestStore.active?
|
|
RequestStore.store[key] ||= {}
|
|
access = RequestStore.store[key]
|
|
end
|
|
|
|
# Look up only the IDs we need
|
|
user_ids = user_ids - access.keys
|
|
|
|
return access if user_ids.empty?
|
|
|
|
users_access = project.project_authorizations.
|
|
where(user: user_ids).
|
|
group(:user_id).
|
|
maximum(:access_level)
|
|
|
|
access.merge!(users_access)
|
|
|
|
missing_user_ids = user_ids - users_access.keys
|
|
|
|
missing_user_ids.each do |user_id|
|
|
access[user_id] = Gitlab::Access::NO_ACCESS
|
|
end
|
|
|
|
access
|
|
end
|
|
|
|
def max_member_access(user_id)
|
|
max_member_access_for_user_ids([user_id])[user_id] || Gitlab::Access::NO_ACCESS
|
|
end
|
|
|
|
private
|
|
|
|
def fetch_members(level = nil)
|
|
members = project.authorized_users
|
|
members = members.where(project_authorizations: { access_level: level }) if level
|
|
|
|
members
|
|
end
|
|
|
|
def group
|
|
project.group
|
|
end
|
|
end
|