Commit dbfad621 authored by Tan Le's avatar Tan Le

Use instance MR approval settings on projects

When instance-level MR approval settings are scoped to a compliance
framework, all compliance projects will use these instance-level
settings as SSOT. MR approval settings in project settings page will be
locked out for further changes as they are now centrally managed in
Admin area.

If compliance framework is not selected at instance-level or project has
no longer has compliance label applied, the project-level MR approval
settings will refer to project-level ones as SSOT.

This change is behind a feature toggle
`project_compliance_merge_request_approval_settings`
parent 42109dd9
...@@ -214,6 +214,13 @@ module EE ...@@ -214,6 +214,13 @@ module EE
end end
end end
def has_regulated_settings?
return unless compliance_framework_setting
compliance_framework_id = ::ComplianceManagement::ComplianceFramework::FRAMEWORKS[compliance_framework_setting.framework.to_sym]
::Gitlab::CurrentSettings.current_application_settings.compliance_frameworks.include?(compliance_framework_id)
end
def can_store_security_reports? def can_store_security_reports?
namespace.store_security_reports_available? || public? namespace.store_security_reports_available? || public?
end end
...@@ -672,29 +679,36 @@ module EE ...@@ -672,29 +679,36 @@ module EE
def disable_overriding_approvers_per_merge_request def disable_overriding_approvers_per_merge_request
return super unless License.feature_available?(:admin_merge_request_approvers_rules) return super unless License.feature_available?(:admin_merge_request_approvers_rules)
return (::Gitlab::CurrentSettings.disable_overriding_approvers_per_merge_request? || super) unless project_compliance_mr_approval_settings?
return super unless has_regulated_settings?
::Gitlab::CurrentSettings.disable_overriding_approvers_per_merge_request? || ::Gitlab::CurrentSettings.disable_overriding_approvers_per_merge_request?
super
end end
alias_method :disable_overriding_approvers_per_merge_request?, :disable_overriding_approvers_per_merge_request alias_method :disable_overriding_approvers_per_merge_request?, :disable_overriding_approvers_per_merge_request
def merge_requests_author_approval def merge_requests_author_approval
return super unless License.feature_available?(:admin_merge_request_approvers_rules) return super unless License.feature_available?(:admin_merge_request_approvers_rules)
return false if !project_compliance_mr_approval_settings? && ::Gitlab::CurrentSettings.prevent_merge_requests_author_approval?
return super if !project_compliance_mr_approval_settings? && !::Gitlab::CurrentSettings.prevent_merge_requests_author_approval?
return super unless has_regulated_settings?
return false if ::Gitlab::CurrentSettings.prevent_merge_requests_author_approval? !::Gitlab::CurrentSettings.prevent_merge_requests_author_approval?
super
end end
alias_method :merge_requests_author_approval?, :merge_requests_author_approval alias_method :merge_requests_author_approval?, :merge_requests_author_approval
def merge_requests_disable_committers_approval def merge_requests_disable_committers_approval
return super unless License.feature_available?(:admin_merge_request_approvers_rules) return super unless License.feature_available?(:admin_merge_request_approvers_rules)
return (::Gitlab::CurrentSettings.prevent_merge_requests_committers_approval? || super) unless project_compliance_mr_approval_settings?
return super unless has_regulated_settings?
::Gitlab::CurrentSettings.prevent_merge_requests_committers_approval? || ::Gitlab::CurrentSettings.prevent_merge_requests_committers_approval?
super
end end
alias_method :merge_requests_disable_committers_approval?, :merge_requests_disable_committers_approval alias_method :merge_requests_disable_committers_approval?, :merge_requests_disable_committers_approval
def project_compliance_mr_approval_settings?
::Feature.enabled?(:project_compliance_merge_request_approval_settings, self)
end
def license_compliance def license_compliance
strong_memoize(:license_compliance) { SCA::LicenseCompliance.new(self) } strong_memoize(:license_compliance) { SCA::LicenseCompliance.new(self) }
end end
......
...@@ -75,6 +75,36 @@ module EE ...@@ -75,6 +75,36 @@ module EE
.prevent_merge_requests_committers_approval .prevent_merge_requests_committers_approval
end end
with_scope :subject
condition(:regulated_merge_request_approval_settings) do
License.feature_available?(:admin_merge_request_approvers_rules) &&
@subject.has_regulated_settings?
end
condition(:cannot_modify_approvers_rules) do
if @subject.project_compliance_mr_approval_settings?
regulated_merge_request_approval_settings?
else
owner_cannot_modify_approvers_rules? && !admin?
end
end
condition(:cannot_modify_merge_request_author_setting) do
if @subject.project_compliance_mr_approval_settings?
regulated_merge_request_approval_settings?
else
owner_cannot_modify_merge_request_author_setting? && !admin?
end
end
condition(:cannot_modify_merge_request_committer_setting) do
if @subject.project_compliance_mr_approval_settings?
regulated_merge_request_approval_settings?
else
owner_cannot_modify_merge_request_committer_setting? && !admin?
end
end
with_scope :global with_scope :global
condition(:cluster_health_available) do condition(:cluster_health_available) do
License.feature_available?(:cluster_health) License.feature_available?(:cluster_health)
...@@ -372,24 +402,21 @@ module EE ...@@ -372,24 +402,21 @@ module EE
prevent :read_project prevent :read_project
end end
rule { owner_cannot_modify_approvers_rules & ~admin }.policy do rule { cannot_modify_approvers_rules }.policy do
prevent :modify_approvers_rules prevent :modify_approvers_rules
prevent :modify_approvers_list
end end
rule { owner_cannot_modify_merge_request_author_setting & ~admin }.policy do rule { cannot_modify_merge_request_author_setting }.policy do
prevent :modify_merge_request_author_setting prevent :modify_merge_request_author_setting
end end
rule { owner_cannot_modify_merge_request_committer_setting & ~admin }.policy do rule { cannot_modify_merge_request_committer_setting }.policy do
prevent :modify_merge_request_committer_setting prevent :modify_merge_request_committer_setting
end end
rule { can?(:read_cluster) & cluster_health_available }.enable :read_cluster_health rule { can?(:read_cluster) & cluster_health_available }.enable :read_cluster_health
rule { owner_cannot_modify_approvers_rules & ~admin }.policy do
prevent :modify_approvers_list
end
rule { can?(:read_merge_request) & code_review_analytics_enabled }.enable :read_code_review_analytics rule { can?(:read_merge_request) & code_review_analytics_enabled }.enable :read_code_review_analytics
rule { can?(:read_project) & requirements_available }.enable :read_requirement rule { can?(:read_project) & requirements_available }.enable :read_requirement
......
...@@ -50,133 +50,44 @@ RSpec.describe ApprovalState do ...@@ -50,133 +50,44 @@ RSpec.describe ApprovalState do
before do before do
allow(merge_request).to receive(:committers).and_return(User.where(id: committers)) allow(merge_request).to receive(:committers).and_return(User.where(id: committers))
project.update!( allow(project).to receive(:merge_requests_author_approval?).and_return(merge_requests_author_approval)
merge_requests_author_approval: merge_requests_author_approval, allow(project).to receive(:merge_requests_disable_committers_approval?).and_return(merge_requests_disable_committers_approval)
merge_requests_disable_committers_approval: merge_requests_disable_committers_approval
)
create_rule(users: committers) create_rule(users: committers)
end end
context 'when self approval is disabled on project level' do context 'when self approval is disabled on project' do
let(:merge_requests_author_approval) { false } let(:merge_requests_author_approval) { false }
it 'excludes authors' do it 'excludes authors' do
expect(results).not_to include(merge_request.author) expect(results).not_to include(merge_request.author)
end end
context 'when self approval is enabled on instance level' do
before do
stub_application_setting(prevent_merge_requests_author_approval: false)
stub_licensed_features(admin_merge_request_approvers_rules: true)
end
it 'excludes author' do
expect(results).not_to include(merge_request.author)
end
end end
context 'when self approval is disabled on instance level' do context 'when self approval is enabled on project' do
before do
stub_licensed_features(admin_merge_request_approvers_rules: true)
stub_application_setting(prevent_merge_requests_author_approval: true)
end
it 'excludes authors' do
expect(results).not_to include(merge_request.author)
end
end
end
context 'when self approval is enabled on project level' do
let(:merge_requests_author_approval) { true } let(:merge_requests_author_approval) { true }
it 'includes author' do it 'includes author' do
expect(results).to include(merge_request.author) expect(results).to include(merge_request.author)
end end
context 'when self approval is enabled on instance level' do
before do
stub_application_setting(prevent_merge_requests_author_approval: false)
stub_licensed_features(admin_merge_request_approvers_rules: true)
end
it 'includes author' do
expect(results).to include(merge_request.author)
end
end
context 'when self approval is disabled on instance level' do
before do
stub_application_setting(prevent_merge_requests_author_approval: true)
stub_licensed_features(admin_merge_request_approvers_rules: true)
end
it 'excludes authors' do
expect(results).not_to include(merge_request.author)
end
end
end end
context 'when committers approval is enabled on project level' do context 'when committers approval is enabled on project' do
let(:merge_requests_author_approval) { true } let(:merge_requests_author_approval) { true }
let(:merge_requests_disable_committers_approval) { false } let(:merge_requests_disable_committers_approval) { false }
it 'includes committers' do it 'includes committers' do
expect(results).to include(*committers) expect(results).to include(*committers)
end end
context 'when committers approval is enabled on instance level' do
before do
stub_application_setting(prevent_merge_requests_committers_approval: false)
stub_licensed_features(admin_merge_request_approvers_rules: true)
end
it 'includes committers' do
expect(results).to include(*committers)
end
end
context 'when committers approval is disabled on instance level' do
before do
stub_application_setting(prevent_merge_requests_committers_approval: true)
stub_licensed_features(admin_merge_request_approvers_rules: true)
end
it 'excludes committers' do
expect(results).not_to include(*committers)
end
end
end end
context 'when committers approval is disabled on project level' do context 'when committers approval is disabled on project' do
let(:merge_requests_author_approval) { true } let(:merge_requests_author_approval) { true }
let(:merge_requests_disable_committers_approval) { true } let(:merge_requests_disable_committers_approval) { true }
it 'excludes committers' do it 'excludes committers' do
expect(results).not_to include(*committers) expect(results).not_to include(*committers)
end end
context 'when committers approval is enabled on instance level' do
before do
stub_application_setting(prevent_merge_requests_committers_approval: false)
stub_licensed_features(admin_merge_request_approvers_rules: true)
end
it 'excludes committers' do
expect(results).not_to include(*committers)
end
end
context 'when committers approval is disabled on instance level' do
before do
stub_application_setting(prevent_merge_requests_committers_approval: true)
stub_licensed_features(admin_merge_request_approvers_rules: true)
end
it 'excludes committers' do
expect(results).not_to include(*committers)
end
end
end end
end end
......
...@@ -520,6 +520,40 @@ RSpec.describe Project do ...@@ -520,6 +520,40 @@ RSpec.describe Project do
context 'merge requests related settings' do context 'merge requests related settings' do
shared_examples 'setting modified by application setting' do shared_examples 'setting modified by application setting' do
context 'when compliance merge request approval settings feature flag is enabled' do
using RSpec::Parameterized::TableSyntax
where(:app_setting, :project_setting, :regulated_settings, :final_setting) do
true | true | true | true
false | true | true | false
true | false | true | true
false | false | true | false
true | true | false | true
false | true | false | true
true | false | false | false
false | false | false | false
end
with_them do
let(:project) { create(:project) }
before do
stub_feature_flags(project_compliance_merge_request_approval_settings: true)
stub_licensed_features(admin_merge_request_approvers_rules: true)
allow(project).to receive(:has_regulated_settings?).and_return(regulated_settings)
stub_application_setting(application_setting => app_setting)
project.update(setting => project_setting)
end
it 'shows proper setting' do
expect(project.send(setting)).to eq(final_setting)
expect(project.send("#{setting}?")).to eq(final_setting)
end
end
end
context 'when compliance merge request approval settings feature flag is disabled' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
where(:app_setting, :project_setting, :feature_enabled, :final_setting) do where(:app_setting, :project_setting, :feature_enabled, :final_setting) do
...@@ -537,6 +571,8 @@ RSpec.describe Project do ...@@ -537,6 +571,8 @@ RSpec.describe Project do
let(:project) { create(:project) } let(:project) { create(:project) }
before do before do
stub_feature_flags(project_compliance_merge_request_approval_settings: false)
stub_licensed_features(feature => feature_enabled) stub_licensed_features(feature => feature_enabled)
stub_application_setting(application_setting => app_setting) stub_application_setting(application_setting => app_setting)
project.update(setting => project_setting) project.update(setting => project_setting)
...@@ -548,6 +584,7 @@ RSpec.describe Project do ...@@ -548,6 +584,7 @@ RSpec.describe Project do
end end
end end
end end
end
describe '#disable_overriding_approvers_per_merge_request' do describe '#disable_overriding_approvers_per_merge_request' do
it_behaves_like 'setting modified by application setting' do it_behaves_like 'setting modified by application setting' do
...@@ -566,13 +603,19 @@ RSpec.describe Project do ...@@ -566,13 +603,19 @@ RSpec.describe Project do
end end
describe '#merge_requests_author_approval' do describe '#merge_requests_author_approval' do
let(:project) { create(:project) }
let(:feature) { :admin_merge_request_approvers_rules }
let(:setting) { :merge_requests_author_approval }
let(:application_setting) { :prevent_merge_requests_author_approval }
context 'when compliance merge request approval settings feature flag is enabled' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
where(:app_setting, :project_setting, :feature_enabled, :final_setting) do where(:app_setting, :project_setting, :regulated_settings, :final_setting) do
true | true | true | false true | true | true | false
false | true | true | true false | true | true | true
true | false | true | false true | false | true | false
false | false | true | false false | false | true | true
true | true | false | true true | true | false | true
false | true | false | true false | true | false | true
true | false | false | false true | false | false | false
...@@ -581,11 +624,41 @@ RSpec.describe Project do ...@@ -581,11 +624,41 @@ RSpec.describe Project do
with_them do with_them do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:feature) { :admin_merge_request_approvers_rules }
let(:setting) { :merge_requests_author_approval }
let(:application_setting) { :prevent_merge_requests_author_approval }
before do before do
stub_feature_flags(project_compliance_merge_request_approval_settings: true)
stub_licensed_features(admin_merge_request_approvers_rules: true)
allow(project).to receive(:has_regulated_settings?).and_return(regulated_settings)
stub_application_setting(application_setting => app_setting)
project.update(setting => project_setting)
end
it 'shows proper setting' do
expect(project.send(setting)).to eq(final_setting)
expect(project.send("#{setting}?")).to eq(final_setting)
end
end
end
context 'when compliance merge request approval settings feature flag is disabled' do
using RSpec::Parameterized::TableSyntax
where(:app_setting, :project_setting, :feature_enabled, :final_setting) do
true | true | true | false
false | true | true | true
true | false | true | false
false | false | true | false
true | true | false | true
false | true | false | true
true | false | false | false
false | false | false | false
end
with_them do
before do
stub_feature_flags(project_compliance_merge_request_approval_settings: false)
stub_licensed_features(feature => feature_enabled) stub_licensed_features(feature => feature_enabled)
stub_application_setting(application_setting => app_setting) stub_application_setting(application_setting => app_setting)
project.update(setting => project_setting) project.update(setting => project_setting)
...@@ -598,6 +671,37 @@ RSpec.describe Project do ...@@ -598,6 +671,37 @@ RSpec.describe Project do
end end
end end
end end
end
describe '#has_regulated_settings?' do
let_it_be(:framework) { ComplianceManagement::ComplianceFramework::FRAMEWORKS.first }
let_it_be(:compliance_framework_setting) { create(:compliance_framework_project_setting, framework: framework.first.to_s) }
let_it_be(:project) { create(:project, compliance_framework_setting: compliance_framework_setting) }
subject { project.has_regulated_settings? }
context 'framework is regulated' do
before do
stub_application_setting(compliance_frameworks: [framework.last])
end
it { is_expected.to be_truthy }
end
context 'framework is not regulated' do
before do
stub_application_setting(compliance_frameworks: [])
end
it { is_expected.to be_falsey }
end
context 'project does not have compliance framework' do
let_it_be(:project) { create(:project) }
it { is_expected.to be_falsey }
end
end
describe '#has_active_hooks?' do describe '#has_active_hooks?' do
context "with group hooks" do context "with group hooks" do
......
...@@ -1325,9 +1325,14 @@ RSpec.describe ProjectPolicy do ...@@ -1325,9 +1325,14 @@ RSpec.describe ProjectPolicy do
shared_examples 'merge request rules' do shared_examples 'merge request rules' do
let(:project) { create(:project, namespace: owner.namespace) } let(:project) { create(:project, namespace: owner.namespace) }
context 'when compliance merge request approval settings feature flag is enabled' do
before do
stub_feature_flags(project_compliance_merge_request_approval_settings: true)
end
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
context 'with merge request approvers rules available in license' do context 'with merge request approvers rules available in license' do
where(:role, :setting, :admin_mode, :allowed) do where(:role, :regulated_setting, :admin_mode, :allowed) do
:guest | true | nil | false :guest | true | nil | false
:reporter | true | nil | false :reporter | true | nil | false
:developer | true | nil | false :developer | true | nil | false
...@@ -1338,7 +1343,7 @@ RSpec.describe ProjectPolicy do ...@@ -1338,7 +1343,7 @@ RSpec.describe ProjectPolicy do
:admin | false | false | false :admin | false | false | false
:admin | false | true | true :admin | false | true | true
:admin | true | false | false :admin | true | false | false
:admin | true | true | true :admin | true | true | false
end end
with_them do with_them do
...@@ -1346,7 +1351,7 @@ RSpec.describe ProjectPolicy do ...@@ -1346,7 +1351,7 @@ RSpec.describe ProjectPolicy do
before do before do
stub_licensed_features(admin_merge_request_approvers_rules: true) stub_licensed_features(admin_merge_request_approvers_rules: true)
stub_application_setting(setting_name => setting) allow(project).to receive(:has_regulated_settings?).and_return(regulated_setting)
enable_admin_mode!(current_user) if admin_mode enable_admin_mode!(current_user) if admin_mode
end end
...@@ -1355,7 +1360,7 @@ RSpec.describe ProjectPolicy do ...@@ -1355,7 +1360,7 @@ RSpec.describe ProjectPolicy do
end end
context 'with merge request approvers not available in license' do context 'with merge request approvers not available in license' do
where(:role, :setting, :admin_mode, :allowed) do where(:role, :regulated_setting, :admin_mode, :allowed) do
:guest | true | nil | false :guest | true | nil | false
:reporter | true | nil | false :reporter | true | nil | false
:developer | true | nil | false :developer | true | nil | false
...@@ -1374,7 +1379,7 @@ RSpec.describe ProjectPolicy do ...@@ -1374,7 +1379,7 @@ RSpec.describe ProjectPolicy do
before do before do
stub_licensed_features(admin_merge_request_approvers_rules: false) stub_licensed_features(admin_merge_request_approvers_rules: false)
stub_application_setting(setting_name => setting) allow(project).to receive(:has_regulated_settings?).and_return(regulated_setting)
enable_admin_mode!(current_user) if admin_mode enable_admin_mode!(current_user) if admin_mode
end end
...@@ -1383,34 +1388,12 @@ RSpec.describe ProjectPolicy do ...@@ -1383,34 +1388,12 @@ RSpec.describe ProjectPolicy do
end end
end end
describe ':modify_approvers_rules' do context 'when compliance merge request approval settings feature flag is disabled' do
it_behaves_like 'merge request rules' do before do
let(:setting_name) { :disable_overriding_approvers_per_merge_request } stub_feature_flags(project_compliance_merge_request_approval_settings: false)
let(:policy) { :modify_approvers_rules }
end
end
describe ':modify_merge_request_author_setting' do
it_behaves_like 'merge request rules' do
let(:setting_name) { :prevent_merge_requests_author_approval }
let(:policy) { :modify_merge_request_author_setting }
end
end
describe ':modify_merge_request_committer_setting' do
it_behaves_like 'merge request rules' do
let(:setting_name) { :prevent_merge_requests_committers_approval }
let(:policy) { :modify_merge_request_committer_setting }
end
end end
describe ':modify_approvers_list' do
let(:setting_name) { :disable_overriding_approvers_per_merge_request }
let(:policy) { :modify_approvers_list }
let(:project) { create(:project, namespace: owner.namespace) }
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
context 'with merge request approvers rules available in license' do context 'with merge request approvers rules available in license' do
where(:role, :setting, :admin_mode, :allowed) do where(:role, :setting, :admin_mode, :allowed) do
:guest | true | nil | false :guest | true | nil | false
...@@ -1467,6 +1450,35 @@ RSpec.describe ProjectPolicy do ...@@ -1467,6 +1450,35 @@ RSpec.describe ProjectPolicy do
end end
end end
end end
end
describe ':modify_approvers_rules' do
it_behaves_like 'merge request rules' do
let(:setting_name) { :disable_overriding_approvers_per_merge_request }
let(:policy) { :modify_approvers_rules }
end
end
describe ':modify_merge_request_author_setting' do
it_behaves_like 'merge request rules' do
let(:setting_name) { :prevent_merge_requests_author_approval }
let(:policy) { :modify_merge_request_author_setting }
end
end
describe ':modify_merge_request_committer_setting' do
it_behaves_like 'merge request rules' do
let(:setting_name) { :prevent_merge_requests_committers_approval }
let(:policy) { :modify_merge_request_committer_setting }
end
end
describe ':modify_approvers_list' do
it_behaves_like 'merge request rules' do
let(:setting_name) { :disable_overriding_approvers_per_merge_request }
let(:policy) { :modify_approvers_list }
end
end
it_behaves_like 'resource with requirement permissions' do it_behaves_like 'resource with requirement permissions' do
let(:resource) { project } let(:resource) { project }
......
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