diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue index da6fb77525808a87e13e3e96773e61438246dfce..6f9c2d941f9e5dcf5d4e5191d908634d75b2fce3 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue @@ -37,6 +37,7 @@ export default { featureFlagsHelpText: s__( 'ProjectSettings|Roll out new features without redeploying with feature flags.', ), + monitorLabel: s__('ProjectSettings|Monitor'), packagesHelpText: s__( 'ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public.', ), @@ -239,6 +240,7 @@ export default { environmentsAccessLevel: featureAccessLevel.EVERYONE, featureFlagsAccessLevel: featureAccessLevel.PROJECT_MEMBERS, releasesAccessLevel: featureAccessLevel.EVERYONE, + monitorAccessLevel: featureAccessLevel.EVERYONE, containerRegistryAccessLevel: featureAccessLevel.EVERYONE, warnAboutPotentiallyUnwantedCharacters: true, lfsEnabled: true, @@ -271,12 +273,6 @@ export default { ); }, - operationsFeatureAccessLevelOptions() { - return this.featureAccessLevelOptions.filter( - ([value]) => value <= this.operationsAccessLevel, - ); - }, - packageRegistryFeatureAccessLevelOptions() { const options = [FEATURE_ACCESS_LEVEL_ANONYMOUS]; @@ -316,6 +312,10 @@ export default { return this.environmentsAccessLevel > featureAccessLevel.NOT_ENABLED; }, + monitorEnabled() { + return this.monitorAccessLevel > featureAccessLevel.NOT_ENABLED; + }, + repositoryEnabled() { return this.repositoryAccessLevel > featureAccessLevel.NOT_ENABLED; }, @@ -355,6 +355,14 @@ export default { splitOperationsEnabled() { return this.glFeatures.splitOperationsVisibilityPermissions; }, + monitorOperationsFeatureAccessLevelOptions() { + if (this.splitOperationsEnabled) { + return this.featureAccessLevelOptions.filter(([value]) => value <= this.monitorAccessLevel); + } + return this.featureAccessLevelOptions.filter( + ([value]) => value <= this.operationsAccessLevel, + ); + }, }, watch: { @@ -423,6 +431,10 @@ export default { featureAccessLevel.PROJECT_MEMBERS, this.releasesAccessLevel, ); + this.monitorAccessLevel = Math.min( + featureAccessLevel.PROJECT_MEMBERS, + this.monitorAccessLevel, + ); this.containerRegistryAccessLevel = Math.min( featureAccessLevel.PROJECT_MEMBERS, this.containerRegistryAccessLevel, @@ -466,6 +478,8 @@ export default { this.operationsAccessLevel = featureAccessLevel.EVERYONE; if (this.environmentsAccessLevel === featureAccessLevel.PROJECT_MEMBERS) this.environmentsAccessLevel = featureAccessLevel.EVERYONE; + if (this.monitorAccessLevel === featureAccessLevel.PROJECT_MEMBERS) + this.monitorAccessLevel = featureAccessLevel.EVERYONE; if (this.containerRegistryAccessLevel === featureAccessLevel.PROJECT_MEMBERS) this.containerRegistryAccessLevel = featureAccessLevel.EVERYONE; @@ -501,6 +515,16 @@ export default { }, operationsAccessLevel(value, oldValue) { + this.updateSubFeatureAccessLevel(value, oldValue); + }, + + monitorAccessLevel(value, oldValue) { + this.updateSubFeatureAccessLevel(value, oldValue); + }, + }, + + methods: { + updateSubFeatureAccessLevel(value, oldValue) { if (value < oldValue) { // sub-features cannot have more permissive access level this.metricsDashboardAccessLevel = Math.min(this.metricsDashboardAccessLevel, value); @@ -508,9 +532,7 @@ export default { this.metricsDashboardAccessLevel = value; } }, - }, - methods: { highlightChanges() { this.highlightChangesClass = true; this.$nextTick(() => { @@ -881,6 +903,22 @@ export default { /> + + + diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 90be0c3adaf4907e966eb638b4d05a01869c3b52..5ceedbc1e01737fbdd0b3f1e5ae82fc2dc53482a 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -442,6 +442,7 @@ def operations_feature_attributes if Feature.enabled?(:split_operations_visibility_permissions, project) %i[ environments_access_level feature_flags_access_level releases_access_level + monitor_access_level ] else %i[operations_access_level] diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index dd637ec352e4e7e0a0b0176b82c7b12daba17b5c..e760fad7be901ec3748627e7d34569e91172dcda 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -638,6 +638,7 @@ def project_permissions_settings(project) emailsDisabled: project.emails_disabled?, metricsDashboardAccessLevel: feature.metrics_dashboard_access_level, operationsAccessLevel: feature.operations_access_level, + monitorAccessLevel: feature.monitor_access_level, showDefaultAwardEmojis: project.show_default_award_emojis?, warnAboutPotentiallyUnwantedCharacters: project.warn_about_potentially_unwanted_characters?, enforceAuthChecksOnUploads: project.enforce_auth_checks_on_uploads?, diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb index 7613691bc2e4c47b7914c9c524e41e1a76010977..2976b6f02a77262a57fc7fb7893991c6d6736d7d 100644 --- a/app/models/concerns/project_features_compatibility.rb +++ b/app/models/concerns/project_features_compatibility.rb @@ -86,6 +86,10 @@ def operations_access_level=(value) write_feature_attribute_string(:operations_access_level, value) end + def monitor_access_level=(value) + write_feature_attribute_string(:monitor_access_level, value) + end + def security_and_compliance_access_level=(value) write_feature_attribute_string(:security_and_compliance_access_level, value) end diff --git a/app/models/project.rb b/app/models/project.rb index 46295545e911c6bbf1c49606c9fc224214f60a76..3a969ae71dd1a72c359cd04af0a5a2dc953b2c9e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -452,7 +452,7 @@ def self.integration_association_name(name) :metrics_dashboard_access_level, :analytics_access_level, :operations_access_level, :security_and_compliance_access_level, :container_registry_access_level, :environments_access_level, :feature_flags_access_level, - :releases_access_level, + :monitor_access_level, :releases_access_level, to: :project_feature, allow_nil: true delegate :show_default_award_emojis, :show_default_award_emojis=, diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index 8623e477c06e56cc2bcfbf166b234298c46880ee..dad8aaf0625986ad6b61c3690e50eea68271d5b1 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -17,6 +17,7 @@ class ProjectFeature < ApplicationRecord pages metrics_dashboard analytics + monitor operations security_and_compliance container_registry diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index ccdad8e6be1716e8cd5b3920a9152f7f3f577ee6..fb162d039557a1cb21370f83a48471e9c11dd4b2 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -208,6 +208,7 @@ class ProjectPolicy < BasePolicy metrics_dashboard analytics operations + monitor security_and_compliance environments feature_flags @@ -402,6 +403,12 @@ class ProjectPolicy < BasePolicy prevent(*create_read_update_admin_destroy(:release)) end + rule { split_operations_visibility_permissions & monitor_disabled }.policy do + prevent(:metrics_dashboard) + prevent(*create_read_update_admin_destroy(:sentry_issue)) + prevent(*create_read_update_admin_destroy(:alert_management_alert)) + end + rule { can?(:metrics_dashboard) }.policy do enable :read_prometheus enable :read_deployment diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index 77311e14803368f3e9b558ab50bc5141f413a22e..c5e4974b749ae4f9a331c69e2e835505c04c6c86 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -293,6 +293,7 @@ included_attributes: - :forking_access_level - :metrics_dashboard_access_level - :operations_access_level + - :monitor_access_level - :analytics_access_level - :security_and_compliance_access_level - :container_registry_access_level @@ -705,6 +706,7 @@ included_attributes: - :metrics_dashboard_access_level - :analytics_access_level - :operations_access_level + - :monitor_access_level - :security_and_compliance_access_level - :container_registry_access_level - :package_registry_access_level diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb index 23e1a95c4015ac3f4737b74964f150b1a7c6bdef..ecd062f333e42a99ff576787c9aecdc771deba2b 100644 --- a/lib/sidebars/projects/menus/monitor_menu.rb +++ b/lib/sidebars/projects/menus/monitor_menu.rb @@ -6,7 +6,7 @@ module Menus class MonitorMenu < ::Sidebars::Menu override :configure_menu_items def configure_menu_items - return false unless context.project.feature_available?(:operations, context.current_user) + return false unless feature_enabled? add_item(metrics_dashboard_menu_item) add_item(error_tracking_menu_item) @@ -41,6 +41,14 @@ def active_routes private + def feature_enabled? + if ::Feature.enabled?(:split_operations_visibility_permissions, context.project) + context.project.feature_available?(:monitor, context.current_user) + else + context.project.feature_available?(:operations, context.current_user) + end + end + def metrics_dashboard_menu_item unless can?(context.current_user, :metrics_dashboard, context.project) return ::Sidebars::NilMenuItem.new(item_id: :metrics) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8446b04684d7de4faab79490b7193e2b8a9f7ee9..08b047479f94ec0298208d35560bbf6191ca8d05 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31171,6 +31171,9 @@ msgstr "" msgid "ProjectSettings|Merging is only allowed when the source branch is up-to-date with its target." msgstr "" +msgid "ProjectSettings|Monitor" +msgstr "" + msgid "ProjectSettings|No merge commits are created." msgstr "" diff --git a/rubocop/cop/gitlab/feature_available_usage.rb b/rubocop/cop/gitlab/feature_available_usage.rb index f748b7d9111f149bfa2cbcf80cf8660329a6bd4d..b6777dc468708dbb7fa4f35dc3fcaa51f5543bba 100644 --- a/rubocop/cop/gitlab/feature_available_usage.rb +++ b/rubocop/cop/gitlab/feature_available_usage.rb @@ -21,6 +21,7 @@ class FeatureAvailableUsage < RuboCop::Cop::Cop metrics_dashboard analytics operations + monitor security_and_compliance container_registry environments diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index e0adad832f5a83c7e164e7b171304e33587de1b0..b30610d98d702bd23bf53e9711778b44f143cba1 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -920,6 +920,7 @@ def update_project_feature environments_access_level feature_flags_access_level releases_access_level + monitor_access_level ] end @@ -947,6 +948,7 @@ def update_project_feature where(:feature_access_level) do %i[ environments_access_level feature_flags_access_level + monitor_access_level ] end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 95b72648cf549337918067e64f536c449666cdef..871917a725ec83858692c1a4afa31c644f5ca274 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -35,6 +35,7 @@ end metrics_dashboard_access_level { ProjectFeature::PRIVATE } operations_access_level { ProjectFeature::ENABLED } + monitor_access_level { ProjectFeature::ENABLED } container_registry_access_level { ProjectFeature::ENABLED } security_and_compliance_access_level { ProjectFeature::PRIVATE } environments_access_level { ProjectFeature::ENABLED } diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb index b888e2f4171eb6e7592853bc6d2cab9d52469d54..f612956600f9b31c28a484e38ce2ac2ca2b7ab23 100644 --- a/spec/features/monitor_sidebar_link_spec.rb +++ b/spec/features/monitor_sidebar_link_spec.rb @@ -4,39 +4,59 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do let_it_be_with_reload(:project) { create(:project, :internal, :repository) } + let_it_be(:user) { create(:user) } - let(:user) { create(:user) } - let(:access_level) { ProjectFeature::PUBLIC } let(:role) { nil } before do project.add_role(user, role) if role - project.project_feature.update_attribute(:operations_access_level, access_level) - sign_in(user) - visit project_issues_path(project) end shared_examples 'shows Monitor menu based on the access level' do - context 'when operations project feature is PRIVATE' do - let(:access_level) { ProjectFeature::PRIVATE } - - it 'shows the `Monitor` menu' do - expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor') - end + using RSpec::Parameterized::TableSyntax + + let(:enabled) { Featurable::PRIVATE } + let(:disabled) { Featurable::DISABLED } + + where(:flag_enabled, :operations_access_level, :monitor_level, :render) do + true | ref(:disabled) | ref(:enabled) | true + true | ref(:disabled) | ref(:disabled) | false + true | ref(:enabled) | ref(:enabled) | true + true | ref(:enabled) | ref(:disabled) | false + false | ref(:disabled) | ref(:enabled) | false + false | ref(:disabled) | ref(:disabled) | false + false | ref(:enabled) | ref(:enabled) | true + false | ref(:enabled) | ref(:disabled) | true end - context 'when operations project feature is DISABLED' do - let(:access_level) { ProjectFeature::DISABLED } + with_them do + it 'renders when expected to' do + stub_feature_flags(split_operations_visibility_permissions: flag_enabled) + project.project_feature.update_attribute(:operations_access_level, operations_access_level) + project.project_feature.update_attribute(:monitor_access_level, monitor_level) + + visit project_issues_path(project) - it 'does not show the `Monitor` menu' do - expect(page).not_to have_selector('a.shortcuts-monitor') + if render + expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor') + else + expect(page).not_to have_selector('a.shortcuts-monitor') + end end end end - context 'user is not a member' do + context 'when user is not a member' do + let(:access_level) { ProjectFeature::PUBLIC } + + before do + project.project_feature.update_attribute(:operations_access_level, access_level) + project.project_feature.update_attribute(:monitor_access_level, access_level) + end + it 'has the correct `Monitor` menu items', :aggregate_failures do + visit project_issues_path(project) expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor') expect(page).to have_link('Incidents', href: project_incidents_path(project)) expect(page).to have_link('Environments', href: project_environments_path(project)) @@ -48,27 +68,50 @@ expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project)) end - context 'when operations project feature is PRIVATE' do - let(:access_level) { ProjectFeature::PRIVATE } + context 'with new monitor visiblity flag disabled' do + stub_feature_flags(split_operations_visibility_permissions: false) - it 'does not show the `Monitor` menu' do - expect(page).not_to have_selector('a.shortcuts-monitor') + context 'when operations project feature is PRIVATE' do + let(:access_level) { ProjectFeature::PRIVATE } + + it 'does not show the `Monitor` menu' do + expect(page).not_to have_selector('a.shortcuts-monitor') + end + end + + context 'when operations project feature is DISABLED' do + let(:access_level) { ProjectFeature::DISABLED } + + it 'does not show the `Operations` menu' do + expect(page).not_to have_selector('a.shortcuts-monitor') + end end end - context 'when operations project feature is DISABLED' do - let(:access_level) { ProjectFeature::DISABLED } + context 'with new monitor visiblity flag enabled' do + context 'when monitor project feature is PRIVATE' do + let(:access_level) { ProjectFeature::PRIVATE } + + it 'does not show the `Monitor` menu' do + expect(page).not_to have_selector('a.shortcuts-monitor') + end + end + + context 'when operations project feature is DISABLED' do + let(:access_level) { ProjectFeature::DISABLED } - it 'does not show the `Operations` menu' do - expect(page).not_to have_selector('a.shortcuts-monitor') + it 'does not show the `Operations` menu' do + expect(page).not_to have_selector('a.shortcuts-monitor') + end end end end - context 'user has guest role' do + context 'when user has guest role' do let(:role) { :guest } it 'has the correct `Monitor` menu items' do + visit project_issues_path(project) expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor') expect(page).to have_link('Incidents', href: project_incidents_path(project)) expect(page).to have_link('Environments', href: project_environments_path(project)) @@ -83,10 +126,11 @@ it_behaves_like 'shows Monitor menu based on the access level' end - context 'user has reporter role' do + context 'when user has reporter role' do let(:role) { :reporter } it 'has the correct `Monitor` menu items' do + visit project_issues_path(project) expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project)) expect(page).to have_link('Environments', href: project_environments_path(project)) @@ -100,10 +144,11 @@ it_behaves_like 'shows Monitor menu based on the access level' end - context 'user has developer role' do + context 'when user has developer role' do let(:role) { :developer } it 'has the correct `Monitor` menu items' do + visit project_issues_path(project) expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project)) expect(page).to have_link('Alerts', href: project_alert_management_index_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project)) @@ -116,10 +161,11 @@ it_behaves_like 'shows Monitor menu based on the access level' end - context 'user has maintainer role' do + context 'when user has maintainer role' do let(:role) { :maintainer } it 'has the correct `Monitor` menu items' do + visit project_issues_path(project) expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project)) expect(page).to have_link('Alerts', href: project_alert_management_index_path(project)) expect(page).to have_link('Incidents', href: project_incidents_path(project)) diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js index b34c64cb1b0688885494aedf33359c18965524d6..1547129ff208f482488b799ea85afe9797a3e506 100644 --- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js +++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js @@ -134,6 +134,7 @@ describe('Settings Panel', () => { const findEnvironmentsSettings = () => wrapper.findComponent({ ref: 'environments-settings' }); const findFeatureFlagsSettings = () => wrapper.findComponent({ ref: 'feature-flags-settings' }); const findReleasesSettings = () => wrapper.findComponent({ ref: 'environments-settings' }); + const findMonitorSettings = () => wrapper.findComponent({ ref: 'monitor-settings' }); afterEach(() => { wrapper.destroy(); @@ -830,7 +831,6 @@ describe('Settings Panel', () => { }); }); }); - describe('Releases', () => { describe('with feature flag', () => { it('should show the releases toggle', () => { @@ -849,4 +849,42 @@ describe('Settings Panel', () => { }); }); }); + describe('Monitor', () => { + const expectedAccessLevel = [ + [10, 'Only Project Members'], + [20, 'Everyone With Access'], + ]; + describe('with feature flag', () => { + it('shows Monitor toggle instead of Operations toggle', () => { + wrapper = mountComponent({ + glFeatures: { splitOperationsVisibilityPermissions: true }, + }); + + expect(findMonitorSettings().exists()).toBe(true); + expect(findOperationsSettings().exists()).toBe(false); + expect(findMonitorSettings().findComponent(ProjectFeatureSetting).props('options')).toEqual( + expectedAccessLevel, + ); + }); + it('when monitorAccessLevel is for project members, it is also for everyone', () => { + wrapper = mountComponent({ + glFeatures: { splitOperationsVisibilityPermissions: true }, + currentSettings: { monitorAccessLevel: featureAccessLevel.PROJECT_MEMBERS }, + }); + + expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.EVERYONE); + }); + }); + describe('without feature flag', () => { + it('shows Operations toggle instead of Monitor toggle', () => { + wrapper = mountComponent({}); + + expect(findMonitorSettings().exists()).toBe(false); + expect(findOperationsSettings().exists()).toBe(true); + expect( + findOperationsSettings().findComponent(ProjectFeatureSetting).props('options'), + ).toEqual(expectedAccessLevel); + }); + }); + }); }); diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 361640659b801c0ab581877440fc155b08a2c311..352255afc8dca071777d9f6d0101aec73fab93cb 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -586,6 +586,7 @@ ProjectFeature: - environments_access_level - feature_flags_access_level - releases_access_level +- monitor_access_level - created_at - updated_at ProtectedBranch::MergeAccessLevel: diff --git a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb index ba5137e2b92f6ef3a4a817b0e806a94598307f88..bd0904b9db2cfbe55c618d81f99361d79ef4e84c 100644 --- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb @@ -12,11 +12,28 @@ subject { described_class.new(context) } describe '#render?' do - context 'when operations feature is disabled' do - it 'returns false' do - project.project_feature.update!(operations_access_level: Featurable::DISABLED) + using RSpec::Parameterized::TableSyntax + let(:enabled) { Featurable::PRIVATE } + let(:disabled) { Featurable::DISABLED } + + where(:flag_enabled, :operations_access_level, :monitor_level, :render) do + true | ref(:disabled) | ref(:enabled) | true + true | ref(:disabled) | ref(:disabled) | false + true | ref(:enabled) | ref(:enabled) | true + true | ref(:enabled) | ref(:disabled) | false + false | ref(:disabled) | ref(:enabled) | false + false | ref(:disabled) | ref(:disabled) | false + false | ref(:enabled) | ref(:enabled) | true + false | ref(:enabled) | ref(:disabled) | true + end + + with_them do + it 'renders when expected to' do + stub_feature_flags(split_operations_visibility_permissions: flag_enabled) + project.project_feature.update!(operations_access_level: operations_access_level) + project.project_feature.update!(monitor_access_level: monitor_level) - expect(subject.render?).to be false + expect(subject.render?).to be render end end diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb index b49b9ce8a2a0a7ffd1eedb869263364246d4e08c..89f34834aa41ce9b5bae64eec4e0a8392eda4cbf 100644 --- a/spec/models/concerns/project_features_compatibility_spec.rb +++ b/spec/models/concerns/project_features_compatibility_spec.rb @@ -8,6 +8,7 @@ let(:features) do features_enabled + %w( repository pages operations container_registry package_registry environments feature_flags releases + monitor ) end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 9bf96475fc1fa5b1c6399e696720ba3dd3b53b38..5b032c533523a6f88d3b65c8b2961cf99c8e7e80 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -1930,14 +1930,10 @@ def set_access_level(access_level) describe 'operations feature' do using RSpec::Parameterized::TableSyntax - before do - stub_feature_flags(split_operations_visibility_permissions: false) - end + let(:guest_permissions) { [:read_environment, :read_deployment] } - let(:guest_operations_permissions) { [:read_environment, :read_deployment] } - - let(:developer_operations_permissions) do - guest_operations_permissions + [ + let(:developer_permissions) do + guest_permissions + [ :read_feature_flag, :read_sentry_issue, :read_alert_management_alert, :read_terraform_state, :metrics_dashboard, :read_pod_logs, :read_prometheus, :create_feature_flag, :create_environment, :create_deployment, :update_feature_flag, :update_environment, @@ -1946,13 +1942,17 @@ def set_access_level(access_level) ] end - let(:maintainer_operations_permissions) do - developer_operations_permissions + [ + let(:maintainer_permissions) do + developer_permissions + [ :read_cluster, :create_cluster, :update_cluster, :admin_environment, :admin_cluster, :admin_terraform_state, :admin_deployment ] end + before do + stub_feature_flags(split_operations_visibility_permissions: false) + end + where(:project_visibility, :access_level, :role, :allowed) do :public | ProjectFeature::ENABLED | :maintainer | true :public | ProjectFeature::ENABLED | :developer | true @@ -2005,33 +2005,22 @@ def set_access_level(access_level) expect_disallowed(*permissions_abilities(role)) end end - - def permissions_abilities(role) - case role - when :maintainer - maintainer_operations_permissions - when :developer - developer_operations_permissions - else - guest_operations_permissions - end - end end end describe 'environments feature' do using RSpec::Parameterized::TableSyntax - let(:guest_environments_permissions) { [:read_environment, :read_deployment] } + let(:guest_permissions) { [:read_environment, :read_deployment] } - let(:developer_environments_permissions) do - guest_environments_permissions + [ + let(:developer_permissions) do + guest_permissions + [ :create_environment, :create_deployment, :update_environment, :update_deployment, :destroy_environment ] end - let(:maintainer_environments_permissions) do - developer_environments_permissions + [:admin_environment, :admin_deployment] + let(:maintainer_permissions) do + developer_permissions + [:admin_environment, :admin_deployment] end where(:project_visibility, :access_level, :role, :allowed) do @@ -2086,15 +2075,73 @@ def permissions_abilities(role) expect_disallowed(*permissions_abilities(role)) end end + end + end - def permissions_abilities(role) - case role - when :maintainer - maintainer_environments_permissions - when :developer - developer_environments_permissions + describe 'monitor feature' do + using RSpec::Parameterized::TableSyntax + + let(:guest_permissions) { [] } + + let(:developer_permissions) do + guest_permissions + [ + :read_sentry_issue, :read_alert_management_alert, :metrics_dashboard, + :update_sentry_issue, :update_alert_management_alert + ] + end + + let(:maintainer_permissions) { developer_permissions } + + where(:project_visibility, :access_level, :role, :allowed) do + :public | ProjectFeature::ENABLED | :maintainer | true + :public | ProjectFeature::ENABLED | :developer | true + :public | ProjectFeature::ENABLED | :guest | true + :public | ProjectFeature::ENABLED | :anonymous | true + :public | ProjectFeature::PRIVATE | :maintainer | true + :public | ProjectFeature::PRIVATE | :developer | true + :public | ProjectFeature::PRIVATE | :guest | true + :public | ProjectFeature::PRIVATE | :anonymous | false + :public | ProjectFeature::DISABLED | :maintainer | false + :public | ProjectFeature::DISABLED | :developer | false + :public | ProjectFeature::DISABLED | :guest | false + :public | ProjectFeature::DISABLED | :anonymous | false + :internal | ProjectFeature::ENABLED | :maintainer | true + :internal | ProjectFeature::ENABLED | :developer | true + :internal | ProjectFeature::ENABLED | :guest | true + :internal | ProjectFeature::ENABLED | :anonymous | false + :internal | ProjectFeature::PRIVATE | :maintainer | true + :internal | ProjectFeature::PRIVATE | :developer | true + :internal | ProjectFeature::PRIVATE | :guest | true + :internal | ProjectFeature::PRIVATE | :anonymous | false + :internal | ProjectFeature::DISABLED | :maintainer | false + :internal | ProjectFeature::DISABLED | :developer | false + :internal | ProjectFeature::DISABLED | :guest | false + :internal | ProjectFeature::DISABLED | :anonymous | false + :private | ProjectFeature::ENABLED | :maintainer | true + :private | ProjectFeature::ENABLED | :developer | true + :private | ProjectFeature::ENABLED | :guest | false + :private | ProjectFeature::ENABLED | :anonymous | false + :private | ProjectFeature::PRIVATE | :maintainer | true + :private | ProjectFeature::PRIVATE | :developer | true + :private | ProjectFeature::PRIVATE | :guest | false + :private | ProjectFeature::PRIVATE | :anonymous | false + :private | ProjectFeature::DISABLED | :maintainer | false + :private | ProjectFeature::DISABLED | :developer | false + :private | ProjectFeature::DISABLED | :guest | false + :private | ProjectFeature::DISABLED | :anonymous | false + end + + with_them do + let(:current_user) { user_subject(role) } + let(:project) { project_subject(project_visibility) } + + it 'allows/disallows the abilities based on the monitor feature access level' do + project.project_feature.update!(monitor_access_level: access_level) + + if allowed + expect_allowed(*permissions_abilities(role)) else - guest_environments_permissions + expect_disallowed(*permissions_abilities(role)) end end end @@ -2682,6 +2729,8 @@ def permissions_abilities(role) end end + private + def project_subject(project_type) case project_type when :public