summaryrefslogtreecommitdiff
path: root/app/policies
diff options
context:
space:
mode:
Diffstat (limited to 'app/policies')
-rw-r--r--app/policies/base_policy.rb116
-rw-r--r--app/policies/ci/build_policy.rb13
-rw-r--r--app/policies/ci/runner_policy.rb13
-rw-r--r--app/policies/commit_status_policy.rb5
-rw-r--r--app/policies/deployment_policy.rb5
-rw-r--r--app/policies/environment_policy.rb5
-rw-r--r--app/policies/external_issue_policy.rb5
-rw-r--r--app/policies/global_policy.rb8
-rw-r--r--app/policies/group_member_policy.rb19
-rw-r--r--app/policies/group_policy.rb45
-rw-r--r--app/policies/issuable_policy.rb14
-rw-r--r--app/policies/issue_policy.rb28
-rw-r--r--app/policies/merge_request_policy.rb3
-rw-r--r--app/policies/namespace_policy.rb10
-rw-r--r--app/policies/note_policy.rb19
-rw-r--r--app/policies/personal_snippet_policy.rb16
-rw-r--r--app/policies/project_member_policy.rb22
-rw-r--r--app/policies/project_policy.rb224
-rw-r--r--app/policies/project_snippet_policy.rb20
-rw-r--r--app/policies/user_policy.rb11
20 files changed, 601 insertions, 0 deletions
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
new file mode 100644
index 00000000000..118c100ca11
--- /dev/null
+++ b/app/policies/base_policy.rb
@@ -0,0 +1,116 @@
+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
diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb
new file mode 100644
index 00000000000..2232e231cf8
--- /dev/null
+++ b/app/policies/ci/build_policy.rb
@@ -0,0 +1,13 @@
+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
diff --git a/app/policies/ci/runner_policy.rb b/app/policies/ci/runner_policy.rb
new file mode 100644
index 00000000000..7edd383530d
--- /dev/null
+++ b/app/policies/ci/runner_policy.rb
@@ -0,0 +1,13 @@
+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
diff --git a/app/policies/commit_status_policy.rb b/app/policies/commit_status_policy.rb
new file mode 100644
index 00000000000..593df738328
--- /dev/null
+++ b/app/policies/commit_status_policy.rb
@@ -0,0 +1,5 @@
+class CommitStatusPolicy < BasePolicy
+ def rules
+ delegate! @subject.project
+ end
+end
diff --git a/app/policies/deployment_policy.rb b/app/policies/deployment_policy.rb
new file mode 100644
index 00000000000..163d070ff90
--- /dev/null
+++ b/app/policies/deployment_policy.rb
@@ -0,0 +1,5 @@
+class DeploymentPolicy < BasePolicy
+ def rules
+ delegate! @subject.project
+ end
+end
diff --git a/app/policies/environment_policy.rb b/app/policies/environment_policy.rb
new file mode 100644
index 00000000000..f4219569161
--- /dev/null
+++ b/app/policies/environment_policy.rb
@@ -0,0 +1,5 @@
+class EnvironmentPolicy < BasePolicy
+ def rules
+ delegate! @subject.project
+ end
+end
diff --git a/app/policies/external_issue_policy.rb b/app/policies/external_issue_policy.rb
new file mode 100644
index 00000000000..d9e28bd107a
--- /dev/null
+++ b/app/policies/external_issue_policy.rb
@@ -0,0 +1,5 @@
+class ExternalIssuePolicy < BasePolicy
+ def rules
+ delegate! @subject.project
+ end
+end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
new file mode 100644
index 00000000000..3c2fbe6b56b
--- /dev/null
+++ b/app/policies/global_policy.rb
@@ -0,0 +1,8 @@
+class GlobalPolicy < BasePolicy
+ def rules
+ return unless @user
+
+ can! :create_group if @user.can_create_group
+ can! :read_users_list
+ end
+end
diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb
new file mode 100644
index 00000000000..62335527654
--- /dev/null
+++ b/app/policies/group_member_policy.rb
@@ -0,0 +1,19 @@
+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
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
new file mode 100644
index 00000000000..97ff6233968
--- /dev/null
+++ b/app/policies/group_policy.rb
@@ -0,0 +1,45 @@
+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
+ 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
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
new file mode 100644
index 00000000000..c253f9a9399
--- /dev/null
+++ b/app/policies/issuable_policy.rb
@@ -0,0 +1,14 @@
+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
diff --git a/app/policies/issue_policy.rb b/app/policies/issue_policy.rb
new file mode 100644
index 00000000000..bd1811a3c54
--- /dev/null
+++ b/app/policies/issue_policy.rb
@@ -0,0 +1,28 @@
+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
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
new file mode 100644
index 00000000000..bc3afc626fb
--- /dev/null
+++ b/app/policies/merge_request_policy.rb
@@ -0,0 +1,3 @@
+class MergeRequestPolicy < IssuablePolicy
+ # pass
+end
diff --git a/app/policies/namespace_policy.rb b/app/policies/namespace_policy.rb
new file mode 100644
index 00000000000..29bb357e00a
--- /dev/null
+++ b/app/policies/namespace_policy.rb
@@ -0,0 +1,10 @@
+class NamespacePolicy < BasePolicy
+ def rules
+ return unless @user
+
+ if @subject.owner == @user || @user.admin?
+ can! :create_projects
+ can! :admin_namespace
+ end
+ end
+end
diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb
new file mode 100644
index 00000000000..83847466ee2
--- /dev/null
+++ b/app/policies/note_policy.rb
@@ -0,0 +1,19 @@
+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
diff --git a/app/policies/personal_snippet_policy.rb b/app/policies/personal_snippet_policy.rb
new file mode 100644
index 00000000000..46c5aa1a5be
--- /dev/null
+++ b/app/policies/personal_snippet_policy.rb
@@ -0,0 +1,16 @@
+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
diff --git a/app/policies/project_member_policy.rb b/app/policies/project_member_policy.rb
new file mode 100644
index 00000000000..1c038dddd4b
--- /dev/null
+++ b/app/policies/project_member_policy.rb
@@ -0,0 +1,22 @@
+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
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
new file mode 100644
index 00000000000..15a9f2f0dca
--- /dev/null
+++ b/app/policies/project_policy.rb
@@ -0,0 +1,224 @@
+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?
+
+ 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
+ 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
+ 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
+ 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
diff --git a/app/policies/project_snippet_policy.rb b/app/policies/project_snippet_policy.rb
new file mode 100644
index 00000000000..57acccfafd9
--- /dev/null
+++ b/app/policies/project_snippet_policy.rb
@@ -0,0 +1,20 @@
+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
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
new file mode 100644
index 00000000000..03a2499e263
--- /dev/null
+++ b/app/policies/user_policy.rb
@@ -0,0 +1,11 @@
+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