mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-13 14:46:05 +10:00
Allow anonymous user to access pipelines ## What does this MR do? It fixes an issue where the Pipelines is shown for the Anonymous users, but they get 404 when clicked. Their session is then logged out. Fixes #17717. See merge request !4233
525 lines
13 KiB
Ruby
525 lines
13 KiB
Ruby
class Ability
|
|
class << self
|
|
def allowed(user, subject)
|
|
return anonymous_abilities(user, subject) if user.nil?
|
|
return [] unless user.is_a?(User)
|
|
return [] if user.blocked?
|
|
|
|
case subject
|
|
when CommitStatus then commit_status_abilities(user, subject)
|
|
when Project then project_abilities(user, subject)
|
|
when Issue then issue_abilities(user, subject)
|
|
when ExternalIssue then external_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)
|
|
when ProjectMember then project_member_abilities(user, subject)
|
|
when User then user_abilities
|
|
else []
|
|
end.concat(global_abilities(user))
|
|
end
|
|
|
|
# List of possible abilities for anonymous user
|
|
def anonymous_abilities(user, subject)
|
|
case true
|
|
when subject.is_a?(PersonalSnippet)
|
|
anonymous_personal_snippet_abilities(subject)
|
|
when subject.is_a?(ProjectSnippet)
|
|
anonymous_project_snippet_abilities(subject)
|
|
when subject.is_a?(CommitStatus)
|
|
anonymous_commit_status_abilities(subject)
|
|
when subject.is_a?(Project) || subject.respond_to?(:project)
|
|
anonymous_project_abilities(subject)
|
|
when subject.is_a?(Group) || subject.respond_to?(:group)
|
|
anonymous_group_abilities(subject)
|
|
when subject.is_a?(User)
|
|
anonymous_user_abilities
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def anonymous_project_abilities(subject)
|
|
project = if subject.is_a?(Project)
|
|
subject
|
|
else
|
|
subject.project
|
|
end
|
|
|
|
if project && project.public?
|
|
rules = [
|
|
:read_project,
|
|
:read_wiki,
|
|
:read_label,
|
|
:read_milestone,
|
|
:read_project_snippet,
|
|
:read_project_member,
|
|
:read_merge_request,
|
|
:read_note,
|
|
:read_pipeline,
|
|
:read_commit_status,
|
|
:read_container_image,
|
|
:download_code
|
|
]
|
|
|
|
# Allow to read builds by anonymous user if guests are allowed
|
|
rules << :read_build if project.public_builds?
|
|
|
|
# Allow to read issues by anonymous user if issue is not confidential
|
|
rules << :read_issue unless subject.is_a?(Issue) && subject.confidential?
|
|
|
|
rules - project_disabled_features_rules(project)
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def anonymous_commit_status_abilities(subject)
|
|
rules = anonymous_project_abilities(subject.project)
|
|
# If subject is Ci::Build which inherits from CommitStatus filter the abilities
|
|
rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
|
|
rules
|
|
end
|
|
|
|
def anonymous_group_abilities(subject)
|
|
rules = []
|
|
|
|
group = if subject.is_a?(Group)
|
|
subject
|
|
else
|
|
subject.group
|
|
end
|
|
|
|
rules << :read_group if group.public?
|
|
|
|
rules
|
|
end
|
|
|
|
def anonymous_personal_snippet_abilities(snippet)
|
|
if snippet.public?
|
|
[:read_personal_snippet]
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def anonymous_project_snippet_abilities(snippet)
|
|
if snippet.public?
|
|
[:read_project_snippet]
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def anonymous_user_abilities
|
|
[:read_user] unless restricted_public_level?
|
|
end
|
|
|
|
def global_abilities(user)
|
|
rules = []
|
|
rules << :create_group if user.can_create_group
|
|
rules << :read_users_list
|
|
rules
|
|
end
|
|
|
|
def project_abilities(user, project)
|
|
rules = []
|
|
key = "/user/#{user.id}/project/#{project.id}"
|
|
|
|
RequestStore.store[key] ||= begin
|
|
# Push abilities on the users team role
|
|
rules.push(*project_team_rules(project.team, user))
|
|
|
|
if project.owner == user ||
|
|
(project.group && project.group.has_owner?(user)) ||
|
|
user.admin?
|
|
|
|
rules.push(*project_owner_rules)
|
|
end
|
|
|
|
if project.public? || (project.internal? && !user.external?)
|
|
rules.push(*public_project_rules)
|
|
|
|
# Allow to read builds for internal projects
|
|
rules << :read_build if project.public_builds?
|
|
end
|
|
|
|
if project.archived?
|
|
rules -= project_archived_rules
|
|
end
|
|
|
|
rules - project_disabled_features_rules(project)
|
|
end
|
|
end
|
|
|
|
def project_team_rules(team, user)
|
|
# Rules based on role in project
|
|
if team.master?(user)
|
|
project_master_rules
|
|
elsif team.developer?(user)
|
|
project_dev_rules
|
|
elsif team.reporter?(user)
|
|
project_report_rules
|
|
elsif team.guest?(user)
|
|
project_guest_rules
|
|
end
|
|
end
|
|
|
|
def public_project_rules
|
|
@public_project_rules ||= project_guest_rules + [
|
|
:download_code,
|
|
:fork_project,
|
|
:read_commit_status
|
|
]
|
|
end
|
|
|
|
def project_guest_rules
|
|
@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,
|
|
:upload_file
|
|
]
|
|
end
|
|
|
|
def project_report_rules
|
|
@project_report_rules ||= project_guest_rules + [
|
|
:download_code,
|
|
:fork_project,
|
|
:create_project_snippet,
|
|
:update_issue,
|
|
:admin_issue,
|
|
:admin_label,
|
|
:read_commit_status,
|
|
:read_build,
|
|
:read_container_image,
|
|
:read_pipeline,
|
|
]
|
|
end
|
|
|
|
def project_dev_rules
|
|
@project_dev_rules ||= project_report_rules + [
|
|
:admin_merge_request,
|
|
:update_merge_request,
|
|
:create_commit_status,
|
|
:update_commit_status,
|
|
:create_build,
|
|
:update_build,
|
|
:create_pipeline,
|
|
:update_pipeline,
|
|
:create_merge_request,
|
|
:create_wiki,
|
|
:push_code,
|
|
:create_container_image,
|
|
:update_container_image,
|
|
]
|
|
end
|
|
|
|
def project_archived_rules
|
|
@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_master_rules ||= project_dev_rules + [
|
|
:push_code_to_protected_branches,
|
|
:update_project_snippet,
|
|
:admin_milestone,
|
|
:admin_project_snippet,
|
|
:admin_project_member,
|
|
:admin_merge_request,
|
|
:admin_note,
|
|
:admin_wiki,
|
|
:admin_project,
|
|
:admin_commit_status,
|
|
:admin_build,
|
|
:admin_container_image,
|
|
:admin_pipeline
|
|
]
|
|
end
|
|
|
|
def project_owner_rules
|
|
@project_owner_rules ||= project_master_rules + [
|
|
:change_namespace,
|
|
:change_visibility_level,
|
|
:rename_project,
|
|
:remove_project,
|
|
:archive_project,
|
|
:remove_fork_project,
|
|
:destroy_merge_request,
|
|
:destroy_issue
|
|
]
|
|
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
|
|
|
|
unless project.builds_enabled
|
|
rules += named_abilities('build')
|
|
rules += named_abilities('pipeline')
|
|
end
|
|
|
|
unless project.container_registry_enabled
|
|
rules += named_abilities('container_image')
|
|
end
|
|
|
|
rules
|
|
end
|
|
|
|
def group_abilities(user, group)
|
|
rules = []
|
|
rules << :read_group if can_read_group?(user, group)
|
|
|
|
# Only group masters and group owners can create new projects
|
|
if group.has_master?(user) || group.has_owner?(user) || user.admin?
|
|
rules += [
|
|
:create_projects,
|
|
:admin_milestones
|
|
]
|
|
end
|
|
|
|
# Only group owner and administrators can admin group
|
|
if group.has_owner?(user) || user.admin?
|
|
rules += [
|
|
:admin_group,
|
|
:admin_namespace,
|
|
:admin_group_member,
|
|
:change_visibility_level
|
|
]
|
|
end
|
|
|
|
rules.flatten
|
|
end
|
|
|
|
def can_read_group?(user, group)
|
|
return true if user.admin?
|
|
return true if group.public?
|
|
return true if group.internal? && !user.external?
|
|
return true if group.users.include?(user)
|
|
|
|
GroupProjectsFinder.new(group).execute(user).any?
|
|
end
|
|
|
|
def namespace_abilities(user, namespace)
|
|
rules = []
|
|
|
|
# Only namespace owner and administrators can admin it
|
|
if namespace.owner == user || user.admin?
|
|
rules += [
|
|
: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 = filter_confidential_issues_abilities(user, subject, rules) if subject.is_a?(Issue)
|
|
rules
|
|
end
|
|
end
|
|
|
|
def note_abilities(user, note)
|
|
rules = []
|
|
|
|
if note.author == user
|
|
rules += [
|
|
:read_note,
|
|
:update_note,
|
|
:admin_note
|
|
]
|
|
end
|
|
|
|
if note.respond_to?(:project) && note.project
|
|
rules += project_abilities(user, note.project)
|
|
end
|
|
|
|
rules
|
|
end
|
|
|
|
def personal_snippet_abilities(user, snippet)
|
|
rules = []
|
|
|
|
if snippet.author == user
|
|
rules += [
|
|
:read_personal_snippet,
|
|
:update_personal_snippet,
|
|
:admin_personal_snippet
|
|
]
|
|
end
|
|
|
|
if snippet.public? || (snippet.internal? && !user.external?)
|
|
rules << :read_personal_snippet
|
|
end
|
|
|
|
rules
|
|
end
|
|
|
|
def project_snippet_abilities(user, snippet)
|
|
rules = []
|
|
|
|
if snippet.author == user || user.admin?
|
|
rules += [
|
|
:read_project_snippet,
|
|
:update_project_snippet,
|
|
:admin_project_snippet
|
|
]
|
|
end
|
|
|
|
if snippet.public? || (snippet.internal? && !user.external?) || (snippet.private? && snippet.project.team.member?(user))
|
|
rules << :read_project_snippet
|
|
end
|
|
|
|
rules
|
|
end
|
|
|
|
def group_member_abilities(user, subject)
|
|
rules = []
|
|
target_user = subject.user
|
|
group = subject.group
|
|
|
|
unless group.last_owner?(target_user)
|
|
can_manage = group_abilities(user, group).include?(:admin_group_member)
|
|
|
|
if can_manage
|
|
rules << :update_group_member
|
|
rules << :destroy_group_member
|
|
elsif user == target_user
|
|
rules << :destroy_group_member
|
|
end
|
|
end
|
|
|
|
rules
|
|
end
|
|
|
|
def project_member_abilities(user, subject)
|
|
rules = []
|
|
target_user = subject.user
|
|
project = subject.project
|
|
|
|
unless target_user == project.owner
|
|
can_manage = project_abilities(user, project).include?(:admin_project_member)
|
|
|
|
if can_manage
|
|
rules << :update_project_member
|
|
rules << :destroy_project_member
|
|
elsif user == target_user
|
|
rules << :destroy_project_member
|
|
end
|
|
end
|
|
|
|
rules
|
|
end
|
|
|
|
def commit_status_abilities(user, subject)
|
|
rules = project_abilities(user, subject.project)
|
|
# If subject is Ci::Build which inherits from CommitStatus filter the abilities
|
|
rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
|
|
rules
|
|
end
|
|
|
|
def filter_build_abilities(rules)
|
|
# If we can't read build we should also not have that
|
|
# ability when looking at this in context of commit_status
|
|
%w(read create update admin).each do |rule|
|
|
rules.delete(:"#{rule}_commit_status") unless rules.include?(:"#{rule}_build")
|
|
end
|
|
rules
|
|
end
|
|
|
|
def user_abilities
|
|
[:read_user]
|
|
end
|
|
|
|
def abilities
|
|
@abilities ||= begin
|
|
abilities = Six.new
|
|
abilities << self
|
|
abilities
|
|
end
|
|
end
|
|
|
|
def external_issue_abilities(user, subject)
|
|
project_abilities(user, subject.project)
|
|
end
|
|
|
|
private
|
|
|
|
def restricted_public_level?
|
|
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
|
|
end
|
|
|
|
def named_abilities(name)
|
|
[
|
|
:"read_#{name}",
|
|
:"create_#{name}",
|
|
:"update_#{name}",
|
|
:"admin_#{name}"
|
|
]
|
|
end
|
|
|
|
def filter_confidential_issues_abilities(user, issue, rules)
|
|
return rules if user.admin? || !issue.confidential?
|
|
|
|
unless issue.author == user || issue.assignee == user || issue.project.team.member?(user.id)
|
|
rules.delete(:admin_issue)
|
|
rules.delete(:read_issue)
|
|
rules.delete(:update_issue)
|
|
end
|
|
|
|
rules
|
|
end
|
|
end
|
|
end
|