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