diff --git a/app/presenters/projects/security/configuration_presenter.rb b/app/presenters/projects/security/configuration_presenter.rb
index cb368b804a12d72948006620ac239520d0564a1e..7df38dd7d6f6543a2edbe895fcf2b0be8645bd87 100644
--- a/app/presenters/projects/security/configuration_presenter.rb
+++ b/app/presenters/projects/security/configuration_presenter.rb
@@ -45,7 +45,7 @@ def can_enable_auto_devops?
end
def user_is_project_admin?
- can?(current_user, :admin_project, self)
+ can?(current_user, :admin_security_testing, self)
end
def gitlab_ci_history_path
diff --git a/app/validators/json_schemas/member_role_permissions.json b/app/validators/json_schemas/member_role_permissions.json
index 92efa0f0f5583f4e6026ec2cfeb37dd68f0c1f74..042ab9ee5b4dc66573773835068fd9da67deb2be 100644
--- a/app/validators/json_schemas/member_role_permissions.json
+++ b/app/validators/json_schemas/member_role_permissions.json
@@ -28,6 +28,9 @@
"admin_runners": {
"type": "boolean"
},
+ "admin_security_testing": {
+ "type": "boolean"
+ },
"admin_terraform_state": {
"type": "boolean"
},
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 2ed172aee518b52b7b727a9cd8e973078ffe8d3c..d36518747a07bd5c920cfaaf7eaee63b56e22c31 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -40942,6 +40942,7 @@ Member role permission.
| `ADMIN_PROTECTED_BRANCH` | Create, read, update, and delete protected branches for a project. |
| `ADMIN_PUSH_RULES` | Configure push rules for repositories at the group or project level. |
| `ADMIN_RUNNERS` | Create, view, edit, and delete group or project Runners. Includes configuring Runner settings. |
+| `ADMIN_SECURITY_TESTING` | Edit and manage security testing configurations and settings. |
| `ADMIN_TERRAFORM_STATE` | Execute terraform commands, lock/unlock terraform state files, and remove file versions. |
| `ADMIN_VULNERABILITY` | Edit the vulnerability object, including the status and linking an issue. Includes the `read_vulnerability` permission actions. |
| `ADMIN_WEB_HOOK` | Manage webhooks. |
@@ -40977,6 +40978,7 @@ Member role standard permission.
| `ADMIN_PROTECTED_BRANCH` | Create, read, update, and delete protected branches for a project. |
| `ADMIN_PUSH_RULES` | Configure push rules for repositories at the group or project level. |
| `ADMIN_RUNNERS` | Create, view, edit, and delete group or project Runners. Includes configuring Runner settings. |
+| `ADMIN_SECURITY_TESTING` | Edit and manage security testing configurations and settings. |
| `ADMIN_TERRAFORM_STATE` | Execute terraform commands, lock/unlock terraform state files, and remove file versions. |
| `ADMIN_VULNERABILITY` | Edit the vulnerability object, including the status and linking an issue. Includes the `read_vulnerability` permission actions. |
| `ADMIN_WEB_HOOK` | Manage webhooks. |
diff --git a/doc/api/member_roles.md b/doc/api/member_roles.md
index 4c768b013bb4edbb306f9c8131aec5039ba20ad7..1d2408bda353ae0e6cad2588549799f9bea0d89a 100644
--- a/doc/api/member_roles.md
+++ b/doc/api/member_roles.md
@@ -24,7 +24,8 @@ DETAILS:
> - [Delete project introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139696) in GitLab 16.8.
> - [Manage group access tokens introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140115) in GitLab 16.8.
> - [Admin terraform state introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140759) in GitLab 16.8.
-> - Ability to create and remove an instance-wide custom role on GitLab Self-Managed [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141562) in GitLab 16.9.
+> - Allow to create and remove an instance-wide custom role on GitLab Self-Managed [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141562) in GitLab 16.9.
+> - [Admin security testing introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176628) in GitLab 17.9 [with a flag](../administration/feature_flags.md) named `custom_ability_admin_security_testing`. Disabled by default.
Use this API to interact with member roles for your GitLab.com groups or entire self-managed instance.
diff --git a/doc/api/openapi/openapi_v2.yaml b/doc/api/openapi/openapi_v2.yaml
index 298ebcd267cca53bb6f7fd2b89829ebc3e1013bc..9b59fb806bf2b494f84f7185de3994695725bb4e 100644
--- a/doc/api/openapi/openapi_v2.yaml
+++ b/doc/api/openapi/openapi_v2.yaml
@@ -44037,6 +44037,8 @@ definitions:
type: boolean
admin_vulnerability:
type: boolean
+ admin_security_testing:
+ type: boolean
admin_web_hook:
type: boolean
archive_project:
diff --git a/ee/app/policies/ee/group_policy.rb b/ee/app/policies/ee/group_policy.rb
index 0f82bbee4923a82c315b52b7395093f152526276..f686c2414a6da4a566a03380fba682ade9c81185 100644
--- a/ee/app/policies/ee/group_policy.rb
+++ b/ee/app/policies/ee/group_policy.rb
@@ -319,6 +319,7 @@ module GroupPolicy
enable :read_jobs_statistics
enable :read_runner_usage
enable :admin_push_rules
+ enable :admin_security_testing
end
rule { (admin | maintainer) & group_analytics_dashboards_available & ~has_parent }.policy do
@@ -574,6 +575,21 @@ module GroupPolicy
enable :admin_vulnerability
end
+ rule { custom_role_enables_admin_security_testing }.policy do
+ enable :admin_security_testing
+ end
+
+ rule { security_dashboard_enabled & can?(:admin_security_testing) }.policy do
+ enable :access_security_and_compliance
+ enable :read_security_configuration
+ enable :read_group_security_dashboard
+ enable :read_security_resource
+ end
+
+ rule { pre_receive_secret_detection_available & can?(:admin_security_testing) }.policy do
+ enable :enable_pre_receive_secret_detection
+ end
+
rule { custom_role_enables_admin_group_member }.policy do
enable :admin_group_member
enable :update_group_member
diff --git a/ee/app/policies/ee/project_policy.rb b/ee/app/policies/ee/project_policy.rb
index 298ef2ca8956b0726a14096dc99c32aef5209263..5e511e76fc01999520782c88340fddd567917acb 100644
--- a/ee/app/policies/ee/project_policy.rb
+++ b/ee/app/policies/ee/project_policy.rb
@@ -456,6 +456,55 @@ module ProjectPolicy
enable :create_on_demand_dast_scan
end
+ rule { security_dashboard_enabled & can?(:maintainer_access) }.policy do
+ enable :admin_security_testing
+ end
+
+ rule { custom_role_enables_admin_security_testing }.policy do
+ enable :admin_security_testing
+ end
+
+ rule { security_dashboard_enabled & can?(:admin_security_testing) }.policy do
+ enable :access_security_and_compliance
+ enable :read_security_configuration
+ enable :read_project_security_dashboard
+ enable :read_security_resource
+
+ # create scanner configuration
+ enable :push_code
+ enable :download_code
+ enable :read_merge_request
+ enable :create_merge_request_from
+ end
+
+ rule { pre_receive_secret_detection_available & can?(:admin_security_testing) }.policy do
+ enable :read_pre_receive_secret_detection_info
+ enable :enable_pre_receive_secret_detection
+ enable :read_project_security_exclusions
+ end
+
+ rule { container_scanning_for_registry_available & can?(:admin_security_testing) }.policy do
+ enable :enable_container_scanning_for_registry
+ end
+
+ rule { coverage_fuzzing_enabled & can?(:admin_security_testing) }.policy do
+ enable :read_coverage_fuzzing
+ enable :create_coverage_fuzzing_corpus
+ end
+
+ rule { security_scans_api_enabled & can?(:admin_security_testing) }.policy do
+ enable :access_security_scans_api
+ end
+
+ rule { on_demand_scans_enabled & can?(:admin_security_testing) }.policy do
+ enable :read_on_demand_dast_scan
+ enable :create_on_demand_dast_scan
+ enable :edit_on_demand_dast_scan
+
+ enable :read_project_runners # read runner tags when creating scan
+ enable :create_pipeline # run a scan
+ end
+
rule { security_dashboard_enabled & security_policy_bot }.policy do
enable :create_vulnerability_state_transition
end
diff --git a/ee/config/custom_abilities/admin_security_testing.yml b/ee/config/custom_abilities/admin_security_testing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..21d9bde664b7092b58225568483cb332d89fca1a
--- /dev/null
+++ b/ee/config/custom_abilities/admin_security_testing.yml
@@ -0,0 +1,13 @@
+---
+title: Manage security testing
+name: admin_security_testing
+description: Edit and manage security testing configurations and settings.
+introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/508649
+introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176628
+feature_category: security_policy_management
+milestone: "17.9"
+group_ability: true
+project_ability: true
+requirements: []
+enabled_for_group_access_levels: [40, 50]
+enabled_for_project_access_levels: [40, 50]
diff --git a/ee/config/feature_flags/beta/custom_ability_admin_security_testing.yml b/ee/config/feature_flags/beta/custom_ability_admin_security_testing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8bda7bdf94ad6fd13684d9edff532386565327bd
--- /dev/null
+++ b/ee/config/feature_flags/beta/custom_ability_admin_security_testing.yml
@@ -0,0 +1,9 @@
+---
+name: custom_ability_admin_security_testing
+feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/508649
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176628
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/513510
+milestone: "17.9"
+group: group::security platform management
+type: beta
+default_enabled: false
diff --git a/ee/spec/policies/group_policy_spec.rb b/ee/spec/policies/group_policy_spec.rb
index a72f75a73559abfb8adc8ad42509f237c6fec40d..a0785058b827bfed2e6b2abcf3ce323eb9c28090 100644
--- a/ee/spec/policies/group_policy_spec.rb
+++ b/ee/spec/policies/group_policy_spec.rb
@@ -3956,6 +3956,27 @@ def create_member_role(member, abilities = member_role_abilities)
it_behaves_like 'custom roles abilities'
end
+ context 'for a custom role with the `admin_security_testing` ability' do
+ let(:member_role_abilities) { { admin_security_testing: true } }
+ let(:licensed_features) do
+ { security_dashboard: true,
+ pre_receive_secret_detection: true,
+ group_level_compliance_dashboard: true }
+ end
+
+ let(:allowed_abilities) do
+ [
+ :access_security_and_compliance,
+ :read_security_configuration,
+ :read_group_security_dashboard,
+ :read_security_resource,
+ :enable_pre_receive_secret_detection
+ ]
+ end
+
+ it_behaves_like 'custom roles abilities'
+ end
+
context 'when compliance framework feature is unavailable' do
before do
create_member_role(group_member_guest)
diff --git a/ee/spec/policies/project_policy_spec.rb b/ee/spec/policies/project_policy_spec.rb
index 3dceb8e71dae3fafa38d9da12218b2fc94ce4624..3d34a18de580073518068361a72068c489668730 100644
--- a/ee/spec/policies/project_policy_spec.rb
+++ b/ee/spec/policies/project_policy_spec.rb
@@ -724,169 +724,128 @@
it_behaves_like 'correct access to security and compliance'
end
- end
- describe 'vulnerability feedback permissions' do
- before do
- stub_licensed_features(security_dashboard: true)
- end
+ describe 'vulnerability feedback permissions' do
+ before do
+ stub_licensed_features(security_dashboard: true)
+ end
- context 'with developer' do
- let(:current_user) { developer }
+ context 'with developer' do
+ let(:current_user) { developer }
- it { is_expected.to be_allowed(:read_vulnerability_feedback) }
- it { is_expected.to be_disallowed(:create_vulnerability_feedback) }
- it { is_expected.to be_disallowed(:update_vulnerability_feedback) }
- it { is_expected.to be_disallowed(:destroy_vulnerability_feedback) }
- end
+ it { is_expected.to be_allowed(:read_vulnerability_feedback) }
+ it { is_expected.to be_disallowed(:create_vulnerability_feedback) }
+ it { is_expected.to be_disallowed(:update_vulnerability_feedback) }
+ it { is_expected.to be_disallowed(:destroy_vulnerability_feedback) }
+ end
- where(permission: %i[
- read_vulnerability_feedback
- create_vulnerability_feedback
- update_vulnerability_feedback
- destroy_vulnerability_feedback
- ])
+ where(permission: %i[
+ read_vulnerability_feedback
+ create_vulnerability_feedback
+ update_vulnerability_feedback
+ destroy_vulnerability_feedback
+ ])
- with_them do
- context 'with admin' do
- let(:current_user) { admin }
+ with_them do
+ context 'with admin' do
+ let(:current_user) { admin }
- context 'when admin mode enabled', :enable_admin_mode do
- it { is_expected.to be_allowed(permission) }
- end
+ context 'when admin mode enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(permission) }
+ end
- context 'when admin mode disabled' do
- it { is_expected.to be_disallowed(permission) }
+ context 'when admin mode disabled' do
+ it { is_expected.to be_disallowed(permission) }
+ end
end
- end
- %w[owner maintainer].each do |role|
- context "with #{role}" do
- let(:current_user) { send(role) }
+ %w[owner maintainer].each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
- it { is_expected.to be_allowed(permission) }
+ it { is_expected.to be_allowed(permission) }
+ end
end
- end
- %w[anonymous non_member guest planner reporter].each do |role|
- context "with #{role}" do
- let(:current_user) { send(role) }
+ %w[anonymous non_member guest planner reporter].each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
- it { is_expected.to be_disallowed(permission) }
+ it { is_expected.to be_disallowed(permission) }
+ end
end
end
end
- end
-
- shared_context 'when security dashboard feature is not available' do
- before do
- stub_licensed_features(security_dashboard: false)
- end
- end
- describe 'read_project_security_dashboard' do
- context 'with developer' do
- let(:current_user) { developer }
-
- include_context 'when security dashboard feature is not available'
-
- it { is_expected.to be_disallowed(:read_project_security_dashboard) }
+ shared_context 'when security dashboard feature is not available' do
+ before do
+ stub_licensed_features(security_dashboard: false)
+ end
end
- end
- describe 'vulnerability permissions' do
- describe 'dismiss_vulnerability' do
+ describe 'read_project_security_dashboard' do
context 'with developer' do
let(:current_user) { developer }
include_context 'when security dashboard feature is not available'
- it { is_expected.to be_disallowed(:admin_vulnerability) }
- it { is_expected.to be_disallowed(:read_vulnerability) }
- it { is_expected.to be_disallowed(:create_vulnerability_export) }
- end
- end
- end
-
- describe 'permissions for security bot' do
- let_it_be(:current_user) { create(:user, :security_bot) }
- let(:project) { private_project }
-
- let(:permissions) do
- %i[
- reporter_access
- push_code
- create_merge_request_from
- create_merge_request_in
- create_vulnerability_feedback
- read_project
- admin_merge_request
- ]
- end
-
- context 'when project does not have a security_setting' do
- before do
- project.security_setting.delete
- project.reload
+ it { is_expected.to be_disallowed(:read_project_security_dashboard) }
end
+ end
- it { is_expected.to be_allowed(*permissions) }
-
- context 'with user other than security bot' do
- let_it_be(:current_user) { create(:user) }
+ describe 'vulnerability permissions' do
+ describe 'dismiss_vulnerability' do
+ context 'with developer' do
+ let(:current_user) { developer }
- it { is_expected.to be_disallowed(*permissions) }
+ include_context 'when security dashboard feature is not available'
+ it { is_expected.to be_disallowed(:admin_vulnerability) }
+ it { is_expected.to be_disallowed(:read_vulnerability) }
+ it { is_expected.to be_disallowed(:create_vulnerability_export) }
+ end
end
end
- end
-
- describe 'security orchestration policies' do
- before do
- stub_licensed_features(security_orchestration_policies: true)
- end
- context 'with developer or maintainer role' do
- where(role: %w[maintainer developer])
+ describe 'permissions for security bot' do
+ let_it_be(:current_user) { create(:user, :security_bot) }
- with_them do
- let(:current_user) { public_send(role) }
+ let(:project) { private_project }
- it { is_expected.to be_allowed(:read_security_orchestration_policies) }
- it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
+ let(:permissions) do
+ %i[
+ reporter_access
+ push_code
+ create_merge_request_from
+ create_merge_request_in
+ create_vulnerability_feedback
+ read_project
+ admin_merge_request
+ ]
end
- end
-
- context 'with owner role' do
- where(role: %w[owner])
-
- with_them do
- let(:current_user) { public_send(role) }
- it { is_expected.to be_allowed(:read_security_orchestration_policies) }
- it { is_expected.to be_allowed(:update_security_orchestration_policy_project) }
- it { is_expected.to be_allowed(:modify_security_policy) }
+ context 'when project does not have a security_setting' do
+ before do
+ project.security_setting.delete
+ project.reload
+ end
- context 'when security_orchestration_policy_configuration is present' do
- let_it_be(:security_policy_management_project) { create(:project) }
+ it { is_expected.to be_allowed(*permissions) }
- before do
- create(:security_orchestration_policy_configuration, project: project, security_policy_management_project: security_policy_management_project)
- end
+ context 'with user other than security bot' do
+ let_it_be(:current_user) { create(:user) }
- it { is_expected.to be_disallowed(:modify_security_policy) }
+ it { is_expected.to be_disallowed(*permissions) }
end
end
end
- context 'with auditor role' do
- where(role: %w[auditor])
-
+ describe 'security orchestration policies' do
before do
- project.project_feature.update!(security_orchestration_policies: feature_status)
+ stub_licensed_features(security_orchestration_policies: true)
end
- context 'with policy feature enabled' do
- let(:feature_status) { ProjectFeature::ENABLED }
+ context 'with developer or maintainer role' do
+ where(role: %w[maintainer developer])
with_them do
let(:current_user) { public_send(role) }
@@ -896,3732 +855,3813 @@
end
end
- context 'with policy feature disabled' do
- let(:feature_status) { ProjectFeature::DISABLED }
+ context 'with owner role' do
+ where(role: %w[owner])
with_them do
let(:current_user) { public_send(role) }
- it { is_expected.to be_disallowed(:read_security_orchestration_policies) }
- it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
+ it { is_expected.to be_allowed(:read_security_orchestration_policies) }
+ it { is_expected.to be_allowed(:update_security_orchestration_policy_project) }
+ it { is_expected.to be_allowed(:modify_security_policy) }
+
+ context 'when security_orchestration_policy_configuration is present' do
+ let_it_be(:security_policy_management_project) { create(:project) }
+
+ before do
+ create(:security_orchestration_policy_configuration, project: project, security_policy_management_project: security_policy_management_project)
+ end
+
+ it { is_expected.to be_disallowed(:modify_security_policy) }
+ end
end
end
- end
- context 'when security_orchestration_policy_configuration is present' do
- let_it_be(:security_policy_management_project) { create(:project) }
- let(:current_user) { developer }
+ context 'with auditor role' do
+ where(role: %w[auditor])
- before do
- create(:security_orchestration_policy_configuration, project: project, security_policy_management_project: security_policy_management_project)
+ before do
+ project.project_feature.update!(security_orchestration_policies: feature_status)
+ end
+
+ context 'with policy feature enabled' do
+ let(:feature_status) { ProjectFeature::ENABLED }
+
+ with_them do
+ let(:current_user) { public_send(role) }
+
+ it { is_expected.to be_allowed(:read_security_orchestration_policies) }
+ it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
+ end
+ end
+
+ context 'with policy feature disabled' do
+ let(:feature_status) { ProjectFeature::DISABLED }
+
+ with_them do
+ let(:current_user) { public_send(role) }
+
+ it { is_expected.to be_disallowed(:read_security_orchestration_policies) }
+ it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
+ end
+ end
end
- context 'when current_user is developer of security_policy_management_project' do
+ context 'when security_orchestration_policy_configuration is present' do
+ let_it_be(:security_policy_management_project) { create(:project) }
+ let(:current_user) { developer }
+
before do
- security_policy_management_project.add_developer(developer)
+ create(:security_orchestration_policy_configuration, project: project, security_policy_management_project: security_policy_management_project)
end
- it { is_expected.to be_allowed(:modify_security_policy) }
- end
+ context 'when current_user is developer of security_policy_management_project' do
+ before do
+ security_policy_management_project.add_developer(developer)
+ end
- context 'when current_user is not developer of security_policy_management_project' do
- it { is_expected.to be_disallowed(:modify_security_policy) }
+ it { is_expected.to be_allowed(:modify_security_policy) }
+ end
+
+ context 'when current_user is not developer of security_policy_management_project' do
+ it { is_expected.to be_disallowed(:modify_security_policy) }
+ end
end
end
- end
- describe 'coverage_fuzzing' do
- context 'when coverage_fuzzing feature is available' do
- before do
- stub_licensed_features(coverage_fuzzing: true)
- end
+ describe 'coverage_fuzzing' do
+ context 'when coverage_fuzzing feature is available' do
+ before do
+ stub_licensed_features(coverage_fuzzing: true)
+ end
- context 'with developer or higher role' do
- where(role: %w[owner maintainer developer])
+ context 'with developer or higher role' do
+ where(role: %w[owner maintainer developer])
- with_them do
- let(:current_user) { public_send(role) }
+ with_them do
+ let(:current_user) { public_send(role) }
- it { is_expected.to be_allowed(:read_coverage_fuzzing) }
+ it { is_expected.to be_allowed(:read_coverage_fuzzing) }
+ end
end
- end
- context 'with admin' do
- let(:current_user) { admin }
+ context 'with admin' do
+ let(:current_user) { admin }
- context 'when admin mode enabled', :enable_admin_mode do
- it { is_expected.to be_allowed(:read_coverage_fuzzing) }
- end
+ context 'when admin mode enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:read_coverage_fuzzing) }
+ end
- context 'when admin mode disabled' do
- it { is_expected.to be_disallowed(:read_coverage_fuzzing) }
+ context 'when admin mode disabled' do
+ it { is_expected.to be_disallowed(:read_coverage_fuzzing) }
+ end
end
- end
- context 'with less than developer role' do
- where(role: %w[reporter planner guest])
+ context 'with less than developer role' do
+ where(role: %w[reporter planner guest])
- with_them do
- let(:current_user) { public_send(role) }
+ with_them do
+ let(:current_user) { public_send(role) }
+
+ it { is_expected.to be_disallowed(:read_coverage_fuzzing) }
+ end
+ end
+
+ context 'with non member' do
+ let(:current_user) { non_member }
it { is_expected.to be_disallowed(:read_coverage_fuzzing) }
end
- end
- context 'with non member' do
- let(:current_user) { non_member }
+ context 'with anonymous' do
+ let(:current_user) { anonymous }
- it { is_expected.to be_disallowed(:read_coverage_fuzzing) }
+ it { is_expected.to be_disallowed(:read_coverage_fuzzing) }
+ end
end
- context 'with anonymous' do
- let(:current_user) { anonymous }
+ context 'when coverage fuzzing feature is not available' do
+ let(:current_user) { admin }
+
+ before do
+ stub_licensed_features(coverage_fuzzing: true)
+ end
it { is_expected.to be_disallowed(:read_coverage_fuzzing) }
end
end
- context 'when coverage fuzzing feature is not available' do
- let(:current_user) { admin }
-
+ describe 'remove_project when default_project_deletion_protection is set to true' do
before do
- stub_licensed_features(coverage_fuzzing: true)
+ allow(Gitlab::CurrentSettings.current_application_settings)
+ .to receive(:default_project_deletion_protection) { true }
end
- it { is_expected.to be_disallowed(:read_coverage_fuzzing) }
- end
- end
+ context 'with admin' do
+ let(:current_user) { admin }
- describe 'remove_project when default_project_deletion_protection is set to true' do
- before do
- allow(Gitlab::CurrentSettings.current_application_settings)
- .to receive(:default_project_deletion_protection) { true }
- end
+ context 'when admin mode enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:remove_project) }
+ end
- context 'with admin' do
- let(:current_user) { admin }
+ context 'when admin mode disabled' do
+ it { is_expected.to be_disallowed(:remove_project) }
+ end
- context 'when admin mode enabled', :enable_admin_mode do
- it { is_expected.to be_allowed(:remove_project) }
- end
+ context 'who owns the project' do
+ let(:project) { create(:project, :public, namespace: admin.namespace) }
- context 'when admin mode disabled' do
- it { is_expected.to be_disallowed(:remove_project) }
+ it { is_expected.to be_disallowed(:remove_project) }
+ end
end
- context 'who owns the project' do
- let(:project) { create(:project, :public, namespace: admin.namespace) }
+ context 'with owner' do
+ let(:current_user) { owner }
it { is_expected.to be_disallowed(:remove_project) }
end
end
- context 'with owner' do
- let(:current_user) { owner }
-
- it { is_expected.to be_disallowed(:remove_project) }
- end
- end
+ describe 'admin_feature_flags_issue_links' do
+ before do
+ stub_licensed_features(feature_flags_related_issues: true)
+ end
- describe 'admin_feature_flags_issue_links' do
- before do
- stub_licensed_features(feature_flags_related_issues: true)
- end
+ context 'with maintainer' do
+ let(:current_user) { maintainer }
- context 'with maintainer' do
- let(:current_user) { maintainer }
+ it { is_expected.to be_allowed(:admin_feature_flags_issue_links) }
- it { is_expected.to be_allowed(:admin_feature_flags_issue_links) }
+ context 'when repository is disabled' do
+ before do
+ project.project_feature.update!(
+ merge_requests_access_level: ProjectFeature::DISABLED,
+ builds_access_level: ProjectFeature::DISABLED,
+ repository_access_level: ProjectFeature::DISABLED
+ )
+ end
- context 'when repository is disabled' do
- before do
- project.project_feature.update!(
- merge_requests_access_level: ProjectFeature::DISABLED,
- builds_access_level: ProjectFeature::DISABLED,
- repository_access_level: ProjectFeature::DISABLED
- )
+ it { is_expected.to be_disallowed(:admin_feature_flags_issue_links) }
end
-
- it { is_expected.to be_disallowed(:admin_feature_flags_issue_links) }
end
- end
- context 'with developer' do
- let(:current_user) { developer }
+ context 'with developer' do
+ let(:current_user) { developer }
- it { is_expected.to be_allowed(:admin_feature_flags_issue_links) }
+ it { is_expected.to be_allowed(:admin_feature_flags_issue_links) }
- context 'when feature is unlicensed' do
- before do
- stub_licensed_features(feature_flags_related_issues: false)
+ context 'when feature is unlicensed' do
+ before do
+ stub_licensed_features(feature_flags_related_issues: false)
+ end
+
+ it { is_expected.to be_disallowed(:admin_feature_flags_issue_links) }
end
+ end
+
+ context 'with reporter' do
+ let(:current_user) { reporter }
it { is_expected.to be_disallowed(:admin_feature_flags_issue_links) }
end
end
- context 'with reporter' do
- let(:current_user) { reporter }
+ describe 'admin_software_license_policy' do
+ context 'without license scanning feature available' do
+ before do
+ stub_licensed_features(license_scanning: false)
+ end
- it { is_expected.to be_disallowed(:admin_feature_flags_issue_links) }
- end
- end
+ let(:current_user) { admin }
- describe 'admin_software_license_policy' do
- context 'without license scanning feature available' do
- before do
- stub_licensed_features(license_scanning: false)
+ it { is_expected.to be_disallowed(:admin_software_license_policy) }
end
- let(:current_user) { admin }
-
- it { is_expected.to be_disallowed(:admin_software_license_policy) }
- end
-
- context 'with admin' do
- let(:current_user) { admin }
+ context 'with admin' do
+ let(:current_user) { admin }
- context 'when admin mode enabled', :enable_admin_mode do
- it { is_expected.to be_allowed(:admin_software_license_policy) }
- end
+ context 'when admin mode enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:admin_software_license_policy) }
+ end
- context 'when admin mode disabled' do
- it { is_expected.to be_disallowed(:admin_software_license_policy) }
+ context 'when admin mode disabled' do
+ it { is_expected.to be_disallowed(:admin_software_license_policy) }
+ end
end
- end
- %w[owner maintainer].each do |role|
- context "with #{role}" do
- let(:current_user) { send(role) }
+ %w[owner maintainer].each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
- it { is_expected.to be_allowed(:admin_software_license_policy) }
+ it { is_expected.to be_allowed(:admin_software_license_policy) }
+ end
end
- end
- %w[anonymous non_member guest planner reporter developer].each do |role|
- context "with #{role}" do
- let(:current_user) { send(role) }
+ %w[anonymous non_member guest planner reporter developer].each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
- it { is_expected.to be_disallowed(:admin_software_license_policy) }
+ it { is_expected.to be_disallowed(:admin_software_license_policy) }
+ end
end
end
- end
-
- describe 'read_software_license_policy' do
- context 'without license scanning feature available' do
- before do
- stub_licensed_features(license_scanning: false)
- end
- let(:current_user) { admin }
+ describe 'read_software_license_policy' do
+ context 'without license scanning feature available' do
+ before do
+ stub_licensed_features(license_scanning: false)
+ end
- it { is_expected.to be_disallowed(:read_software_license_policy) }
- end
- end
+ let(:current_user) { admin }
- describe 'read_dependency' do
- context 'when dependency scanning feature available' do
- before do
- stub_licensed_features(dependency_scanning: true)
+ it { is_expected.to be_disallowed(:read_software_license_policy) }
end
+ end
- context 'with public project' do
- let(:current_user) { create(:user) }
-
- context 'with public access to repository' do
- let(:project) { public_project }
-
- it { is_expected.to be_allowed(:read_dependency) }
- end
-
- context 'with limited access to repository' do
- let(:project) { create(:project, :public, :repository_private) }
-
- it { is_expected.not_to be_allowed(:read_dependency) }
+ describe 'read_dependency' do
+ context 'when dependency scanning feature available' do
+ before do
+ stub_licensed_features(dependency_scanning: true)
end
- end
- context 'with private project' do
- let(:project) { private_project }
+ context 'with public project' do
+ let(:current_user) { create(:user) }
- context 'with admin' do
- let(:current_user) { admin }
+ context 'with public access to repository' do
+ let(:project) { public_project }
- context 'when admin mode enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:read_dependency) }
end
- context 'when admin mode disabled' do
- it { is_expected.to be_disallowed(:read_dependency) }
+ context 'with limited access to repository' do
+ let(:project) { create(:project, :public, :repository_private) }
+
+ it { is_expected.not_to be_allowed(:read_dependency) }
end
end
- %w[owner maintainer developer reporter].each do |role|
- context "with #{role}" do
- let(:current_user) { send(role) }
+ context 'with private project' do
+ let(:project) { private_project }
- it { is_expected.to be_allowed(:read_dependency) }
- end
- end
+ context 'with admin' do
+ let(:current_user) { admin }
- %w[anonymous non_member guest planner].each do |role|
- context "with #{role}" do
- let(:current_user) { send(role) }
+ context 'when admin mode enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:read_dependency) }
+ end
- it { is_expected.to be_disallowed(:read_dependency) }
+ context 'when admin mode disabled' do
+ it { is_expected.to be_disallowed(:read_dependency) }
+ end
end
- end
- end
- end
- context 'when dependency list feature not available' do
- let(:current_user) { admin }
+ %w[owner maintainer developer reporter].each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
- it { is_expected.not_to be_allowed(:read_dependency) }
- end
- end
+ it { is_expected.to be_allowed(:read_dependency) }
+ end
+ end
- describe 'read_licenses' do
- context 'when license management feature available' do
- context 'with public project' do
- let(:current_user) { non_member }
+ %w[anonymous non_member guest planner].each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
- context 'with public access to repository' do
- it { is_expected.to be_allowed(:read_licenses) }
+ it { is_expected.to be_disallowed(:read_dependency) }
+ end
+ end
end
end
- context 'with private project' do
- let(:project) { private_project }
+ context 'when dependency list feature not available' do
+ let(:current_user) { admin }
- where(role: %w[owner maintainer developer reporter])
+ it { is_expected.not_to be_allowed(:read_dependency) }
+ end
+ end
- with_them do
- let(:current_user) { public_send(role) }
+ describe 'read_licenses' do
+ context 'when license management feature available' do
+ context 'with public project' do
+ let(:current_user) { non_member }
- it { is_expected.to be_allowed(:read_licenses) }
+ context 'with public access to repository' do
+ it { is_expected.to be_allowed(:read_licenses) }
+ end
end
- context 'with admin' do
- let(:current_user) { admin }
+ context 'with private project' do
+ let(:project) { private_project }
+
+ where(role: %w[owner maintainer developer reporter])
+
+ with_them do
+ let(:current_user) { public_send(role) }
- context 'when admin mode enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:read_licenses) }
end
- context 'when admin mode disabled' do
- it { is_expected.to be_disallowed(:read_licenses) }
+ context 'with admin' do
+ let(:current_user) { admin }
+
+ context 'when admin mode enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:read_licenses) }
+ end
+
+ context 'when admin mode disabled' do
+ it { is_expected.to be_disallowed(:read_licenses) }
+ end
end
- end
- %w[anonymous non_member guest planner].each do |role|
- context "with #{role}" do
- let(:current_user) { send(role) }
+ %w[anonymous non_member guest planner].each do |role|
+ context "with #{role}" do
+ let(:current_user) { send(role) }
- it { is_expected.to be_disallowed(:read_licenses) }
+ it { is_expected.to be_disallowed(:read_licenses) }
+ end
end
end
end
- end
-
- context 'when license management feature in not available' do
- before do
- stub_licensed_features(license_scanning: false)
- end
-
- let(:current_user) { admin }
- it { is_expected.to be_disallowed(:read_licenses) }
- end
- end
+ context 'when license management feature in not available' do
+ before do
+ stub_licensed_features(license_scanning: false)
+ end
- describe 'publish_status_page' do
- let(:feature) { :status_page }
- let(:policy) { :publish_status_page }
+ let(:current_user) { admin }
- context 'when feature is available' do
- where(:role, :admin_mode, :allowed) do
- :anonymous | nil | false
- :guest | nil | false
- :planner | nil | false
- :reporter | nil | false
- :developer | nil | true
- :maintainer | nil | true
- :owner | nil | true
- :admin | false | false
- :admin | true | true
+ it { is_expected.to be_disallowed(:read_licenses) }
end
+ end
- with_them do
- let(:current_user) { public_send(role) if role }
+ describe 'publish_status_page' do
+ let(:feature) { :status_page }
+ let(:policy) { :publish_status_page }
- before do
- stub_licensed_features(feature => true)
- enable_admin_mode!(current_user) if admin_mode
+ context 'when feature is available' do
+ where(:role, :admin_mode, :allowed) do
+ :anonymous | nil | false
+ :guest | nil | false
+ :planner | nil | false
+ :reporter | nil | false
+ :developer | nil | true
+ :maintainer | nil | true
+ :owner | nil | true
+ :admin | false | false
+ :admin | true | true
end
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ with_them do
+ let(:current_user) { public_send(role) if role }
- context 'when feature is not available' do
before do
- stub_licensed_features(feature => false)
+ stub_licensed_features(feature => true)
+ enable_admin_mode!(current_user) if admin_mode
end
- it { is_expected.to be_disallowed(policy) }
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+
+ context 'when feature is not available' do
+ before do
+ stub_licensed_features(feature => false)
+ end
+
+ it { is_expected.to be_disallowed(policy) }
+ end
end
end
end
- end
-
- describe 'add_project_to_instance_security_dashboard' do
- let(:policy) { :add_project_to_instance_security_dashboard }
- context 'when user is auditor' do
- let(:current_user) { create(:user, :auditor) }
-
- it { is_expected.to be_allowed(policy) }
- end
+ describe 'add_project_to_instance_security_dashboard' do
+ let(:policy) { :add_project_to_instance_security_dashboard }
- context 'when user is not auditor' do
- context 'with developer access' do
- let(:current_user) { developer }
+ context 'when user is auditor' do
+ let(:current_user) { create(:user, :auditor) }
it { is_expected.to be_allowed(policy) }
end
- context 'without developer access' do
- let(:current_user) { create(:user) }
-
- it { is_expected.to be_disallowed(policy) }
- end
- end
- end
+ context 'when user is not auditor' do
+ context 'with developer access' do
+ let(:current_user) { developer }
- context 'visual review bot' do
- let(:current_user) { Users::Internal.visual_review_bot }
+ it { is_expected.to be_allowed(policy) }
+ end
- it { expect_disallowed(:create_note) }
- it { expect_disallowed(:read_note) }
- it { expect_disallowed(:resolve_note) }
- end
+ context 'without developer access' do
+ let(:current_user) { create(:user) }
- context 'when push_rules is not enabled by the current license' do
- before do
- stub_licensed_features(push_rules: false)
+ it { is_expected.to be_disallowed(policy) }
+ end
+ end
end
- let(:current_user) { maintainer }
-
- it { is_expected.to be_disallowed(:change_push_rules) }
- end
+ context 'visual review bot' do
+ let(:current_user) { Users::Internal.visual_review_bot }
- context 'when push_rules is enabled by the current license' do
- before do
- stub_licensed_features(push_rules: true)
+ it { expect_disallowed(:create_note) }
+ it { expect_disallowed(:read_note) }
+ it { expect_disallowed(:resolve_note) }
end
- let(:current_user) { maintainer }
-
- context 'when the user is an admin', :enable_admin_mode do
- let(:current_user) { admin }
-
- it { is_expected.to be_allowed(:change_push_rules) }
- end
+ context 'when push_rules is not enabled by the current license' do
+ before do
+ stub_licensed_features(push_rules: false)
+ end
- context 'when the user is a maintainer' do
let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:change_push_rules) }
- end
-
- context 'when the user is a developer' do
- let(:current_user) { developer }
-
it { is_expected.to be_disallowed(:change_push_rules) }
end
- end
- context 'commit_committer_check is not enabled by the current license' do
- before do
- stub_licensed_features(commit_committer_check: false)
- end
+ context 'when push_rules is enabled by the current license' do
+ before do
+ stub_licensed_features(push_rules: true)
+ end
- let(:current_user) { maintainer }
+ let(:current_user) { maintainer }
- it { is_expected.not_to be_allowed(:change_commit_committer_check) }
- it { is_expected.not_to be_allowed(:read_commit_committer_check) }
- end
+ context 'when the user is an admin', :enable_admin_mode do
+ let(:current_user) { admin }
- context 'commit_committer_check is enabled by the current license' do
- before do
- stub_licensed_features(commit_committer_check: true)
- end
+ it { is_expected.to be_allowed(:change_push_rules) }
+ end
- context 'when the user is an admin', :enable_admin_mode do
- let(:current_user) { admin }
+ context 'when the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:change_commit_committer_check) }
- it { is_expected.to be_allowed(:read_commit_committer_check) }
- end
+ it { is_expected.to be_allowed(:change_push_rules) }
+ end
- context 'the user is a maintainer' do
- let(:current_user) { maintainer }
+ context 'when the user is a developer' do
+ let(:current_user) { developer }
- it { is_expected.to be_allowed(:change_commit_committer_check) }
- it { is_expected.to be_allowed(:read_commit_committer_check) }
+ it { is_expected.to be_disallowed(:change_push_rules) }
+ end
end
- context 'the user is a developer' do
- let(:current_user) { developer }
+ context 'commit_committer_check is not enabled by the current license' do
+ before do
+ stub_licensed_features(commit_committer_check: false)
+ end
+
+ let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.not_to be_allowed(:read_commit_committer_check) }
end
- end
-
- context 'when commit_committer_name_check is not enabled by the current license' do
- before do
- stub_licensed_features(commit_committer_name_check: false)
- end
- let(:current_user) { maintainer }
+ context 'commit_committer_check is enabled by the current license' do
+ before do
+ stub_licensed_features(commit_committer_check: true)
+ end
- it { is_expected.to be_disallowed(:read_commit_committer_name_check) }
- it { is_expected.to be_disallowed(:change_commit_committer_name_check) }
- end
+ context 'when the user is an admin', :enable_admin_mode do
+ let(:current_user) { admin }
- context 'when commit_committer_name_check is enabled by the current license' do
- before do
- stub_licensed_features(commit_committer_name_check: true)
- end
+ it { is_expected.to be_allowed(:change_commit_committer_check) }
+ it { is_expected.to be_allowed(:read_commit_committer_check) }
+ end
- context 'when the user is an admin', :enable_admin_mode do
- let(:current_user) { admin }
+ context 'the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:read_commit_committer_name_check) }
- it { is_expected.to be_allowed(:change_commit_committer_name_check) }
- end
+ it { is_expected.to be_allowed(:change_commit_committer_check) }
+ it { is_expected.to be_allowed(:read_commit_committer_check) }
+ end
- context 'when the user is a maintainer' do
- let(:current_user) { maintainer }
+ context 'the user is a developer' do
+ let(:current_user) { developer }
- it { is_expected.to be_allowed(:read_commit_committer_name_check) }
- it { is_expected.to be_allowed(:change_commit_committer_name_check) }
+ it { is_expected.not_to be_allowed(:change_commit_committer_check) }
+ it { is_expected.not_to be_allowed(:read_commit_committer_check) }
+ end
end
- context 'the user is a developer' do
- let(:current_user) { developer }
+ context 'when commit_committer_name_check is not enabled by the current license' do
+ before do
+ stub_licensed_features(commit_committer_name_check: false)
+ end
+
+ let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:read_commit_committer_name_check) }
it { is_expected.to be_disallowed(:change_commit_committer_name_check) }
end
- end
-
- context 'reject_unsigned_commits is not enabled by the current license' do
- before do
- stub_licensed_features(reject_unsigned_commits: false)
- end
- let(:current_user) { maintainer }
+ context 'when commit_committer_name_check is enabled by the current license' do
+ before do
+ stub_licensed_features(commit_committer_name_check: true)
+ end
- it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
- it { is_expected.not_to be_allowed(:read_reject_unsigned_commits) }
- end
+ context 'when the user is an admin', :enable_admin_mode do
+ let(:current_user) { admin }
- context 'reject_unsigned_commits is enabled by the current license' do
- before do
- stub_licensed_features(reject_unsigned_commits: true)
- end
+ it { is_expected.to be_allowed(:read_commit_committer_name_check) }
+ it { is_expected.to be_allowed(:change_commit_committer_name_check) }
+ end
- context 'when the user is an admin', :enable_admin_mode do
- let(:current_user) { admin }
+ context 'when the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
- it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
- end
+ it { is_expected.to be_allowed(:read_commit_committer_name_check) }
+ it { is_expected.to be_allowed(:change_commit_committer_name_check) }
+ end
- context 'when the user is a maintainer' do
- let(:current_user) { maintainer }
+ context 'the user is a developer' do
+ let(:current_user) { developer }
- it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
- it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
+ it { is_expected.to be_disallowed(:read_commit_committer_name_check) }
+ it { is_expected.to be_disallowed(:change_commit_committer_name_check) }
+ end
end
- context 'when the user is a developer' do
- let(:current_user) { developer }
+ context 'reject_unsigned_commits is not enabled by the current license' do
+ before do
+ stub_licensed_features(reject_unsigned_commits: false)
+ end
+
+ let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.not_to be_allowed(:read_reject_unsigned_commits) }
end
- end
-
- context 'when reject_non_dco_commits is not enabled by the current license' do
- before do
- stub_licensed_features(reject_non_dco_commits: false)
- end
- let(:current_user) { maintainer }
+ context 'reject_unsigned_commits is enabled by the current license' do
+ before do
+ stub_licensed_features(reject_unsigned_commits: true)
+ end
- it { is_expected.to be_disallowed(:read_reject_non_dco_commits) }
- it { is_expected.to be_disallowed(:change_reject_non_dco_commits) }
- end
+ context 'when the user is an admin', :enable_admin_mode do
+ let(:current_user) { admin }
- context 'when reject_non_dco_commits is enabled by the current license' do
- before do
- stub_licensed_features(reject_non_dco_commits: true)
- end
+ it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
+ it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
+ end
- context 'when the user is an admin', :enable_admin_mode do
- let(:current_user) { admin }
+ context 'when the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:read_reject_non_dco_commits) }
- it { is_expected.to be_allowed(:change_reject_non_dco_commits) }
- end
+ it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
+ it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
+ end
- context 'when the user is a maintainer' do
- let(:current_user) { maintainer }
+ context 'when the user is a developer' do
+ let(:current_user) { developer }
- it { is_expected.to be_allowed(:read_reject_non_dco_commits) }
- it { is_expected.to be_allowed(:change_reject_non_dco_commits) }
+ it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
+ it { is_expected.not_to be_allowed(:read_reject_unsigned_commits) }
+ end
end
- context 'when the user is a developer' do
- let(:current_user) { developer }
+ context 'when reject_non_dco_commits is not enabled by the current license' do
+ before do
+ stub_licensed_features(reject_non_dco_commits: false)
+ end
+
+ let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:read_reject_non_dco_commits) }
it { is_expected.to be_disallowed(:change_reject_non_dco_commits) }
end
- end
- context 'when dora4 analytics is available' do
- before do
- stub_licensed_features(dora4_analytics: true)
- end
+ context 'when reject_non_dco_commits is enabled by the current license' do
+ before do
+ stub_licensed_features(reject_non_dco_commits: true)
+ end
- context 'when the user is a developer' do
- let(:current_user) { developer }
+ context 'when the user is an admin', :enable_admin_mode do
+ let(:current_user) { admin }
- it { is_expected.to be_allowed(:read_dora4_analytics) }
- end
+ it { is_expected.to be_allowed(:read_reject_non_dco_commits) }
+ it { is_expected.to be_allowed(:change_reject_non_dco_commits) }
+ end
- context 'when the user is an admin', :enable_admin_mode do
- let(:current_user) { admin }
+ context 'when the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:read_dora4_analytics) }
- end
- end
+ it { is_expected.to be_allowed(:read_reject_non_dco_commits) }
+ it { is_expected.to be_allowed(:change_reject_non_dco_commits) }
+ end
- context 'when dora4 analytics is not available' do
- let(:current_user) { developer }
+ context 'when the user is a developer' do
+ let(:current_user) { developer }
- before do
- stub_licensed_features(dora4_analytics: false)
+ it { is_expected.to be_disallowed(:read_reject_non_dco_commits) }
+ it { is_expected.to be_disallowed(:change_reject_non_dco_commits) }
+ end
end
- it { is_expected.not_to be_allowed(:read_dora4_analytics) }
- end
-
- describe ':read_enterprise_ai_analytics' do
- let(:project) { private_project_in_group }
- let(:guest) { inherited_guest }
- let(:planner) { inherited_planner }
- let(:reporter) { inherited_reporter }
+ context 'when dora4 analytics is available' do
+ before do
+ stub_licensed_features(dora4_analytics: true)
+ end
- context 'when on SAAS', :saas do
- let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, namespace: group) }
+ context 'when the user is a developer' do
+ let(:current_user) { developer }
- it_behaves_like 'ai permission to', :read_enterprise_ai_analytics
- end
+ it { is_expected.to be_allowed(:read_dora4_analytics) }
+ end
- context 'when on self-managed' do
- let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, :self_managed) }
+ context 'when the user is an admin', :enable_admin_mode do
+ let(:current_user) { admin }
- it_behaves_like 'ai permission to', :read_enterprise_ai_analytics
+ it { is_expected.to be_allowed(:read_dora4_analytics) }
+ end
end
- end
- describe ':read_pro_ai_analytics' do
- let(:project) { private_project_in_group }
- let(:guest) { inherited_guest }
- let(:reporter) { inherited_reporter }
-
- context 'when on SAAS', :saas do
- context 'with pro subscription' do
- let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :gitlab_duo_pro, namespace: group) }
+ context 'when dora4 analytics is not available' do
+ let(:current_user) { developer }
- it_behaves_like 'ai permission to', :read_pro_ai_analytics
+ before do
+ stub_licensed_features(dora4_analytics: false)
end
- context 'with enterprise subscription' do
- let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, namespace: group) }
-
- it_behaves_like 'ai permission to', :read_pro_ai_analytics
- end
+ it { is_expected.not_to be_allowed(:read_dora4_analytics) }
end
- context 'when on self-managed' do
- context 'with pro subscription' do
- let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :gitlab_duo_pro, :self_managed) }
+ describe ':read_enterprise_ai_analytics' do
+ let(:project) { private_project_in_group }
+ let(:guest) { inherited_guest }
+ let(:planner) { inherited_planner }
+ let(:reporter) { inherited_reporter }
- it_behaves_like 'ai permission to', :read_pro_ai_analytics
+ context 'when on SAAS', :saas do
+ let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, namespace: group) }
+
+ it_behaves_like 'ai permission to', :read_enterprise_ai_analytics
end
- context 'with enterprise subscription' do
+ context 'when on self-managed' do
let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, :self_managed) }
- it_behaves_like 'ai permission to', :read_pro_ai_analytics
+ it_behaves_like 'ai permission to', :read_enterprise_ai_analytics
end
end
- end
- describe ':read_code_review_analytics' do
- let(:project) { private_project }
-
- where(:role, :admin_mode, :allowed) do
- :guest | nil | false
- :planner | nil | true
- :reporter | nil | true
- :developer | nil | true
- :maintainer | nil | true
- :owner | nil | true
- :admin | false | false
- :admin | true | true
- end
+ describe ':read_pro_ai_analytics' do
+ let(:project) { private_project_in_group }
+ let(:guest) { inherited_guest }
+ let(:reporter) { inherited_reporter }
- with_them do
- let(:current_user) { public_send(role) }
+ context 'when on SAAS', :saas do
+ context 'with pro subscription' do
+ let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :gitlab_duo_pro, namespace: group) }
- before do
- stub_licensed_features(code_review_analytics: true)
- enable_admin_mode!(current_user) if admin_mode
+ it_behaves_like 'ai permission to', :read_pro_ai_analytics
+ end
+
+ context 'with enterprise subscription' do
+ let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, namespace: group) }
+
+ it_behaves_like 'ai permission to', :read_pro_ai_analytics
+ end
end
- it { is_expected.to(allowed ? be_allowed(:read_code_review_analytics) : be_disallowed(:read_code_review_analytics)) }
- end
+ context 'when on self-managed' do
+ context 'with pro subscription' do
+ let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :gitlab_duo_pro, :self_managed) }
- context 'with code review analytics is not available in license' do
- let(:current_user) { owner }
+ it_behaves_like 'ai permission to', :read_pro_ai_analytics
+ end
- before do
- stub_licensed_features(code_review_analytics: false)
- end
+ context 'with enterprise subscription' do
+ let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, :self_managed) }
- it { is_expected.to be_disallowed(:read_code_review_analytics) }
+ it_behaves_like 'ai permission to', :read_pro_ai_analytics
+ end
+ end
end
- end
- shared_examples 'merge request approval settings' do |admin_override_allowed = false|
- let(:project) { private_project }
-
- context 'with merge request approvers rules available in license' do
- where(:role, :setting, :admin_mode, :allowed) do
- :guest | true | nil | false
- :planner | true | nil | false
- :reporter | true | nil | false
- :developer | true | nil | false
- :maintainer | false | nil | true
- :maintainer | true | nil | false
- :owner | false | nil | true
- :owner | true | nil | false
- :admin | false | false | false
- :admin | false | true | true
- :admin | true | false | false
- :admin | true | true | admin_override_allowed
+ describe ':read_code_review_analytics' do
+ let(:project) { private_project }
+
+ where(:role, :admin_mode, :allowed) do
+ :guest | nil | false
+ :planner | nil | true
+ :reporter | nil | true
+ :developer | nil | true
+ :maintainer | nil | true
+ :owner | nil | true
+ :admin | false | false
+ :admin | true | true
end
with_them do
let(:current_user) { public_send(role) }
before do
- stub_licensed_features(admin_merge_request_approvers_rules: true)
- stub_application_setting(app_setting => setting)
+ stub_licensed_features(code_review_analytics: true)
enable_admin_mode!(current_user) if admin_mode
end
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
- end
- end
-
- context 'with merge request approvers rules not available in license' do
- where(:role, :setting, :admin_mode, :allowed) do
- :guest | true | nil | false
- :planner | true | nil | false
- :reporter | true | nil | false
- :developer | true | nil | false
- :maintainer | false | nil | true
- :maintainer | true | nil | true
- :owner | false | nil | true
- :owner | true | nil | true
- :admin | false | false | false
- :admin | false | true | true
- :admin | true | false | false
- :admin | true | true | true
+ it { is_expected.to(allowed ? be_allowed(:read_code_review_analytics) : be_disallowed(:read_code_review_analytics)) }
end
- with_them do
- let(:current_user) { public_send(role) }
+ context 'with code review analytics is not available in license' do
+ let(:current_user) { owner }
before do
- stub_licensed_features(admin_merge_request_approvers_rules: false)
- stub_application_setting(app_setting => setting)
- enable_admin_mode!(current_user) if admin_mode
+ stub_licensed_features(code_review_analytics: false)
end
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ it { is_expected.to be_disallowed(:read_code_review_analytics) }
end
end
- end
- describe ':admin_merge_request_approval_settings' do
- let(:project) { private_project }
-
- where(:role, :licensed, :allowed) do
- :guest | true | false
- :planner | true | false
- :reporter | true | false
- :developer | true | false
- :maintainer | false | false
- :maintainer | true | true
- :owner | false | false
- :owner | true | true
- :admin | true | true
- :admin | false | false
- end
+ shared_examples 'merge request approval settings' do |admin_override_allowed = false|
+ let(:project) { private_project }
- with_them do
- let(:current_user) { public_send(role) }
-
- before do
- stub_licensed_features(merge_request_approvers: licensed)
- enable_admin_mode!(current_user) if role == :admin
- end
-
- it { is_expected.to(allowed ? be_allowed(:admin_merge_request_approval_settings) : be_disallowed(:admin_merge_request_approval_settings)) }
- end
- end
-
- describe ':modify_approvers_rules' do
- it_behaves_like 'merge request approval settings', true do
- let(:app_setting) { :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 approval settings' do
- let(:app_setting) { :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 approval settings' do
- let(:app_setting) { :prevent_merge_requests_committers_approval }
- let(:policy) { :modify_merge_request_committer_setting }
- end
- end
-
- it_behaves_like 'resource with requirement permissions' do
- let(:resource) { project }
- end
-
- describe 'Quality Management test case' do
- let(:policy) { :create_test_case }
-
- where(:role, :admin_mode, :allowed) do
- :guest | nil | false
- :planner | nil | true
- :reporter | nil | true
- :developer | nil | true
- :maintainer | nil | true
- :owner | nil | true
- :admin | false | false
- :admin | true | true
- end
-
- before do
- stub_licensed_features(quality_management: true)
- enable_admin_mode!(current_user) if admin_mode
- end
-
- with_them do
- let(:current_user) { public_send(role) }
-
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
-
- context 'with unavailable license' do
- before do
- stub_licensed_features(quality_management: false)
+ context 'with merge request approvers rules available in license' do
+ where(:role, :setting, :admin_mode, :allowed) do
+ :guest | true | nil | false
+ :planner | true | nil | false
+ :reporter | true | nil | false
+ :developer | true | nil | false
+ :maintainer | false | nil | true
+ :maintainer | true | nil | false
+ :owner | false | nil | true
+ :owner | true | nil | false
+ :admin | false | false | false
+ :admin | false | true | true
+ :admin | true | false | false
+ :admin | true | true | admin_override_allowed
end
- it { is_expected.to(be_disallowed(policy)) }
- end
- end
- end
-
- shared_examples_for 'prevents CI cancellation ability' do
- context 'when feature is enabled' do
- where(:restricted_role, :actual_role, :allowed) do
- :developer | :planner | false
- :developer | :guest | false
- :developer | :reporter | false
- :developer | :developer | true
- :developer | :maintainer | true
- :developer | :owner | true
- :maintainer | :planner | false
- :maintainer | :guest | false
- :maintainer | :reporter | false
- :maintainer | :developer | false
- :maintainer | :maintainer | true
- :maintainer | :owner | true
- :no_one | :planner | false
- :no_one | :guest | false
- :no_one | :reporter | false
- :no_one | :developer | false
- :no_one | :maintainer | false
- :no_one | :owner | false
- end
+ with_them do
+ let(:current_user) { public_send(role) }
- with_them do
- let(:current_user) { public_send(actual_role) }
+ before do
+ stub_licensed_features(admin_merge_request_approvers_rules: true)
+ stub_application_setting(app_setting => setting)
+ enable_admin_mode!(current_user) if admin_mode
+ end
- before do
- stub_licensed_features(ci_pipeline_cancellation_restrictions: true)
- project.update!(restrict_pipeline_cancellation_role: restricted_role)
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
-
- it { is_expected.to(allowed ? be_allowed(ability) : be_disallowed(ability)) }
end
- end
- end
- describe 'prevents cancel_pipeline when CI cancllation restricted' do
- let(:ability) { :cancel_pipeline }
-
- it_behaves_like 'prevents CI cancellation ability'
- end
-
- describe 'prevents cancel_build when CI cancllation restricted' do
- let(:ability) { :cancel_build }
-
- it_behaves_like 'prevents CI cancellation ability'
- end
-
- describe 'project level compliance features' do
- shared_examples 'project level compliance feature' do |feature, permission|
- context 'when enabled' do
- before do
- stub_licensed_features({ feature => true })
+ context 'with merge request approvers rules not available in license' do
+ where(:role, :setting, :admin_mode, :allowed) do
+ :guest | true | nil | false
+ :planner | true | nil | false
+ :reporter | true | nil | false
+ :developer | true | nil | false
+ :maintainer | false | nil | true
+ :maintainer | true | nil | true
+ :owner | false | nil | true
+ :owner | true | nil | true
+ :admin | false | false | false
+ :admin | false | true | true
+ :admin | true | false | false
+ :admin | true | true | true
end
- context 'when project is in group' do
- let(:project) { public_project_in_group }
-
- context 'when user is eligible for access' do
- where(role: %w[owner auditor])
-
- with_them do
- let(:current_user) { public_send(role) }
-
- it { is_expected.to be_allowed(permission) }
- end
- end
-
- context 'allows admin', :enable_admin_mode do
- let(:current_user) { admin }
+ with_them do
+ let(:current_user) { public_send(role) }
- it { is_expected.to be_allowed(permission) }
+ before do
+ stub_licensed_features(admin_merge_request_approvers_rules: false)
+ stub_application_setting(app_setting => setting)
+ enable_admin_mode!(current_user) if admin_mode
end
- end
- context 'when project is in personal namespace' do
- let(:current_user) { owner }
- let(:project) { public_project }
-
- it { is_expected.to be_disallowed(permission) }
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
+ end
- context 'when disabled' do
- before do
- stub_licensed_features({ feature => false })
- end
+ describe ':admin_merge_request_approval_settings' do
+ let(:project) { private_project }
- context 'when user is eligible for access' do
- where(role: %w[owner auditor])
+ where(:role, :licensed, :allowed) do
+ :guest | true | false
+ :planner | true | false
+ :reporter | true | false
+ :developer | true | false
+ :maintainer | false | false
+ :maintainer | true | true
+ :owner | false | false
+ :owner | true | true
+ :admin | true | true
+ :admin | false | false
+ end
- with_them do
- let(:current_user) { public_send(role) }
+ with_them do
+ let(:current_user) { public_send(role) }
- it { is_expected.to be_disallowed(permission) }
- end
+ before do
+ stub_licensed_features(merge_request_approvers: licensed)
+ enable_admin_mode!(current_user) if role == :admin
end
- context 'disallows admin', :enable_admin_mode do
- let(:current_user) { admin }
-
- it { is_expected.to be_disallowed(permission) }
- end
+ it { is_expected.to(allowed ? be_allowed(:admin_merge_request_approval_settings) : be_disallowed(:admin_merge_request_approval_settings)) }
end
end
- describe 'project level compliance dashboard' do
- it_behaves_like 'project level compliance feature', :project_level_compliance_dashboard, :read_compliance_dashboard
- end
-
- describe 'project level compliance adherence report' do
- it_behaves_like 'project level compliance feature', :project_level_compliance_adherence_report, :read_compliance_adherence_report
- end
-
- describe 'project level compliance violations report' do
- it_behaves_like 'project level compliance feature', :project_level_compliance_violations_report, :read_compliance_violations_report
+ describe ':modify_approvers_rules' do
+ it_behaves_like 'merge request approval settings', true do
+ let(:app_setting) { :disable_overriding_approvers_per_merge_request }
+ let(:policy) { :modify_approvers_rules }
+ end
end
- end
- describe ':compliance_framework_available' do
- let(:policy) { :admin_compliance_framework }
-
- where(:role, :feature_enabled, :admin_mode, :allowed) do
- :guest | false | nil | false
- :guest | true | nil | false
- :planner | false | nil | false
- :planner | true | nil | false
- :reporter | false | nil | false
- :reporter | true | nil | false
- :developer | false | nil | false
- :maintainer | false | nil | false
- :maintainer | true | nil | false
- :owner | false | nil | false
- :owner | true | nil | true
- :admin | false | false | false
- :admin | false | true | false
- :admin | true | false | false
- :admin | true | true | true
+ describe ':modify_merge_request_author_setting' do
+ it_behaves_like 'merge request approval settings' do
+ let(:app_setting) { :prevent_merge_requests_author_approval }
+ let(:policy) { :modify_merge_request_author_setting }
+ end
end
- with_them do
- let(:current_user) { public_send(role) }
-
- before do
- stub_licensed_features(compliance_framework: feature_enabled)
- enable_admin_mode!(current_user) if admin_mode
+ describe ':modify_merge_request_committer_setting' do
+ it_behaves_like 'merge request approval settings' do
+ let(:app_setting) { :prevent_merge_requests_committers_approval }
+ let(:policy) { :modify_merge_request_committer_setting }
end
-
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
- end
- describe 'Incident Management on-call schedules' do
- let(:current_user) { public_send(role) }
- let(:admin_mode) { false }
-
- before do
- enable_admin_mode!(current_user) if admin_mode
- stub_licensed_features(oncall_schedules: true)
+ it_behaves_like 'resource with requirement permissions' do
+ let(:resource) { project }
end
- context ':read_incident_management_oncall_schedule' do
- let(:policy) { :read_incident_management_oncall_schedule }
+ describe 'Quality Management test case' do
+ let(:policy) { :create_test_case }
where(:role, :admin_mode, :allowed) do
:guest | nil | false
- :planner | nil | false
+ :planner | nil | true
:reporter | nil | true
:developer | nil | true
:maintainer | nil | true
:owner | nil | true
:admin | false | false
:admin | true | true
- :auditor | false | true
+ end
+
+ before do
+ stub_licensed_features(quality_management: true)
+ enable_admin_mode!(current_user) if admin_mode
end
with_them do
+ let(:current_user) { public_send(role) }
+
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
context 'with unavailable license' do
before do
- stub_licensed_features(oncall_schedules: false)
+ stub_licensed_features(quality_management: false)
end
it { is_expected.to(be_disallowed(policy)) }
end
end
-
- it_behaves_like 'monitor feature visibility', allow_lowest_role: :reporter
end
- context ':admin_incident_management_oncall_schedule' do
- let(:policy) { :admin_incident_management_oncall_schedule }
-
- where(:role, :admin_mode, :allowed) do
- :guest | nil | false
- :planner | nil | false
- :reporter | nil | false
- :developer | nil | false
- :maintainer | nil | true
- :owner | nil | true
- :admin | false | false
- :admin | true | true
- :auditor | false | false
- end
+ shared_examples_for 'prevents CI cancellation ability' do
+ context 'when feature is enabled' do
+ where(:restricted_role, :actual_role, :allowed) do
+ :developer | :planner | false
+ :developer | :guest | false
+ :developer | :reporter | false
+ :developer | :developer | true
+ :developer | :maintainer | true
+ :developer | :owner | true
+ :maintainer | :planner | false
+ :maintainer | :guest | false
+ :maintainer | :reporter | false
+ :maintainer | :developer | false
+ :maintainer | :maintainer | true
+ :maintainer | :owner | true
+ :no_one | :planner | false
+ :no_one | :guest | false
+ :no_one | :reporter | false
+ :no_one | :developer | false
+ :no_one | :maintainer | false
+ :no_one | :owner | false
+ end
- with_them do
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ with_them do
+ let(:current_user) { public_send(actual_role) }
- context 'with unavailable license' do
before do
- stub_licensed_features(oncall_schedules: false)
+ stub_licensed_features(ci_pipeline_cancellation_restrictions: true)
+ project.update!(restrict_pipeline_cancellation_role: restricted_role)
end
- it { is_expected.to(be_disallowed(policy)) }
+ it { is_expected.to(allowed ? be_allowed(ability) : be_disallowed(ability)) }
end
end
-
- it_behaves_like 'monitor feature visibility', allow_lowest_role: :maintainer
end
- end
- describe 'Escalation Policies' do
- let(:current_user) { public_send(role) }
- let(:admin_mode) { false }
+ describe 'prevents cancel_pipeline when CI cancllation restricted' do
+ let(:ability) { :cancel_pipeline }
- before do
- enable_admin_mode!(current_user) if admin_mode
- allow(::Gitlab::IncidentManagement).to receive(:escalation_policies_available?).with(project).and_return(true)
+ it_behaves_like 'prevents CI cancellation ability'
end
- context ':read_incident_management_escalation_policy' do
- let(:policy) { :read_incident_management_escalation_policy }
-
- where(:role, :admin_mode, :allowed) do
- :guest | nil | false
- :planner | nil | false
- :reporter | nil | true
- :developer | nil | true
- :maintainer | nil | true
- :owner | nil | true
- :admin | false | false
- :admin | true | true
- :auditor | false | true
- end
+ describe 'prevents cancel_build when CI cancllation restricted' do
+ let(:ability) { :cancel_build }
- with_them do
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ it_behaves_like 'prevents CI cancellation ability'
+ end
- context 'with unavailable escalation policies' do
+ describe 'project level compliance features' do
+ shared_examples 'project level compliance feature' do |feature, permission|
+ context 'when enabled' do
before do
- allow(::Gitlab::IncidentManagement).to receive(:escalation_policies_available?).with(project).and_return(false)
+ stub_licensed_features({ feature => true })
end
- it { is_expected.to(be_disallowed(policy)) }
- end
- end
+ context 'when project is in group' do
+ let(:project) { public_project_in_group }
- it_behaves_like 'monitor feature visibility', allow_lowest_role: :reporter
- end
+ context 'when user is eligible for access' do
+ where(role: %w[owner auditor])
- context ':admin_incident_management_escalation_policy' do
- let(:policy) { :admin_incident_management_escalation_policy }
+ with_them do
+ let(:current_user) { public_send(role) }
- where(:role, :admin_mode, :allowed) do
- :guest | nil | false
- :planner | nil | false
- :reporter | nil | false
- :developer | nil | false
- :maintainer | nil | true
- :owner | nil | true
- :admin | false | false
- :admin | true | true
- :auditor | false | false
- end
+ it { is_expected.to be_allowed(permission) }
+ end
+ end
- with_them do
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ context 'allows admin', :enable_admin_mode do
+ let(:current_user) { admin }
- context 'with unavailable escalation policies' do
- before do
- allow(::Gitlab::IncidentManagement).to receive(:escalation_policies_available?).with(project).and_return(false)
+ it { is_expected.to be_allowed(permission) }
+ end
end
- it { is_expected.to(be_disallowed(policy)) }
+ context 'when project is in personal namespace' do
+ let(:current_user) { owner }
+ let(:project) { public_project }
+
+ it { is_expected.to be_disallowed(permission) }
+ end
end
- end
- it_behaves_like 'monitor feature visibility', allow_lowest_role: :maintainer
- end
- end
+ context 'when disabled' do
+ before do
+ stub_licensed_features({ feature => false })
+ end
- context 'when project is read only on the namespace' do
- let(:project) { public_project_in_group }
- let(:current_user) { maintainer }
- let(:abilities) do
- described_class.readonly_features.flat_map { |feature| described_class.create_update_admin(feature) } +
- described_class.readonly_abilities
- end
+ context 'when user is eligible for access' do
+ where(role: %w[owner auditor])
- before do
- allow(project.root_namespace).to receive(:read_only?).and_return(read_only)
- allow(project).to receive(:design_management_enabled?).and_return(true)
- stub_licensed_features(security_dashboard: true, license_scanning: true, quality_management: true)
- end
+ with_them do
+ let(:current_user) { public_send(role) }
- context 'when the group is read only' do
- let(:read_only) { true }
+ it { is_expected.to be_disallowed(permission) }
+ end
+ end
- it { is_expected.to(be_disallowed(*abilities)) }
- end
+ context 'disallows admin', :enable_admin_mode do
+ let(:current_user) { admin }
- context 'when the group is not read only' do
- let(:read_only) { false }
+ it { is_expected.to be_disallowed(permission) }
+ end
+ end
+ end
- # 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_issue_board_list create_issue_board update_issue_board
- update_issue_board_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 create_feature_flags_client
- update_feature_flags_client update_iteration update_vulnerability create_vulnerability]
+ describe 'project level compliance dashboard' do
+ it_behaves_like 'project level compliance feature', :project_level_compliance_dashboard, :read_compliance_dashboard
end
- it { is_expected.to(be_allowed(*(abilities - abilities_not_currently_enabled))) }
+ describe 'project level compliance adherence report' do
+ it_behaves_like 'project level compliance feature', :project_level_compliance_adherence_report, :read_compliance_adherence_report
+ end
+
+ describe 'project level compliance violations report' do
+ it_behaves_like 'project level compliance feature', :project_level_compliance_violations_report, :read_compliance_violations_report
+ end
end
- end
- context 'project access tokens' do
- context 'GitLab.com Core resource access tokens', :saas do
- before do
- stub_ee_application_setting(should_check_namespace_plan: true)
+ describe ':compliance_framework_available' do
+ let(:policy) { :admin_compliance_framework }
+
+ where(:role, :feature_enabled, :admin_mode, :allowed) do
+ :guest | false | nil | false
+ :guest | true | nil | false
+ :planner | false | nil | false
+ :planner | true | nil | false
+ :reporter | false | nil | false
+ :reporter | true | nil | false
+ :developer | false | nil | false
+ :maintainer | false | nil | false
+ :maintainer | true | nil | false
+ :owner | false | nil | false
+ :owner | true | nil | true
+ :admin | false | false | false
+ :admin | false | true | false
+ :admin | true | false | false
+ :admin | true | true | true
end
- context 'with admin access' do
- let(:current_user) { owner }
+ with_them do
+ let(:current_user) { public_send(role) }
before do
- project.add_owner(owner)
+ stub_licensed_features(compliance_framework: feature_enabled)
+ enable_admin_mode!(current_user) if admin_mode
end
- context 'when project belongs to a group' do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, group: group) }
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ end
+ end
- it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
- it { is_expected.to be_allowed(:read_resource_access_tokens) }
- it { is_expected.to be_allowed(:destroy_resource_access_tokens) }
- end
+ describe 'Incident Management on-call schedules' do
+ let(:current_user) { public_send(role) }
+ let(:admin_mode) { false }
- context 'when project belongs to personal namespace' do
- it { is_expected.to be_allowed(:create_resource_access_tokens) }
- it { is_expected.to be_allowed(:read_resource_access_tokens) }
- it { is_expected.to be_allowed(:destroy_resource_access_tokens) }
- end
+ before do
+ enable_admin_mode!(current_user) if admin_mode
+ stub_licensed_features(oncall_schedules: true)
end
- context 'with non admin access' do
- let(:current_user) { developer }
+ context ':read_incident_management_oncall_schedule' do
+ let(:policy) { :read_incident_management_oncall_schedule }
- before do
- project.add_developer(developer)
+ where(:role, :admin_mode, :allowed) do
+ :guest | nil | false
+ :planner | nil | false
+ :reporter | nil | true
+ :developer | nil | true
+ :maintainer | nil | true
+ :owner | nil | true
+ :admin | false | false
+ :admin | true | true
+ :auditor | false | true
end
- context 'when project belongs to a group' do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, group: group) }
+ with_them do
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
- it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
- it { is_expected.not_to be_allowed(:read_resource_access_tokens) }
- it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
- end
+ context 'with unavailable license' do
+ before do
+ stub_licensed_features(oncall_schedules: false)
+ end
- context 'when project belongs to personal namespace' do
- it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
- it { is_expected.not_to be_allowed(:read_resource_access_tokens) }
- it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
+ it { is_expected.to(be_disallowed(policy)) }
+ end
end
- end
- end
- context 'on GitLab.com paid', :saas do
- let_it_be(:group) { create(:group_with_plan, plan: :bronze_plan) }
- let_it_be(:project) { create(:project, group: group) }
+ it_behaves_like 'monitor feature visibility', allow_lowest_role: :reporter
+ end
- context 'with maintainer access' do
- let(:current_user) { maintainer }
+ context ':admin_incident_management_oncall_schedule' do
+ let(:policy) { :admin_incident_management_oncall_schedule }
- before do
- project.add_maintainer(maintainer)
+ where(:role, :admin_mode, :allowed) do
+ :guest | nil | false
+ :planner | nil | false
+ :reporter | nil | false
+ :developer | nil | false
+ :maintainer | nil | true
+ :owner | nil | true
+ :admin | false | false
+ :admin | true | true
+ :auditor | false | false
end
- it_behaves_like 'GitLab.com Paid plan resource access tokens'
-
- context 'create resource access tokens' do
- it { is_expected.to be_allowed(:create_resource_access_tokens) }
-
- context 'with a personal namespace project' do
- let(:namespace) { create(:namespace_with_plan, plan: :bronze_plan) }
- let(:project) { create(:project, namespace: namespace) }
-
- it { is_expected.to be_allowed(:create_resource_access_tokens) }
- end
+ with_them do
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
- context 'when resource access token creation is not allowed' do
+ context 'with unavailable license' do
before do
- group.namespace_settings.update_column(:resource_access_token_creation_allowed, false)
+ stub_licensed_features(oncall_schedules: false)
end
- it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
- end
-
- context 'when parent group has resource access token creation disabled' do
- let(:resource_access_token_creation_allowed) { false }
- let(:ns_for_parent) { create(:namespace_settings, resource_access_token_creation_allowed: resource_access_token_creation_allowed) }
- let(:parent) { create(:group_with_plan, plan: :bronze_plan, namespace_settings: ns_for_parent) }
- let(:group) { create(:group, parent: parent) }
- let(:project) { create(:project, group: group) }
-
- context 'cannot create resource access tokens' do
- it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
- end
+ it { is_expected.to(be_disallowed(policy)) }
end
end
- context 'read resource access tokens' do
- it { is_expected.to be_allowed(:read_resource_access_tokens) }
- end
+ it_behaves_like 'monitor feature visibility', allow_lowest_role: :maintainer
+ end
+ end
- context 'destroy resource access tokens' do
- it { is_expected.to be_allowed(:destroy_resource_access_tokens) }
- end
+ describe 'Escalation Policies' do
+ let(:current_user) { public_send(role) }
+ let(:admin_mode) { false }
+
+ before do
+ enable_admin_mode!(current_user) if admin_mode
+ allow(::Gitlab::IncidentManagement).to receive(:escalation_policies_available?).with(project).and_return(true)
end
- context 'with developer access' do
- let(:current_user) { developer }
+ context ':read_incident_management_escalation_policy' do
+ let(:policy) { :read_incident_management_escalation_policy }
- before do
- project.add_developer(developer)
+ where(:role, :admin_mode, :allowed) do
+ :guest | nil | false
+ :planner | nil | false
+ :reporter | nil | true
+ :developer | nil | true
+ :maintainer | nil | true
+ :owner | nil | true
+ :admin | false | false
+ :admin | true | true
+ :auditor | false | true
end
- context 'create resource access tokens' do
- it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
- end
+ with_them do
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
- context 'read resource access tokens' do
- it { is_expected.not_to be_allowed(:read_resource_access_tokens) }
- end
+ context 'with unavailable escalation policies' do
+ before do
+ allow(::Gitlab::IncidentManagement).to receive(:escalation_policies_available?).with(project).and_return(false)
+ end
- context 'destroy resource access tokens' do
- it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
+ it { is_expected.to(be_disallowed(policy)) }
+ end
end
+
+ it_behaves_like 'monitor feature visibility', allow_lowest_role: :reporter
end
- context 'with auditor access' do
- let(:current_user) { auditor }
+ context ':admin_incident_management_escalation_policy' do
+ let(:policy) { :admin_incident_management_escalation_policy }
- context 'read resource access tokens' do
- it { is_expected.to be_allowed(:read_resource_access_tokens) }
+ where(:role, :admin_mode, :allowed) do
+ :guest | nil | false
+ :planner | nil | false
+ :reporter | nil | false
+ :developer | nil | false
+ :maintainer | nil | true
+ :owner | nil | true
+ :admin | false | false
+ :admin | true | true
+ :auditor | false | false
end
- context 'cannot create resource access tokens' do
- it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
- end
+ with_them do
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+
+ context 'with unavailable escalation policies' do
+ before do
+ allow(::Gitlab::IncidentManagement).to receive(:escalation_policies_available?).with(project).and_return(false)
+ end
- context 'cannot destroy resource access tokens' do
- it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
+ it { is_expected.to(be_disallowed(policy)) }
+ end
end
+
+ it_behaves_like 'monitor feature visibility', allow_lowest_role: :maintainer
end
end
- end
- describe 'read_analytics' do
- context 'with various analytics features' do
- let_it_be(:project_with_analytics_disabled) { create(:project, :analytics_disabled) }
- let_it_be(:project_with_analytics_private) { create(:project, :analytics_private) }
- let_it_be(:project_with_analytics_enabled) { create(:project, :analytics_enabled) }
-
- let(:all_read_analytics_permissions) do
- %i[
- read_project_merge_request_analytics
- read_code_review_analytics
- read_cycle_analytics
- read_issue_analytics
- ]
+ context 'when project is read only on the namespace' do
+ let(:project) { public_project_in_group }
+ let(:current_user) { maintainer }
+ let(:abilities) do
+ described_class.readonly_features.flat_map { |feature| described_class.create_update_admin(feature) } +
+ described_class.readonly_abilities
end
before do
- stub_licensed_features(issues_analytics: true, code_review_analytics: true, project_merge_request_analytics: true, cycle_analytics_for_projects: true)
-
- project_with_analytics_disabled.add_developer(developer)
- project_with_analytics_private.add_developer(developer)
- project_with_analytics_enabled.add_developer(developer)
+ allow(project.root_namespace).to receive(:read_only?).and_return(read_only)
+ allow(project).to receive(:design_management_enabled?).and_return(true)
+ stub_licensed_features(security_dashboard: true, license_scanning: true, quality_management: true)
end
- context 'when analytics is disabled for the project' do
- let(:project) { project_with_analytics_disabled }
+ context 'when the group is read only' do
+ let(:read_only) { true }
- %w[guest planner developer admin auditor].each do |role|
- context "for #{role} user" do
- let(:current_user) { send(role) }
+ it { is_expected.to(be_disallowed(*abilities)) }
+ end
- it { is_expected.to be_disallowed(*all_read_analytics_permissions) }
- end
+ context 'when the group is not read only' do
+ let(:read_only) { 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_issue_board_list create_issue_board update_issue_board
+ update_issue_board_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 create_feature_flags_client
+ update_feature_flags_client update_iteration update_vulnerability create_vulnerability]
end
+
+ it { is_expected.to(be_allowed(*(abilities - abilities_not_currently_enabled))) }
end
+ end
- context 'when analytics is private for the project' do
- let(:project) { project_with_analytics_private }
+ context 'project access tokens' do
+ context 'GitLab.com Core resource access tokens', :saas do
+ before do
+ stub_ee_application_setting(should_check_namespace_plan: true)
+ end
- %w[guest planner].each do |role|
- context "for #{role} user" do
- let(:current_user) { send(role) }
+ context 'with admin access' do
+ let(:current_user) { owner }
- it { is_expected.to be_disallowed(*all_read_analytics_permissions) }
+ before do
+ project.add_owner(owner)
end
- end
- context 'for developer' do
- let(:current_user) { developer }
+ context 'when project belongs to a group' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
- it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
+ it { is_expected.to be_allowed(:read_resource_access_tokens) }
+ it { is_expected.to be_allowed(:destroy_resource_access_tokens) }
+ end
+
+ context 'when project belongs to personal namespace' do
+ it { is_expected.to be_allowed(:create_resource_access_tokens) }
+ it { is_expected.to be_allowed(:read_resource_access_tokens) }
+ it { is_expected.to be_allowed(:destroy_resource_access_tokens) }
+ end
end
- context 'for admin', :enable_admin_mode do
- let(:current_user) { admin }
+ context 'with non admin access' do
+ let(:current_user) { developer }
- it { is_expected.to be_allowed(*all_read_analytics_permissions) }
- end
+ before do
+ project.add_developer(developer)
+ end
- context 'for auditor' do
- let(:current_user) { auditor }
+ context 'when project belongs to a group' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
- it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
+ it { is_expected.not_to be_allowed(:read_resource_access_tokens) }
+ it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
+ end
+
+ context 'when project belongs to personal namespace' do
+ it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
+ it { is_expected.not_to be_allowed(:read_resource_access_tokens) }
+ it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
+ end
end
end
- context 'when analytics is enabled for the project' do
- let(:project) { project_with_analytics_enabled }
+ context 'on GitLab.com paid', :saas do
+ let_it_be(:group) { create(:group_with_plan, plan: :bronze_plan) }
+ let_it_be(:project) { create(:project, group: group) }
- %w[guest planner].each do |role|
- context "for #{role} user" do
- let(:current_user) { send(role) }
+ context 'with maintainer access' do
+ let(:current_user) { maintainer }
+
+ before do
+ project.add_maintainer(maintainer)
+ end
+
+ it_behaves_like 'GitLab.com Paid plan resource access tokens'
+
+ context 'create resource access tokens' do
+ it { is_expected.to be_allowed(:create_resource_access_tokens) }
+
+ context 'with a personal namespace project' do
+ let(:namespace) { create(:namespace_with_plan, plan: :bronze_plan) }
+ let(:project) { create(:project, namespace: namespace) }
+
+ it { is_expected.to be_allowed(:create_resource_access_tokens) }
+ end
+
+ context 'when resource access token creation is not allowed' do
+ before do
+ group.namespace_settings.update_column(:resource_access_token_creation_allowed, false)
+ end
+
+ it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
+ end
+
+ context 'when parent group has resource access token creation disabled' do
+ let(:resource_access_token_creation_allowed) { false }
+ let(:ns_for_parent) { create(:namespace_settings, resource_access_token_creation_allowed: resource_access_token_creation_allowed) }
+ let(:parent) { create(:group_with_plan, plan: :bronze_plan, namespace_settings: ns_for_parent) }
+ let(:group) { create(:group, parent: parent) }
+ let(:project) { create(:project, group: group) }
+
+ context 'cannot create resource access tokens' do
+ it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
+ end
+ end
+ end
+
+ context 'read resource access tokens' do
+ it { is_expected.to be_allowed(:read_resource_access_tokens) }
+ end
- it { is_expected.to be_disallowed(:read_project_merge_request_analytics) }
- it { is_expected.to be_disallowed(:read_code_review_analytics) }
- it { is_expected.to be_disallowed(:read_cycle_analytics) }
- it { is_expected.to be_allowed(:read_issue_analytics) }
+ context 'destroy resource access tokens' do
+ it { is_expected.to be_allowed(:destroy_resource_access_tokens) }
end
end
- context 'for developer' do
+ context 'with developer access' do
let(:current_user) { developer }
- it { is_expected.to be_allowed(*all_read_analytics_permissions) }
- end
+ before do
+ project.add_developer(developer)
+ end
- context 'for admin', :enable_admin_mode do
- let(:current_user) { admin }
+ context 'create resource access tokens' do
+ it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
+ end
+
+ context 'read resource access tokens' do
+ it { is_expected.not_to be_allowed(:read_resource_access_tokens) }
+ end
- it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ context 'destroy resource access tokens' do
+ it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
+ end
end
- context 'for auditor' do
+ context 'with auditor access' do
let(:current_user) { auditor }
- it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ context 'read resource access tokens' do
+ it { is_expected.to be_allowed(:read_resource_access_tokens) }
+ end
+
+ context 'cannot create resource access tokens' do
+ it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
+ end
+
+ context 'cannot destroy resource access tokens' do
+ it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
+ end
end
end
end
- end
- describe ':build_read_project' do
- let(:policy) { :build_read_project }
-
- where(:role, :project_visibility, :allowed) do
- :guest | 'public' | true
- :planner | 'public' | true
- :reporter | 'public' | true
- :developer | 'public' | true
- :maintainer | 'public' | true
- :owner | 'public' | true
- :admin | 'public' | true
- :guest | 'internal' | true
- :planner | 'internal' | true
- :reporter | 'internal' | true
- :developer | 'internal' | true
- :maintainer | 'internal' | true
- :owner | 'internal' | true
- :admin | 'internal' | true
- :guest | 'private' | false
- :planner | 'private' | false
- :reporter | 'private' | true
- :developer | 'private' | true
- :maintainer | 'private' | true
- :owner | 'private' | true
- :admin | 'private' | false
- end
+ describe 'read_analytics' do
+ context 'with various analytics features' do
+ let_it_be(:project_with_analytics_disabled) { create(:project, :analytics_disabled) }
+ let_it_be(:project_with_analytics_private) { create(:project, :analytics_private) }
+ let_it_be(:project_with_analytics_enabled) { create(:project, :analytics_enabled) }
- with_them do
- let(:current_user) { public_send(role) }
+ let(:all_read_analytics_permissions) do
+ %i[
+ read_project_merge_request_analytics
+ read_code_review_analytics
+ read_cycle_analytics
+ read_issue_analytics
+ ]
+ end
- before do
- project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(project_visibility))
- end
+ before do
+ stub_licensed_features(issues_analytics: true, code_review_analytics: true, project_merge_request_analytics: true, cycle_analytics_for_projects: true)
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
- end
- end
+ project_with_analytics_disabled.add_developer(developer)
+ project_with_analytics_private.add_developer(developer)
+ project_with_analytics_enabled.add_developer(developer)
+ end
- describe 'pending member permissions' do
- let_it_be(:current_user) { create(:user) }
- let_it_be(:group) { create(:group, :public) }
+ context 'when analytics is disabled for the project' do
+ let(:project) { project_with_analytics_disabled }
- context 'with a pending membership in a private project' do
- let_it_be(:project) { create(:project, :private, public_builds: false) }
+ %w[guest planner developer admin auditor].each do |role|
+ context "for #{role} user" do
+ let(:current_user) { send(role) }
- where(:role) do
- Gitlab::Access.sym_options.keys.map(&:to_sym)
- end
+ it { is_expected.to be_disallowed(*all_read_analytics_permissions) }
+ end
+ end
+ end
- with_them do
- it 'a pending member has permissions to the project as if the user is not a member' do
- create(:project_member, :awaiting, role, source: project, user: current_user)
+ context 'when analytics is private for the project' do
+ let(:project) { project_with_analytics_private }
- expect_private_project_permissions_as_if_non_member
- end
- end
- end
+ %w[guest planner].each do |role|
+ context "for #{role} user" do
+ let(:current_user) { send(role) }
- context 'with a group invited to a project' do
- let_it_be(:project) { create(:project, :private, public_builds: false) }
+ it { is_expected.to be_disallowed(*all_read_analytics_permissions) }
+ end
+ end
- before_all do
- create(:project_group_link, project: project, group: group)
- end
+ context 'for developer' do
+ let(:current_user) { developer }
- where(:role) do
- Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
- end
+ it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ end
- with_them do
- it 'a pending member in the group has permissions to the project as if the user is not a member' do
- create(:group_member, :awaiting, role, source: group, user: current_user)
+ context 'for admin', :enable_admin_mode do
+ let(:current_user) { admin }
- expect_private_project_permissions_as_if_non_member
+ it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ end
+
+ context 'for auditor' do
+ let(:current_user) { auditor }
+
+ it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ end
end
- end
- end
- context 'with a group invited to another group' do
- let_it_be(:other_group) { create(:group, :public) }
- let_it_be(:project) { create(:project, :private, public_builds: false, namespace: other_group) }
+ context 'when analytics is enabled for the project' do
+ let(:project) { project_with_analytics_enabled }
- before_all do
- create(:group_group_link, shared_with_group: group, shared_group: other_group)
- end
+ %w[guest planner].each do |role|
+ context "for #{role} user" do
+ let(:current_user) { send(role) }
- where(:role) do
- Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
- end
+ it { is_expected.to be_disallowed(:read_project_merge_request_analytics) }
+ it { is_expected.to be_disallowed(:read_code_review_analytics) }
+ it { is_expected.to be_disallowed(:read_cycle_analytics) }
+ it { is_expected.to be_allowed(:read_issue_analytics) }
+ end
+ end
- with_them do
- it "a pending member in the group has permissions to the other group's project as if the user is not a member" do
- create(:group_member, :awaiting, role, source: group, user: current_user)
+ context 'for developer' do
+ let(:current_user) { developer }
- expect_private_project_permissions_as_if_non_member
- end
- end
- end
+ it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ end
- context 'with a subgroup' do
- let_it_be(:subgroup) { create(:group, :private, parent: group) }
- let_it_be(:project) { create(:project, :private, public_builds: false, namespace: subgroup) }
+ context 'for admin', :enable_admin_mode do
+ let(:current_user) { admin }
- where(:role) do
- Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
- end
+ it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ end
- with_them do
- it 'a pending member in the group has permissions to the subgroup project as if the user is not a member' do
- create(:group_member, :awaiting, role, source: group, user: current_user)
+ context 'for auditor' do
+ let(:current_user) { auditor }
- expect_private_project_permissions_as_if_non_member
+ it { is_expected.to be_allowed(*all_read_analytics_permissions) }
+ end
end
end
end
- def expect_private_project_permissions_as_if_non_member
- expect_disallowed(*guest_permissions)
- expect_disallowed(*reporter_permissions)
- expect_disallowed(*team_member_reporter_permissions)
- expect_disallowed(*developer_permissions)
- expect_disallowed(*maintainer_permissions)
- expect_disallowed(*owner_permissions)
- end
-
- describe ':read_approvers' do
- let(:policy) { :read_approvers }
+ describe ':build_read_project' do
+ let(:policy) { :build_read_project }
- where(:role, :allowed) do
- :guest | false
- :planner | false
- :reporter | false
- :developer | false
- :maintainer | true
- :auditor | true
- :owner | true
- :admin | true
+ where(:role, :project_visibility, :allowed) do
+ :guest | 'public' | true
+ :planner | 'public' | true
+ :reporter | 'public' | true
+ :developer | 'public' | true
+ :maintainer | 'public' | true
+ :owner | 'public' | true
+ :admin | 'public' | true
+ :guest | 'internal' | true
+ :planner | 'internal' | true
+ :reporter | 'internal' | true
+ :developer | 'internal' | true
+ :maintainer | 'internal' | true
+ :owner | 'internal' | true
+ :admin | 'internal' | true
+ :guest | 'private' | false
+ :planner | 'private' | false
+ :reporter | 'private' | true
+ :developer | 'private' | true
+ :maintainer | 'private' | true
+ :owner | 'private' | true
+ :admin | 'private' | false
end
with_them do
let(:current_user) { public_send(role) }
before do
- enable_admin_mode!(current_user) if role == :admin
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(project_visibility))
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
- end
- context 'importing members from another project' do
- let(:current_user) { owner }
+ describe 'pending member permissions' do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:group) { create(:group, :public) }
- context 'for a personal project' do
- it { is_expected.to be_allowed(:import_project_members_from_another_project) }
- end
+ context 'with a pending membership in a private project' do
+ let_it_be(:project) { create(:project, :private, public_builds: false) }
+
+ where(:role) do
+ Gitlab::Access.sym_options.keys.map(&:to_sym)
+ end
- context 'for a project in a group' do
- let(:project) { create(:project, group: create(:group)) }
+ with_them do
+ it 'a pending member has permissions to the project as if the user is not a member' do
+ create(:project_member, :awaiting, role, source: project, user: current_user)
- context 'when the project has locked their membership' do
- context 'via the parent group' do
- before do
- project.group.update!(membership_lock: true)
+ expect_private_project_permissions_as_if_non_member
end
-
- it { is_expected.to be_disallowed(:import_project_members_from_another_project) }
end
+ end
- context 'via LDAP' do
- before do
- stub_application_setting(lock_memberships_to_ldap: true)
- end
+ context 'with a group invited to a project' do
+ let_it_be(:project) { create(:project, :private, public_builds: false) }
- it { is_expected.to be_disallowed(:import_project_members_from_another_project) }
+ before_all do
+ create(:project_group_link, project: project, group: group)
end
- context 'via SAML' do
- before do
- stub_application_setting(lock_memberships_to_saml: true)
- end
+ where(:role) do
+ Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
+ end
+
+ with_them do
+ it 'a pending member in the group has permissions to the project as if the user is not a member' do
+ create(:group_member, :awaiting, role, source: group, user: current_user)
- it { is_expected.to be_disallowed(:import_project_members_from_another_project) }
+ expect_private_project_permissions_as_if_non_member
+ end
end
end
- end
- end
- describe 'inviting a group' do
- let_it_be_with_reload(:current_user) { developer }
- let_it_be_with_reload(:project) { public_project }
+ context 'with a group invited to another group' do
+ let_it_be(:other_group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :private, public_builds: false, namespace: other_group) }
- let_it_be(:banned_group) { create(:group) }
- let_it_be(:banned_subgroup) { create(:group, parent: banned_group) }
+ before_all do
+ create(:group_group_link, shared_with_group: group, shared_group: other_group)
+ end
- before do
- stub_licensed_features(unique_project_download_limit: true)
- create(:namespace_ban, user: current_user, namespace: banned_group)
- end
+ where(:role) do
+ Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
+ end
- it { is_expected.to be_allowed(:read_project) }
+ with_them do
+ it "a pending member in the group has permissions to the other group's project as if the user is not a member" do
+ create(:group_member, :awaiting, role, source: group, user: current_user)
- context 'when the user is banned from the invited group' do
- before do
- create(:project_group_link, project: project, group: banned_group)
+ expect_private_project_permissions_as_if_non_member
+ end
+ end
end
- it { is_expected.to be_disallowed(:read_project) }
- end
+ context 'with a subgroup' do
+ let_it_be(:subgroup) { create(:group, :private, parent: group) }
+ let_it_be(:project) { create(:project, :private, public_builds: false, namespace: subgroup) }
- context 'when the user is banned from the invited subgroup' do
- before do
- create(:project_group_link, project: project, group: banned_subgroup)
- end
+ where(:role) do
+ Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
+ end
- it { is_expected.to be_disallowed(:read_project) }
- end
- end
+ with_them do
+ it 'a pending member in the group has permissions to the subgroup project as if the user is not a member' do
+ create(:group_member, :awaiting, role, source: group, user: current_user)
- describe 'user banned from namespace' do
- let_it_be_with_reload(:current_user) { create(:user) }
+ expect_private_project_permissions_as_if_non_member
+ end
+ end
+ end
- let_it_be(:group) { create(:group, :private) }
- let_it_be(:project) { create(:project, :private, public_builds: false, group: group) }
+ def expect_private_project_permissions_as_if_non_member
+ expect_disallowed(*guest_permissions)
+ expect_disallowed(*reporter_permissions)
+ expect_disallowed(*team_member_reporter_permissions)
+ expect_disallowed(*developer_permissions)
+ expect_disallowed(*maintainer_permissions)
+ expect_disallowed(*owner_permissions)
+ end
- before do
- stub_licensed_features(unique_project_download_limit: true)
- project.add_developer(current_user)
- end
+ describe ':read_approvers' do
+ let(:policy) { :read_approvers }
- context 'when user is not banned' do
- it { is_expected.to be_allowed(:read_project) }
- end
+ where(:role, :allowed) do
+ :guest | false
+ :planner | false
+ :reporter | false
+ :developer | false
+ :maintainer | true
+ :auditor | true
+ :owner | true
+ :admin | true
+ end
- context 'when user is banned' do
- before do
- create(:namespace_ban, user: current_user, namespace: group.root_ancestor)
- end
+ with_them do
+ let(:current_user) { public_send(role) }
- it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
+ before do
+ enable_admin_mode!(current_user) if role == :admin
+ end
- context 'as an owner of the project' do
- before do
- project.add_owner(current_user)
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
-
- it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
end
+ end
- context 'when project is inside subgroup' do
- let_it_be(:subgroup) { create(:group, :private, parent: group) }
- let_it_be(:project) { create(:project, :private, public_builds: false, group: subgroup) }
+ context 'importing members from another project' do
+ let(:current_user) { owner }
- it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
+ context 'for a personal project' do
+ it { is_expected.to be_allowed(:import_project_members_from_another_project) }
end
- context 'as an admin' do
- let_it_be(:current_user) { admin }
+ context 'for a project in a group' do
+ let(:project) { create(:project, group: create(:group)) }
- context 'when admin mode is enabled', :enable_admin_mode do
- it { is_expected.to be_allowed(:read_project) }
- end
- end
+ context 'when the project has locked their membership' do
+ context 'via the parent group' do
+ before do
+ project.group.update!(membership_lock: true)
+ end
- context 'when project is public' do
- let_it_be(:group) { create(:group, :public) }
- let_it_be(:project) { create(:project, :public, public_builds: false, group: group) }
+ it { is_expected.to be_disallowed(:import_project_members_from_another_project) }
+ end
- it { is_expected.to be_disallowed(:read_project) }
- end
+ context 'via LDAP' do
+ before do
+ stub_application_setting(lock_memberships_to_ldap: true)
+ end
- context 'when the limit_unique_project_downloads_per_namespace_user feature flag is disabled' do
- before do
- stub_feature_flags(limit_unique_project_downloads_per_namespace_user: false)
- end
+ it { is_expected.to be_disallowed(:import_project_members_from_another_project) }
+ end
- it { is_expected.to be_allowed(:read_project) }
- end
+ context 'via SAML' do
+ before do
+ stub_application_setting(lock_memberships_to_saml: true)
+ end
- context 'when licensed feature unique_project_download_limit is not available' do
- before do
- stub_licensed_features(unique_project_download_limit: false)
+ it { is_expected.to be_disallowed(:import_project_members_from_another_project) }
+ end
end
-
- it { is_expected.to be_allowed(:read_project) }
end
end
- end
- describe 'create_objective' do
- let(:okr_policies) { [:create_objective, :create_key_result] }
-
- where(:role, :allowed) do
- :guest | true
- :planner | true
- :reporter | true
- :developer | true
- :maintainer | true
- :auditor | false
- :owner | true
- :admin | true
- end
+ describe 'inviting a group' do
+ let_it_be_with_reload(:current_user) { developer }
+ let_it_be_with_reload(:project) { public_project }
- with_them do
- let(:current_user) { public_send(role) }
+ let_it_be(:banned_group) { create(:group) }
+ let_it_be(:banned_subgroup) { create(:group, parent: banned_group) }
before do
- enable_admin_mode!(current_user) if role == :admin
- stub_licensed_features(okrs: true)
+ stub_licensed_features(unique_project_download_limit: true)
+ create(:namespace_ban, user: current_user, namespace: banned_group)
end
- context 'when okrs_mvc feature flag is enabled' do
- it { is_expected.to(allowed ? be_allowed(*okr_policies) : be_disallowed(*okr_policies)) }
- end
+ it { is_expected.to be_allowed(:read_project) }
- context 'when okrs_mvc feature flag is disabled' do
+ context 'when the user is banned from the invited group' do
before do
- stub_feature_flags(okrs_mvc: false)
+ create(:project_group_link, project: project, group: banned_group)
end
- it { is_expected.to be_disallowed(*okr_policies) }
+ it { is_expected.to be_disallowed(:read_project) }
end
- context 'when okrs license feature is not available' do
+ context 'when the user is banned from the invited subgroup' do
before do
- stub_licensed_features(okrs: false)
+ create(:project_group_link, project: project, group: banned_subgroup)
end
- it { is_expected.to be_disallowed(*okr_policies) }
+ it { is_expected.to be_disallowed(:read_project) }
end
end
- end
- describe 'read_member_role' do
- let_it_be_with_reload(:project) { private_project_in_group }
- let_it_be_with_reload(:current_user) { create(:user) }
-
- let(:permission) { :read_member_role }
-
- where(:role, :allowed) do
- :guest | true
- :planner | true
- :reporter | true
- :developer | true
- :maintainer | true
- :auditor | false
- :owner | true
- :admin | true
- end
+ describe 'user banned from namespace' do
+ let_it_be_with_reload(:current_user) { create(:user) }
+
+ let_it_be(:group) { create(:group, :private) }
+ let_it_be(:project) { create(:project, :private, public_builds: false, group: group) }
- with_them do
before do
- if role == :admin
- current_user.update!(admin: true)
- elsif role == :auditor
- current_user.update!(auditor: true)
- else
- create(:project_member, role, source: project, user: current_user)
- end
+ stub_licensed_features(unique_project_download_limit: true)
+ project.add_developer(current_user)
+ end
- enable_admin_mode!(current_user) if role == :admin
+ context 'when user is not banned' do
+ it { is_expected.to be_allowed(:read_project) }
end
- context 'when custom_roles feature is enabled' do
+ context 'when user is banned' do
before do
- stub_licensed_features(custom_roles: true)
+ create(:namespace_ban, user: current_user, namespace: group.root_ancestor)
end
- it do
- is_expected.to(allowed ? be_allowed(permission) : be_disallowed(permission))
- end
- end
+ it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
- context 'when custom_roles feature is disabled' do
- before do
- stub_licensed_features(custom_roles: false)
+ context 'as an owner of the project' do
+ before do
+ project.add_owner(current_user)
+ end
+
+ it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
end
- it { is_expected.to be_disallowed(permission) }
- end
- end
- end
+ context 'when project is inside subgroup' do
+ let_it_be(:subgroup) { create(:group, :private, parent: group) }
+ let_it_be(:project) { create(:project, :private, public_builds: false, group: subgroup) }
- context 'hidden projects' do
- let(:project) { create(:project, :repository, hidden: true) }
- let(:current_user) { create(:user) }
+ it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
+ end
- before do
- project.add_owner(current_user)
- end
+ context 'as an admin' do
+ let_it_be(:current_user) { admin }
- it { is_expected.to be_disallowed(:download_code) }
- it { is_expected.to be_disallowed(:build_download_code) }
- end
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:read_project) }
+ end
+ end
- context 'custom role' do
- let_it_be(:guest) { create(:user) }
- let_it_be(:project) { private_project_in_group }
- let_it_be(:group_member_guest) do
- create(
- :group_member,
- user: guest,
- source: project.group,
- access_level: Gitlab::Access::GUEST
- )
- end
+ context 'when project is public' do
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :public, public_builds: false, group: group) }
- let_it_be(:project_member_guest) do
- create(
- :project_member,
- :guest,
- user: guest,
- project: project,
- access_level: Gitlab::Access::GUEST
- )
- end
+ it { is_expected.to be_disallowed(:read_project) }
+ end
- let(:member_role_abilities) { {} }
- let(:allowed_abilities) { [] }
- let(:disallowed_abilities) { [] }
- let(:current_user) { guest }
- let(:licensed_features) { {} }
+ context 'when the limit_unique_project_downloads_per_namespace_user feature flag is disabled' do
+ before do
+ stub_feature_flags(limit_unique_project_downloads_per_namespace_user: false)
+ end
- subject { described_class.new(current_user, project) }
+ it { is_expected.to be_allowed(:read_project) }
+ end
- def create_member_role(member, abilities = member_role_abilities)
- params = abilities.merge(namespace: project.group)
+ context 'when licensed feature unique_project_download_limit is not available' do
+ before do
+ stub_licensed_features(unique_project_download_limit: false)
+ end
- create(:member_role, :guest, params).tap do |role|
- role.members << member
+ it { is_expected.to be_allowed(:read_project) }
+ end
end
end
- shared_examples 'custom roles abilities' do
- context 'with custom_roles license disabled' do
- before do
- create_member_role(group_member_guest)
-
- stub_licensed_features(licensed_features.merge(custom_roles: false))
- end
+ describe 'create_objective' do
+ let(:okr_policies) { [:create_objective, :create_key_result] }
- it { expect_disallowed(*allowed_abilities) }
+ where(:role, :allowed) do
+ :guest | true
+ :planner | true
+ :reporter | true
+ :developer | true
+ :maintainer | true
+ :auditor | false
+ :owner | true
+ :admin | true
end
- context 'with custom_roles license enabled' do
+ with_them do
+ let(:current_user) { public_send(role) }
+
before do
- stub_licensed_features(licensed_features.merge(custom_roles: true))
+ enable_admin_mode!(current_user) if role == :admin
+ stub_licensed_features(okrs: true)
end
- context 'custom role for parent group' do
- context 'when a role enables the abilities' do
- before do
- create_member_role(group_member_guest)
- end
+ context 'when okrs_mvc feature flag is enabled' do
+ it { is_expected.to(allowed ? be_allowed(*okr_policies) : be_disallowed(*okr_policies)) }
+ end
- it { expect_allowed(*allowed_abilities) }
- it { expect_disallowed(*disallowed_abilities) }
+ context 'when okrs_mvc feature flag is disabled' do
+ before do
+ stub_feature_flags(okrs_mvc: false)
end
- context 'when a role does not enable the abilities' do
- it { expect_disallowed(*allowed_abilities) }
+ it { is_expected.to be_disallowed(*okr_policies) }
+ end
+
+ context 'when okrs license feature is not available' do
+ before do
+ stub_licensed_features(okrs: false)
end
+
+ it { is_expected.to be_disallowed(*okr_policies) }
end
+ end
+ end
- context 'custom role on project membership' do
- context 'when a role enables the abilities' do
- before do
- create_member_role(project_member_guest)
- end
+ describe 'read_member_role' do
+ let_it_be_with_reload(:project) { private_project_in_group }
+ let_it_be_with_reload(:current_user) { create(:user) }
+
+ let(:permission) { :read_member_role }
+
+ where(:role, :allowed) do
+ :guest | true
+ :planner | true
+ :reporter | true
+ :developer | true
+ :maintainer | true
+ :auditor | false
+ :owner | true
+ :admin | true
+ end
+
+ with_them do
+ before do
+ if role == :admin
+ current_user.update!(admin: true)
+ elsif role == :auditor
+ current_user.update!(auditor: true)
+ else
+ create(:project_member, role, source: project, user: current_user)
+ end
+
+ enable_admin_mode!(current_user) if role == :admin
+ end
- it { expect_allowed(*allowed_abilities) }
- it { expect_disallowed(*disallowed_abilities) }
+ context 'when custom_roles feature is enabled' do
+ before do
+ stub_licensed_features(custom_roles: true)
end
- context 'when a role does not enable the abilities' do
- it { expect_disallowed(*allowed_abilities) }
+ it do
+ is_expected.to(allowed ? be_allowed(permission) : be_disallowed(permission))
end
end
- end
- end
- context 'for a member role with read_code true' do
- let(:member_role_abilities) { { read_code: true } }
- let(:allowed_abilities) { [:read_code] }
+ context 'when custom_roles feature is disabled' do
+ before do
+ stub_licensed_features(custom_roles: false)
+ end
- it_behaves_like 'custom roles abilities'
+ it { is_expected.to be_disallowed(permission) }
+ end
+ end
end
- context 'for a member role with admin_runners true' do
- let(:member_role_abilities) { { admin_runners: true } }
- let(:allowed_abilities) do
- [
- :admin_runner,
- :create_runner,
- :read_runner
- ]
+ context 'hidden projects' do
+ let(:project) { create(:project, :repository, hidden: true) }
+ let(:current_user) { create(:user) }
+
+ before do
+ project.add_owner(current_user)
end
- it_behaves_like 'custom roles abilities'
+ it { is_expected.to be_disallowed(:download_code) }
+ it { is_expected.to be_disallowed(:build_download_code) }
end
- context 'for a member role with read_vulnerability true' do
- let(:member_role_abilities) { { read_vulnerability: true } }
- let(:allowed_abilities) do
- [
- :access_security_and_compliance,
- :create_vulnerability_export,
- :read_security_resource,
- :read_vulnerability,
- :read_vulnerability_feedback,
- :read_vulnerability_scanner
- ]
+ context 'custom role' do
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:project) { private_project_in_group }
+ let_it_be(:group_member_guest) do
+ create(
+ :group_member,
+ user: guest,
+ source: project.group,
+ access_level: Gitlab::Access::GUEST
+ )
end
- it_behaves_like 'custom roles abilities'
-
- it 'does not enable to admin_vulnerability' do
- expect(subject).to be_disallowed(:admin_vulnerability)
+ let_it_be(:project_member_guest) do
+ create(
+ :project_member,
+ :guest,
+ user: guest,
+ project: project,
+ access_level: Gitlab::Access::GUEST
+ )
end
- end
- context 'for a member role with admin_terraform_state true' do
- let(:member_role_abilities) { { admin_terraform_state: true } }
- let(:allowed_abilities) { [:read_terraform_state, :admin_terraform_state] }
+ let(:member_role_abilities) { {} }
+ let(:allowed_abilities) { [] }
+ let(:disallowed_abilities) { [] }
+ let(:current_user) { guest }
+ let(:licensed_features) { {} }
- it_behaves_like 'custom roles abilities'
- end
+ subject { described_class.new(current_user, project) }
- context 'for a member role with admin_vulnerability true' do
- let(:member_role_abilities) { { read_vulnerability: true, admin_vulnerability: true } }
- let(:allowed_abilities) do
- [
- :admin_vulnerability,
- :create_vulnerability_feedback,
- :destroy_vulnerability_feedback,
- :read_vulnerability,
- :read_vulnerability_feedback,
- :update_vulnerability_feedback,
- :create_vulnerability_state_transition
- ]
+ def create_member_role(member, abilities = member_role_abilities)
+ params = abilities.merge(namespace: project.group)
+
+ create(:member_role, :guest, params).tap do |role|
+ role.members << member
+ end
end
- it_behaves_like 'custom roles abilities'
- end
+ shared_examples 'custom roles abilities' do
+ context 'with custom_roles license disabled' do
+ before do
+ create_member_role(group_member_guest)
- context 'for a member role with read_dependency true' do
- let(:member_role_abilities) { { read_dependency: true } }
- let(:allowed_abilities) { [:access_security_and_compliance, :read_dependency] }
- let(:licensed_features) { { dependency_scanning: true } }
+ stub_licensed_features(licensed_features.merge(custom_roles: false))
+ end
- it_behaves_like 'custom roles abilities'
- end
+ it { expect_disallowed(*allowed_abilities) }
+ end
+
+ context 'with custom_roles license enabled' do
+ before do
+ stub_licensed_features(licensed_features.merge(custom_roles: true))
+ end
- context 'for a member role with admin_merge_request true' do
- let(:member_role_abilities) { { admin_merge_request: true } }
- let(:allowed_abilities) { [:admin_merge_request] }
+ context 'custom role for parent group' do
+ context 'when a role enables the abilities' do
+ before do
+ create_member_role(group_member_guest)
+ end
- it_behaves_like 'custom roles abilities'
+ it { expect_allowed(*allowed_abilities) }
+ it { expect_disallowed(*disallowed_abilities) }
+ end
- context 'when the merge requests access level is set as private' do
- before do
- project.project_feature.update_column(:merge_requests_access_level, ProjectFeature::PRIVATE)
+ context 'when a role does not enable the abilities' do
+ it { expect_disallowed(*allowed_abilities) }
+ end
+ end
+
+ context 'custom role on project membership' do
+ context 'when a role enables the abilities' do
+ before do
+ create_member_role(project_member_guest)
+ end
+
+ it { expect_allowed(*allowed_abilities) }
+ it { expect_disallowed(*disallowed_abilities) }
+ end
+
+ context 'when a role does not enable the abilities' do
+ it { expect_disallowed(*allowed_abilities) }
+ end
+ end
end
+ end
+
+ context 'for a member role with read_code true' do
+ let(:member_role_abilities) { { read_code: true } }
+ let(:allowed_abilities) { [:read_code] }
it_behaves_like 'custom roles abilities'
end
- context 'when the merge requests access level is set as disabled' do
- before do
- project.project_feature.update_column(:merge_requests_access_level, ProjectFeature::DISABLED)
+ context 'for a member role with admin_runners true' do
+ let(:member_role_abilities) { { admin_runners: true } }
+ let(:allowed_abilities) do
+ [
+ :admin_runner,
+ :create_runner,
+ :read_runner
+ ]
end
- it { is_expected.to be_disallowed(:read_merge_request, :admin_merge_request, :download_code) }
+ it_behaves_like 'custom roles abilities'
end
- end
- context 'for a member role with manage_project_access_tokens true' do
- let(:member_role_abilities) { { manage_project_access_tokens: true } }
- let(:allowed_abilities) { [:manage_resource_access_tokens] }
+ context 'for a member role with read_vulnerability true' do
+ let(:member_role_abilities) { { read_vulnerability: true } }
+ let(:allowed_abilities) do
+ [
+ :access_security_and_compliance,
+ :create_vulnerability_export,
+ :read_security_resource,
+ :read_vulnerability,
+ :read_vulnerability_feedback,
+ :read_vulnerability_scanner
+ ]
+ end
- it_behaves_like 'custom roles abilities'
- end
+ it_behaves_like 'custom roles abilities'
- context 'for a member role with archive_project true' do
- let(:member_role_abilities) { { archive_project: true } }
- let(:allowed_abilities) { [:archive_project, :view_edit_page] }
+ it 'does not enable to admin_vulnerability' do
+ expect(subject).to be_disallowed(:admin_vulnerability)
+ end
+ end
- it_behaves_like 'custom roles abilities'
- end
+ context 'for a member role with admin_terraform_state true' do
+ let(:member_role_abilities) { { admin_terraform_state: true } }
+ let(:allowed_abilities) { [:read_terraform_state, :admin_terraform_state] }
- context 'for a member role with `remove_project` true' do
- let(:member_role_abilities) { { remove_project: true } }
- let(:allowed_abilities) { [:remove_project, :view_edit_page] }
+ it_behaves_like 'custom roles abilities'
+ end
- it_behaves_like 'custom roles abilities'
- end
+ context 'for a member role with admin_vulnerability true' do
+ let(:member_role_abilities) { { read_vulnerability: true, admin_vulnerability: true } }
+ let(:allowed_abilities) do
+ [
+ :admin_vulnerability,
+ :create_vulnerability_feedback,
+ :destroy_vulnerability_feedback,
+ :read_vulnerability,
+ :read_vulnerability_feedback,
+ :update_vulnerability_feedback,
+ :create_vulnerability_state_transition
+ ]
+ end
- context 'for a member role with `manage_security_policy_link` true' do
- let(:member_role_abilities) { { manage_security_policy_link: true } }
- let(:licensed_features) { { security_orchestration_policies: true } }
- let(:allowed_abilities) do
- [:read_security_orchestration_policies, :update_security_orchestration_policy_project,
- :access_security_and_compliance]
+ it_behaves_like 'custom roles abilities'
end
- let(:disallowed_abilities) do
- [:modify_security_policy]
+ context 'for a member role with read_dependency true' do
+ let(:member_role_abilities) { { read_dependency: true } }
+ let(:allowed_abilities) { [:access_security_and_compliance, :read_dependency] }
+ let(:licensed_features) { { dependency_scanning: true } }
+
+ it_behaves_like 'custom roles abilities'
end
- it_behaves_like 'custom roles abilities'
- end
+ context 'for a member role with admin_merge_request true' do
+ let(:member_role_abilities) { { admin_merge_request: true } }
+ let(:allowed_abilities) { [:admin_merge_request] }
- context 'when a user is assigned to custom roles in both group and project' do
- before do
- stub_licensed_features(custom_roles: true, dependency_scanning: true)
+ it_behaves_like 'custom roles abilities'
+
+ context 'when the merge requests access level is set as private' do
+ before do
+ project.project_feature.update_column(:merge_requests_access_level, ProjectFeature::PRIVATE)
+ end
- create_member_role(group_member_guest, { read_dependency: true })
- create_member_role(project_member_guest, { read_code: true })
+ it_behaves_like 'custom roles abilities'
+ end
+
+ context 'when the merge requests access level is set as disabled' do
+ before do
+ project.project_feature.update_column(:merge_requests_access_level, ProjectFeature::DISABLED)
+ end
+
+ it { is_expected.to be_disallowed(:read_merge_request, :admin_merge_request, :download_code) }
+ end
end
- it { is_expected.to be_allowed(:read_dependency) }
- it { is_expected.to be_allowed(:read_code) }
- end
+ context 'for a member role with manage_project_access_tokens true' do
+ let(:member_role_abilities) { { manage_project_access_tokens: true } }
+ let(:allowed_abilities) { [:manage_resource_access_tokens] }
- context 'for a custom role with the `admin_cicd_variables` ability' do
- let(:member_role_abilities) { { admin_cicd_variables: true } }
- let(:allowed_abilities) { [:admin_cicd_variables] }
+ it_behaves_like 'custom roles abilities'
+ end
- it_behaves_like 'custom roles abilities'
- end
+ context 'for a member role with archive_project true' do
+ let(:member_role_abilities) { { archive_project: true } }
+ let(:allowed_abilities) { [:archive_project, :view_edit_page] }
- context 'for a custom role with the `admin_push_rules` ability' do
- let(:member_role_abilities) { { admin_push_rules: true } }
- let(:allowed_abilities) { [:admin_push_rules] }
+ it_behaves_like 'custom roles abilities'
+ end
- it_behaves_like 'custom roles abilities'
+ context 'for a member role with `remove_project` true' do
+ let(:member_role_abilities) { { remove_project: true } }
+ let(:allowed_abilities) { [:remove_project, :view_edit_page] }
- context 'when push rules feature is enabled' do
- before do
- stub_licensed_features(
- custom_roles: true,
- push_rules: true,
- commit_committer_check: true,
- commit_committer_name_check: true,
- reject_unsigned_commits: true,
- reject_non_dco_commits: true
- )
+ it_behaves_like 'custom roles abilities'
+ end
- create_member_role(group_member_guest)
+ context 'for a member role with `manage_security_policy_link` true' do
+ let(:member_role_abilities) { { manage_security_policy_link: true } }
+ let(:licensed_features) { { security_orchestration_policies: true } }
+ let(:allowed_abilities) do
+ [:read_security_orchestration_policies, :update_security_orchestration_policy_project,
+ :access_security_and_compliance]
end
- it do
- expect_allowed(
- :change_push_rules,
- :read_commit_committer_check,
- :change_commit_committer_check,
- :change_commit_committer_name_check,
- :read_reject_unsigned_commits,
- :change_reject_unsigned_commits,
- :change_reject_non_dco_commits
- )
+ let(:disallowed_abilities) do
+ [:modify_security_policy]
end
- end
- end
- context 'for a custom role with the `admin_compliance_framework` ability' do
- let(:licensed_features) do
- {
- compliance_framework: true,
- project_level_compliance_dashboard: true,
- project_level_compliance_adherence_report: true,
- project_level_compliance_violations_report: true
- }
+ it_behaves_like 'custom roles abilities'
end
- let(:member_role_abilities) { { read_compliance_dashboard: true, admin_compliance_framework: true } }
+ context 'when a user is assigned to custom roles in both group and project' do
+ before do
+ stub_licensed_features(custom_roles: true, dependency_scanning: true)
- let(:allowed_abilities) do
- [
- :admin_compliance_framework,
- :read_compliance_dashboard,
- :read_compliance_adherence_report,
- :read_compliance_violations_report
- ]
+ create_member_role(group_member_guest, { read_dependency: true })
+ create_member_role(project_member_guest, { read_code: true })
+ end
+
+ it { is_expected.to be_allowed(:read_dependency) }
+ it { is_expected.to be_allowed(:read_code) }
end
- it_behaves_like 'custom roles abilities'
- end
+ context 'for a custom role with the `admin_cicd_variables` ability' do
+ let(:member_role_abilities) { { admin_cicd_variables: true } }
+ let(:allowed_abilities) { [:admin_cicd_variables] }
- context 'for a custom role with the `read_compliance_dashboard` ability' do
- let(:licensed_features) do
- {
- project_level_compliance_dashboard: true,
- project_level_compliance_adherence_report: true,
- project_level_compliance_violations_report: true
- }
+ it_behaves_like 'custom roles abilities'
end
- let(:member_role_abilities) { { read_compliance_dashboard: true } }
+ context 'for a custom role with the `admin_push_rules` ability' do
+ let(:member_role_abilities) { { admin_push_rules: true } }
+ let(:allowed_abilities) { [:admin_push_rules] }
- let(:allowed_abilities) do
- [
- :read_compliance_dashboard,
- :read_compliance_adherence_report,
- :read_compliance_violations_report
- ]
- end
+ it_behaves_like 'custom roles abilities'
- it_behaves_like 'custom roles abilities'
- end
+ context 'when push rules feature is enabled' do
+ before do
+ stub_licensed_features(
+ custom_roles: true,
+ push_rules: true,
+ commit_committer_check: true,
+ commit_committer_name_check: true,
+ reject_unsigned_commits: true,
+ reject_non_dco_commits: true
+ )
+
+ create_member_role(group_member_guest)
+ end
- context 'for a member role with `admin_web_hook` true' do
- let(:member_role_abilities) { { admin_web_hook: true } }
- let(:allowed_abilities) { [:admin_web_hook, :read_web_hook] }
+ it do
+ expect_allowed(
+ :change_push_rules,
+ :read_commit_committer_check,
+ :change_commit_committer_check,
+ :change_commit_committer_name_check,
+ :read_reject_unsigned_commits,
+ :change_reject_unsigned_commits,
+ :change_reject_non_dco_commits
+ )
+ end
+ end
+ end
- it_behaves_like 'custom roles abilities'
- end
+ context 'for a custom role with the `admin_compliance_framework` ability' do
+ let(:licensed_features) do
+ {
+ compliance_framework: true,
+ project_level_compliance_dashboard: true,
+ project_level_compliance_adherence_report: true,
+ project_level_compliance_violations_report: true
+ }
+ end
- context 'for a member role with `manage_deploy_tokens` true' do
- let(:member_role_abilities) { { manage_deploy_tokens: true } }
- let(:allowed_abilities) { [:manage_deploy_tokens, :read_deploy_token, :create_deploy_token, :destroy_deploy_token] }
+ let(:member_role_abilities) { { read_compliance_dashboard: true, admin_compliance_framework: true } }
- it_behaves_like 'custom roles abilities'
- end
+ let(:allowed_abilities) do
+ [
+ :admin_compliance_framework,
+ :read_compliance_dashboard,
+ :read_compliance_adherence_report,
+ :read_compliance_violations_report
+ ]
+ end
- context 'for a custom role with the `manage_merge_request_settings` ability' do
- let(:member_role_abilities) { { read_code: true, manage_merge_request_settings: true } }
- let(:allowed_abilities) do
- [
- :manage_merge_request_settings,
- :edit_approval_rule,
- :modify_approvers_rules,
- :modify_merge_request_author_setting,
- :modify_merge_request_committer_setting
- ]
+ it_behaves_like 'custom roles abilities'
end
- it_behaves_like 'custom roles abilities'
+ context 'for a custom role with the `read_compliance_dashboard` ability' do
+ let(:licensed_features) do
+ {
+ project_level_compliance_dashboard: true,
+ project_level_compliance_adherence_report: true,
+ project_level_compliance_violations_report: true
+ }
+ end
+
+ let(:member_role_abilities) { { read_compliance_dashboard: true } }
- context 'when `target_branch_rules` feature is available' do
- let(:licensed_features) { { target_branch_rules: true } }
- let(:allowed_abilities) { [:admin_target_branch_rule] }
+ let(:allowed_abilities) do
+ [
+ :read_compliance_dashboard,
+ :read_compliance_adherence_report,
+ :read_compliance_violations_report
+ ]
+ end
it_behaves_like 'custom roles abilities'
end
- context 'when `merge_request_approvers` feature is available' do
- let(:licensed_features) { { merge_request_approvers: true } }
- let(:allowed_abilities) { [:admin_merge_request_approval_settings] }
+ context 'for a member role with `admin_web_hook` true' do
+ let(:member_role_abilities) { { admin_web_hook: true } }
+ let(:allowed_abilities) { [:admin_web_hook, :read_web_hook] }
it_behaves_like 'custom roles abilities'
end
- end
- context 'for a custom role with the `admin_integrations` ability' do
- let(:member_role_abilities) { { admin_integrations: true } }
- let(:allowed_abilities) { [:admin_integrations] }
+ context 'for a member role with `manage_deploy_tokens` true' do
+ let(:member_role_abilities) { { manage_deploy_tokens: true } }
+ let(:allowed_abilities) { [:manage_deploy_tokens, :read_deploy_token, :create_deploy_token, :destroy_deploy_token] }
- it_behaves_like 'custom roles abilities'
- end
+ it_behaves_like 'custom roles abilities'
+ end
+
+ context 'for a custom role with the `manage_merge_request_settings` ability' do
+ let(:member_role_abilities) { { read_code: true, manage_merge_request_settings: true } }
+ let(:allowed_abilities) do
+ [
+ :manage_merge_request_settings,
+ :edit_approval_rule,
+ :modify_approvers_rules,
+ :modify_merge_request_author_setting,
+ :modify_merge_request_committer_setting
+ ]
+ end
+
+ it_behaves_like 'custom roles abilities'
- context 'for a custom role with the `read_runners` ability' do
- let(:member_role_abilities) { { read_runners: true } }
- let(:allowed_abilities) { [:read_project_runners, :read_runner] }
+ context 'when `target_branch_rules` feature is available' do
+ let(:licensed_features) { { target_branch_rules: true } }
+ let(:allowed_abilities) { [:admin_target_branch_rule] }
- it_behaves_like 'custom roles abilities'
- end
+ it_behaves_like 'custom roles abilities'
+ end
+
+ context 'when `merge_request_approvers` feature is available' do
+ let(:licensed_features) { { merge_request_approvers: true } }
+ let(:allowed_abilities) { [:admin_merge_request_approval_settings] }
- context 'for a member role with `admin_protected_branch` true' do
- let(:member_role_abilities) { { admin_protected_branch: true } }
- let(:allowed_abilities) do
- [:admin_protected_branch, :read_protected_branch, :create_protected_branch,
- :update_protected_branch, :destroy_protected_branch]
+ it_behaves_like 'custom roles abilities'
+ end
end
- it_behaves_like 'custom roles abilities'
- end
+ context 'for a custom role with the `admin_integrations` ability' do
+ let(:member_role_abilities) { { admin_integrations: true } }
+ let(:allowed_abilities) { [:admin_integrations] }
- context 'for a member role with `manage_protected_tags` true' do
- let(:member_role_abilities) { { manage_protected_tags: true } }
- let(:allowed_abilities) { [:manage_protected_tags] }
+ it_behaves_like 'custom roles abilities'
+ end
- it_behaves_like 'custom roles abilities'
- end
- end
+ context 'for a custom role with the `read_runners` ability' do
+ let(:member_role_abilities) { { read_runners: true } }
+ let(:allowed_abilities) { [:read_project_runners, :read_runner] }
- describe 'permissions for suggested reviewers bot', :saas do
- let(:permissions) { [:admin_project_member, :create_resource_access_tokens] }
- let(:namespace) { build_stubbed(:namespace) }
- let(:project) { build_stubbed(:project, namespace: namespace) }
+ it_behaves_like 'custom roles abilities'
+ end
- context 'when user is suggested_reviewers_bot' do
- let(:current_user) { Users::Internal.suggested_reviewers_bot }
+ context 'for a member role with `admin_protected_branch` true' do
+ let(:member_role_abilities) { { admin_protected_branch: true } }
+ let(:allowed_abilities) do
+ [:admin_protected_branch, :read_protected_branch, :create_protected_branch,
+ :update_protected_branch, :destroy_protected_branch]
+ end
- where(:suggested_reviewers_available, :token_creation_allowed, :allowed) do
- false | false | false
- false | true | false
- true | false | false
- true | true | true
+ it_behaves_like 'custom roles abilities'
end
- with_them do
+ context 'for a custom role with the `admin_security_testing` ability' do
+ let(:member_role_abilities) { { admin_security_testing: true } }
+ let(:licensed_features) do
+ { security_dashboard: true,
+ security_scans_api: true,
+ security_on_demand_scans: true,
+ coverage_fuzzing: true,
+ pre_receive_secret_detection: true,
+ container_scanning_for_registry: true,
+ project_level_compliance_dashboard: true }
+ end
+
+ let(:allowed_abilities) do
+ [
+ :access_security_and_compliance,
+ :read_security_configuration,
+ :access_security_scans_api,
+ :read_on_demand_dast_scan,
+ :create_on_demand_dast_scan,
+ :edit_on_demand_dast_scan,
+ :enable_pre_receive_secret_detection,
+ :read_project_security_dashboard,
+ :read_project_security_exclusions,
+ :read_coverage_fuzzing,
+ :create_coverage_fuzzing_corpus,
+ :enable_container_scanning_for_registry,
+ :push_code,
+ :create_merge_request_from,
+ :create_pipeline,
+ :build_download_code,
+ :read_merge_request,
+ :download_code,
+ :read_project_runners,
+ :read_pre_receive_secret_detection_info
+ ]
+ end
+
before do
- allow(project).to receive(:can_suggest_reviewers?).and_return(suggested_reviewers_available)
+ # Ensure merge requests are enabled for the project
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::ENABLED)
+ allow(CloudConnector::AvailableServices).to receive(:find_by_name).with(:sast).and_return(
+ instance_double(CloudConnector::BaseAvailableServiceData, free_access?: true))
+ end
- allow(::Gitlab::CurrentSettings)
- .to receive(:personal_access_tokens_disabled?)
- .and_return(!token_creation_allowed)
+ it_behaves_like 'custom roles abilities'
+ end
+ end
+
+ describe 'permissions for suggested reviewers bot', :saas do
+ let(:permissions) { [:admin_project_member, :create_resource_access_tokens] }
+ let(:namespace) { build_stubbed(:namespace) }
+ let(:project) { build_stubbed(:project, namespace: namespace) }
+
+ context 'when user is suggested_reviewers_bot' do
+ let(:current_user) { Users::Internal.suggested_reviewers_bot }
+
+ where(:suggested_reviewers_available, :token_creation_allowed, :allowed) do
+ false | false | false
+ false | true | false
+ true | false | false
+ true | true | true
end
- it 'always allows permissions except when feature disabled' do
- if allowed
- expect_allowed(*permissions)
- else
- expect_disallowed(*permissions)
+ with_them do
+ before do
+ allow(project).to receive(:can_suggest_reviewers?).and_return(suggested_reviewers_available)
+
+ allow(::Gitlab::CurrentSettings)
+ .to receive(:personal_access_tokens_disabled?)
+ .and_return(!token_creation_allowed)
+ end
+
+ it 'always allows permissions except when feature disabled' do
+ if allowed
+ expect_allowed(*permissions)
+ else
+ expect_disallowed(*permissions)
+ end
end
end
end
- end
- context 'when user is not suggested_reviewers_bot' do
- let(:current_user) { developer }
+ context 'when user is not suggested_reviewers_bot' do
+ let(:current_user) { developer }
- before do
- allow(project).to receive(:can_suggest_reviewers?).and_return(true)
+ before do
+ allow(project).to receive(:can_suggest_reviewers?).and_return(true)
- allow(::Gitlab::CurrentSettings)
- .to receive(:personal_access_tokens_disabled?)
- .and_return(false)
- end
+ allow(::Gitlab::CurrentSettings)
+ .to receive(:personal_access_tokens_disabled?)
+ .and_return(false)
+ end
- it 'does not allow permissions' do
- expect_disallowed(*permissions)
+ it 'does not allow permissions' do
+ expect_disallowed(*permissions)
+ end
end
end
- end
- describe 'read_project_runners' do
- context 'with auditor' do
- let(:current_user) { auditor }
+ describe 'read_project_runners' do
+ context 'with auditor' do
+ let(:current_user) { auditor }
- it { is_expected.to be_allowed(:read_project_runners) }
+ it { is_expected.to be_allowed(:read_project_runners) }
+ end
end
- end
- describe 'read_runner_usage' do
- where(:licensed, :current_user, :project, :enable_admin_mode, :clickhouse_configured, :expected) do
- true | ref(:admin) | ref(:public_project_in_group) | true | true | true
- false | ref(:maintainer) | ref(:public_project_in_group) | false | true | false
- true | ref(:maintainer) | ref(:public_project_in_group) | false | false | false
- true | ref(:maintainer) | ref(:public_project_in_group) | false | true | true
- true | ref(:auditor) | ref(:public_project_in_group) | false | true | false
- true | ref(:developer) | ref(:public_project_in_group) | false | true | false
- true | ref(:admin) | ref(:public_project) | true | true | false
- true | ref(:maintainer) | ref(:public_project) | false | true | false
- end
+ describe 'read_runner_usage' do
+ where(:licensed, :current_user, :project, :enable_admin_mode, :clickhouse_configured, :expected) do
+ true | ref(:admin) | ref(:public_project_in_group) | true | true | true
+ false | ref(:maintainer) | ref(:public_project_in_group) | false | true | false
+ true | ref(:maintainer) | ref(:public_project_in_group) | false | false | false
+ true | ref(:maintainer) | ref(:public_project_in_group) | false | true | true
+ true | ref(:auditor) | ref(:public_project_in_group) | false | true | false
+ true | ref(:developer) | ref(:public_project_in_group) | false | true | false
+ true | ref(:admin) | ref(:public_project) | true | true | false
+ true | ref(:maintainer) | ref(:public_project) | false | true | false
+ end
- with_them do
- before do
- stub_licensed_features(runner_performance_insights_for_namespace: licensed)
+ with_them do
+ before do
+ stub_licensed_features(runner_performance_insights_for_namespace: licensed)
- enable_admin_mode!(admin) if enable_admin_mode
+ enable_admin_mode!(admin) if enable_admin_mode
- allow(::Gitlab::ClickHouse).to receive(:configured?).and_return(clickhouse_configured)
- end
+ allow(::Gitlab::ClickHouse).to receive(:configured?).and_return(clickhouse_configured)
+ end
- it 'matches expectation' do
- if expected
- is_expected.to be_allowed(:read_runner_usage)
- else
- is_expected.to be_disallowed(:read_runner_usage)
+ it 'matches expectation' do
+ if expected
+ is_expected.to be_allowed(:read_runner_usage)
+ else
+ is_expected.to be_disallowed(:read_runner_usage)
+ end
end
end
end
- end
- describe 'workspace creation' do
- context 'with no user' do
- let(:current_user) { nil }
+ describe 'workspace creation' do
+ context 'with no user' do
+ let(:current_user) { nil }
- it { is_expected.to be_disallowed(:create_workspace) }
- end
+ it { is_expected.to be_disallowed(:create_workspace) }
+ end
- context 'with an authorized user' do
- let(:current_user) { developer }
+ context 'with an authorized user' do
+ let(:current_user) { developer }
- it { is_expected.to be_allowed(:create_workspace) }
+ it { is_expected.to be_allowed(:create_workspace) }
+ end
end
- end
-
- describe 'create_pipeline policy' do
- context 'as a guest member' do
- let(:current_user) { guest }
-
- it { is_expected.not_to be_allowed(:create_pipeline) }
- context 'and user is a security_policy_bot' do
- let_it_be(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
- let(:current_user) { security_policy_bot }
+ describe 'create_pipeline policy' do
+ context 'as a guest member' do
+ let(:current_user) { guest }
it { is_expected.not_to be_allowed(:create_pipeline) }
- shared_examples 'allows to create pipeline' do
- let(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
+ context 'and user is a security_policy_bot' do
+ let_it_be(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
+ let(:current_user) { security_policy_bot }
- before do
- project.add_guest(security_policy_bot)
- end
+ it { is_expected.not_to be_allowed(:create_pipeline) }
- it { is_expected.to be_allowed(:create_pipeline) }
- end
+ shared_examples 'allows to create pipeline' do
+ let(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
- context 'and user is a member of the project' do
- context 'and the project is private' do
- let(:project) { private_project }
+ before do
+ project.add_guest(security_policy_bot)
+ end
- it_behaves_like 'allows to create pipeline'
+ it { is_expected.to be_allowed(:create_pipeline) }
end
- context 'and the project is internal' do
- let(:project) { internal_project }
+ context 'and user is a member of the project' do
+ context 'and the project is private' do
+ let(:project) { private_project }
- it_behaves_like 'allows to create pipeline'
- end
+ it_behaves_like 'allows to create pipeline'
+ end
- context 'and the project is public' do
- let(:project) { public_project }
+ context 'and the project is internal' do
+ let(:project) { internal_project }
- it_behaves_like 'allows to create pipeline'
- end
+ it_behaves_like 'allows to create pipeline'
+ end
- context 'and the project is public in group' do
- let(:project) { public_project_in_group }
+ context 'and the project is public' do
+ let(:project) { public_project }
- it_behaves_like 'allows to create pipeline'
+ it_behaves_like 'allows to create pipeline'
+ end
+
+ context 'and the project is public in group' do
+ let(:project) { public_project_in_group }
+
+ it_behaves_like 'allows to create pipeline'
+ end
end
end
end
end
- end
- describe 'build_download_code policy' do
- let(:project) { private_project }
+ describe 'build_download_code policy' do
+ let(:project) { private_project }
- context 'as a guest member' do
- let(:current_user) { guest }
+ context 'as a guest member' do
+ let(:current_user) { guest }
- it { is_expected.not_to be_allowed(:build_download_code) }
+ it { is_expected.not_to be_allowed(:build_download_code) }
- context 'and user is a security_policy_bot' do
- let_it_be(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
- let(:current_user) { security_policy_bot }
+ context 'and user is a security_policy_bot' do
+ let_it_be(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
+ let(:current_user) { security_policy_bot }
- it { is_expected.not_to be_allowed(:build_download_code) }
+ it { is_expected.not_to be_allowed(:build_download_code) }
- context 'and user is a member of the project' do
- before do
- [private_project, internal_project, public_project, public_project_in_group].each do |project|
- project.add_guest(security_policy_bot)
+ context 'and user is a member of the project' do
+ before do
+ [private_project, internal_project, public_project, public_project_in_group].each do |project|
+ project.add_guest(security_policy_bot)
+ end
end
- end
- it { is_expected.to be_allowed(:build_download_code) }
+ it { is_expected.to be_allowed(:build_download_code) }
+ end
end
end
end
- end
- describe 'create_bot_pipeline policy' do
- context 'as a guest member' do
- let(:current_user) { guest }
+ describe 'create_bot_pipeline policy' do
+ context 'as a guest member' do
+ let(:current_user) { guest }
- it { is_expected.not_to be_allowed(:create_bot_pipeline) }
+ it { is_expected.not_to be_allowed(:create_bot_pipeline) }
- context 'and user is a security_policy_bot' do
- let_it_be(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
- let(:current_user) { security_policy_bot }
+ context 'and user is a security_policy_bot' do
+ let_it_be(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
+ let(:current_user) { security_policy_bot }
- it { is_expected.not_to be_allowed(:create_bot_pipeline) }
+ it { is_expected.not_to be_allowed(:create_bot_pipeline) }
- context 'and user is a member of the project' do
- shared_examples 'allows to create_bot_pipeline' do
- let(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
+ context 'and user is a member of the project' do
+ shared_examples 'allows to create_bot_pipeline' do
+ let(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
- before do
- project.add_guest(security_policy_bot)
- end
+ before do
+ project.add_guest(security_policy_bot)
+ end
- it { is_expected.to be_allowed(:create_bot_pipeline) }
- end
+ it { is_expected.to be_allowed(:create_bot_pipeline) }
+ end
- context 'and the project is private' do
- let(:project) { private_project }
+ context 'and the project is private' do
+ let(:project) { private_project }
- it_behaves_like 'allows to create_bot_pipeline'
- end
+ it_behaves_like 'allows to create_bot_pipeline'
+ end
- context 'and the project is internal' do
- let(:project) { internal_project }
+ context 'and the project is internal' do
+ let(:project) { internal_project }
- it_behaves_like 'allows to create_bot_pipeline'
- end
+ it_behaves_like 'allows to create_bot_pipeline'
+ end
- context 'and the project is public' do
- let(:project) { public_project }
+ context 'and the project is public' do
+ let(:project) { public_project }
- it_behaves_like 'allows to create_bot_pipeline'
- end
+ it_behaves_like 'allows to create_bot_pipeline'
+ end
- context 'and the project is public in group' do
- let(:project) { public_project_in_group }
+ context 'and the project is public in group' do
+ let(:project) { public_project_in_group }
- it_behaves_like 'allows to create_bot_pipeline'
+ it_behaves_like 'allows to create_bot_pipeline'
+ end
end
end
end
end
- end
- describe 'security_policy_bot policy' do
- let_it_be(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
- let(:current_user) { security_policy_bot }
+ describe 'security_policy_bot policy' do
+ let_it_be(:security_policy_bot) { create(:user, user_type: :security_policy_bot) }
+ let(:current_user) { security_policy_bot }
- context 'when user is authenticated via CI_JOB_TOKEN', :request_store do
- let(:project) { public_project }
- let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) }
- let(:scope_project) { project }
+ context 'when user is authenticated via CI_JOB_TOKEN', :request_store do
+ let(:project) { public_project }
+ let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) }
+ let(:scope_project) { project }
- let_it_be(:other_private_project) { create(:project, :private) }
+ let_it_be(:other_private_project) { create(:project, :private) }
- before do
- project.add_guest(security_policy_bot)
- current_user.set_ci_job_token_scope!(job)
- project.update!(
- ci_outbound_job_token_scope_enabled: token_scope_enabled,
- ci_inbound_job_token_scope_enabled: token_scope_enabled
- )
- scope_project.update!(
- ci_outbound_job_token_scope_enabled: token_scope_enabled,
- ci_inbound_job_token_scope_enabled: token_scope_enabled
- )
- end
+ before do
+ project.add_guest(security_policy_bot)
+ current_user.set_ci_job_token_scope!(job)
+ project.update!(
+ ci_outbound_job_token_scope_enabled: token_scope_enabled,
+ ci_inbound_job_token_scope_enabled: token_scope_enabled
+ )
+ scope_project.update!(
+ ci_outbound_job_token_scope_enabled: token_scope_enabled,
+ ci_inbound_job_token_scope_enabled: token_scope_enabled
+ )
+ end
+
+ context 'when token scope is disabled' do
+ let(:token_scope_enabled) { false }
+
+ context 'when pipeline is executed in project where bot is invited' do
+ it { is_expected.to be_allowed(:create_pipeline) }
+ it { is_expected.to be_allowed(:create_bot_pipeline) }
+ it { is_expected.to be_allowed(:build_download_code) }
+ end
- context 'when token scope is disabled' do
- let(:token_scope_enabled) { false }
+ context 'when pipeline is executed in project where bot is not invited' do
+ let(:scope_project) { other_private_project }
- context 'when pipeline is executed in project where bot is invited' do
- it { is_expected.to be_allowed(:create_pipeline) }
- it { is_expected.to be_allowed(:create_bot_pipeline) }
- it { is_expected.to be_allowed(:build_download_code) }
+ it { is_expected.to be_allowed(:create_pipeline) }
+ it { is_expected.to be_allowed(:create_bot_pipeline) }
+ it { is_expected.to be_allowed(:build_download_code) }
+ end
end
- context 'when pipeline is executed in project where bot is not invited' do
- let(:scope_project) { other_private_project }
+ context 'when token scope is enabled' do
+ let(:token_scope_enabled) { true }
+
+ context 'when pipeline is executed in project where bot is invited' do
+ it { is_expected.to be_allowed(:create_pipeline) }
+ it { is_expected.to be_allowed(:create_bot_pipeline) }
+ it { is_expected.to be_allowed(:build_download_code) }
+ end
- it { is_expected.to be_allowed(:create_pipeline) }
- it { is_expected.to be_allowed(:create_bot_pipeline) }
- it { is_expected.to be_allowed(:build_download_code) }
+ context 'when pipeline is executed in project where bot is not invited' do
+ let(:scope_project) { other_private_project }
+
+ it { is_expected.to be_disallowed(:create_pipeline) }
+ it { is_expected.to be_disallowed(:create_bot_pipeline) }
+ it { is_expected.to be_disallowed(:build_download_code) }
+ end
end
end
- context 'when token scope is enabled' do
- let(:token_scope_enabled) { true }
+ context 'when security policy bot is on the project' do
+ before do
+ project.add_guest(security_policy_bot)
+ end
- context 'when pipeline is executed in project where bot is invited' do
- it { is_expected.to be_allowed(:create_pipeline) }
- it { is_expected.to be_allowed(:create_bot_pipeline) }
- it { is_expected.to be_allowed(:build_download_code) }
+ context 'when security_dashboard is not enabled' do
+ it { is_expected.to be_disallowed(:create_vulnerability_state_transition) }
end
- context 'when pipeline is executed in project where bot is not invited' do
- let(:scope_project) { other_private_project }
+ context 'when security_dashboard is enabled' do
+ before do
+ stub_licensed_features(security_dashboard: true)
+ end
- it { is_expected.to be_disallowed(:create_pipeline) }
- it { is_expected.to be_disallowed(:create_bot_pipeline) }
- it { is_expected.to be_disallowed(:build_download_code) }
+ it { is_expected.to be_allowed(:create_vulnerability_state_transition) }
end
end
end
- context 'when security policy bot is on the project' do
- before do
- project.add_guest(security_policy_bot)
- end
+ describe 'download_code_spp_repository policy' do
+ let(:current_user) { guest }
- context 'when security_dashboard is not enabled' do
- it { is_expected.to be_disallowed(:create_vulnerability_state_transition) }
- end
+ it { is_expected.not_to be_allowed(:download_code_spp_repository) }
- context 'when security_dashboard is enabled' do
+ context 'when project is a security policy project' do
before do
- stub_licensed_features(security_dashboard: true)
+ create(:security_orchestration_policy_configuration, security_policy_management_project: project)
end
- it { is_expected.to be_allowed(:create_vulnerability_state_transition) }
- end
- end
- end
+ it { is_expected.not_to be_allowed(:download_code_spp_repository) }
- describe 'download_code_spp_repository policy' do
- let(:current_user) { guest }
+ context 'and project allows spp_repository_pipeline_access' do
+ before do
+ project.project_setting.update!(spp_repository_pipeline_access: true)
+ end
- it { is_expected.not_to be_allowed(:download_code_spp_repository) }
+ context 'and the project is private' do
+ let(:project) { private_project }
- context 'when project is a security policy project' do
- before do
- create(:security_orchestration_policy_configuration, security_policy_management_project: project)
- end
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- it { is_expected.not_to be_allowed(:download_code_spp_repository) }
+ context 'and the project is internal' do
+ let(:project) { internal_project }
- context 'and project allows spp_repository_pipeline_access' do
- before do
- project.project_setting.update!(spp_repository_pipeline_access: true)
- end
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- context 'and the project is private' do
- let(:project) { private_project }
+ context 'and the project is public' do
+ let(:project) { public_project }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
- end
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- context 'and the project is internal' do
- let(:project) { internal_project }
+ context 'and the project is public in group' do
+ let(:project) { public_project_in_group }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
end
- context 'and the project is public' do
- let(:project) { public_project }
+ context 'and namespace allows spp_repository_pipeline_access' do
+ before do
+ project.group.namespace_settings.update!(spp_repository_pipeline_access: true)
+ end
+
+ context 'and the project is private in group' do
+ let(:project) { private_project_in_group }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
- end
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- context 'and the project is public in group' do
- let(:project) { public_project_in_group }
+ context 'and the project is internal in group' do
+ let_it_be_with_refind(:internal_project_in_group) { create(:project, :internal, namespace: group) }
+ let(:project) { internal_project_in_group }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
- end
- end
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- context 'and namespace allows spp_repository_pipeline_access' do
- before do
- project.group.namespace_settings.update!(spp_repository_pipeline_access: true)
+ context 'and the project is public in group' do
+ let(:project) { public_project_in_group }
+
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
end
- context 'and the project is private in group' do
- let(:project) { private_project_in_group }
+ context 'and application setting allows spp_repository_pipeline_access' do
+ before do
+ stub_application_setting(spp_repository_pipeline_access: true)
+ end
- it { is_expected.to be_allowed(:download_code_spp_repository) }
- end
+ context 'and the project is private' do
+ let(:project) { private_project }
- context 'and the project is internal in group' do
- let_it_be_with_refind(:internal_project_in_group) { create(:project, :internal, namespace: group) }
- let(:project) { internal_project_in_group }
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- it { is_expected.to be_allowed(:download_code_spp_repository) }
- end
+ context 'and the project is internal' do
+ let(:project) { internal_project }
+
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
+
+ context 'and the project is public' do
+ let(:project) { public_project }
+
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- context 'and the project is public in group' do
- let(:project) { public_project_in_group }
+ context 'and the project is public in group' do
+ let(:project) { public_project_in_group }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
end
end
- context 'and application setting allows spp_repository_pipeline_access' do
+ context 'when user is authenticated via CI_JOB_TOKEN', :request_store do
+ let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) }
+ let(:scope_project) { project }
+
+ let_it_be(:other_private_project) { create(:project, :private) }
+
before do
- stub_application_setting(spp_repository_pipeline_access: true)
+ current_user.set_ci_job_token_scope!(job)
+ create(:security_orchestration_policy_configuration, security_policy_management_project: project)
+ project.project_setting.update!(spp_repository_pipeline_access: true)
+ project.update!(
+ ci_outbound_job_token_scope_enabled: token_scope_enabled,
+ ci_inbound_job_token_scope_enabled: token_scope_enabled
+ )
+ scope_project.update!(
+ ci_outbound_job_token_scope_enabled: token_scope_enabled,
+ ci_inbound_job_token_scope_enabled: token_scope_enabled
+ )
end
- context 'and the project is private' do
- let(:project) { private_project }
+ context 'when token scope is disabled' do
+ let(:token_scope_enabled) { false }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
- end
+ context 'when accessing from the same project' do
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- context 'and the project is internal' do
- let(:project) { internal_project }
+ context 'when accessing from other project' do
+ let(:scope_project) { other_private_project }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
end
- context 'and the project is public' do
- let(:project) { public_project }
+ context 'when token scope is enabled' do
+ let(:token_scope_enabled) { true }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
- end
+ context 'when accessing from the same project' do
+ it { is_expected.to be_allowed(:download_code_spp_repository) }
+ end
- context 'and the project is public in group' do
- let(:project) { public_project_in_group }
+ context 'when accessing from other project' do
+ let(:scope_project) { other_private_project }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
+ it { is_expected.to be_disallowed(:download_code_spp_repository) }
+ end
end
end
end
- context 'when user is authenticated via CI_JOB_TOKEN', :request_store do
- let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) }
- let(:scope_project) { project }
-
- let_it_be(:other_private_project) { create(:project, :private) }
+ describe 'generate_description' do
+ let(:authorizer) { instance_double(::Gitlab::Llm::FeatureAuthorizer) }
+ let(:current_user) { guest }
+ let(:project) { private_project }
before do
- current_user.set_ci_job_token_scope!(job)
- create(:security_orchestration_policy_configuration, security_policy_management_project: project)
- project.project_setting.update!(spp_repository_pipeline_access: true)
- project.update!(
- ci_outbound_job_token_scope_enabled: token_scope_enabled,
- ci_inbound_job_token_scope_enabled: token_scope_enabled
- )
- scope_project.update!(
- ci_outbound_job_token_scope_enabled: token_scope_enabled,
- ci_inbound_job_token_scope_enabled: token_scope_enabled
- )
+ allow(::Gitlab::Llm::FeatureAuthorizer).to receive(:new).and_return(authorizer)
end
- context 'when token scope is disabled' do
- let(:token_scope_enabled) { false }
+ context "when feature is authorized" do
+ before do
+ allow(authorizer).to receive(:allowed?).and_return(true)
+ end
- context 'when accessing from the same project' do
- it { is_expected.to be_allowed(:download_code_spp_repository) }
+ context 'when user can create issue' do
+ it { is_expected.to be_allowed(:generate_description) }
end
- context 'when accessing from other project' do
- let(:scope_project) { other_private_project }
+ context 'when user cannot create issue' do
+ let(:current_user) { create(:user) }
- it { is_expected.to be_allowed(:download_code_spp_repository) }
+ it { is_expected.to be_disallowed(:generate_description) }
end
end
- context 'when token scope is enabled' do
- let(:token_scope_enabled) { true }
-
- context 'when accessing from the same project' do
- it { is_expected.to be_allowed(:download_code_spp_repository) }
+ context "when feature is not authorized" do
+ before do
+ allow(authorizer).to receive(:allowed?).and_return(false)
end
- context 'when accessing from other project' do
- let(:scope_project) { other_private_project }
-
- it { is_expected.to be_disallowed(:download_code_spp_repository) }
- end
+ it { is_expected.to be_disallowed(:generate_description) }
end
end
- end
- describe 'generate_description' do
- let(:authorizer) { instance_double(::Gitlab::Llm::FeatureAuthorizer) }
- let(:current_user) { guest }
- let(:project) { private_project }
+ describe 'summarize_new_merge_request policy' do
+ let_it_be(:namespace) { group }
+ let_it_be(:project) { private_project }
+ let_it_be(:current_user) { maintainer }
- before do
- allow(::Gitlab::Llm::FeatureAuthorizer).to receive(:new).and_return(authorizer)
- end
+ let(:authorizer) { instance_double(::Gitlab::Llm::FeatureAuthorizer) }
- context "when feature is authorized" do
before do
- allow(authorizer).to receive(:allowed?).and_return(true)
- end
-
- context 'when user can create issue' do
- it { is_expected.to be_allowed(:generate_description) }
- end
-
- context 'when user cannot create issue' do
- let(:current_user) { create(:user) }
-
- it { is_expected.to be_disallowed(:generate_description) }
+ allow(::Gitlab::Llm::FeatureAuthorizer).to receive(:new).and_return(authorizer)
+ allow(project).to receive(:namespace).and_return(namespace)
end
- end
- context "when feature is not authorized" do
- before do
- allow(authorizer).to receive(:allowed?).and_return(false)
- end
+ context "when feature is authorized" do
+ before do
+ allow(authorizer).to receive(:allowed?).and_return(true)
+ end
- it { is_expected.to be_disallowed(:generate_description) }
- end
- end
+ it { is_expected.to be_allowed(:summarize_new_merge_request) }
- describe 'summarize_new_merge_request policy' do
- let_it_be(:namespace) { group }
- let_it_be(:project) { private_project }
- let_it_be(:current_user) { maintainer }
+ context 'when add_ai_summary_for_new_mr feature flag is disabled' do
+ before do
+ stub_feature_flags(add_ai_summary_for_new_mr: false)
+ end
- let(:authorizer) { instance_double(::Gitlab::Llm::FeatureAuthorizer) }
+ it { is_expected.to be_disallowed(:summarize_new_merge_request) }
+ end
- before do
- allow(::Gitlab::Llm::FeatureAuthorizer).to receive(:new).and_return(authorizer)
- allow(project).to receive(:namespace).and_return(namespace)
- end
+ context 'when user cannot create_merge_request_in' do
+ let(:current_user) { guest }
- context "when feature is authorized" do
- before do
- allow(authorizer).to receive(:allowed?).and_return(true)
+ it { is_expected.to be_disallowed(:summarize_new_merge_request) }
+ end
end
- it { is_expected.to be_allowed(:summarize_new_merge_request) }
-
- context 'when add_ai_summary_for_new_mr feature flag is disabled' do
+ context "when feature is not authorized" do
before do
- stub_feature_flags(add_ai_summary_for_new_mr: false)
+ allow(authorizer).to receive(:allowed?).and_return(false)
end
it { is_expected.to be_disallowed(:summarize_new_merge_request) }
end
+ end
- context 'when user cannot create_merge_request_in' do
- let(:current_user) { guest }
+ describe 'admin_target_branch_rule policy' do
+ let(:current_user) { owner }
- it { is_expected.to be_disallowed(:summarize_new_merge_request) }
- end
- end
+ describe 'when the project does not have the correct license' do
+ before do
+ stub_licensed_features(target_branch_rules: false)
+ end
- context "when feature is not authorized" do
- before do
- allow(authorizer).to receive(:allowed?).and_return(false)
+ it { is_expected.to be_disallowed(:admin_target_branch_rule) }
end
- it { is_expected.to be_disallowed(:summarize_new_merge_request) }
- end
- end
-
- describe 'admin_target_branch_rule policy' do
- let(:current_user) { owner }
+ describe 'when the user does not have permissions' do
+ let(:current_user) { auditor }
- describe 'when the project does not have the correct license' do
- before do
- stub_licensed_features(target_branch_rules: false)
+ it { is_expected.to be_disallowed(:admin_target_branch_rule) }
end
- it { is_expected.to be_disallowed(:admin_target_branch_rule) }
+ describe 'when the user has permission' do
+ before do
+ stub_licensed_features(target_branch_rules: true)
+ end
+
+ it { is_expected.to be_allowed(:admin_target_branch_rule) }
+ end
end
- describe 'when the user does not have permissions' do
- let(:current_user) { auditor }
+ describe 'read_target_branch_rule policy' do
+ let(:current_user) { owner }
- it { is_expected.to be_disallowed(:admin_target_branch_rule) }
- end
+ describe 'when the user has permission' do
+ before do
+ stub_licensed_features(target_branch_rules: true)
+ end
- describe 'when the user has permission' do
- before do
- stub_licensed_features(target_branch_rules: true)
+ it { is_expected.to be_allowed(:read_target_branch_rule) }
end
-
- it { is_expected.to be_allowed(:admin_target_branch_rule) }
end
- end
- describe 'read_target_branch_rule policy' do
- let(:current_user) { owner }
+ describe 'read_observability policy' do
+ let(:current_user) { reporter }
- describe 'when the user has permission' do
before do
- stub_licensed_features(target_branch_rules: true)
+ stub_licensed_features(observability: true)
end
- it { is_expected.to be_allowed(:read_target_branch_rule) }
- end
- end
+ describe 'when observability_features is disabled' do
+ before do
+ stub_feature_flags(observability_features: false)
+ end
- describe 'read_observability policy' do
- let(:current_user) { reporter }
+ it { is_expected.to be_disallowed(:read_observability) }
+ end
- before do
- stub_licensed_features(observability: true)
- end
+ describe 'when observability feature flag is enabled for root namespace' do
+ before do
+ stub_feature_flags(observability_features: project.root_namespace)
+ end
- describe 'when observability_features is disabled' do
- before do
- stub_feature_flags(observability_features: false)
+ it { is_expected.to be_allowed(:read_observability) }
end
- it { is_expected.to be_disallowed(:read_observability) }
- end
+ describe 'when the project does not have the correct license' do
+ before do
+ stub_feature_flags(observability_features: true)
+ stub_licensed_features(observability: false)
+ end
- describe 'when observability feature flag is enabled for root namespace' do
- before do
- stub_feature_flags(observability_features: project.root_namespace)
+ it { is_expected.to be_disallowed(:read_observability) }
end
- it { is_expected.to be_allowed(:read_observability) }
- end
+ describe 'when the user does not have permission' do
+ let(:current_user) { guest }
- describe 'when the project does not have the correct license' do
- before do
- stub_feature_flags(observability_features: true)
- stub_licensed_features(observability: false)
+ before do
+ stub_feature_flags(observability_features: true)
+ stub_licensed_features(observability: true)
+ end
+
+ it { is_expected.to be_disallowed(:read_observability) }
end
- it { is_expected.to be_disallowed(:read_observability) }
+ describe 'when the user has permission' do
+ before do
+ stub_feature_flags(observability_features: true)
+ stub_licensed_features(observability: true)
+ end
+
+ it { is_expected.to be_allowed(:read_observability) }
+ end
end
- describe 'when the user does not have permission' do
- let(:current_user) { guest }
+ describe 'write_observability policy' do
+ let(:current_user) { developer }
before do
- stub_feature_flags(observability_features: true)
stub_licensed_features(observability: true)
end
- it { is_expected.to be_disallowed(:read_observability) }
- end
+ describe 'when observability and observability_tracing feature flags are disabled' do
+ before do
+ stub_feature_flags(observability_features: false)
+ stub_feature_flags(observability_tracing: false)
+ end
- describe 'when the user has permission' do
- before do
- stub_feature_flags(observability_features: true)
- stub_licensed_features(observability: true)
+ it { is_expected.to be_disallowed(:write_observability) }
end
- it { is_expected.to be_allowed(:read_observability) }
- end
- end
+ describe 'when observability feature flag is enabled for root namespace' do
+ before do
+ stub_feature_flags(observability_features: project.root_namespace)
+ end
- describe 'write_observability policy' do
- let(:current_user) { developer }
+ it { is_expected.to be_allowed(:write_observability) }
+ end
- before do
- stub_licensed_features(observability: true)
- end
+ describe 'when observability_tracing feature flag is enabled for root namespace' do
+ before do
+ stub_feature_flags(observability_tracing: project.root_namespace)
+ end
- describe 'when observability and observability_tracing feature flags are disabled' do
- before do
- stub_feature_flags(observability_features: false)
- stub_feature_flags(observability_tracing: false)
+ it { is_expected.to be_allowed(:write_observability) }
end
- it { is_expected.to be_disallowed(:write_observability) }
- end
+ describe 'when the project does not have the correct license' do
+ before do
+ stub_feature_flags(observability_features: true)
+ stub_licensed_features(observability: false)
+ end
- describe 'when observability feature flag is enabled for root namespace' do
- before do
- stub_feature_flags(observability_features: project.root_namespace)
+ it { is_expected.to be_disallowed(:write_observability) }
end
- it { is_expected.to be_allowed(:write_observability) }
- end
+ describe 'when the user does not have permission' do
+ let(:current_user) { reporter }
- describe 'when observability_tracing feature flag is enabled for root namespace' do
- before do
- stub_feature_flags(observability_tracing: project.root_namespace)
+ before do
+ stub_feature_flags(observability_features: true)
+ stub_licensed_features(observability: true)
+ end
+
+ it { is_expected.to be_disallowed(:write_observability) }
end
- it { is_expected.to be_allowed(:write_observability) }
+ describe 'when the user has permission' do
+ before do
+ stub_feature_flags(observability_features: true)
+ stub_licensed_features(observability: true)
+ end
+
+ it { is_expected.to be_allowed(:write_observability) }
+ end
end
- describe 'when the project does not have the correct license' do
+ describe "#admin_vulnerability" do
before do
- stub_feature_flags(observability_features: true)
- stub_licensed_features(observability: false)
+ stub_licensed_features(security_dashboard: true)
end
- it { is_expected.to be_disallowed(:write_observability) }
- end
-
- describe 'when the user does not have permission' do
- let(:current_user) { reporter }
-
- before do
- stub_feature_flags(observability_features: true)
- stub_licensed_features(observability: true)
+ let(:expected_permissions) do
+ [
+ :admin_vulnerability,
+ :read_vulnerability,
+ :create_vulnerability_feedback,
+ :destroy_vulnerability_feedback,
+ :update_vulnerability_feedback,
+ :create_vulnerability_state_transition
+ ]
end
- it { is_expected.to be_disallowed(:write_observability) }
- end
+ context "with guest" do
+ let(:current_user) { guest }
- describe 'when the user has permission' do
- before do
- stub_feature_flags(observability_features: true)
- stub_licensed_features(observability: true)
+ it { is_expected.to be_disallowed(:admin_vulnerability) }
end
- it { is_expected.to be_allowed(:write_observability) }
- end
- end
-
- describe "#admin_vulnerability" do
- before do
- stub_licensed_features(security_dashboard: true)
- end
+ context "with planner" do
+ let(:current_user) { planner }
- let(:expected_permissions) do
- [
- :admin_vulnerability,
- :read_vulnerability,
- :create_vulnerability_feedback,
- :destroy_vulnerability_feedback,
- :update_vulnerability_feedback,
- :create_vulnerability_state_transition
- ]
- end
+ it { is_expected.to be_disallowed(:admin_vulnerability) }
+ end
- context "with guest" do
- let(:current_user) { guest }
+ context "with reporter" do
+ let(:current_user) { reporter }
- it { is_expected.to be_disallowed(:admin_vulnerability) }
- end
+ it { is_expected.to be_disallowed(:admin_vulnerability) }
+ end
- context "with planner" do
- let(:current_user) { planner }
+ context "with developer" do
+ let(:current_user) { developer }
- it { is_expected.to be_disallowed(:admin_vulnerability) }
- end
+ it { is_expected.to be_disallowed(:admin_vulnerability) }
+ end
- context "with reporter" do
- let(:current_user) { reporter }
+ context "with maintainer" do
+ let(:current_user) { maintainer }
- it { is_expected.to be_disallowed(:admin_vulnerability) }
- end
+ it { is_expected.to be_allowed(*expected_permissions) }
+ end
- context "with developer" do
- let(:current_user) { developer }
+ context "with owner" do
+ let(:current_user) { owner }
- it { is_expected.to be_disallowed(:admin_vulnerability) }
+ it { is_expected.to be_allowed(*expected_permissions) }
+ end
end
- context "with maintainer" do
- let(:current_user) { maintainer }
+ describe 'generate_cube_query policy' do
+ let(:current_user) { owner }
+ let(:authorizer) { instance_double(::Gitlab::Llm::FeatureAuthorizer) }
- it { is_expected.to be_allowed(*expected_permissions) }
- end
+ where(:ai_global_switch, :flag_enabled, :licensed, :allowed) do
+ true | true | true | true
+ true | true | false | false
+ true | false | true | false
+ true | false | false | false
+ false | true | true | false
+ false | true | false | false
+ false | false | true | false
+ false | false | false | false
+ end
- context "with owner" do
- let(:current_user) { owner }
+ with_them do
+ before do
+ stub_feature_flags(ai_global_switch: ai_global_switch)
+ stub_feature_flags(generate_cube_query: flag_enabled)
+ allow(::Gitlab::Llm::FeatureAuthorizer).to receive(:new).and_return(authorizer)
+ allow(authorizer).to receive(:allowed?).and_return(licensed)
+ end
- it { is_expected.to be_allowed(*expected_permissions) }
+ it 'permits the correct abilities' do
+ if allowed
+ is_expected.to be_allowed(:generate_cube_query)
+ else
+ is_expected.to be_disallowed(:generate_cube_query)
+ end
+ end
+ end
end
- end
- describe 'generate_cube_query policy' do
- let(:current_user) { owner }
- let(:authorizer) { instance_double(::Gitlab::Llm::FeatureAuthorizer) }
-
- where(:ai_global_switch, :flag_enabled, :licensed, :allowed) do
- true | true | true | true
- true | true | false | false
- true | false | true | false
- true | false | false | false
- false | true | true | false
- false | true | false | false
- false | false | true | false
- false | false | false | false
- end
+ describe 'read_ai_agents' do
+ where(:feature_flag_enabled, :licensed_feature, :current_user, :allowed) do
+ true | true | ref(:owner) | true
+ true | true | ref(:reporter) | true
+ true | true | ref(:planner) | true
+ true | true | ref(:guest) | true
+ true | true | ref(:non_member) | false
+ true | false | ref(:owner) | false
+ true | false | ref(:reporter) | false
+ true | false | ref(:planner) | false
+ true | false | ref(:guest) | false
+ true | false | ref(:non_member) | false
+ false | true | ref(:owner) | false
+ false | true | ref(:reporter) | false
+ false | true | ref(:planner) | false
+ false | true | ref(:guest) | false
+ false | true | ref(:non_member) | false
+ false | false | ref(:owner) | false
+ false | false | ref(:reporter) | false
+ false | false | ref(:planner) | false
+ false | false | ref(:guest) | false
+ false | false | ref(:non_member) | false
+ end
+ with_them do
+ before do
+ stub_feature_flags(agent_registry: feature_flag_enabled)
+ stub_licensed_features(ai_agents: licensed_feature)
+ end
- with_them do
- before do
- stub_feature_flags(ai_global_switch: ai_global_switch)
- stub_feature_flags(generate_cube_query: flag_enabled)
- allow(::Gitlab::Llm::FeatureAuthorizer).to receive(:new).and_return(authorizer)
- allow(authorizer).to receive(:allowed?).and_return(licensed)
+ if params[:allowed]
+ it { expect_allowed(:read_ai_agents) }
+ else
+ it { expect_disallowed(:read_ai_agents) }
+ end
+ end
+ end
+
+ describe 'write_ai_agents' do
+ where(:feature_flag_enabled, :licensed_feature, :current_user, :allowed) do
+ true | true | ref(:owner) | true
+ true | true | ref(:reporter) | true
+ true | true | ref(:planner) | false
+ true | true | ref(:guest) | false
+ true | true | ref(:non_member) | false
+ true | false | ref(:owner) | false
+ true | false | ref(:reporter) | false
+ true | false | ref(:planner) | false
+ true | false | ref(:guest) | false
+ true | false | ref(:non_member) | false
+ false | true | ref(:owner) | false
+ false | true | ref(:reporter) | false
+ false | true | ref(:planner) | false
+ false | true | ref(:guest) | false
+ false | true | ref(:non_member) | false
+ false | false | ref(:owner) | false
+ false | false | ref(:reporter) | false
+ false | false | ref(:planner) | false
+ false | false | ref(:guest) | false
+ false | false | ref(:non_member) | false
end
+ with_them do
+ before do
+ stub_feature_flags(agent_registry: feature_flag_enabled)
+ stub_licensed_features(ai_agents: licensed_feature)
+ end
- it 'permits the correct abilities' do
- if allowed
- is_expected.to be_allowed(:generate_cube_query)
+ if params[:allowed]
+ it { expect_allowed(:write_ai_agents) }
else
- is_expected.to be_disallowed(:generate_cube_query)
+ it { expect_disallowed(:write_ai_agents) }
end
end
end
- end
-
- describe 'read_ai_agents' do
- where(:feature_flag_enabled, :licensed_feature, :current_user, :allowed) do
- true | true | ref(:owner) | true
- true | true | ref(:reporter) | true
- true | true | ref(:planner) | true
- true | true | ref(:guest) | true
- true | true | ref(:non_member) | false
- true | false | ref(:owner) | false
- true | false | ref(:reporter) | false
- true | false | ref(:planner) | false
- true | false | ref(:guest) | false
- true | false | ref(:non_member) | false
- false | true | ref(:owner) | false
- false | true | ref(:reporter) | false
- false | true | ref(:planner) | false
- false | true | ref(:guest) | false
- false | true | ref(:non_member) | false
- false | false | ref(:owner) | false
- false | false | ref(:reporter) | false
- false | false | ref(:planner) | false
- false | false | ref(:guest) | false
- false | false | ref(:non_member) | false
- end
- with_them do
- before do
- stub_feature_flags(agent_registry: feature_flag_enabled)
- stub_licensed_features(ai_agents: licensed_feature)
- end
- if params[:allowed]
- it { expect_allowed(:read_ai_agents) }
- else
- it { expect_disallowed(:read_ai_agents) }
- end
- end
- end
+ describe 'access_duo_chat' do
+ let_it_be(:current_user) { create(:user) }
+ let(:project) { create(:project, :public, group: group) }
- describe 'write_ai_agents' do
- where(:feature_flag_enabled, :licensed_feature, :current_user, :allowed) do
- true | true | ref(:owner) | true
- true | true | ref(:reporter) | true
- true | true | ref(:planner) | false
- true | true | ref(:guest) | false
- true | true | ref(:non_member) | false
- true | false | ref(:owner) | false
- true | false | ref(:reporter) | false
- true | false | ref(:planner) | false
- true | false | ref(:guest) | false
- true | false | ref(:non_member) | false
- false | true | ref(:owner) | false
- false | true | ref(:reporter) | false
- false | true | ref(:planner) | false
- false | true | ref(:guest) | false
- false | true | ref(:non_member) | false
- false | false | ref(:owner) | false
- false | false | ref(:reporter) | false
- false | false | ref(:planner) | false
- false | false | ref(:guest) | false
- false | false | ref(:non_member) | false
- end
- with_them do
- before do
- stub_feature_flags(agent_registry: feature_flag_enabled)
- stub_licensed_features(ai_agents: licensed_feature)
- end
+ subject { described_class.new(current_user, project) }
- if params[:allowed]
- it { expect_allowed(:write_ai_agents) }
- else
- it { expect_disallowed(:write_ai_agents) }
- end
- end
- end
+ context 'when on SaaS instance', :saas do
+ let_it_be_with_reload(:group) { create(:group_with_plan, plan: :premium_plan) }
- describe 'access_duo_chat' do
- let_it_be(:current_user) { create(:user) }
- let(:project) { create(:project, :public, group: group) }
+ context 'when container is a group with AI enabled' do
+ include_context 'with duo features enabled and ai chat available for group on SaaS'
- subject { described_class.new(current_user, project) }
+ context 'when user is a member of the group' do
+ before do
+ group.add_guest(current_user)
+ end
- context 'when on SaaS instance', :saas do
- let_it_be_with_reload(:group) { create(:group_with_plan, plan: :premium_plan) }
+ it { is_expected.to be_allowed(:access_duo_chat) }
- context 'when container is a group with AI enabled' do
- include_context 'with duo features enabled and ai chat available for group on SaaS'
+ context 'when the group does not have an Premium SaaS license' do
+ let_it_be(:group) { create(:group) }
- context 'when user is a member of the group' do
- before do
- group.add_guest(current_user)
+ it { is_expected.to be_disallowed(:access_duo_chat) }
+ end
end
- it { is_expected.to be_allowed(:access_duo_chat) }
+ context 'when user is not a member of the parent group' do
+ context 'when the user has AI enabled via another group' do
+ before do
+ allow(current_user).to receive(:any_group_with_ai_chat_available?).and_return(true)
+ end
- context 'when the group does not have an Premium SaaS license' do
- let_it_be(:group) { create(:group) }
+ context 'user can view project' do
+ it 'is allowed' do
+ is_expected.to be_allowed(:access_duo_chat)
+ end
+ end
- it { is_expected.to be_disallowed(:access_duo_chat) }
+ context 'user cannot view project' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'is not allowed' do
+ is_expected.to be_disallowed(:access_duo_chat)
+ end
+ end
+ end
end
- end
- context 'when user is not a member of the parent group' do
- context 'when the user has AI enabled via another group' do
+ context 'when user is a member of the project' do
before do
- allow(current_user).to receive(:any_group_with_ai_chat_available?).and_return(true)
+ project.add_guest(current_user)
end
- context 'user can view project' do
+ context 'when the user has AI enabled through parent group' do
it 'is allowed' do
+ allow(current_user).to receive(:any_group_with_ai_chat_available?).and_return(true)
+
is_expected.to be_allowed(:access_duo_chat)
end
end
+ end
+ end
+ end
- context 'user cannot view project' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- end
+ context 'for self-managed', :with_cloud_connector do
+ let_it_be_with_reload(:group) { create(:group) }
+ let(:policy) { :access_duo_chat }
- it 'is not allowed' do
- is_expected.to be_disallowed(:access_duo_chat)
- end
- end
- end
+ before do
+ project.add_guest(current_user)
end
- context 'when user is a member of the project' do
- before do
- project.add_guest(current_user)
+ context 'when not on .org or .com' do
+ where(:enabled_for_user, :duo_features_enabled, :duo_chat_matcher) do
+ true | false | be_disallowed(policy)
+ true | true | be_allowed(policy)
+ false | false | be_disallowed(policy)
+ false | true | be_disallowed(policy)
end
- context 'when the user has AI enabled through parent group' do
- it 'is allowed' do
- allow(current_user).to receive(:any_group_with_ai_chat_available?).and_return(true)
-
- is_expected.to be_allowed(:access_duo_chat)
+ with_them do
+ before do
+ allow(::Gitlab).to receive(:org_or_com?).and_return(false)
+ stub_ee_application_setting(duo_features_enabled: duo_features_enabled, lock_duo_features_enabled: true)
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(current_user, :access_duo_chat).and_return(enabled_for_user)
end
+
+ it { is_expected.to duo_chat_matcher }
end
end
end
end
- context 'for self-managed', :with_cloud_connector do
- let_it_be_with_reload(:group) { create(:group) }
- let(:policy) { :access_duo_chat }
+ context 'access_duo_features' do
+ let(:project) { private_project }
- before do
- project.add_guest(current_user)
+ where(:current_user, :duo_features_enabled, :cs_matcher) do
+ ref(:guest) | true | be_allowed(:access_duo_features)
+ ref(:guest) | false | be_disallowed(:access_duo_features)
+ nil | true | be_disallowed(:access_duo_features)
+ nil | false | be_disallowed(:access_duo_features)
end
- context 'when not on .org or .com' do
- where(:enabled_for_user, :duo_features_enabled, :duo_chat_matcher) do
- true | false | be_disallowed(policy)
- true | true | be_allowed(policy)
- false | false | be_disallowed(policy)
- false | true | be_disallowed(policy)
+ with_them do
+ before do
+ project.update!(duo_features_enabled: duo_features_enabled)
end
- with_them do
- before do
- allow(::Gitlab).to receive(:org_or_com?).and_return(false)
- stub_ee_application_setting(duo_features_enabled: duo_features_enabled, lock_duo_features_enabled: true)
- allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?).with(current_user, :access_duo_chat).and_return(enabled_for_user)
- end
-
- it { is_expected.to duo_chat_matcher }
+ it do
+ is_expected.to cs_matcher
end
end
end
- end
-
- context 'access_duo_features' do
- let(:project) { private_project }
- where(:current_user, :duo_features_enabled, :cs_matcher) do
- ref(:guest) | true | be_allowed(:access_duo_features)
- ref(:guest) | false | be_disallowed(:access_duo_features)
- nil | true | be_disallowed(:access_duo_features)
- nil | false | be_disallowed(:access_duo_features)
- end
+ describe 'access to project for duo workflow' do
+ let_it_be_with_reload(:project) { public_project }
- with_them do
- before do
- project.update!(duo_features_enabled: duo_features_enabled)
+ where(:current_user, :token_info, :duo_features_enabled, :cs_matcher) do
+ ref(:guest) | nil | true | be_allowed(:read_project)
+ ref(:guest) | { token_scopes: [:ai_workflows] } | true | be_allowed(:read_project)
+ ref(:guest) | { token_scopes: [:ai_workflows] } | false | be_disallowed(:read_project, :admin_project)
+ ref(:guest) | { token_scopes: [:other_scope] } | true | be_allowed(:read_project)
+ ref(:maintainer) | { token_scopes: [:ai_workflows] } | false | be_disallowed(:read_project, :admin_project)
end
- it do
- is_expected.to cs_matcher
- end
- end
- end
-
- describe 'access to project for duo workflow' do
- let_it_be_with_reload(:project) { public_project }
-
- where(:current_user, :token_info, :duo_features_enabled, :cs_matcher) do
- ref(:guest) | nil | true | be_allowed(:read_project)
- ref(:guest) | { token_scopes: [:ai_workflows] } | true | be_allowed(:read_project)
- ref(:guest) | { token_scopes: [:ai_workflows] } | false | be_disallowed(:read_project, :admin_project)
- ref(:guest) | { token_scopes: [:other_scope] } | true | be_allowed(:read_project)
- ref(:maintainer) | { token_scopes: [:ai_workflows] } | false | be_disallowed(:read_project, :admin_project)
- end
+ with_them do
+ before do
+ project.update!(duo_features_enabled: duo_features_enabled)
+ ::Current.token_info = token_info
+ end
- with_them do
- before do
- project.update!(duo_features_enabled: duo_features_enabled)
- ::Current.token_info = token_info
+ it { is_expected.to cs_matcher }
end
-
- it { is_expected.to cs_matcher }
end
- end
- describe 'on_demand_scans_enabled policy' do
- let(:current_user) { owner }
- let(:permissions) { [:read_on_demand_dast_scan, :create_on_demand_dast_scan, :edit_on_demand_dast_scan] }
+ describe 'on_demand_scans_enabled policy' do
+ let(:current_user) { owner }
+ let(:permissions) { [:read_on_demand_dast_scan, :create_on_demand_dast_scan, :edit_on_demand_dast_scan] }
- where(:feature_available, :allowed) do
- false | false
- true | true
- end
+ where(:feature_available, :allowed) do
+ false | false
+ true | true
+ end
- with_them do
- context "when feature is #{params[:feature_available] ? 'available' : 'unavailable'}" do
- before do
- stub_licensed_features(security_on_demand_scans: feature_available)
- end
+ with_them do
+ context "when feature is #{params[:feature_available] ? 'available' : 'unavailable'}" do
+ before do
+ stub_licensed_features(security_on_demand_scans: feature_available)
+ end
- it "on demand scan permissions are #{params[:allowed] ? 'allowed' : 'disallowed'}" do
- if allowed
- expect_allowed(*permissions)
- else
- expect_disallowed(*permissions)
+ it "on demand scan permissions are #{params[:allowed] ? 'allowed' : 'disallowed'}" do
+ if allowed
+ expect_allowed(*permissions)
+ else
+ expect_disallowed(*permissions)
+ end
end
end
end
end
- end
- describe 'read_runner_cloud_provisioning_info policy' do
- let(:current_user) { maintainer }
+ describe 'read_runner_cloud_provisioning_info policy' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_disallowed(:read_runner_cloud_provisioning_info) }
+ it { is_expected.to be_disallowed(:read_runner_cloud_provisioning_info) }
- context 'when SaaS-only feature is available' do
- before do
- stub_saas_features(google_cloud_support: true)
- end
+ context 'when SaaS-only feature is available' do
+ before do
+ stub_saas_features(google_cloud_support: true)
+ end
- context 'the user is a maintainer' do
- let(:current_user) { maintainer }
+ context 'the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:read_runner_cloud_provisioning_info) }
- end
+ it { is_expected.to be_allowed(:read_runner_cloud_provisioning_info) }
+ end
- context 'the user is a guest' do
- let(:current_user) { guest }
+ context 'the user is a guest' do
+ let(:current_user) { guest }
- it { is_expected.to be_disallowed(:read_runner_cloud_provisioning_info) }
+ it { is_expected.to be_disallowed(:read_runner_cloud_provisioning_info) }
+ end
end
end
- end
- describe 'read_runner_gke_provisioning_info policy' do
- let(:current_user) { maintainer }
+ describe 'read_runner_gke_provisioning_info policy' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_disallowed(:read_runner_gke_provisioning_info) }
+ it { is_expected.to be_disallowed(:read_runner_gke_provisioning_info) }
- context 'when SaaS-only feature is available' do
- before do
- stub_saas_features(google_cloud_support: true)
- end
+ context 'when SaaS-only feature is available' do
+ before do
+ stub_saas_features(google_cloud_support: true)
+ end
- context 'the user is a maintainer' do
- let(:current_user) { maintainer }
+ context 'the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:read_runner_gke_provisioning_info) }
- end
+ it { is_expected.to be_allowed(:read_runner_gke_provisioning_info) }
+ end
- context 'the user is a guest' do
- let(:current_user) { guest }
+ context 'the user is a guest' do
+ let(:current_user) { guest }
- it { is_expected.to be_disallowed(:read_runner_gke_provisioning_info) }
+ it { is_expected.to be_disallowed(:read_runner_gke_provisioning_info) }
+ end
end
end
- end
- describe 'provision_cloud_runner policy' do
- let(:current_user) { maintainer }
+ describe 'provision_cloud_runner policy' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_disallowed(:provision_cloud_runner) }
+ it { is_expected.to be_disallowed(:provision_cloud_runner) }
- context 'when SaaS-only feature is available' do
- before do
- stub_saas_features(google_cloud_support: true)
- end
+ context 'when SaaS-only feature is available' do
+ before do
+ stub_saas_features(google_cloud_support: true)
+ end
- context 'the user is a maintainer' do
- let(:current_user) { maintainer }
+ context 'the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:provision_cloud_runner) }
- end
+ it { is_expected.to be_allowed(:provision_cloud_runner) }
+ end
- context 'the user is a guest' do
- let(:current_user) { guest }
+ context 'the user is a guest' do
+ let(:current_user) { guest }
- it { is_expected.to be_disallowed(:provision_cloud_runner) }
+ it { is_expected.to be_disallowed(:provision_cloud_runner) }
+ end
end
end
- end
- describe 'provision_gke_runner policy' do
- let(:current_user) { maintainer }
+ describe 'provision_gke_runner policy' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_disallowed(:provision_gke_runner) }
+ it { is_expected.to be_disallowed(:provision_gke_runner) }
- context 'when SaaS-only feature is available' do
- before do
- stub_saas_features(google_cloud_support: true)
- end
+ context 'when SaaS-only feature is available' do
+ before do
+ stub_saas_features(google_cloud_support: true)
+ end
- context 'the user is a maintainer' do
- let(:current_user) { maintainer }
+ context 'the user is a maintainer' do
+ let(:current_user) { maintainer }
- it { is_expected.to be_allowed(:provision_gke_runner) }
- end
+ it { is_expected.to be_allowed(:provision_gke_runner) }
+ end
- context 'the user is a guest' do
- let(:current_user) { guest }
+ context 'the user is a guest' do
+ let(:current_user) { guest }
- it { is_expected.to be_disallowed(:provision_gke_runner) }
+ it { is_expected.to be_disallowed(:provision_gke_runner) }
+ end
end
end
- end
-
- describe 'read_google_cloud_artifact_registry' do
- where(:saas_feature_enabled, :current_user, :match_expected_result) do
- true | ref(:owner) | be_allowed(:read_google_cloud_artifact_registry)
- true | ref(:reporter) | be_allowed(:read_google_cloud_artifact_registry)
- true | ref(:planner) | be_disallowed(:read_google_cloud_artifact_registry)
- true | ref(:guest) | be_disallowed(:read_google_cloud_artifact_registry)
- true | ref(:non_member) | be_disallowed(:read_google_cloud_artifact_registry)
- false | ref(:owner) | be_disallowed(:read_google_cloud_artifact_registry)
- false | ref(:reporter) | be_disallowed(:read_google_cloud_artifact_registry)
- false | ref(:planner) | be_disallowed(:read_google_cloud_artifact_registry)
- false | ref(:guest) | be_disallowed(:read_google_cloud_artifact_registry)
- false | ref(:non_member) | be_disallowed(:read_google_cloud_artifact_registry)
- end
- with_them do
- before do
- stub_saas_features(google_cloud_support: saas_feature_enabled)
+ describe 'read_google_cloud_artifact_registry' do
+ where(:saas_feature_enabled, :current_user, :match_expected_result) do
+ true | ref(:owner) | be_allowed(:read_google_cloud_artifact_registry)
+ true | ref(:reporter) | be_allowed(:read_google_cloud_artifact_registry)
+ true | ref(:planner) | be_disallowed(:read_google_cloud_artifact_registry)
+ true | ref(:guest) | be_disallowed(:read_google_cloud_artifact_registry)
+ true | ref(:non_member) | be_disallowed(:read_google_cloud_artifact_registry)
+ false | ref(:owner) | be_disallowed(:read_google_cloud_artifact_registry)
+ false | ref(:reporter) | be_disallowed(:read_google_cloud_artifact_registry)
+ false | ref(:planner) | be_disallowed(:read_google_cloud_artifact_registry)
+ false | ref(:guest) | be_disallowed(:read_google_cloud_artifact_registry)
+ false | ref(:non_member) | be_disallowed(:read_google_cloud_artifact_registry)
end
- it { is_expected.to match_expected_result }
- end
- end
+ with_them do
+ before do
+ stub_saas_features(google_cloud_support: saas_feature_enabled)
+ end
- describe 'admin_google_cloud_artifact_registry' do
- where(:saas_feature_enabled, :current_user, :match_expected_result) do
- true | ref(:owner) | be_allowed(:admin_google_cloud_artifact_registry)
- true | ref(:maintainer) | be_allowed(:admin_google_cloud_artifact_registry)
- true | ref(:developer) | be_disallowed(:admin_google_cloud_artifact_registry)
- true | ref(:non_member) | be_disallowed(:admin_google_cloud_artifact_registry)
- false | ref(:owner) | be_disallowed(:admin_google_cloud_artifact_registry)
- false | ref(:maintainer) | be_disallowed(:admin_google_cloud_artifact_registry)
- false | ref(:developer) | be_disallowed(:admin_google_cloud_artifact_registry)
- false | ref(:non_member) | be_disallowed(:admin_google_cloud_artifact_registry)
+ it { is_expected.to match_expected_result }
+ end
end
- with_them do
- before do
- stub_saas_features(google_cloud_support: saas_feature_enabled)
+ describe 'admin_google_cloud_artifact_registry' do
+ where(:saas_feature_enabled, :current_user, :match_expected_result) do
+ true | ref(:owner) | be_allowed(:admin_google_cloud_artifact_registry)
+ true | ref(:maintainer) | be_allowed(:admin_google_cloud_artifact_registry)
+ true | ref(:developer) | be_disallowed(:admin_google_cloud_artifact_registry)
+ true | ref(:non_member) | be_disallowed(:admin_google_cloud_artifact_registry)
+ false | ref(:owner) | be_disallowed(:admin_google_cloud_artifact_registry)
+ false | ref(:maintainer) | be_disallowed(:admin_google_cloud_artifact_registry)
+ false | ref(:developer) | be_disallowed(:admin_google_cloud_artifact_registry)
+ false | ref(:non_member) | be_disallowed(:admin_google_cloud_artifact_registry)
end
- it { is_expected.to match_expected_result }
- end
- end
-
- context 'saved replies permissions' do
- let(:current_user) { owner }
+ with_them do
+ before do
+ stub_saas_features(google_cloud_support: saas_feature_enabled)
+ end
- context 'when no license is present' do
- before do
- stub_licensed_features(project_saved_replies: false)
+ it { is_expected.to match_expected_result }
end
-
- it { is_expected.to be_disallowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
end
- context 'with correct license' do
- before do
- stub_licensed_features(project_saved_replies: true)
+ context 'saved replies permissions' do
+ let(:current_user) { owner }
+
+ context 'when no license is present' do
+ before do
+ stub_licensed_features(project_saved_replies: false)
+ end
+
+ it { is_expected.to be_disallowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
end
- it { is_expected.to be_allowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
+ context 'with correct license' do
+ before do
+ stub_licensed_features(project_saved_replies: true)
+ end
- context 'when the user is a guest' do
- let(:current_user) { guest }
+ it { is_expected.to be_allowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
- it { is_expected.to be_allowed(:read_saved_replies) }
+ context 'when the user is a guest' do
+ let(:current_user) { guest }
- it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
- end
+ it { is_expected.to be_allowed(:read_saved_replies) }
- context 'when the user is a reporter' do
- let(:current_user) { reporter }
+ it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
+ end
- it { is_expected.to be_allowed(:read_saved_replies) }
+ context 'when the user is a reporter' do
+ let(:current_user) { reporter }
- it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
- end
+ it { is_expected.to be_allowed(:read_saved_replies) }
- context 'when the user is a developer' do
- let(:current_user) { developer }
+ it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
+ end
- it { is_expected.to be_allowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
- end
+ context 'when the user is a developer' do
+ let(:current_user) { developer }
- context 'when the user is a guest member of the project' do
- let(:current_user) { guest }
+ it { is_expected.to be_allowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
+ end
- it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
- end
- end
- end
+ context 'when the user is a guest member of the project' do
+ let(:current_user) { guest }
- describe 'enable_pre_receive_secret_detection' do
- where(:current_user, :licensed, :match_expected_result) do
- ref(:owner) | true | be_allowed(:enable_pre_receive_secret_detection)
- ref(:maintainer) | true | be_allowed(:enable_pre_receive_secret_detection)
- ref(:developer) | true | be_disallowed(:enable_pre_receive_secret_detection)
- ref(:owner) | false | be_disallowed(:enable_pre_receive_secret_detection)
- ref(:maintainer) | false | be_disallowed(:enable_pre_receive_secret_detection)
- ref(:developer) | false | be_disallowed(:enable_pre_receive_secret_detection)
+ it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
+ end
+ end
end
- with_them do
- before do
- stub_licensed_features(pre_receive_secret_detection: licensed)
+ describe 'enable_pre_receive_secret_detection' do
+ where(:current_user, :licensed, :match_expected_result) do
+ ref(:owner) | true | be_allowed(:enable_pre_receive_secret_detection)
+ ref(:maintainer) | true | be_allowed(:enable_pre_receive_secret_detection)
+ ref(:developer) | true | be_disallowed(:enable_pre_receive_secret_detection)
+ ref(:owner) | false | be_disallowed(:enable_pre_receive_secret_detection)
+ ref(:maintainer) | false | be_disallowed(:enable_pre_receive_secret_detection)
+ ref(:developer) | false | be_disallowed(:enable_pre_receive_secret_detection)
end
- it { is_expected.to match_expected_result }
- end
-
- describe 'when the project does not have the correct license' do
- let(:current_user) { owner }
+ with_them do
+ before do
+ stub_licensed_features(pre_receive_secret_detection: licensed)
+ end
- it { is_expected.to be_disallowed(:enable_pre_receive_secret_detection) }
- end
- end
+ it { is_expected.to match_expected_result }
+ end
- describe 'duo_workflow' do
- let(:project) { public_project_in_group }
-
- where(:duo_workflow_feature_flag, :stage_check_available, :duo_features_enabled, :current_user, :match_expected_result) do
- true | true | true | ref(:owner) | be_allowed(:duo_workflow)
- true | true | true | ref(:maintainer) | be_allowed(:duo_workflow)
- true | true | true | ref(:developer) | be_allowed(:duo_workflow)
- true | true | true | ref(:planner) | be_disallowed(:duo_workflow)
- true | true | true | ref(:guest) | be_disallowed(:duo_workflow)
- true | true | true | ref(:non_member) | be_disallowed(:duo_workflow)
- true | false | true | ref(:owner) | be_disallowed(:duo_workflow)
- true | false | true | ref(:maintainer) | be_disallowed(:duo_workflow)
- true | false | true | ref(:developer) | be_disallowed(:duo_workflow)
- true | false | true | ref(:planner) | be_disallowed(:duo_workflow)
- true | false | true | ref(:guest) | be_disallowed(:duo_workflow)
- true | false | true | ref(:non_member) | be_disallowed(:duo_workflow)
- false | true | true | ref(:owner) | be_disallowed(:duo_workflow)
- false | true | true | ref(:maintainer) | be_disallowed(:duo_workflow)
- false | true | true | ref(:developer) | be_disallowed(:duo_workflow)
- false | true | true | ref(:planner) | be_disallowed(:duo_workflow)
- false | true | true | ref(:guest) | be_disallowed(:duo_workflow)
- false | true | true | ref(:non_member) | be_disallowed(:duo_workflow)
- false | false | true | ref(:owner) | be_disallowed(:duo_workflow)
- false | false | true | ref(:maintainer) | be_disallowed(:duo_workflow)
- false | false | true | ref(:developer) | be_disallowed(:duo_workflow)
- false | false | true | ref(:planner) | be_disallowed(:duo_workflow)
- false | false | true | ref(:guest) | be_disallowed(:duo_workflow)
- false | false | true | ref(:non_member) | be_disallowed(:duo_workflow)
- false | false | false | ref(:owner) | be_disallowed(:duo_workflow)
- false | false | false | ref(:maintainer) | be_disallowed(:duo_workflow)
- false | false | false | ref(:developer) | be_disallowed(:duo_workflow)
- false | false | false | ref(:planner) | be_disallowed(:duo_workflow)
- false | false | false | ref(:guest) | be_disallowed(:duo_workflow)
- false | false | false | ref(:non_member) | be_disallowed(:duo_workflow)
- end
+ describe 'when the project does not have the correct license' do
+ let(:current_user) { owner }
- with_them do
- before do
- stub_feature_flags(duo_workflow: duo_workflow_feature_flag)
- allow(::Gitlab::Llm::StageCheck).to receive(:available?).with(project, :duo_workflow).and_return(stage_check_available)
- stub_ee_application_setting(duo_features_enabled: duo_features_enabled)
+ it { is_expected.to be_disallowed(:enable_pre_receive_secret_detection) }
end
-
- it { is_expected.to match_expected_result }
end
- end
- describe 'enable_container_scanning_for_registry' do
- where(:current_user, :match_expected_result) do
- ref(:owner) | be_allowed(:enable_container_scanning_for_registry)
- ref(:maintainer) | be_allowed(:enable_container_scanning_for_registry)
- ref(:developer) | be_disallowed(:enable_container_scanning_for_registry)
- ref(:non_member) | be_disallowed(:enable_container_scanning_for_registry)
- end
+ describe 'duo_workflow' do
+ let(:project) { public_project_in_group }
- with_them do
- before do
- stub_licensed_features(container_scanning_for_registry: true)
+ where(:duo_workflow_feature_flag, :stage_check_available, :duo_features_enabled, :current_user, :match_expected_result) do
+ true | true | true | ref(:owner) | be_allowed(:duo_workflow)
+ true | true | true | ref(:maintainer) | be_allowed(:duo_workflow)
+ true | true | true | ref(:developer) | be_allowed(:duo_workflow)
+ true | true | true | ref(:planner) | be_disallowed(:duo_workflow)
+ true | true | true | ref(:guest) | be_disallowed(:duo_workflow)
+ true | true | true | ref(:non_member) | be_disallowed(:duo_workflow)
+ true | false | true | ref(:owner) | be_disallowed(:duo_workflow)
+ true | false | true | ref(:maintainer) | be_disallowed(:duo_workflow)
+ true | false | true | ref(:developer) | be_disallowed(:duo_workflow)
+ true | false | true | ref(:planner) | be_disallowed(:duo_workflow)
+ true | false | true | ref(:guest) | be_disallowed(:duo_workflow)
+ true | false | true | ref(:non_member) | be_disallowed(:duo_workflow)
+ false | true | true | ref(:owner) | be_disallowed(:duo_workflow)
+ false | true | true | ref(:maintainer) | be_disallowed(:duo_workflow)
+ false | true | true | ref(:developer) | be_disallowed(:duo_workflow)
+ false | true | true | ref(:planner) | be_disallowed(:duo_workflow)
+ false | true | true | ref(:guest) | be_disallowed(:duo_workflow)
+ false | true | true | ref(:non_member) | be_disallowed(:duo_workflow)
+ false | false | true | ref(:owner) | be_disallowed(:duo_workflow)
+ false | false | true | ref(:maintainer) | be_disallowed(:duo_workflow)
+ false | false | true | ref(:developer) | be_disallowed(:duo_workflow)
+ false | false | true | ref(:planner) | be_disallowed(:duo_workflow)
+ false | false | true | ref(:guest) | be_disallowed(:duo_workflow)
+ false | false | true | ref(:non_member) | be_disallowed(:duo_workflow)
+ false | false | false | ref(:owner) | be_disallowed(:duo_workflow)
+ false | false | false | ref(:maintainer) | be_disallowed(:duo_workflow)
+ false | false | false | ref(:developer) | be_disallowed(:duo_workflow)
+ false | false | false | ref(:planner) | be_disallowed(:duo_workflow)
+ false | false | false | ref(:guest) | be_disallowed(:duo_workflow)
+ false | false | false | ref(:non_member) | be_disallowed(:duo_workflow)
end
- it { is_expected.to match_expected_result }
+ with_them do
+ before do
+ stub_feature_flags(duo_workflow: duo_workflow_feature_flag)
+ allow(::Gitlab::Llm::StageCheck).to receive(:available?).with(project, :duo_workflow).and_return(stage_check_available)
+ stub_ee_application_setting(duo_features_enabled: duo_features_enabled)
+ end
+
+ it { is_expected.to match_expected_result }
+ end
end
- context 'when license feature is not available' do
+ describe 'enable_container_scanning_for_registry' do
where(:current_user, :match_expected_result) do
- ref(:owner) | be_disallowed(:enable_container_scanning_for_registry)
- ref(:maintainer) | be_disallowed(:enable_container_scanning_for_registry)
+ ref(:owner) | be_allowed(:enable_container_scanning_for_registry)
+ ref(:maintainer) | be_allowed(:enable_container_scanning_for_registry)
ref(:developer) | be_disallowed(:enable_container_scanning_for_registry)
ref(:non_member) | be_disallowed(:enable_container_scanning_for_registry)
end
with_them do
before do
- stub_licensed_features(container_scanning_for_registry: false)
+ stub_licensed_features(container_scanning_for_registry: true)
end
it { is_expected.to match_expected_result }
end
- end
- end
- describe 'read_pre_receive_secret_detection_info' do
- where(:current_user, :match_expected_result) do
- ref(:owner) | be_allowed(:read_pre_receive_secret_detection_info)
- ref(:maintainer) | be_allowed(:read_pre_receive_secret_detection_info)
- ref(:developer) | be_allowed(:read_pre_receive_secret_detection_info)
- ref(:planner) | be_disallowed(:read_pre_receive_secret_detection_info)
- ref(:guest) | be_disallowed(:read_pre_receive_secret_detection_info)
- ref(:non_member) | be_disallowed(:read_pre_receive_secret_detection_info)
+ context 'when license feature is not available' do
+ where(:current_user, :match_expected_result) do
+ ref(:owner) | be_disallowed(:enable_container_scanning_for_registry)
+ ref(:maintainer) | be_disallowed(:enable_container_scanning_for_registry)
+ ref(:developer) | be_disallowed(:enable_container_scanning_for_registry)
+ ref(:non_member) | be_disallowed(:enable_container_scanning_for_registry)
+ end
+
+ with_them do
+ before do
+ stub_licensed_features(container_scanning_for_registry: false)
+ end
+
+ it { is_expected.to match_expected_result }
+ end
+ end
end
- with_them do
- before do
- stub_licensed_features(pre_receive_secret_detection: true)
+ describe 'read_pre_receive_secret_detection_info' do
+ where(:current_user, :match_expected_result) do
+ ref(:owner) | be_allowed(:read_pre_receive_secret_detection_info)
+ ref(:maintainer) | be_allowed(:read_pre_receive_secret_detection_info)
+ ref(:developer) | be_allowed(:read_pre_receive_secret_detection_info)
+ ref(:planner) | be_disallowed(:read_pre_receive_secret_detection_info)
+ ref(:guest) | be_disallowed(:read_pre_receive_secret_detection_info)
+ ref(:non_member) | be_disallowed(:read_pre_receive_secret_detection_info)
end
- it { is_expected.to match_expected_result }
- end
- end
+ with_them do
+ before do
+ stub_licensed_features(pre_receive_secret_detection: true)
+ end
- describe 'admin_project_secrets_manager' do
- where(:current_user, :match_expected_result) do
- ref(:owner) | be_allowed(:admin_project_secrets_manager)
- ref(:maintainer) | be_disallowed(:admin_project_secrets_manager)
- ref(:developer) | be_disallowed(:admin_project_secrets_manager)
- ref(:non_member) | be_disallowed(:admin_project_secrets_manager)
+ it { is_expected.to match_expected_result }
+ end
end
- with_them do
- it { is_expected.to match_expected_result }
- end
- end
+ describe 'admin_project_secrets_manager' do
+ where(:current_user, :match_expected_result) do
+ ref(:owner) | be_allowed(:admin_project_secrets_manager)
+ ref(:maintainer) | be_disallowed(:admin_project_secrets_manager)
+ ref(:developer) | be_disallowed(:admin_project_secrets_manager)
+ ref(:non_member) | be_disallowed(:admin_project_secrets_manager)
+ end
- describe 'manage_project_security_exclusions' do
- let(:policy) { :manage_project_security_exclusions }
-
- where(:role, :allowed) do
- :guest | false
- :planner | false
- :reporter | false
- :developer | false
- :maintainer | true
- :auditor | false
- :owner | true
- :admin | true
+ with_them do
+ it { is_expected.to match_expected_result }
+ end
end
- with_them do
- let(:current_user) { public_send(role) }
+ describe 'manage_project_security_exclusions' do
+ let(:policy) { :manage_project_security_exclusions }
- before do
- enable_admin_mode!(current_user) if role == :admin
+ where(:role, :allowed) do
+ :guest | false
+ :planner | false
+ :reporter | false
+ :developer | false
+ :maintainer | true
+ :auditor | false
+ :owner | true
+ :admin | true
end
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
- end
- end
+ with_them do
+ let(:current_user) { public_send(role) }
+
+ before do
+ enable_admin_mode!(current_user) if role == :admin
+ end
- describe 'read_project_security_exclusions' do
- let(:policy) { :read_project_security_exclusions }
-
- where(:role, :allowed) do
- :guest | false
- :planner | false
- :reporter | false
- :developer | true
- :maintainer | true
- :auditor | true
- :owner | true
- :admin | true
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ end
end
- with_them do
- let(:current_user) { public_send(role) }
+ describe 'read_project_security_exclusions' do
+ let(:policy) { :read_project_security_exclusions }
- before do
- enable_admin_mode!(current_user) if role == :admin
+ where(:role, :allowed) do
+ :guest | false
+ :planner | false
+ :reporter | false
+ :developer | true
+ :maintainer | true
+ :auditor | true
+ :owner | true
+ :admin | true
end
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
- end
- end
+ with_them do
+ let(:current_user) { public_send(role) }
- describe 'manage_security_settings' do
- let(:policy) { :manage_security_settings }
-
- where(:role, :allowed) do
- :guest | false
- :reporter | false
- :developer | false
- :maintainer | true
- :auditor | false
- :owner | true
- :admin | true
+ before do
+ enable_admin_mode!(current_user) if role == :admin
+ end
+
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
+ end
end
- with_them do
- let(:current_user) { public_send(role) }
+ describe 'manage_security_settings' do
+ let(:policy) { :manage_security_settings }
- before do
- enable_admin_mode!(current_user) if role == :admin
+ where(:role, :allowed) do
+ :guest | false
+ :reporter | false
+ :developer | false
+ :maintainer | true
+ :auditor | false
+ :owner | true
+ :admin | true
end
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
- end
- end
-
- describe 'read_security_settings' do
- let(:policy) { :read_security_settings }
-
- where(:role, :allowed) do
- :guest | false
- :reporter | false
- :developer | true
- :maintainer | true
- :auditor | true
- :owner | true
- :admin | true
- end
+ with_them do
+ let(:current_user) { public_send(role) }
- with_them do
- let(:current_user) { public_send(role) }
+ before do
+ enable_admin_mode!(current_user) if role == :admin
+ end
- before do
- enable_admin_mode!(current_user) if role == :admin
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
-
- it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
- end
- describe 'access_security_scans_api' do
- context 'when feature is disabled' do
- let(:current_user) { owner }
+ describe 'read_security_settings' do
+ let(:policy) { :read_security_settings }
- before do
- stub_licensed_features(security_scans_api: false)
+ where(:role, :allowed) do
+ :guest | false
+ :reporter | false
+ :developer | true
+ :maintainer | true
+ :auditor | true
+ :owner | true
+ :admin | true
end
- it 'is not allowed' do
- is_expected.to be_disallowed(:access_security_scans_api)
+ with_them do
+ let(:current_user) { public_send(role) }
+
+ before do
+ enable_admin_mode!(current_user) if role == :admin
+ end
+
+ it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
- context 'when feature is enabled' do
- where(:free_access, :current_user, :allowed) do
- true | ref(:owner) | true
- true | ref(:maintainer) | true
- true | ref(:developer) | true
- true | ref(:guest) | false
- true | ref(:planner) | false
- true | ref(:reporter) | false
- true | ref(:non_member) | false
- false | ref(:owner) | false
- false | ref(:maintainer) | false
- false | ref(:developer) | false
- false | ref(:guest) | false
- false | ref(:planner) | false
- false | ref(:reporter) | false
- false | ref(:non_member) | false
- end
+ describe 'access_security_scans_api' do
+ context 'when feature is disabled' do
+ let(:current_user) { owner }
- with_them do
before do
- stub_licensed_features(security_scans_api: true)
- allow(CloudConnector::AvailableServices).to receive(:find_by_name).with(:sast).and_return(
- instance_double(CloudConnector::BaseAvailableServiceData, free_access?: free_access))
+ stub_licensed_features(security_scans_api: false)
end
- it { is_expected.to(allowed ? be_allowed(:access_security_scans_api) : be_disallowed(:access_security_scans_api)) }
+ it 'is not allowed' do
+ is_expected.to be_disallowed(:access_security_scans_api)
+ end
end
- end
- end
- describe 'access_ai_review_mr' do
- let(:current_user) { owner }
+ context 'when feature is enabled' do
+ where(:free_access, :current_user, :allowed) do
+ true | ref(:owner) | true
+ true | ref(:maintainer) | true
+ true | ref(:developer) | true
+ true | ref(:guest) | false
+ true | ref(:planner) | false
+ true | ref(:reporter) | false
+ true | ref(:non_member) | false
+ false | ref(:owner) | false
+ false | ref(:maintainer) | false
+ false | ref(:developer) | false
+ false | ref(:guest) | false
+ false | ref(:planner) | false
+ false | ref(:reporter) | false
+ false | ref(:non_member) | false
+ end
- where(:duo_features_enabled, :allowed_to_use, :enabled_for_user) do
- true | false | be_disallowed(:access_ai_review_mr)
- false | true | be_disallowed(:access_ai_review_mr)
- true | true | be_allowed(:access_ai_review_mr)
+ with_them do
+ before do
+ stub_licensed_features(security_scans_api: true)
+ allow(CloudConnector::AvailableServices).to receive(:find_by_name).with(:sast).and_return(
+ instance_double(CloudConnector::BaseAvailableServiceData, free_access?: free_access))
+ end
+
+ it { is_expected.to(allowed ? be_allowed(:access_security_scans_api) : be_disallowed(:access_security_scans_api)) }
+ end
+ end
end
- with_them do
- before do
- allow(project).to receive(:duo_features_enabled).and_return(duo_features_enabled)
+ describe 'access_ai_review_mr' do
+ let(:current_user) { owner }
- allow(current_user).to receive(:allowed_to_use?)
- .with(:review_merge_request, licensed_feature: :ai_review_mr).and_return(allowed_to_use)
+ where(:duo_features_enabled, :allowed_to_use, :enabled_for_user) do
+ true | false | be_disallowed(:access_ai_review_mr)
+ false | true | be_disallowed(:access_ai_review_mr)
+ true | true | be_allowed(:access_ai_review_mr)
end
- it { is_expected.to enabled_for_user }
+ with_them do
+ before do
+ allow(project).to receive(:duo_features_enabled).and_return(duo_features_enabled)
+
+ allow(current_user).to receive(:allowed_to_use?)
+ .with(:review_merge_request, licensed_feature: :ai_review_mr).and_return(allowed_to_use)
+ end
+
+ it { is_expected.to enabled_for_user }
+ end
end
end
end
diff --git a/ee/spec/requests/custom_roles/admin_security_testing/request_spec.rb b/ee/spec/requests/custom_roles/admin_security_testing/request_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9172925f6f8e1da477570912b5c17970d9c6ec88
--- /dev/null
+++ b/ee/spec/requests/custom_roles/admin_security_testing/request_spec.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User with admin_security_testing custom role', feature_category: :security_policy_management do
+ let_it_be(:project) { create(:project, :in_group) }
+ let_it_be(:group) { project.group }
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:role) { create(:member_role, :guest, :admin_security_testing, namespace: group) }
+ let_it_be(:membership) { create(:group_member, :guest, member_role: role, user: user, source: group) }
+
+ before do
+ stub_licensed_features(
+ custom_roles: true,
+ security_dashboard: true,
+ security_on_demand_scans: true,
+ security_scans_api: true,
+ pre_receive_secret_detection: true,
+ container_scanning_for_registry: true,
+ coverage_fuzzing: true)
+ stub_feature_flags(custom_ability_admin_security_testing: true)
+ sign_in(user)
+ end
+
+ describe "Controllers endpoints" do
+ describe Projects::Security::ApiFuzzingConfigurationController do
+ it 'can access the show endpoint' do
+ get project_security_configuration_api_fuzzing_path(project)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe Projects::Security::DastConfigurationController do
+ it 'can access the show endpoint' do
+ get project_security_configuration_dast_path(project)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe Projects::Security::SecretDetectionConfigurationController do
+ it 'can access the show endpoint' do
+ get project_security_configuration_secret_detection_path(project)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe Projects::Security::SastConfigurationController do
+ it 'can access the show endpoint' do
+ get project_security_configuration_sast_path(project)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe Projects::Security::CorpusManagementController do
+ it 'can access the show endpoint' do
+ get project_security_configuration_corpus_management_path(project)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe Projects::Security::DastProfilesController do
+ it 'can access the show endpoint' do
+ get project_security_configuration_profile_library_path(project)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe Projects::Security::DastScannerProfilesController do
+ it 'can access the show endpoint' do
+ get new_project_security_configuration_profile_library_dast_scanner_profile_path(project)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe Projects::Security::DastSiteProfilesController do
+ it 'can access the show endpoint' do
+ get new_project_security_configuration_profile_library_dast_site_profile_path(project)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ describe 'GraphQL mutations' do
+ include GraphqlHelpers
+
+ let(:full_path) { project.full_path }
+
+ let(:fields) do
+ <<~FIELDS
+ errors
+ FIELDS
+ end
+
+ let(:mutation_name) { nil }
+ let(:mutation) { graphql_mutation(mutation_name, input, fields) }
+
+ subject(:execute_mutation) { post_graphql_mutation(mutation, current_user: user) }
+
+ describe Mutations::Security::CiConfiguration::ConfigureContainerScanning do
+ let(:mutation_name) { :configureContainerScanning }
+ let(:input) { { project_path: full_path } }
+
+ it 'has access via a custom role' do
+ execute_mutation
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
+ describe Mutations::Security::CiConfiguration::ConfigureDependencyScanning do
+ let(:mutation_name) { :configureDependencyScanning }
+ let(:input) { { project_path: full_path } }
+
+ it 'has access via a custom role' do
+ execute_mutation
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
+ describe Mutations::Security::CiConfiguration::SetContainerScanningForRegistry do
+ let(:mutation_name) { :setContainerScanningForRegistry }
+ let(:input) { { namespace_path: full_path, enable: true } }
+
+ it 'has access via a custom role' do
+ execute_mutation
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
+ describe Mutations::Security::CiConfiguration::SetGroupSecretPushProtection do
+ let(:mutation_name) { :setGroupSecretPushProtection }
+ let(:input) { { namespace_path: group.full_path, secret_push_protection_enabled: true } }
+
+ it 'has access via a custom role' do
+ execute_mutation
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
+ describe Mutations::Security::CiConfiguration::SetPreReceiveSecretDetection do
+ let(:mutation_name) { :setPreReceiveSecretDetection }
+ let(:input) { { namespace_path: full_path, enable: true } }
+
+ it 'has access via a custom role' do
+ execute_mutation
+ expect_graphql_errors_to_be_empty
+ end
+ end
+ end
+end