Commit c5adebd6 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'rs-port-ability-refactor' into 'master'

Port policies to EE

Ports https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5796 to EE, maintaining EE-only differences.

Assuming this all works, I think we should put this block in CE as well just to prevent future conflicts:

```ruby
    # EE-only
    if defined?(License) && License.block_changes?
      cannot! :create_issue
      cannot! :create_merge_request
      cannot! :push_code
      cannot! :push_code_to_protected_branches
    end
```

In addition to cherry-picking the gitlab-org/gitlab-ce@e71cd7a300017cf85e16de3b1c68fdb25c3a4b4d commit, which is the merge of the Policy refactor itself, I had to cherry-pick gitlab-org/gitlab-ce@6686084c6502f10fd7e6b8963ab52526cb6831bf because it added the `Project#has_wiki?` method that specs depended on.

See merge request !702
parents 52c9ff53 dcbef45d
...@@ -50,6 +50,7 @@ v 8.11.3 (unreleased) ...@@ -50,6 +50,7 @@ v 8.11.3 (unreleased)
- Allow system info page to handle case where info is unavailable - Allow system info page to handle case where info is unavailable
- Label list shows all issues (opened or closed) with that label - Label list shows all issues (opened or closed) with that label
- Don't show resolve conflicts link before MR status is updated - Don't show resolve conflicts link before MR status is updated
- Fix "Wiki" link not appearing in navigation for projects with external wiki
- Fix IE11 fork button bug !598 - Fix IE11 fork button bug !598
- Don't prevent viewing the MR when git refs for conflicts can't be found on disk - Don't prevent viewing the MR when git refs for conflicts can't be found on disk
- Fix external issue tracker "Issues" link leading to 404s - Fix external issue tracker "Issues" link leading to 404s
......
...@@ -102,9 +102,6 @@ gem 'fog-rackspace', '~> 0.1.1' ...@@ -102,9 +102,6 @@ gem 'fog-rackspace', '~> 0.1.1'
# for aws storage # for aws storage
gem 'unf', '~> 0.1.4' gem 'unf', '~> 0.1.4'
# Authorization
gem 'six', '~> 0.2.0'
# Seed data # Seed data
gem 'seed-fu', '~> 2.3.5' gem 'seed-fu', '~> 2.3.5'
......
...@@ -707,7 +707,6 @@ GEM ...@@ -707,7 +707,6 @@ GEM
rack (~> 1.5) rack (~> 1.5)
rack-protection (~> 1.4) rack-protection (~> 1.4)
tilt (>= 1.3, < 3) tilt (>= 1.3, < 3)
six (0.2.0)
slack-notifier (1.2.1) slack-notifier (1.2.1)
slop (3.6.0) slop (3.6.0)
spinach (0.8.10) spinach (0.8.10)
...@@ -987,7 +986,6 @@ DEPENDENCIES ...@@ -987,7 +986,6 @@ DEPENDENCIES
sidekiq-cron (~> 0.4.0) sidekiq-cron (~> 0.4.0)
simplecov (= 0.12.0) simplecov (= 0.12.0)
sinatra (~> 1.4.4) sinatra (~> 1.4.4)
six (~> 0.2.0)
slack-notifier (~> 1.2.0) slack-notifier (~> 1.2.0)
spinach-rails (~> 0.2.1) spinach-rails (~> 0.2.1)
spinach-rerun-reporter (~> 0.0.2) spinach-rerun-reporter (~> 0.0.2)
......
...@@ -24,7 +24,7 @@ class ApplicationController < ActionController::Base ...@@ -24,7 +24,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception protect_from_forgery with: :exception
helper_method :abilities, :can?, :current_application_settings helper_method :can?, :current_application_settings
helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled? helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
rescue_from Encoding::CompatibilityError do |exception| rescue_from Encoding::CompatibilityError do |exception|
...@@ -101,12 +101,8 @@ class ApplicationController < ActionController::Base ...@@ -101,12 +101,8 @@ class ApplicationController < ActionController::Base
end end
end end
def abilities
Ability.abilities
end
def can?(object, action, subject) def can?(object, action, subject)
abilities.allowed?(object, action, subject) Ability.allowed?(object, action, subject)
end end
def access_denied! def access_denied!
......
...@@ -14,7 +14,7 @@ class NamespacesController < ApplicationController ...@@ -14,7 +14,7 @@ class NamespacesController < ApplicationController
if user if user
redirect_to user_path(user) redirect_to user_path(user)
elsif group && can?(current_user, :read_group, namespace) elsif group && can?(current_user, :read_group, group)
redirect_to group_path(group) redirect_to group_path(group)
elsif current_user.nil? elsif current_user.nil?
authenticate_user! authenticate_user!
......
...@@ -65,7 +65,7 @@ class IssuableFinder ...@@ -65,7 +65,7 @@ class IssuableFinder
if project? if project?
@project = Project.find(params[:project_id]) @project = Project.find(params[:project_id])
unless Ability.abilities.allowed?(current_user, :read_project, @project) unless Ability.allowed?(current_user, :read_project, @project)
@project = nil @project = nil
end end
else else
......
...@@ -83,7 +83,7 @@ class TodosFinder ...@@ -83,7 +83,7 @@ class TodosFinder
if project? if project?
@project = Project.find(params[:project_id]) @project = Project.find(params[:project_id])
unless Ability.abilities.allowed?(current_user, :read_project, @project) unless Ability.allowed?(current_user, :read_project, @project)
@project = nil @project = nil
end end
else else
......
...@@ -9,7 +9,7 @@ class BaseMailer < ActionMailer::Base ...@@ -9,7 +9,7 @@ class BaseMailer < ActionMailer::Base
default reply_to: Proc.new { default_reply_to_address.format } default reply_to: Proc.new { default_reply_to_address.format }
def can? def can?
Ability.abilities.allowed?(current_user, action, subject) Ability.allowed?(current_user, action, subject)
end end
private private
......
class Ability class Ability
class << self class << self
# rubocop: disable Metrics/CyclomaticComplexity
def allowed(user, subject)
return anonymous_abilities(user, subject) if user.nil?
return [] unless user.is_a?(User)
return [] if user.blocked?
abilities = abilities_by_subject_class(user: user, subject: subject)
abilities -= license_blocked_abilities if License.block_changes?
abilities
end
def abilities_by_subject_class(user:, subject:)
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 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
when ExternalIssue, Deployment, Environment then project_abilities(user, subject.project)
when Ci::Runner then runner_abilities(user, subject)
else []
end.concat(global_abilities(user))
end
def license_blocked_abilities
[
:create_issue,
:create_merge_request,
:push_code,
:push_code_to_protected_branches
]
end
# Given a list of users and a project this method returns the users that can # Given a list of users and a project this method returns the users that can
# read the given project. # read the given project.
def users_that_can_read_project(users, project) def users_that_can_read_project(users, project)
...@@ -74,370 +32,7 @@ class Ability ...@@ -74,370 +32,7 @@ class Ability
issues.select { |issue| issue.visible_to_user?(user) } issues.select { |issue| issue.visible_to_user?(user) }
end end
# List of possible abilities for anonymous user # TODO: make this private and use the actual abilities stuff for this
def anonymous_abilities(user, subject)
if subject.is_a?(PersonalSnippet)
anonymous_personal_snippet_abilities(subject)
elsif subject.is_a?(ProjectSnippet)
anonymous_project_snippet_abilities(subject)
elsif subject.is_a?(CommitStatus)
anonymous_commit_status_abilities(subject)
elsif subject.is_a?(Project) || subject.respond_to?(:project)
anonymous_project_abilities(subject)
elsif subject.is_a?(Group) || subject.respond_to?(:group)
anonymous_group_abilities(subject)
elsif 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_board,
:read_list,
: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)
key = "/user/#{user.id}/project/#{project.id}"
if RequestStore.active?
RequestStore.store[key] ||= uncached_project_abilities(user, project)
else
uncached_project_abilities(user, project)
end
end
def uncached_project_abilities(user, project)
rules = []
# Push abilities on the users team role
rules.push(*project_team_rules(project.team, user))
rules << :change_repository_storage if user.admin?
owner = user.admin? ||
project.owner == user ||
(project.group && project.group.has_owner?(user))
if owner
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?
unless owner || project.team.member?(user) || project_group_member?(project, user)
rules << :request_access if project.request_access_enabled
end
end
if project.archived?
rules -= project_archived_rules
end
(rules - project_disabled_features_rules(project)).uniq
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
else
[]
end
end
def public_project_rules
@public_project_rules ||= project_guest_rules + [
:download_code,
:fork_project,
:read_commit_status,
:read_pipeline,
:read_container_image
]
end
def project_guest_rules
@project_guest_rules ||= [
:read_project,
:read_wiki,
:read_issue,
:read_board,
:read_list,
: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,
:admin_list,
:read_commit_status,
:read_build,
:read_container_image,
:read_pipeline,
:read_environment,
:read_deployment
]
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,
:resolve_note,
:create_container_image,
:update_container_image,
:create_environment,
:create_deployment
]
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 + [
:read_pages,
:push_code_to_protected_branches,
:update_project_snippet,
:update_pages,
:update_environment,
:update_deployment,
: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_pages,
:admin_pipeline,
:admin_environment,
:admin_deployment,
:admin_path_locks
]
end
def project_owner_rules
@project_owner_rules ||= project_master_rules + [
:change_namespace,
:change_visibility_level,
:rename_project,
:remove_project,
:remove_pages,
: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')
rules += named_abilities('environment')
rules += named_abilities('deployment')
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)
owner = user.admin? || group.has_owner?(user)
master = owner || group.has_master?(user)
# Only group masters and group owners can create new projects
if master
rules += [
:create_projects,
:admin_milestones
]
end
# Only group owner and administrators can admin group
if owner
rules += [
:admin_group,
:admin_namespace,
:admin_group_member,
:change_visibility_level
]
if group.ldap_synced?
rules.delete(:admin_group_member)
end
end
if group.public? || (group.internal? && !user.external?)
rules << :request_access if group.request_access_enabled && group.users.exclude?(user)
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 can_edit_note?(user, note) def can_edit_note?(user, note)
return false if !note.editable? || !user.present? return false if !note.editable? || !user.present?
return true if note.author == user || user.admin? return true if note.author == user || user.admin?
...@@ -450,207 +45,23 @@ class Ability ...@@ -450,207 +45,23 @@ class Ability
end end
end end
def namespace_abilities(user, namespace) def allowed?(user, action, subject)
rules = [] allowed(user, subject).include?(action)
# 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,
:resolve_note
]
end
if note.respond_to?(:project) && note.project
rules += project_abilities(user, note.project)
end
if note.for_merge_request? && note.noteable.author == user
rules << :resolve_note
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 end
def group_member_abilities(user, subject) def allowed(user, subject)
rules = [] return uncached_allowed(user, subject) unless RequestStore.active?
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 runner_abilities(user, runner)
if user.is_admin?
[:assign_runner]
elsif runner.is_shared? || runner.locked?
[]
elsif user.ci_authorized_runners.include?(runner)
[:assign_runner]
else
[]
end
end
def user_abilities
[:read_user]
end
def abilities user_key = user ? user.id : 'anonymous'
@abilities ||= begin subject_key = subject ? "#{subject.class.name}/#{subject.id}" : 'global'
abilities = Six.new key = "/ability/#{user_key}/#{subject_key}"
abilities << self RequestStore[key] ||= uncached_allowed(user, subject).freeze
abilities
end
end end
private private
def restricted_public_level? def uncached_allowed(user, subject)
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) BasePolicy.class_for(subject).abilities(user, subject)
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, Gitlab::Access::REPORTER)
rules.delete(:admin_issue)
rules.delete(:read_issue)
rules.delete(:update_issue)
end
rules
end
def project_group_member?(project, user)
project.group &&
(
project.group.members.exists?(user_id: user.id) ||
project.group.requesters.exists?(user_id: user.id)
)
end end
end end
end end
...@@ -70,7 +70,7 @@ class Event < ActiveRecord::Base ...@@ -70,7 +70,7 @@ class Event < ActiveRecord::Base
elsif created_project? elsif created_project?
true true
elsif issue? || issue_note? elsif issue? || issue_note?
Ability.abilities.allowed?(user, :read_issue, note? ? note_target : target) Ability.allowed?(user, :read_issue, note? ? note_target : target)
else else
((merge_request? || note?) && target.present?) || milestone? ((merge_request? || note?) && target.present?) || milestone?
end end
......
...@@ -429,7 +429,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -429,7 +429,7 @@ class MergeRequest < ActiveRecord::Base
def can_remove_source_branch?(current_user) def can_remove_source_branch?(current_user)
!source_project.protected_branch?(source_branch) && !source_project.protected_branch?(source_branch) &&
!source_project.root_ref?(source_branch) && !source_project.root_ref?(source_branch) &&
Ability.abilities.allowed?(current_user, :push_code, source_project) && Ability.allowed?(current_user, :push_code, source_project) &&
diff_head_commit == source_branch_head diff_head_commit == source_branch_head
end end
......
...@@ -772,6 +772,10 @@ class Project < ActiveRecord::Base ...@@ -772,6 +772,10 @@ class Project < ActiveRecord::Base
update_column(:has_external_issue_tracker, services.external_issue_trackers.any?) update_column(:has_external_issue_tracker, services.external_issue_trackers.any?)
end end
def has_wiki?
wiki_enabled? || has_external_wiki?
end
def external_wiki def external_wiki
if has_external_wiki.nil? if has_external_wiki.nil?
cache_has_external_wiki # Populate cache_has_external_wiki # Populate
......
...@@ -497,16 +497,12 @@ class User < ActiveRecord::Base ...@@ -497,16 +497,12 @@ class User < ActiveRecord::Base
can?(:create_group, nil) can?(:create_group, nil)
end end
def abilities
Ability.abilities
end
def can_select_namespace? def can_select_namespace?
several_namespaces? || admin several_namespaces? || admin
end end
def can?(action, subject) def can?(action, subject)
abilities.allowed?(self, action, subject) Ability.allowed?(self, action, subject)
end end
def first_name def first_name
......
class BasePolicy
class RuleSet
attr_reader :can_set, :cannot_set
def initialize(can_set, cannot_set)
@can_set = can_set
@cannot_set = cannot_set
end
def size
to_set.size
end
def self.empty
new(Set.new, Set.new)
end
def can?(ability)
@can_set.include?(ability) && !@cannot_set.include?(ability)
end
def include?(ability)
can?(ability)
end
def to_set
@can_set - @cannot_set
end
def merge(other)
@can_set.merge(other.can_set)
@cannot_set.merge(other.cannot_set)
end
def can!(*abilities)
@can_set.merge(abilities)
end
def cannot!(*abilities)
@cannot_set.merge(abilities)
end
def freeze
@can_set.freeze
@cannot_set.freeze
super
end
end
def self.abilities(user, subject)
new(user, subject).abilities
end
def self.class_for(subject)
return GlobalPolicy if subject.nil?
subject.class.ancestors.each do |klass|
next unless klass.name
begin
policy_class = "#{klass.name}Policy".constantize
# NOTE: the < operator here tests whether policy_class
# inherits from BasePolicy
return policy_class if policy_class < BasePolicy
rescue NameError
nil
end
end
raise "no policy for #{subject.class.name}"
end
attr_reader :user, :subject
def initialize(user, subject)
@user = user
@subject = subject
end
def abilities
return RuleSet.empty if @user && @user.blocked?
return anonymous_abilities if @user.nil?
collect_rules { rules }
end
def anonymous_abilities
collect_rules { anonymous_rules }
end
def anonymous_rules
rules
end
def delegate!(new_subject)
@rule_set.merge(Ability.allowed(@user, new_subject))
end
def can?(rule)
@rule_set.can?(rule)
end
def can!(*rules)
@rule_set.can!(*rules)
end
def cannot!(*rules)
@rule_set.cannot!(*rules)
end
private
def collect_rules(&b)
@rule_set = RuleSet.empty
yield
@rule_set
end
end
module Ci
class BuildPolicy < CommitStatusPolicy
def rules
super
# 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|
cannot! :"#{rule}_commit_status" unless can? :"#{rule}_build"
end
end
end
end
module Ci
class RunnerPolicy < BasePolicy
def rules
return unless @user
can! :assign_runner if @user.is_admin?
return if @subject.is_shared? || @subject.locked?
can! :assign_runner if @user.ci_authorized_runners.include?(@subject)
end
end
end
class CommitStatusPolicy < BasePolicy
def rules
delegate! @subject.project
end
end
class DeploymentPolicy < BasePolicy
def rules
delegate! @subject.project
end
end
class EnvironmentPolicy < BasePolicy
def rules
delegate! @subject.project
end
end
class ExternalIssuePolicy < BasePolicy
def rules
delegate! @subject.project
end
end
class GlobalPolicy < BasePolicy
def rules
return unless @user
can! :create_group if @user.can_create_group
can! :read_users_list
end
end
class GroupMemberPolicy < BasePolicy
def rules
return unless @user
target_user = @subject.user
group = @subject.group
return if group.last_owner?(target_user)
can_manage = Ability.allowed?(@user, :admin_group_member, group)
if can_manage
can! :update_group_member
can! :destroy_group_member
elsif @user == target_user
can! :destroy_group_member
end
end
end
class GroupPolicy < BasePolicy
def rules
can! :read_group if @subject.public?
return unless @user
globally_viewable = @subject.public? || (@subject.internal? && !@user.external?)
member = @subject.users.include?(@user)
owner = @user.admin? || @subject.has_owner?(@user)
master = owner || @subject.has_master?(@user)
can_read = false
can_read ||= globally_viewable
can_read ||= member
can_read ||= @user.admin?
can_read ||= GroupProjectsFinder.new(@subject).execute(@user).any?
can! :read_group if can_read
# Only group masters and group owners can create new projects
if master
can! :create_projects
can! :admin_milestones
end
# Only group owner and administrators can admin group
if owner
can! :admin_group
can! :admin_namespace
can! :admin_group_member
can! :change_visibility_level
end
if globally_viewable && @subject.request_access_enabled && !member
can! :request_access
end
# EE-only
cannot! :admin_group_member if @subject.ldap_synced?
end
def can_read_group?
return true if @subject.public?
return true if @user.admin?
return true if @subject.internal? && !@user.external?
return true if @subject.users.include?(@user)
GroupProjectsFinder.new(@subject).execute(@user).any?
end
end
class IssuablePolicy < BasePolicy
def action_name
@subject.class.name.underscore
end
def rules
if @user && (@subject.author == @user || @subject.assignee == @user)
can! :"read_#{action_name}"
can! :"update_#{action_name}"
end
delegate! @subject.project
end
end
class IssuePolicy < IssuablePolicy
def issue
@subject
end
def rules
super
if @subject.confidential? && !can_read_confidential?
cannot! :read_issue
cannot! :admin_issue
cannot! :update_issue
cannot! :read_issue
end
end
private
def can_read_confidential?
return false unless @user
return true if @user.admin?
return true if @subject.author == @user
return true if @subject.assignee == @user
return true if @subject.project.team.member?(@user, Gitlab::Access::REPORTER)
false
end
end
class MergeRequestPolicy < IssuablePolicy
# pass
end
class NamespacePolicy < BasePolicy
def rules
return unless @user
if @subject.owner == @user || @user.admin?
can! :create_projects
can! :admin_namespace
end
end
end
class NotePolicy < BasePolicy
def rules
delegate! @subject.project
return unless @user
if @subject.author == @user
can! :read_note
can! :update_note
can! :admin_note
can! :resolve_note
end
if @subject.for_merge_request? &&
@subject.noteable.author == @user
can! :resolve_note
end
end
end
class PersonalSnippetPolicy < BasePolicy
def rules
can! :read_personal_snippet if @subject.public?
return unless @user
if @subject.author == @user
can! :read_personal_snippet
can! :update_personal_snippet
can! :admin_personal_snippet
end
if @subject.internal? && !@user.external?
can! :read_personal_snippet
end
end
end
class ProjectMemberPolicy < BasePolicy
def rules
# anonymous users have no abilities here
return unless @user
target_user = @subject.user
project = @subject.project
return if target_user == project.owner
can_manage = Ability.allowed?(@user, :admin_project_member, project)
if can_manage
can! :update_project_member
can! :destroy_project_member
end
if @user == target_user
can! :destroy_project_member
end
end
end
class ProjectPolicy < BasePolicy
def rules
team_access!(user)
owner = user.admin? ||
project.owner == user ||
(project.group && project.group.has_owner?(user))
owner_access! if owner
if project.public? || (project.internal? && !user.external?)
guest_access!
public_access!
# Allow to read builds for internal projects
can! :read_build if project.public_builds?
if project.request_access_enabled &&
!(owner || project.team.member?(user) || project_group_member?(user))
can! :request_access
end
end
archived_access! if project.archived?
# EE-only
can! :change_repository_storage if user.admin?
disabled_features!
end
def project
@subject
end
def guest_access!
can! :read_project
can! :read_board
can! :read_list
can! :read_wiki
can! :read_issue
can! :read_label
can! :read_milestone
can! :read_project_snippet
can! :read_project_member
can! :read_merge_request
can! :read_note
can! :create_project
can! :create_issue
can! :create_note
can! :upload_file
end
def reporter_access!
can! :download_code
can! :fork_project
can! :create_project_snippet
can! :update_issue
can! :admin_issue
can! :admin_label
can! :admin_list
can! :read_commit_status
can! :read_build
can! :read_container_image
can! :read_pipeline
can! :read_environment
can! :read_deployment
end
def developer_access!
can! :admin_merge_request
can! :update_merge_request
can! :create_commit_status
can! :update_commit_status
can! :create_build
can! :update_build
can! :create_pipeline
can! :update_pipeline
can! :create_merge_request
can! :create_wiki
can! :push_code
can! :resolve_note
can! :create_container_image
can! :update_container_image
can! :create_environment
can! :create_deployment
end
def master_access!
can! :push_code_to_protected_branches
can! :update_project_snippet
can! :update_environment
can! :update_deployment
can! :admin_milestone
can! :admin_project_snippet
can! :admin_project_member
can! :admin_merge_request
can! :admin_note
can! :admin_wiki
can! :admin_project
can! :admin_commit_status
can! :admin_build
can! :admin_container_image
can! :admin_pipeline
can! :admin_environment
can! :admin_deployment
# EE-only
can! :admin_path_locks
can! :admin_pages
can! :read_pages
can! :update_pages
end
def public_access!
can! :download_code
can! :fork_project
can! :read_commit_status
can! :read_pipeline
can! :read_container_image
end
def owner_access!
guest_access!
reporter_access!
developer_access!
master_access!
can! :change_namespace
can! :change_visibility_level
can! :rename_project
can! :remove_project
can! :archive_project
can! :remove_fork_project
can! :destroy_merge_request
can! :destroy_issue
# EE-only
can! :remove_pages
end
# Push abilities on the users team role
def team_access!(user)
access = project.team.max_member_access(user.id)
guest_access! if access >= Gitlab::Access::GUEST
reporter_access! if access >= Gitlab::Access::REPORTER
developer_access! if access >= Gitlab::Access::DEVELOPER
master_access! if access >= Gitlab::Access::MASTER
end
def archived_access!
cannot! :create_merge_request
cannot! :push_code
cannot! :push_code_to_protected_branches
cannot! :update_merge_request
cannot! :admin_merge_request
end
def disabled_features!
unless project.issues_enabled
cannot!(*named_abilities(:issue))
end
unless project.merge_requests_enabled
cannot!(*named_abilities(:merge_request))
end
unless project.issues_enabled || project.merge_requests_enabled
cannot!(*named_abilities(:label))
cannot!(*named_abilities(:milestone))
end
unless project.snippets_enabled
cannot!(*named_abilities(:project_snippet))
end
unless project.has_wiki?
cannot!(*named_abilities(:wiki))
end
unless project.builds_enabled
cannot!(*named_abilities(:build))
cannot!(*named_abilities(:pipeline))
cannot!(*named_abilities(:environment))
cannot!(*named_abilities(:deployment))
end
unless project.container_registry_enabled
cannot!(*named_abilities(:container_image))
end
# EE-only
if defined?(License) && License.block_changes?
cannot! :create_issue
cannot! :create_merge_request
cannot! :push_code
cannot! :push_code_to_protected_branches
end
end
def anonymous_rules
return unless project.public?
can! :read_project
can! :read_board
can! :read_list
can! :read_wiki
can! :read_label
can! :read_milestone
can! :read_project_snippet
can! :read_project_member
can! :read_merge_request
can! :read_note
can! :read_pipeline
can! :read_commit_status
can! :read_container_image
can! :download_code
# NOTE: may be overridden by IssuePolicy
can! :read_issue
# Allow to read builds by anonymous user if guests are allowed
can! :read_build if project.public_builds?
disabled_features!
end
def project_group_member?(user)
project.group &&
(
project.group.members.exists?(user_id: user.id) ||
project.group.requesters.exists?(user_id: user.id)
)
end
def named_abilities(name)
[
:"read_#{name}",
:"create_#{name}",
:"update_#{name}",
:"admin_#{name}"
]
end
end
class ProjectSnippetPolicy < BasePolicy
def rules
can! :read_project_snippet if @subject.public?
return unless @user
if @user && @subject.author == @user || @user.admin?
can! :read_project_snippet
can! :update_project_snippet
can! :admin_project_snippet
end
if @subject.internal? && !@user.external?
can! :read_project_snippet
end
if @subject.private? && @subject.project.team.member?(@user)
can! :read_project_snippet
end
end
end
class UserPolicy < BasePolicy
include Gitlab::CurrentSettings
def rules
can! :read_user if @user || !restricted_public_level?
end
def restricted_public_level?
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end
end
...@@ -7,12 +7,8 @@ class BaseService ...@@ -7,12 +7,8 @@ class BaseService
@project, @current_user, @params = project, user, params.dup @project, @current_user, @params = project, user, params.dup
end end
def abilities
Ability.abilities
end
def can?(object, action, subject) def can?(object, action, subject)
abilities.allowed?(object, action, subject) Ability.allowed?(object, action, subject)
end end
def notification_service def notification_service
......
...@@ -32,7 +32,7 @@ module API ...@@ -32,7 +32,7 @@ module API
# Example Request: # Example Request:
# POST /groups # POST /groups
post do post do
authorize! :create_group, current_user authorize! :create_group
required_attributes! [:name, :path] required_attributes! [:name, :path]
attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :membership_lock, :share_with_group_lock] attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :membership_lock, :share_with_group_lock]
......
...@@ -136,7 +136,7 @@ module API ...@@ -136,7 +136,7 @@ module API
forbidden! unless current_user.is_admin? forbidden! unless current_user.is_admin?
end end
def authorize!(action, subject) def authorize!(action, subject = nil)
forbidden! unless can?(current_user, action, subject) forbidden! unless can?(current_user, action, subject)
end end
...@@ -159,7 +159,7 @@ module API ...@@ -159,7 +159,7 @@ module API
end end
def can?(object, action, subject) def can?(object, action, subject)
abilities.allowed?(object, action, subject) Ability.allowed?(object, action, subject)
end end
# Checks the occurrences of required attributes, each attribute must be present in the params hash # Checks the occurrences of required attributes, each attribute must be present in the params hash
...@@ -419,14 +419,6 @@ module API ...@@ -419,14 +419,6 @@ module API
links.join(', ') links.join(', ')
end end
def abilities
@abilities ||= begin
abilities = Six.new
abilities << Ability
abilities
end
end
def secret_token def secret_token
File.read(Gitlab.config.gitlab_shell.secret_file).chomp File.read(Gitlab.config.gitlab_shell.secret_file).chomp
end end
......
...@@ -211,7 +211,7 @@ module Banzai ...@@ -211,7 +211,7 @@ module Banzai
end end
def can?(user, permission, subject) def can?(user, permission, subject)
Ability.abilities.allowed?(user, permission, subject) Ability.allowed?(user, permission, subject)
end end
def find_projects_for_hash_keys(hash) def find_projects_for_hash_keys(hash)
......
...@@ -41,8 +41,8 @@ describe Projects::Boards::IssuesController do ...@@ -41,8 +41,8 @@ describe Projects::Boards::IssuesController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
allow(Ability.abilities).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability.abilities).to receive(:allowed?).with(user, :read_issue, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_issue, project).and_return(false)
end end
it 'returns a successful 403 response' do it 'returns a successful 403 response' do
......
...@@ -35,8 +35,8 @@ describe Projects::Boards::ListsController do ...@@ -35,8 +35,8 @@ describe Projects::Boards::ListsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
allow(Ability.abilities).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability.abilities).to receive(:allowed?).with(user, :read_list, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_list, project).and_return(false)
end end
it 'returns a successful 403 response' do it 'returns a successful 403 response' do
......
...@@ -23,8 +23,8 @@ describe Projects::BoardsController do ...@@ -23,8 +23,8 @@ describe Projects::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
allow(Ability.abilities).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability.abilities).to receive(:allowed?).with(user, :read_board, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
end end
it 'returns a successful 404 response' do it 'returns a successful 404 response' do
......
...@@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do ...@@ -30,7 +30,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
it 'returns the nodes if the attribute value equals the current project ID' do it 'returns the nodes if the attribute value equals the current project ID' do
link['data-project'] = project.id.to_s link['data-project'] = project.id.to_s
expect(Ability.abilities).not_to receive(:allowed?) expect(Ability).not_to receive(:allowed?)
expect(subject.nodes_visible_to_user(user, [link])).to eq([link]) expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end end
...@@ -39,7 +39,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do ...@@ -39,7 +39,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
link['data-project'] = other_project.id.to_s link['data-project'] = other_project.id.to_s
expect(Ability.abilities).to receive(:allowed?). expect(Ability).to receive(:allowed?).
with(user, :read_project, other_project). with(user, :read_project, other_project).
and_return(true) and_return(true)
...@@ -57,7 +57,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do ...@@ -57,7 +57,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
link['data-project'] = other_project.id.to_s link['data-project'] = other_project.id.to_s
expect(Ability.abilities).to receive(:allowed?). expect(Ability).to receive(:allowed?).
with(user, :read_project, other_project). with(user, :read_project, other_project).
and_return(false) and_return(false)
...@@ -221,7 +221,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do ...@@ -221,7 +221,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
it 'delegates the permissions check to the Ability class' do it 'delegates the permissions check to the Ability class' do
user = double(:user) user = double(:user)
expect(Ability.abilities).to receive(:allowed?). expect(Ability).to receive(:allowed?).
with(user, :read_project, project) with(user, :read_project, project)
subject.can?(user, :read_project, project) subject.can?(user, :read_project, project)
......
...@@ -82,7 +82,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do ...@@ -82,7 +82,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
end end
it 'returns the nodes if the user can read the group' do it 'returns the nodes if the user can read the group' do
expect(Ability.abilities).to receive(:allowed?). expect(Ability).to receive(:allowed?).
with(user, :read_group, group). with(user, :read_group, group).
and_return(true) and_return(true)
...@@ -90,7 +90,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do ...@@ -90,7 +90,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
end end
it 'returns an empty Array if the user can not read the group' do it 'returns an empty Array if the user can not read the group' do
expect(Ability.abilities).to receive(:allowed?). expect(Ability).to receive(:allowed?).
with(user, :read_group, group). with(user, :read_group, group).
and_return(false) and_return(false)
...@@ -103,7 +103,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do ...@@ -103,7 +103,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
it 'returns the nodes if the attribute value equals the current project ID' do it 'returns the nodes if the attribute value equals the current project ID' do
link['data-project'] = project.id.to_s link['data-project'] = project.id.to_s
expect(Ability.abilities).not_to receive(:allowed?) expect(Ability).not_to receive(:allowed?)
expect(subject.nodes_visible_to_user(user, [link])).to eq([link]) expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end end
...@@ -113,7 +113,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do ...@@ -113,7 +113,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
link['data-project'] = other_project.id.to_s link['data-project'] = other_project.id.to_s
expect(Ability.abilities).to receive(:allowed?). expect(Ability).to receive(:allowed?).
with(user, :read_project, other_project). with(user, :read_project, other_project).
and_return(true) and_return(true)
...@@ -125,7 +125,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do ...@@ -125,7 +125,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
link['data-project'] = other_project.id.to_s link['data-project'] = other_project.id.to_s
expect(Ability.abilities).to receive(:allowed?). expect(Ability).to receive(:allowed?).
with(user, :read_project, other_project). with(user, :read_project, other_project).
and_return(false) and_return(false)
......
...@@ -171,70 +171,6 @@ describe Ability, lib: true do ...@@ -171,70 +171,6 @@ describe Ability, lib: true do
end end
end end
shared_examples_for ".project_abilities" do |enable_request_store|
before do
RequestStore.begin! if enable_request_store
end
after do
if enable_request_store
RequestStore.end!
RequestStore.clear!
end
end
describe '.project_abilities' do
let!(:project) { create(:empty_project, :public) }
let!(:user) { create(:user) }
it 'returns permissions for admin user' do
admin = create(:admin)
results = described_class.project_abilities(admin, project)
expect(results.count).to eq(74)
end
it 'returns permissions for an owner' do
results = described_class.project_abilities(project.owner, project)
expect(results.count).to eq(73)
end
it 'returns permissions for a master' do
project.team << [user, :master]
results = described_class.project_abilities(user, project)
expect(results.count).to eq(64)
end
it 'returns permissions for a developer' do
project.team << [user, :developer]
results = described_class.project_abilities(user, project)
expect(results.count).to eq(44)
end
it 'returns permissions for a guest' do
project.team << [user, :guest]
results = described_class.project_abilities(user, project)
expect(results.count).to eq(21)
end
end
end
describe '.project_abilities with RequestStore' do
it_behaves_like ".project_abilities", true
end
describe '.project_abilities without RequestStore' do
it_behaves_like ".project_abilities", false
end
describe '.issues_readable_by_user' do describe '.issues_readable_by_user' do
context 'with an admin user' do context 'with an admin user' do
it 'returns all given issues' do it 'returns all given issues' do
...@@ -282,4 +218,17 @@ describe Ability, lib: true do ...@@ -282,4 +218,17 @@ describe Ability, lib: true do
end end
end end
end end
describe '.project_disabled_features_rules' do
let(:project) { build(:project) }
subject { described_class.allowed(project.owner, project) }
context 'wiki named abilities' do
it 'disables wiki abilities if the project has no wiki' do
expect(project).to receive(:has_wiki?).and_return(false)
expect(subject).not_to include(:read_wiki, :create_wiki, :update_wiki, :admin_wiki)
end
end
end
end end
...@@ -71,9 +71,6 @@ describe ProjectMember, models: true do ...@@ -71,9 +71,6 @@ describe ProjectMember, models: true do
describe :import_team do describe :import_team do
before do before do
@abilities = Six.new
@abilities << Ability
@project_1 = create :project @project_1 = create :project
@project_2 = create :project @project_2 = create :project
...@@ -92,8 +89,8 @@ describe ProjectMember, models: true do ...@@ -92,8 +89,8 @@ describe ProjectMember, models: true do
it { expect(@project_2.users).to include(@user_1) } it { expect(@project_2.users).to include(@user_1) }
it { expect(@project_2.users).to include(@user_2) } it { expect(@project_2.users).to include(@user_2) }
it { expect(@abilities.allowed?(@user_1, :create_project, @project_2)).to be_truthy } it { expect(Ability.allowed?(@user_1, :create_project, @project_2)).to be_truthy }
it { expect(@abilities.allowed?(@user_2, :read_project, @project_2)).to be_truthy } it { expect(Ability.allowed?(@user_2, :read_project, @project_2)).to be_truthy }
end end
describe 'project 1 should not be changed' do describe 'project 1 should not be changed' do
......
...@@ -85,8 +85,6 @@ describe Note, models: true do ...@@ -85,8 +85,6 @@ describe Note, models: true do
@u1 = create(:user) @u1 = create(:user)
@u2 = create(:user) @u2 = create(:user)
@u3 = create(:user) @u3 = create(:user)
@abilities = Six.new
@abilities << Ability
end end
describe 'read' do describe 'read' do
...@@ -95,9 +93,9 @@ describe Note, models: true do ...@@ -95,9 +93,9 @@ describe Note, models: true do
@p2.project_members.create(user: @u3, access_level: ProjectMember::GUEST) @p2.project_members.create(user: @u3, access_level: ProjectMember::GUEST)
end end
it { expect(@abilities.allowed?(@u1, :read_note, @p1)).to be_falsey } it { expect(Ability.allowed?(@u1, :read_note, @p1)).to be_falsey }
it { expect(@abilities.allowed?(@u2, :read_note, @p1)).to be_truthy } it { expect(Ability.allowed?(@u2, :read_note, @p1)).to be_truthy }
it { expect(@abilities.allowed?(@u3, :read_note, @p1)).to be_falsey } it { expect(Ability.allowed?(@u3, :read_note, @p1)).to be_falsey }
end end
describe 'write' do describe 'write' do
...@@ -106,9 +104,9 @@ describe Note, models: true do ...@@ -106,9 +104,9 @@ describe Note, models: true do
@p2.project_members.create(user: @u3, access_level: ProjectMember::DEVELOPER) @p2.project_members.create(user: @u3, access_level: ProjectMember::DEVELOPER)
end end
it { expect(@abilities.allowed?(@u1, :create_note, @p1)).to be_falsey } it { expect(Ability.allowed?(@u1, :create_note, @p1)).to be_falsey }
it { expect(@abilities.allowed?(@u2, :create_note, @p1)).to be_truthy } it { expect(Ability.allowed?(@u2, :create_note, @p1)).to be_truthy }
it { expect(@abilities.allowed?(@u3, :create_note, @p1)).to be_falsey } it { expect(Ability.allowed?(@u3, :create_note, @p1)).to be_falsey }
end end
describe 'admin' do describe 'admin' do
...@@ -118,9 +116,9 @@ describe Note, models: true do ...@@ -118,9 +116,9 @@ describe Note, models: true do
@p2.project_members.create(user: @u3, access_level: ProjectMember::MASTER) @p2.project_members.create(user: @u3, access_level: ProjectMember::MASTER)
end end
it { expect(@abilities.allowed?(@u1, :admin_note, @p1)).to be_falsey } it { expect(Ability.allowed?(@u1, :admin_note, @p1)).to be_falsey }
it { expect(@abilities.allowed?(@u2, :admin_note, @p1)).to be_truthy } it { expect(Ability.allowed?(@u2, :admin_note, @p1)).to be_truthy }
it { expect(@abilities.allowed?(@u3, :admin_note, @p1)).to be_falsey } it { expect(Ability.allowed?(@u3, :admin_note, @p1)).to be_falsey }
end end
end end
......
require 'spec_helper'
describe Project, models: true do
describe 'authorization' do
before do
@p1 = create(:project)
@u1 = create(:user)
@u2 = create(:user)
@u3 = create(:user)
@u4 = @p1.owner
@abilities = Six.new
@abilities << Ability
end
let(:guest_actions) { Ability.project_guest_rules }
let(:report_actions) { Ability.project_report_rules }
let(:dev_actions) { Ability.project_dev_rules }
let(:master_actions) { Ability.project_master_rules }
let(:owner_actions) { Ability.project_owner_rules }
describe "Non member rules" do
it "denies for non-project users any actions" do
owner_actions.each do |action|
expect(@abilities.allowed?(@u1, action, @p1)).to be_falsey
end
end
end
describe "Guest Rules" do
before do
@p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::GUEST)
end
it "allows for project user any guest actions" do
guest_actions.each do |action|
expect(@abilities.allowed?(@u2, action, @p1)).to be_truthy
end
end
end
describe "Report Rules" do
before do
@p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::REPORTER)
end
it "allows for project user any report actions" do
report_actions.each do |action|
expect(@abilities.allowed?(@u2, action, @p1)).to be_truthy
end
end
end
describe "Developer Rules" do
before do
@p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::REPORTER)
@p1.project_members.create(project: @p1, user: @u3, access_level: ProjectMember::DEVELOPER)
end
it "denies for developer master-specific actions" do
[dev_actions - report_actions].each do |action|
expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey
end
end
it "allows for project user any dev actions" do
dev_actions.each do |action|
expect(@abilities.allowed?(@u3, action, @p1)).to be_truthy
end
end
end
describe "Master Rules" do
before do
@p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::DEVELOPER)
@p1.project_members.create(project: @p1, user: @u3, access_level: ProjectMember::MASTER)
end
it "denies for developer master-specific actions" do
[master_actions - dev_actions].each do |action|
expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey
end
end
it "allows for project user any master actions" do
master_actions.each do |action|
expect(@abilities.allowed?(@u3, action, @p1)).to be_truthy
end
end
end
describe "Owner Rules" do
before do
@p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::DEVELOPER)
@p1.project_members.create(project: @p1, user: @u3, access_level: ProjectMember::MASTER)
end
it "denies for masters admin-specific actions" do
[owner_actions - master_actions].each do |action|
expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey
end
end
it "allows for project owner any admin actions" do
owner_actions.each do |action|
expect(@abilities.allowed?(@u4, action, @p1)).to be_truthy
end
end
end
end
end
...@@ -516,6 +516,18 @@ describe Project, models: true do ...@@ -516,6 +516,18 @@ describe Project, models: true do
end end
end end
describe '#has_wiki?' do
let(:no_wiki_project) { build(:project, wiki_enabled: false, has_external_wiki: false) }
let(:wiki_enabled_project) { build(:project, wiki_enabled: true) }
let(:external_wiki_project) { build(:project, has_external_wiki: true) }
it 'returns true if project is wiki enabled or has external wiki' do
expect(wiki_enabled_project).to have_wiki
expect(external_wiki_project).to have_wiki
expect(no_wiki_project).not_to have_wiki
end
end
describe '#external_wiki' do describe '#external_wiki' do
let(:project) { create(:project) } let(:project) { create(:project) }
......
require 'spec_helper'
describe ProjectPolicy, models: true do
let(:project) { create(:empty_project, :public) }
let(:guest) { create(:user) }
let(:reporter) { create(:user) }
let(:dev) { create(:user) }
let(:master) { create(:user) }
let(:owner) { create(:user) }
let(:admin) { create(:admin) }
let(:users_ordered_by_permissions) do
[nil, guest, reporter, dev, master, owner, admin]
end
let(:users_permissions) do
users_ordered_by_permissions.map { |u| Ability.allowed(u, project).size }
end
before do
project.team << [guest, :guest]
project.team << [master, :master]
project.team << [dev, :developer]
project.team << [reporter, :reporter]
group = create(:group)
project.project_group_links.create(
group: group,
group_access: Gitlab::Access::MASTER)
group.add_owner(owner)
end
it 'returns increasing permissions for each level' do
expect(users_permissions).to eq(users_permissions.sort.uniq)
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment