mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-13 14:46:05 +10:00
Fix (i.e. prevent) access to disabled features for unauthenticated users Unauthenticated users had access to disabled features of public projects. The code has been slightly refactored so that feature checks are done in a separate method and can also be applied for public access. See merge request !1006
331 lines
7.8 KiB
Ruby
331 lines
7.8 KiB
Ruby
class Ability
|
|
class << self
|
|
def allowed(user, subject)
|
|
return not_auth_abilities(user, subject) if user.nil?
|
|
return [] unless user.kind_of?(User)
|
|
return [] if user.blocked?
|
|
|
|
case subject.class.name
|
|
when "Project" then project_abilities(user, subject)
|
|
when "Issue" then issue_abilities(user, subject)
|
|
when "Note" then note_abilities(user, subject)
|
|
when "ProjectSnippet" then project_snippet_abilities(user, subject)
|
|
when "PersonalSnippet" then personal_snippet_abilities(user, subject)
|
|
when "MergeRequest" then merge_request_abilities(user, subject)
|
|
when "Group" then group_abilities(user, subject)
|
|
when "Namespace" then namespace_abilities(user, subject)
|
|
when "GroupMember" then group_member_abilities(user, subject)
|
|
else []
|
|
end.concat(global_abilities(user))
|
|
end
|
|
|
|
# List of possible abilities
|
|
# for non-authenticated user
|
|
def not_auth_abilities(user, subject)
|
|
project = if subject.kind_of?(Project)
|
|
subject
|
|
elsif subject.respond_to?(:project)
|
|
subject.project
|
|
else
|
|
nil
|
|
end
|
|
|
|
if project && project.public?
|
|
rules = [
|
|
:read_project,
|
|
:read_wiki,
|
|
:read_issue,
|
|
:read_label,
|
|
:read_milestone,
|
|
:read_project_snippet,
|
|
:read_project_member,
|
|
:read_merge_request,
|
|
:read_note,
|
|
:download_code
|
|
]
|
|
|
|
rules - project_disabled_features_rules(project)
|
|
else
|
|
group = if subject.kind_of?(Group)
|
|
subject
|
|
elsif subject.respond_to?(:group)
|
|
subject.group
|
|
else
|
|
nil
|
|
end
|
|
|
|
if group && group.public_profile?
|
|
[:read_group]
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
end
|
|
|
|
def global_abilities(user)
|
|
rules = []
|
|
rules << :create_group if user.can_create_group
|
|
rules
|
|
end
|
|
|
|
def project_abilities(user, project)
|
|
rules = []
|
|
key = "/user/#{user.id}/project/#{project.id}"
|
|
|
|
RequestStore.store[key] ||= begin
|
|
team = project.team
|
|
|
|
# Rules based on role in project
|
|
if team.master?(user)
|
|
rules.push(*project_master_rules)
|
|
|
|
elsif team.developer?(user)
|
|
rules.push(*project_dev_rules)
|
|
|
|
elsif team.reporter?(user)
|
|
rules.push(*project_report_rules)
|
|
|
|
elsif team.guest?(user)
|
|
rules.push(*project_guest_rules)
|
|
end
|
|
|
|
if project.public? || project.internal?
|
|
rules.push(*public_project_rules)
|
|
end
|
|
|
|
if project.owner == user || user.admin?
|
|
rules.push(*project_admin_rules)
|
|
end
|
|
|
|
if project.group && project.group.has_owner?(user)
|
|
rules.push(*project_admin_rules)
|
|
end
|
|
|
|
if project.archived?
|
|
rules -= project_archived_rules
|
|
end
|
|
|
|
rules - project_disabled_features_rules(project)
|
|
end
|
|
end
|
|
|
|
def public_project_rules
|
|
project_guest_rules + [
|
|
:download_code,
|
|
:fork_project
|
|
]
|
|
end
|
|
|
|
def project_guest_rules
|
|
[
|
|
:read_project,
|
|
:read_wiki,
|
|
:read_issue,
|
|
:read_label,
|
|
:read_milestone,
|
|
:read_project_snippet,
|
|
:read_project_member,
|
|
:read_merge_request,
|
|
:read_note,
|
|
:create_project,
|
|
:create_issue,
|
|
:create_note
|
|
]
|
|
end
|
|
|
|
def project_report_rules
|
|
project_guest_rules + [
|
|
:download_code,
|
|
:fork_project,
|
|
:create_project_snippet,
|
|
:update_issue,
|
|
:admin_issue,
|
|
:admin_label,
|
|
]
|
|
end
|
|
|
|
def project_dev_rules
|
|
project_report_rules + [
|
|
:create_merge_request,
|
|
:create_wiki,
|
|
:push_code
|
|
]
|
|
end
|
|
|
|
def project_archived_rules
|
|
[
|
|
:create_merge_request,
|
|
:push_code,
|
|
:push_code_to_protected_branches,
|
|
:update_merge_request,
|
|
:admin_merge_request
|
|
]
|
|
end
|
|
|
|
def project_master_rules
|
|
project_dev_rules + [
|
|
:push_code_to_protected_branches,
|
|
:update_project_snippet,
|
|
:update_merge_request,
|
|
:admin_milestone,
|
|
:admin_project_snippet,
|
|
:admin_project_member,
|
|
:admin_merge_request,
|
|
:admin_note,
|
|
:admin_wiki,
|
|
:admin_project
|
|
]
|
|
end
|
|
|
|
def project_admin_rules
|
|
project_master_rules + [
|
|
:change_namespace,
|
|
:change_visibility_level,
|
|
:rename_project,
|
|
:remove_project,
|
|
:archive_project
|
|
]
|
|
end
|
|
|
|
def project_disabled_features_rules(project)
|
|
rules = []
|
|
|
|
unless project.issues_enabled
|
|
rules += named_abilities('issue')
|
|
end
|
|
|
|
unless project.merge_requests_enabled
|
|
rules += named_abilities('merge_request')
|
|
end
|
|
|
|
unless project.issues_enabled or project.merge_requests_enabled
|
|
rules += named_abilities('label')
|
|
rules += named_abilities('milestone')
|
|
end
|
|
|
|
unless project.snippets_enabled
|
|
rules += named_abilities('project_snippet')
|
|
end
|
|
|
|
unless project.wiki_enabled
|
|
rules += named_abilities('wiki')
|
|
end
|
|
|
|
rules
|
|
end
|
|
|
|
def group_abilities(user, group)
|
|
rules = []
|
|
|
|
if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
|
|
rules << :read_group
|
|
end
|
|
|
|
# Only group masters and group owners can create new projects in group
|
|
if group.has_master?(user) || group.has_owner?(user) || user.admin?
|
|
rules.push(*[
|
|
:create_projects,
|
|
])
|
|
end
|
|
|
|
# Only group owner and administrators can admin group
|
|
if group.has_owner?(user) || user.admin?
|
|
rules.push(*[
|
|
:admin_group,
|
|
:admin_namespace
|
|
])
|
|
end
|
|
|
|
rules.flatten
|
|
end
|
|
|
|
def namespace_abilities(user, namespace)
|
|
rules = []
|
|
|
|
# Only namespace owner and administrators can admin it
|
|
if namespace.owner == user || user.admin?
|
|
rules.push(*[
|
|
:create_projects,
|
|
:admin_namespace
|
|
])
|
|
end
|
|
|
|
rules.flatten
|
|
end
|
|
|
|
|
|
[:issue, :merge_request].each do |name|
|
|
define_method "#{name}_abilities" do |user, subject|
|
|
rules = []
|
|
|
|
if subject.author == user || (subject.respond_to?(:assignee) && subject.assignee == user)
|
|
rules += [
|
|
:"read_#{name}",
|
|
:"update_#{name}",
|
|
]
|
|
end
|
|
|
|
rules += project_abilities(user, subject.project)
|
|
rules
|
|
end
|
|
end
|
|
|
|
[:note, :project_snippet, :personal_snippet].each do |name|
|
|
define_method "#{name}_abilities" do |user, subject|
|
|
rules = []
|
|
|
|
if subject.author == user
|
|
rules += [
|
|
:"read_#{name}",
|
|
:"update_#{name}",
|
|
:"admin_#{name}"
|
|
]
|
|
end
|
|
|
|
if subject.respond_to?(:project) && subject.project
|
|
rules += project_abilities(user, subject.project)
|
|
end
|
|
|
|
rules
|
|
end
|
|
end
|
|
|
|
def group_member_abilities(user, subject)
|
|
rules = []
|
|
target_user = subject.user
|
|
group = subject.group
|
|
can_manage = group_abilities(user, group).include?(:admin_group)
|
|
|
|
if can_manage && (user != target_user)
|
|
rules << :update_group_member
|
|
rules << :destroy_group_member
|
|
end
|
|
|
|
if !group.last_owner?(user) && (can_manage || (user == target_user))
|
|
rules << :destroy_group_member
|
|
end
|
|
|
|
rules
|
|
end
|
|
|
|
def abilities
|
|
@abilities ||= begin
|
|
abilities = Six.new
|
|
abilities << self
|
|
abilities
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def named_abilities(name)
|
|
[
|
|
:"read_#{name}",
|
|
:"create_#{name}",
|
|
:"update_#{name}",
|
|
:"admin_#{name}"
|
|
]
|
|
end
|
|
end
|
|
end
|