diff --git a/app/policies/protected_branch_policy.rb b/app/policies/protected_branch_policy.rb new file mode 100644 index 00000000000..8d44cff1b42 --- /dev/null +++ b/app/policies/protected_branch_policy.rb @@ -0,0 +1,15 @@ +class ProtectedBranchPolicy < BasePolicy + delegate { @subject.project } + + condition(:requires_admin_to_unprotect?, scope: :subject) do + @subject.name == 'master' && Gitlab::CurrentSettings.only_admins_can_unprotect_master_branch? + end + + rule { can?(:admin_project) }.policy do + enable :update_protected_branch + end + + rule { requires_admin_to_unprotect? & ~admin }.policy do + prevent :update_protected_branch + end +end diff --git a/app/services/protected_branches/update_service.rb b/app/services/protected_branches/update_service.rb index 4b3337a5c9d..95e46645374 100644 --- a/app/services/protected_branches/update_service.rb +++ b/app/services/protected_branches/update_service.rb @@ -1,7 +1,7 @@ module ProtectedBranches class UpdateService < BaseService def execute(protected_branch) - raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_project, project) + raise Gitlab::Access::AccessDeniedError unless can?(current_user, :update_protected_branch, protected_branch) protected_branch.update(params) protected_branch diff --git a/spec/policies/protected_branch_policy_spec.rb b/spec/policies/protected_branch_policy_spec.rb new file mode 100644 index 00000000000..00a7d8153ae --- /dev/null +++ b/spec/policies/protected_branch_policy_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe ProtectedBranchPolicy do + let(:user) { create(:user) } + let(:name) { 'feature' } + let(:protected_branch) { create(:protected_branch, name: name) } + let(:project) { protected_branch.project } + + subject { described_class.new(user, protected_branch) } + + context 'when unprotection restriction feature is disabled' do + it "branches can't be updated by guests" do + project.add_guest(user) + + is_expected.to be_disallowed(:update_protected_branch) + end + + it 'branches can be updated via access to project settings' do + project.add_master(user) + + is_expected.to be_allowed(:update_protected_branch) + end + end + + context 'when unprotection restriction feature is enabled' do + before do + # stub_licensed_features(unprotection_restrictions: true) + end + + context 'and unprotection is limited to admins' do #TODO: remove this is temporary exploration + before do + stub_ee_application_setting(only_admins_can_unprotect_master_branch: true) + end + + context 'and the protection is for master' do + let(:name) { 'master' } + + it 'project owners cannot remove protections' do + project.add_master(user) + + is_expected.not_to be_allowed(:update_protected_branch) + end + + it 'admins can remove protections' do + user.update!(admin: true) + + is_expected.to be_allowed(:update_protected_branch) + end + end + + context "and the protection isn't for master" do + it 'project owners can remove protections' do + project.add_master(user) + + is_expected.to be_allowed(:update_protected_branch) + end + end + end + end +end