diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index 5699925ae9df2e0892392fa9d2a58ad785884713..e6de6d4df5b675a2396f56ea39c78619a590c060 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -54,6 +54,7 @@ def update_auto_devops
def authorize_show_cicd_settings!
return if can_any?(current_user, [
:admin_cicd_variables,
+ :admin_protected_environments,
:admin_runner
], group)
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 21deac8b74cb8b73f5135ab44a550adbe568b0a3..3b3b5cb78a6a33159780542be8e87877a3e18314 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -113,6 +113,7 @@ def authorize_reset_cache!
def authorize_show_cicd_settings!
return if can_any?(current_user, [
:admin_cicd_variables,
+ :admin_protected_environments,
:admin_runner
], project)
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 295cc0eb5e8b4b99ec3aee4d95205a49da15c6ca..b3e5f65e303e3ce2f85bebebc4543e2359fccd9c 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -283,6 +283,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :admin_package
enable :admin_runner
enable :admin_integrations
+ enable :admin_protected_environments
enable :change_visibility_level
enable :read_usage_quotas
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 5fb962692920234d06e7743508647da3fb66673f..3c782fd7cce5d4c8759a8810462c0997e172668d 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -665,6 +665,7 @@ class ProjectPolicy < BasePolicy
enable :change_restrict_user_defined_variables
enable :create_protected_branch
enable :admin_protected_branch
+ enable :admin_protected_environments
end
rule { can?(:manage_protected_tags) }.policy do
diff --git a/app/validators/json_schemas/member_role_permissions.json b/app/validators/json_schemas/member_role_permissions.json
index b365412c333be15f3e160b02ab71636cc8d8782a..dc5a9f81b96ca17cb2d7ce52f1da17b62e6e4c8b 100644
--- a/app/validators/json_schemas/member_role_permissions.json
+++ b/app/validators/json_schemas/member_role_permissions.json
@@ -22,6 +22,9 @@
"admin_protected_branch": {
"type": "boolean"
},
+ "admin_protected_environments": {
+ "type": "boolean"
+ },
"admin_push_rules": {
"type": "boolean"
},
diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml
index a066eee31f6d2abb5bc983de526d4068adfa8ef5..2ffb61291a6e1939e270b8f7b175bfa5c4069bf1 100644
--- a/app/views/groups/settings/ci_cd/show.html.haml
+++ b/app/views/groups/settings/ci_cd/show.html.haml
@@ -50,4 +50,5 @@
- c.with_body do
= render 'groups/settings/ci_cd/auto_devops_form', group: @group
+- if can?(current_user, :admin_protected_environments, @group)
= render_if_exists 'groups/settings/ci_cd/protected_environments', expanded: expanded
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index f36d4b5bf443ce78f25711c1238b0a887cbf87a1..1583f2ff3b120ca90f90081c50a244c753aa7688 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -29,6 +29,7 @@
- c.with_body do
= render 'autodevops_form', auto_devops_enabled: @project.auto_devops_enabled?
+- if can?(current_user, :admin_protected_environments, @project)
= render_if_exists 'projects/settings/ci_cd/protected_environments', expanded: expanded
- if can?(current_user, :admin_runner, @project)
diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md
index a56622dc9936c20d17dde13a2fe2ee4f380f3373..025ef77dbf0e466c3feb14d0fa32a1692733685f 100644
--- a/doc/api/graphql/reference/_index.md
+++ b/doc/api/graphql/reference/_index.md
@@ -41214,6 +41214,7 @@ Member role permission.
| `ADMIN_INTEGRATIONS` | Create, read, update, and delete integrations with external applications. |
| `ADMIN_MERGE_REQUEST` | Allows approval of merge requests. |
| `ADMIN_PROTECTED_BRANCH` | Create, read, update, and delete protected branches for a project. |
+| `ADMIN_PROTECTED_ENVIRONMENTS` | Create, read, update, and delete environments. |
| `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. |
@@ -41253,6 +41254,7 @@ Member role standard permission.
| `ADMIN_INTEGRATIONS` | Create, read, update, and delete integrations with external applications. |
| `ADMIN_MERGE_REQUEST` | Allows approval of merge requests. |
| `ADMIN_PROTECTED_BRANCH` | Create, read, update, and delete protected branches for a project. |
+| `ADMIN_PROTECTED_ENVIRONMENTS` | Create, read, update, and delete environments. |
| `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. |
diff --git a/ee/app/controllers/groups/protected_environments_controller.rb b/ee/app/controllers/groups/protected_environments_controller.rb
index 793999d888a83d262b28c528b4bb8d6c6da89aaa..7a2c4d7c613bc84b225c5b890ea20f18b2d157eb 100644
--- a/ee/app/controllers/groups/protected_environments_controller.rb
+++ b/ee/app/controllers/groups/protected_environments_controller.rb
@@ -2,7 +2,7 @@
module Groups
class ProtectedEnvironmentsController < Groups::ApplicationController
- before_action :authorize_admin_protected_environment!
+ before_action :authorize_admin_protected_environments!
before_action :protected_environment, except: [:create]
feature_category :continuous_delivery
@@ -62,8 +62,8 @@ def deploy_access_level_attributes
%i[id _destroy group_id]
end
- def authorize_admin_protected_environment!
- not_found unless can?(current_user, :admin_protected_environment, group)
+ def authorize_admin_protected_environments!
+ not_found unless can?(current_user, :admin_protected_environments, group)
end
def serialized_protected_environment
diff --git a/ee/app/controllers/projects/protected_environments_controller.rb b/ee/app/controllers/projects/protected_environments_controller.rb
index 5044edd869f443906b13239d640d565d9d8d29c8..9279a2cc999265f4ee8db182d731b7d74fda0df4 100644
--- a/ee/app/controllers/projects/protected_environments_controller.rb
+++ b/ee/app/controllers/projects/protected_environments_controller.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
class Projects::ProtectedEnvironmentsController < Projects::ApplicationController
- before_action :authorize_admin_project!
+ before_action :authorize_admin_protected_environments!
before_action :protected_environment, except: [:create, :search]
feature_category :continuous_delivery
diff --git a/ee/app/policies/ee/group_policy.rb b/ee/app/policies/ee/group_policy.rb
index d88d7393feb2de68a8dda646f5465cc8df48d578..3f05693d461ce8246c4c2de0350c30d53fa664ef 100644
--- a/ee/app/policies/ee/group_policy.rb
+++ b/ee/app/policies/ee/group_policy.rb
@@ -348,7 +348,7 @@ module GroupPolicy
end
rule { owner }.policy do
- enable :admin_protected_environment
+ enable :admin_protected_environments
enable :admin_licensed_seat
end
@@ -630,6 +630,10 @@ module GroupPolicy
enable :admin_cicd_variables
end
+ rule { custom_role_enables_admin_protected_environments }.policy do
+ enable :admin_protected_environments
+ end
+
rule { custom_role_enables_admin_compliance_framework }.policy do
enable :admin_compliance_framework
enable :admin_compliance_pipeline_configuration
diff --git a/ee/app/policies/ee/project_policy.rb b/ee/app/policies/ee/project_policy.rb
index 670c33a46f48da8bc46b77de28ba5cf6b1c05a6e..60236bce79f89845f5344ff3bb293c30661c091e 100644
--- a/ee/app/policies/ee/project_policy.rb
+++ b/ee/app/policies/ee/project_policy.rb
@@ -298,6 +298,10 @@ module ProjectPolicy
enable :admin_cicd_variables
end
+ rule { custom_role_enables_admin_protected_environments }.policy do
+ enable :admin_protected_environments
+ end
+
rule { custom_role_enables_admin_push_rules }.policy do
enable :admin_push_rules
end
diff --git a/ee/config/custom_abilities/admin_protected_environments.yml b/ee/config/custom_abilities/admin_protected_environments.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7f45448bde3211759e6289470809b6a9b22e6f91
--- /dev/null
+++ b/ee/config/custom_abilities/admin_protected_environments.yml
@@ -0,0 +1,12 @@
+---
+title: Manage Protected Environments
+name: admin_protected_environments
+description: Create, read, update, and delete environments
+introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/471385
+introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178283
+feature_category: continuous_delivery
+milestone: '17.9'
+group_ability: true
+enabled_for_group_access_levels: [50]
+project_ability: true
+enabled_for_project_access_levels: [40, 50]
diff --git a/ee/config/feature_flags/wip/custom_ability_admin_protected_environments.yml b/ee/config/feature_flags/wip/custom_ability_admin_protected_environments.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7b8b9bbb25aaa07e0be8009d901541e9cd71886d
--- /dev/null
+++ b/ee/config/feature_flags/wip/custom_ability_admin_protected_environments.yml
@@ -0,0 +1,9 @@
+---
+name: custom_ability_admin_protected_environments
+feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/471385
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178283
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/514698
+milestone: '17.9'
+group: group::authorization
+type: wip
+default_enabled: false
diff --git a/ee/lib/api/protected_environments.rb b/ee/lib/api/protected_environments.rb
index c82f52cf90f13353d5b2c4b54a90b368b0332a93..ebfa03caf214aea28d828f36c000602b8ca1ebf9 100644
--- a/ee/lib/api/protected_environments.rb
+++ b/ee/lib/api/protected_environments.rb
@@ -97,7 +97,7 @@ def protected_environment
end
end
- before { authorize_admin_project }
+ before { authorize! :admin_protected_environments, user_project }
desc 'List protected environments' do
detail 'Gets a list of protected environments from a project. This feature was introduced in GitLab 12.8.'
@@ -232,7 +232,7 @@ def protected_environment
end
before do
- authorize! :admin_protected_environment, user_group
+ authorize! :admin_protected_environments, user_group
end
desc 'List group-level protected environments' do
diff --git a/ee/lib/ee/sidebars/groups/menus/settings_menu.rb b/ee/lib/ee/sidebars/groups/menus/settings_menu.rb
index 26bd314b11521b14cefbeab2de0040655c53b639..e3fcb17bd599c9a35a9a90232e59ac22ba46011f 100644
--- a/ee/lib/ee/sidebars/groups/menus/settings_menu.rb
+++ b/ee/lib/ee/sidebars/groups/menus/settings_menu.rb
@@ -34,7 +34,8 @@ def configure_menu_items
add_menu_item_for_abilities(integrations_menu_item, :admin_integrations)
add_menu_item_for_abilities(access_tokens_menu_item, :read_resource_access_tokens)
add_menu_item_for_abilities(repository_menu_item, [:admin_push_rules, :manage_deploy_tokens])
- add_menu_item_for_abilities(ci_cd_menu_item, [:admin_cicd_variables, :admin_runner])
+ add_menu_item_for_abilities(ci_cd_menu_item, [:admin_cicd_variables,
+ :admin_protected_environments, :admin_runner])
add_menu_item_for_abilities(billing_menu_item, :read_billing)
add_menu_item_for_abilities(workspaces_menu_item, :read_remote_development_cluster_agent_mapping)
end
diff --git a/ee/lib/ee/sidebars/projects/menus/settings_menu.rb b/ee/lib/ee/sidebars/projects/menus/settings_menu.rb
index 41d2fc1e8ba2e24aacd7a5888397e7fc357a121b..185829ab19f9589d099ecfa905af0cded4efc623 100644
--- a/ee/lib/ee/sidebars/projects/menus/settings_menu.rb
+++ b/ee/lib/ee/sidebars/projects/menus/settings_menu.rb
@@ -25,6 +25,7 @@ module SettingsMenu
],
ci_cd_menu_item: [
:admin_cicd_variables,
+ :admin_protected_environments,
:admin_runner
],
integrations_menu_item: [
diff --git a/ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb b/ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb
index af775c736b4558c544b1a84bfa75a26e807aed55..304f26b0c66dfd46a1260146e90ff7031134bb6f 100644
--- a/ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb
+++ b/ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb
@@ -439,6 +439,7 @@
:admin_cicd_variables | 'CI/CD'
:admin_compliance_framework | 'General'
:admin_push_rules | 'Repository'
+ :admin_protected_environments | 'CI/CD'
:admin_runners | 'CI/CD'
:manage_deploy_tokens | 'Repository'
:manage_group_access_tokens | 'Access tokens'
diff --git a/ee/spec/lib/ee/sidebars/projects/menus/settings_menu_spec.rb b/ee/spec/lib/ee/sidebars/projects/menus/settings_menu_spec.rb
index ab09edf5cfe36fea7e7a538b7cba274149d148f3..251ecc4b6a97f4634628f71e3c47993648b8e7c6 100644
--- a/ee/spec/lib/ee/sidebars/projects/menus/settings_menu_spec.rb
+++ b/ee/spec/lib/ee/sidebars/projects/menus/settings_menu_spec.rb
@@ -90,6 +90,7 @@
:admin_push_rules | 'Repository'
:manage_protected_tags | 'Repository'
:admin_protected_branch | 'Repository'
+ :admin_protected_environments | 'CI/CD'
:admin_runners | 'CI/CD'
:manage_deploy_tokens | 'Repository'
:manage_merge_request_settings | 'Merge requests'
diff --git a/ee/spec/policies/group_policy_spec.rb b/ee/spec/policies/group_policy_spec.rb
index ce737440e5976d3fc94ec07037e1c75e45dfc91c..7f217113f928b1200b925dec0c193c5a568024da 100644
--- a/ee/spec/policies/group_policy_spec.rb
+++ b/ee/spec/policies/group_policy_spec.rb
@@ -3888,6 +3888,13 @@ def create_member_role(member, abilities = member_role_abilities)
it_behaves_like 'custom roles abilities'
end
+ context 'for a custom role with the `admin_protected_environments` ability' do
+ let(:member_role_abilities) { { admin_protected_environments: true } }
+ let(:allowed_abilities) { [:admin_protected_environments] }
+
+ it_behaves_like 'custom roles abilities'
+ end
+
context 'for a member role with admin_compliance_framework true' do
let(:member_role_abilities) { { read_compliance_dashboard: true, admin_compliance_framework: true } }
diff --git a/ee/spec/policies/project_policy_spec.rb b/ee/spec/policies/project_policy_spec.rb
index 067bbfa4ef277afe3cb0d14366502ea4124cc7e8..d4e494a6cbd4cd3fbf31969eed7b439226474d5e 100644
--- a/ee/spec/policies/project_policy_spec.rb
+++ b/ee/spec/policies/project_policy_spec.rb
@@ -2966,6 +2966,13 @@ def create_member_role(member, abilities = member_role_abilities)
it_behaves_like 'custom roles abilities'
end
+ context 'for a custom role with the `admin_protected_environments` ability' do
+ let(:member_role_abilities) { { admin_protected_environments: true } }
+ let(:allowed_abilities) { [:admin_protected_environments] }
+
+ it_behaves_like 'custom roles abilities'
+ end
+
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] }
diff --git a/ee/spec/requests/custom_roles/admin_protected_environments/groups_request_spec.rb b/ee/spec/requests/custom_roles/admin_protected_environments/groups_request_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a786b2629f1fb5481ed3455bb144f27f46114b76
--- /dev/null
+++ b/ee/spec/requests/custom_roles/admin_protected_environments/groups_request_spec.rb
@@ -0,0 +1,200 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User with admin_protected_environments custom role', feature_category: :continuous_delivery do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:protected_environment) { create(:protected_environment, :production, :group_level, group: group) }
+
+ shared_context 'with protected environment setting' do |enabled|
+ let_it_be(:role) { create(:member_role, :guest, namespace: group, admin_protected_environments: enabled) }
+ let_it_be(:group_member) { create(:group_member, :guest, member_role: role, user: user, group: group) }
+ end
+
+ before do
+ stub_licensed_features(custom_roles: true)
+ sign_in(user)
+ end
+
+ describe Groups::Settings::CiCdController do
+ let(:settings_page_path) { group_settings_ci_cd_path(group) }
+ let(:update_params) { { group: { max_artifacts_size: 100 } } }
+
+ context 'when user has protected environment access' do
+ include_context 'with protected environment setting', true
+
+ describe '#GET show' do
+ subject(:view_settings_page) { get settings_page_path }
+
+ it 'displays the CI/CD settings page successfully' do
+ view_settings_page
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to include('CI/CD Settings')
+ end
+ end
+
+ describe '#PUT update' do
+ subject(:update_settings) { put settings_page_path, params: update_params }
+
+ it 'prevents modification of CI/CD settings' do
+ update_settings
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'when user lacks protected environment access' do
+ include_context 'with protected environment setting', false
+
+ describe '#GET show' do
+ subject(:view_settings_page) { get settings_page_path }
+
+ it 'denies access to the CI/CD settings page' do
+ view_settings_page
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe '#PUT update' do
+ subject(:update_settings) { put settings_page_path, params: update_params }
+
+ it 'prevents modification of CI/CD settings' do
+ update_settings
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
+ describe Groups::ProtectedEnvironmentsController do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:protected_environment) { create(:protected_environment, :group_level, group: group, name: 'production') }
+
+ let(:environment_params) do
+ {
+ protected_environment: {
+ name: 'staging',
+ deploy_access_levels_attributes: [{ group_id: group.id }]
+ }
+ }
+ end
+
+ let(:base_path) { group_protected_environments_path(group) }
+ let(:environment_path) { group_protected_environment_path(group, protected_environment) }
+ let(:settings_redirect) { group_settings_ci_cd_path(group, anchor: 'js-protected-environments-settings') }
+
+ context 'when user has protected environment access' do
+ include_context 'with protected environment setting', true
+
+ describe '#POST create' do
+ subject(:create_environment) { post base_path, params: environment_params, as: :json }
+
+ it 'creates a new protected environment' do
+ expect { create_environment }.to change { ProtectedEnvironment.count }
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(flash[:notice]).to eq('Your environment has been protected.')
+ expect(response).to redirect_to(settings_redirect)
+ end
+ end
+
+ describe '#PUT update' do
+ let(:update_params) do
+ {
+ protected_environment: {
+ name: protected_environment.name,
+ deploy_access_levels_attributes: [
+ { id: protected_environment.deploy_access_levels.first.id, group_id: group.id }
+ ]
+ }
+ }
+ end
+
+ subject(:update_environment) { put environment_path, params: update_params }
+
+ it 'updates the protected environment' do
+ expect { update_environment }
+ .not_to change { protected_environment.reload.name }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe '#DELETE destroy' do
+ let_it_be(:deletable_environment) do
+ create(:protected_environment, :group_level,
+ group: group,
+ name: 'testing'
+ )
+ end
+
+ subject(:delete_environment) { delete group_protected_environment_path(group, deletable_environment) }
+
+ it 'removes the protected environment' do
+ expect { delete_environment }.to change { ProtectedEnvironment.count }
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(flash[:notice]).to eq('Your environment has been unprotected')
+ expect(response).to redirect_to(settings_redirect)
+ end
+ end
+ end
+
+ context 'when user lacks protected environment access' do
+ include_context 'with protected environment setting', false
+
+ describe '#POST create' do
+ subject(:create_environment) { post base_path, params: environment_params, as: :json }
+
+ it 'denies environment creation' do
+ expect { create_environment }.not_to change { ProtectedEnvironment.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe '#PUT update' do
+ let(:update_params) do
+ {
+ protected_environment: {
+ name: protected_environment.name,
+ deploy_access_levels_attributes: [
+ { id: protected_environment.deploy_access_levels.first.id, group_id: group.id }
+ ]
+ }
+ }
+ end
+
+ subject(:update_environment) { put environment_path, params: update_params }
+
+ it 'denies environment update' do
+ update_environment
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe '#DELETE destroy' do
+ let_it_be(:deletable_environment) do
+ create(:protected_environment, :group_level,
+ group: group,
+ name: 'testing'
+ )
+ end
+
+ subject(:delete_environment) { delete group_protected_environment_path(group, deletable_environment) }
+
+ it 'denies environment deletion' do
+ expect { delete_environment }.not_to change { ProtectedEnvironment.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+end
diff --git a/ee/spec/requests/custom_roles/admin_protected_environments/projects_request_spec.rb b/ee/spec/requests/custom_roles/admin_protected_environments/projects_request_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..74b1ce7065c90112105b4e7175d625a90e8ad480
--- /dev/null
+++ b/ee/spec/requests/custom_roles/admin_protected_environments/projects_request_spec.rb
@@ -0,0 +1,179 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User with admin_protected_environments custom role', feature_category: :continuous_delivery do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:protected_environment) { create(:protected_environment, project: project) }
+
+ before do
+ stub_licensed_features(custom_roles: true)
+ sign_in(user)
+ end
+
+ shared_context 'with protected environment value' do |env|
+ let_it_be(:role) { create(:member_role, :guest, namespace: group, admin_protected_environments: env) }
+ let_it_be(:member) { create(:project_member, :guest, member_role: role, user: user, project: project) }
+ end
+
+ describe Projects::Settings::CiCdController do
+ let(:settings_page_path) { project_settings_ci_cd_path(project) }
+ let(:update_params) { { namespace_id: project.namespace, project_id: project } }
+
+ context 'when user has protected environment access' do
+ include_context 'with protected environment value', true
+
+ describe '#GET show' do
+ subject(:view_settings_page) { get settings_page_path }
+
+ it 'displays the CI/CD settings page successfully' do
+ view_settings_page
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to include('CI/CD Settings')
+ end
+ end
+
+ describe '#PUT update' do
+ subject(:update_settings) { put settings_page_path, params: update_params }
+
+ it 'prevents modification of CI/CD settings' do
+ update_settings
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'when user lacks protected environment access' do
+ include_context 'with protected environment value', false
+
+ describe '#GET show' do
+ subject(:view_settings_page) { get settings_page_path }
+
+ it 'denies access to the CI/CD settings page' do
+ view_settings_page
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe '#PUT update' do
+ subject(:update_settings) { put settings_page_path, params: update_params }
+
+ it 'prevents modification of CI/CD settings' do
+ update_settings
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
+ describe Projects::ProtectedEnvironmentsController do
+ let(:environment_params) do
+ {
+ name: 'testenv',
+ deploy_access_levels_attributes: [
+ { access_level: Gitlab::Access::DEVELOPER }
+ ]
+ }
+ end
+
+ let(:base_path) { project_protected_environments_path(project) }
+ let(:environment_path) { project_protected_environment_path(project, protected_environment) }
+ let(:search_path) { search_project_protected_environments_path(project) }
+
+ context 'when user has protected environment access' do
+ include_context 'with protected environment value', true
+
+ describe '#POST create' do
+ subject(:create_environment) { post base_path, params: { protected_environment: environment_params } }
+
+ it 'creates a new protected environment' do
+ create_environment
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(flash[:notice]).to eq('Your environment has been protected.')
+ end
+ end
+
+ describe '#PUT update' do
+ subject(:update_environment) { put environment_path, params: { protected_environment: environment_params } }
+
+ it 'updates the protected environment' do
+ update_environment
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe '#DELETE destroy' do
+ subject(:delete_environment) { delete environment_path }
+
+ it 'removes the protected environment' do
+ delete_environment
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(flash[:notice]).to eq('Your environment has been unprotected')
+ end
+ end
+
+ describe '#GET search' do
+ subject(:search_environments) { get search_path, params: { query: 'prod' } }
+
+ it 'performs environment search' do
+ search_environments
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ context 'when user lacks protected environment access' do
+ include_context 'with protected environment value', false
+
+ describe '#POST create' do
+ subject(:create_environment) { post base_path, params: { protected_environment: environment_params } }
+
+ it 'denies environment creation' do
+ create_environment
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe '#PUT update' do
+ subject(:update_environment) { put environment_path, params: { protected_environment: environment_params } }
+
+ it 'denies environment update' do
+ update_environment
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe '#DELETE destroy' do
+ subject(:delete_environment) { delete environment_path }
+
+ it 'denies environment deletion' do
+ delete_environment
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe '#GET search' do
+ subject(:search_environments) { get search_path, params: { query: 'prod' } }
+
+ it 'denies environment search' do
+ search_environments
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+end