Commit 75686b93 authored by Tyler Amos's avatar Tyler Amos

Block abilities when project over storage

- Introduce ReadonlyAbilities concerns in order to DRY up shared
  abilities and features between archived and over_storage_limit
  rules.
- Include and use ReadonlyAbilities concern in project policy for
  archived and over_storage_limit rules.
- Add create_update_admin method to CrudPolicyHelpers.
- Add specs for CrudPolicyHelpers which was missing previously.
parent c566de6b
...@@ -12,11 +12,17 @@ module CrudPolicyHelpers ...@@ -12,11 +12,17 @@ module CrudPolicyHelpers
end end
def create_update_admin_destroy(name) def create_update_admin_destroy(name)
[
*create_update_admin(name),
:"destroy_#{name}"
]
end
def create_update_admin(name)
[ [
:"create_#{name}", :"create_#{name}",
:"update_#{name}", :"update_#{name}",
:"admin_#{name}", :"admin_#{name}"
:"destroy_#{name}"
] ]
end end
end end
......
# frozen_string_literal: true
module ReadonlyAbilities
extend ActiveSupport::Concern
READONLY_ABILITIES = %i[
admin_tag
push_code
push_to_delete_protected_branch
request_access
upload_file
resolve_note
create_merge_request_from
create_merge_request_in
award_emoji
].freeze
READONLY_FEATURES = %i[
issue
list
merge_request
label
milestone
snippet
wiki
design
note
pipeline
pipeline_schedule
build
trigger
environment
deployment
commit_status
container_image
pages
cluster
release
].freeze
class_methods do
def readonly_abilities
READONLY_ABILITIES
end
def readonly_features
READONLY_FEATURES
end
end
end
ReadonlyAbilities::ClassMethods.prepend_if_ee('EE::ReadonlyAbilities::ClassMethods')
...@@ -2,29 +2,7 @@ ...@@ -2,29 +2,7 @@
class ProjectPolicy < BasePolicy class ProjectPolicy < BasePolicy
include CrudPolicyHelpers include CrudPolicyHelpers
include ReadonlyAbilities
READONLY_FEATURES_WHEN_ARCHIVED = %i[
issue
list
merge_request
label
milestone
snippet
wiki
design
note
pipeline
pipeline_schedule
build
trigger
environment
deployment
commit_status
container_image
pages
cluster
release
].freeze
desc "User is a project owner" desc "User is a project owner"
condition :owner do condition :owner do
...@@ -405,16 +383,9 @@ class ProjectPolicy < BasePolicy ...@@ -405,16 +383,9 @@ class ProjectPolicy < BasePolicy
rule { can?(:push_code) }.enable :admin_tag rule { can?(:push_code) }.enable :admin_tag
rule { archived }.policy do rule { archived }.policy do
prevent :push_code prevent(*readonly_abilities)
prevent :push_to_delete_protected_branch
prevent :request_access
prevent :upload_file
prevent :resolve_note
prevent :create_merge_request_from
prevent :create_merge_request_in
prevent :award_emoji
READONLY_FEATURES_WHEN_ARCHIVED.each do |feature| readonly_features.each do |feature|
prevent(*create_update_admin_destroy(feature)) prevent(*create_update_admin_destroy(feature))
end end
end end
......
...@@ -5,18 +5,6 @@ module EE ...@@ -5,18 +5,6 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
READONLY_FEATURES_WHEN_ARCHIVED = %i[
board
issue_link
approvers
vulnerability_feedback
vulnerability
license_management
feature_flag
feature_flags_client
iteration
].freeze
prepended do prepended do
with_scope :subject with_scope :subject
condition(:related_issues_disabled) { !@subject.feature_available?(:related_issues) } condition(:related_issues_disabled) { !@subject.feature_available?(:related_issues) }
...@@ -182,6 +170,10 @@ module EE ...@@ -182,6 +170,10 @@ module EE
@subject.feature_available?(:group_timelogs) @subject.feature_available?(:group_timelogs)
end end
condition(:over_storage_limit, scope: :subject) do
@subject.root_namespace.over_storage_limit?
end
rule { visual_review_bot }.policy do rule { visual_review_bot }.policy do
prevent :read_note prevent :read_note
enable :create_note enable :create_note
...@@ -336,14 +328,6 @@ module EE ...@@ -336,14 +328,6 @@ module EE
rule { ~admin & owner & owner_cannot_destroy_project }.prevent :remove_project rule { ~admin & owner & owner_cannot_destroy_project }.prevent :remove_project
rule { archived }.policy do
prevent :modify_auto_fix_setting
READONLY_FEATURES_WHEN_ARCHIVED.each do |feature|
prevent(*::ProjectPolicy.create_update_admin_destroy(feature))
end
end
condition(:needs_new_sso_session) do condition(:needs_new_sso_session) do
::Gitlab::Auth::GroupSaml::SsoEnforcer.group_access_restricted?(subject.group) ::Gitlab::Auth::GroupSaml::SsoEnforcer.group_access_restricted?(subject.group)
end end
...@@ -401,6 +385,14 @@ module EE ...@@ -401,6 +385,14 @@ module EE
rule { status_page_available & can?(:developer_access) }.enable :publish_status_page rule { status_page_available & can?(:developer_access) }.enable :publish_status_page
rule { public_project }.enable :view_embedded_analytics_report rule { public_project }.enable :view_embedded_analytics_report
rule { over_storage_limit }.policy do
prevent(*readonly_abilities)
readonly_features.each do |feature|
prevent(*create_update_admin(feature))
end
end
end end
override :lookup_access_level! override :lookup_access_level!
......
# frozen_string_literal: true
module EE
module ReadonlyAbilities
extend ActiveSupport::Concern
READONLY_ABILITIES_EE = %i[
modify_auto_fix_setting
].freeze
READONLY_FEATURES_EE = %i[
board
issue_link
approvers
vulnerability_feedback
vulnerability
license_management
feature_flag
feature_flags_client
iteration
].freeze
class_methods do
extend ::Gitlab::Utils::Override
override :readonly_abilities
def readonly_abilities
(super + READONLY_ABILITIES_EE).freeze
end
override :readonly_features
def readonly_features
(super + READONLY_FEATURES_EE).freeze
end
end
end
end
---
title: Block some project abilities when namespace over storage limit
merge_request: 36734
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe EE::ReadonlyAbilities do
let(:test_class) do
Class.new do
include ReadonlyAbilities
end
end
before do
stub_const('TestClass', test_class)
end
describe '.readonly_abilities' do
it 'returns an array of abilites to be prevented when readonly' do
expect(TestClass.readonly_abilities).to include(*described_class::READONLY_ABILITIES_EE)
end
end
describe '.readonly_features' do
it 'returns an array of features to be prevented when readonly' do
expect(TestClass.readonly_features).to include(*described_class::READONLY_FEATURES_EE)
end
end
end
...@@ -1353,5 +1353,44 @@ RSpec.describe ProjectPolicy do ...@@ -1353,5 +1353,44 @@ RSpec.describe ProjectPolicy do
end end
end end
context 'when project is readonly because the storage usage limit has been exceeded on the root namespace' do
let(:current_user) { owner }
let(:abilities) do
described_class.readonly_features.flat_map { |feature| described_class.create_update_admin(feature) } +
described_class.readonly_abilities
end
before do
allow(project.root_namespace).to receive(:over_storage_limit?).and_return(over_storage_limit)
allow(project).to receive(:design_management_enabled?).and_return(true)
stub_licensed_features(security_dashboard: true, license_scanning: true, feature_flags: true)
end
context 'when the group has exceeded its storage limit' do
let(:over_storage_limit) { true }
it { is_expected.to(be_disallowed(*abilities)) }
end
context 'when the group has not exceeded its storage limit' do
let(:over_storage_limit) { false }
# These are abilities that are not explicitly allowed by policies because most of them are not
# real abilities. They are prevented due to the use of create_update_admin helper method.
let(:abilities_not_currently_enabled) do
%i[create_merge_request create_list update_list create_label update_label create_milestone
update_milestone update_wiki update_design admin_design update_note
update_pipeline_schedule admin_pipeline_schedule create_trigger update_trigger
admin_trigger create_pages admin_release request_access create_board update_board
create_issue_link update_issue_link create_approvers admin_approvers
admin_vulnerability_feedback update_vulnerability create_license_management
update_license_management admin_license_management create_feature_flags_client
update_feature_flags_client update_iteration]
end
it { is_expected.to(be_allowed(*(abilities - abilities_not_currently_enabled))) }
end
end
include_examples 'analytics report embedding' include_examples 'analytics report embedding'
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe CrudPolicyHelpers do
let(:policy_test_class) do
Class.new do
include CrudPolicyHelpers
end
end
let(:feature_name) { :foo }
before do
stub_const('PolicyTestClass', policy_test_class)
end
describe '.create_read_update_admin_destroy' do
it 'returns an array of the appropriate abilites given a feature name' do
expect(PolicyTestClass.create_read_update_admin_destroy(feature_name)).to eq([
:read_foo,
:create_foo,
:update_foo,
:admin_foo,
:destroy_foo
])
end
end
describe '.create_update_admin_destroy' do
it 'returns an array of the appropriate abilites given a feature name' do
expect(PolicyTestClass.create_update_admin_destroy(feature_name)).to eq([
:create_foo,
:update_foo,
:admin_foo,
:destroy_foo
])
end
end
describe '.create_update_admin' do
it 'returns an array of the appropriate abilites given a feature name' do
expect(PolicyTestClass.create_update_admin(feature_name)).to eq([
:create_foo,
:update_foo,
:admin_foo
])
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ReadonlyAbilities do
let(:test_class) do
Class.new do
include ReadonlyAbilities
end
end
before do
stub_const('TestClass', test_class)
end
describe '.readonly_abilities' do
it 'returns an array of abilites to be prevented when readonly' do
expect(TestClass.readonly_abilities).to include(*described_class::READONLY_ABILITIES)
end
end
describe '.readonly_features' do
it 'returns an array of features to be prevented when readonly' do
expect(TestClass.readonly_features).to include(*described_class::READONLY_FEATURES)
end
end
end
...@@ -2,24 +2,13 @@ ...@@ -2,24 +2,13 @@
RSpec.shared_examples 'archived project policies' do RSpec.shared_examples 'archived project policies' do
let(:feature_write_abilities) do let(:feature_write_abilities) do
described_class::READONLY_FEATURES_WHEN_ARCHIVED.flat_map do |feature| described_class.readonly_features.flat_map do |feature|
described_class.create_update_admin_destroy(feature) described_class.create_update_admin_destroy(feature)
end + additional_maintainer_permissions end + additional_maintainer_permissions
end end
let(:other_write_abilities) do let(:other_write_abilities) do
%i[ described_class.readonly_abilities
create_merge_request_in
create_merge_request_from
push_to_delete_protected_branch
push_code
request_access
upload_file
resolve_note
award_emoji
admin_tag
admin_issue_link
]
end end
context 'when the project is archived' do context 'when the project is archived' do
......
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