diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb
index 59b24e8103de302aeaa01a73eeb06b172365c776..9715fad056d62ad61c5d4dbd20a1628052bede00 100644
--- a/app/controllers/groups/settings/integrations_controller.rb
+++ b/app/controllers/groups/settings/integrations_controller.rb
@@ -5,7 +5,7 @@ module Settings
class IntegrationsController < Groups::ApplicationController
include ::Integrations::Actions
- before_action :authorize_admin_group!
+ before_action :authorize_admin_integrations!
feature_category :integrations
diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb
index ad8ddf182b713893a4b478da1f1d9e3319e87c8e..fb304a5b77cd9972b58090476796ebcb0e2f532b 100644
--- a/app/controllers/projects/settings/integrations_controller.rb
+++ b/app/controllers/projects/settings/integrations_controller.rb
@@ -6,7 +6,7 @@ class IntegrationsController < Projects::ApplicationController
include ::Integrations::Params
include ::InternalRedirect
- before_action :authorize_admin_project!
+ before_action :authorize_admin_integrations!
before_action :ensure_integration_enabled, only: [:edit, :update, :test]
before_action :integration, only: [:edit, :update, :test]
before_action :default_integration, only: [:edit, :update]
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index ce6f286a95f4c2121bd3cc9bf02fe448ef1e2320..554bdf97dfce1e1163d412ffa75ba69c477a574a 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -267,6 +267,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :admin_namespace
enable :admin_group_member
enable :admin_package
+ enable :admin_integrations
enable :change_visibility_level
enable :read_usage_quotas
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index f47861ca9148f9018549fc1b847e1d361b6eb3fa..cca2750d1fd96aa9269915d8c1cceccc801d01a0 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -569,6 +569,7 @@ class ProjectPolicy < BasePolicy
enable :admin_note
enable :admin_wiki
enable :admin_project
+ enable :admin_integrations
enable :admin_commit_status
enable :admin_build
enable :admin_container_image
diff --git a/app/validators/json_schemas/member_role_permissions.json b/app/validators/json_schemas/member_role_permissions.json
index 191cbdb8b39622d896ec6b78506cbb98a5a873bc..e56df0c903036ae4fbbb4139a850c881f22fb595 100644
--- a/app/validators/json_schemas/member_role_permissions.json
+++ b/app/validators/json_schemas/member_role_permissions.json
@@ -13,6 +13,9 @@
"admin_group_member": {
"type": "boolean"
},
+ "admin_integrations": {
+ "type": "boolean"
+ },
"admin_merge_request": {
"type": "boolean"
},
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 684b12a920f753d70ce48e2d37cf33f90a9a1b3f..0f0a3e67f77bab19bc6ec78003c4f00423edcf87 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -34325,6 +34325,7 @@ Member role permission.
| `ADMIN_CICD_VARIABLES` | Create, read, update, and delete CI/CD variables. |
| `ADMIN_COMPLIANCE_FRAMEWORK` | Create, read, update, and delete compliance frameworks. Users with this permission can also assign a compliance framework label to a project, and set the default framework of a group. |
| `ADMIN_GROUP_MEMBER` | Add or remove users in a group, and assign roles to users. When assigning a role, users with this custom permission must select a role that has the same or fewer permissions as the default role used as the base for their custom role. |
+| `ADMIN_INTEGRATIONS` | Create, read, update, and delete integrations with external applications. |
| `ADMIN_MERGE_REQUEST` | Allows approval of merge requests. |
| `ADMIN_PUSH_RULES` | Configure push rules for repositories at the group or project level. |
| `ADMIN_TERRAFORM_STATE` | Execute terraform commands, lock/unlock terraform state files, and remove file versions. |
diff --git a/doc/user/custom_roles/abilities.md b/doc/user/custom_roles/abilities.md
index 7e9544587a9c7dcaf8ebadecb84d8e44c645701a..e9e5f33682115dbaa1591529488a6062b9767dd4 100644
--- a/doc/user/custom_roles/abilities.md
+++ b/doc/user/custom_roles/abilities.md
@@ -56,6 +56,12 @@ These requirements are documented in the `Required permission` column in the fol
|:-----|:------------|:------------------|:---------|:--------------|:---------|
| [`admin_terraform_state`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140759) | | Execute terraform commands, lock/unlock terraform state files, and remove file versions. | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/421789) | | |
+## Integrations
+
+| Name | Required permission | Description | Introduced in | Feature flag | Enabled in |
+|:-----|:------------|:------------------|:---------|:--------------|:---------|
+| [`admin_integrations`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/TODO) | | Create, read, update, and delete integrations with external applications. | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/460522) | | |
+
## Secrets management
| Name | Required permission | Description | Introduced in | Feature flag | Enabled in |
diff --git a/ee/app/policies/ee/group_policy.rb b/ee/app/policies/ee/group_policy.rb
index f68aea3688e5c75eef6879cd9692945eaf529037..9f094c25d8c19005e3aa498ca29d96af7698c3e6 100644
--- a/ee/app/policies/ee/group_policy.rb
+++ b/ee/app/policies/ee/group_policy.rb
@@ -549,6 +549,10 @@ module GroupPolicy
enable :admin_push_rules
end
+ rule { custom_role_enables_admin_integrations }.policy do
+ enable :admin_integrations
+ end
+
rule { can?(:admin_group) | can?(:admin_compliance_framework) | can?(:manage_deploy_tokens) | can?(:manage_merge_request_settings) }.policy do
enable :view_edit_page
end
diff --git a/ee/app/policies/ee/project_policy.rb b/ee/app/policies/ee/project_policy.rb
index fd263748cec2a34718a5ad24d232ee20998e6331..6466e0c9b0688471c688035ff67fb2a23d1c7105 100644
--- a/ee/app/policies/ee/project_policy.rb
+++ b/ee/app/policies/ee/project_policy.rb
@@ -314,6 +314,10 @@ module ProjectPolicy
enable :admin_push_rules
end
+ rule { custom_role_enables_admin_integrations }.policy do
+ enable :admin_integrations
+ end
+
condition(:ci_cancellation_maintainers_only, scope: :subject) do
project.ci_cancellation_restriction.maintainers_only_allowed?
end
diff --git a/ee/config/custom_abilities/admin_integrations.yml b/ee/config/custom_abilities/admin_integrations.yml
new file mode 100644
index 0000000000000000000000000000000000000000..28263484ada58817a5719e9ed72ac990a6e54cbb
--- /dev/null
+++ b/ee/config/custom_abilities/admin_integrations.yml
@@ -0,0 +1,10 @@
+---
+name: admin_integrations
+description: Create, read, update, and delete integrations with external applications.
+introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/460522
+introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/TODO
+feature_category: integrations
+milestone: '17.1'
+group_ability: true
+project_ability: true
+requirements: []
diff --git a/ee/lib/ee/sidebars/groups/menus/settings_menu.rb b/ee/lib/ee/sidebars/groups/menus/settings_menu.rb
index 350cee524ea382f101572d3e2bfd226c7cb2b7bf..4eb03cc003d7b139a23d8f699d1087ac5f62f92d 100644
--- a/ee/lib/ee/sidebars/groups/menus/settings_menu.rb
+++ b/ee/lib/ee/sidebars/groups/menus/settings_menu.rb
@@ -26,6 +26,7 @@ def configure_menu_items
else
add_menu_item_for_abilities(general_menu_item, [:remove_group, :admin_compliance_framework,
:manage_merge_request_settings])
+ 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)
diff --git a/ee/lib/ee/sidebars/projects/menus/settings_menu.rb b/ee/lib/ee/sidebars/projects/menus/settings_menu.rb
index d34933d170aaed9deee2d07ca698989486c1d28d..00a068da2280a3388b457d3a5dfd84d2f7f3bbdc 100644
--- a/ee/lib/ee/sidebars/projects/menus/settings_menu.rb
+++ b/ee/lib/ee/sidebars/projects/menus/settings_menu.rb
@@ -23,6 +23,9 @@ module SettingsMenu
],
ci_cd_menu_item: [
:admin_cicd_variables
+ ],
+ integrations_menu_item: [
+ :admin_integrations
]
}.freeze
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 f16fa33d1502e84e5097f542e976d25b732d9713..4a1ee1a491bd3eca87471f3de35dc0e8daabccae 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
@@ -318,6 +318,7 @@
:manage_group_access_tokens | 'Access Tokens'
:manage_merge_request_settings | 'General'
:remove_group | 'General'
+ :admin_integrations | 'Integrations'
end
with_them do
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 d82a4b1f645edff1b1ba221f03f9b10da4c65f71..9047663cec545b1345609ba404955b328a6e614c 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
@@ -121,6 +121,7 @@
:admin_push_rules | 'Repository'
:manage_merge_request_settings | 'Merge requests'
:manage_project_access_tokens | 'Access Tokens'
+ :admin_integrations | 'Integrations'
end
with_them do
diff --git a/ee/spec/policies/group_policy_spec.rb b/ee/spec/policies/group_policy_spec.rb
index 13607845cb98da224d67781783592becc3b1a30e..3fb94033f5dea479f193614f3bbf4f4b3346ba99 100644
--- a/ee/spec/policies/group_policy_spec.rb
+++ b/ee/spec/policies/group_policy_spec.rb
@@ -3670,6 +3670,16 @@ def create_member_role(member, abilities = member_role_abilities)
it { is_expected.to be_allowed(:admin_merge_request_approval_settings) }
end
end
+
+ context 'for a custom role with the `admin_integrations` permission' do
+ let(:member_role_abilities) { { admin_integrations: true } }
+
+ let(:allowed_abilities) do
+ [:admin_integrations]
+ end
+
+ it_behaves_like 'custom roles abilities'
+ end
end
context 'for :read_limit_alert' do
diff --git a/ee/spec/policies/project_policy_spec.rb b/ee/spec/policies/project_policy_spec.rb
index 82791962f287b9aceb4b9a4d89eb264444f79ab6..a7bf4704f02545536c8604c9b695e40249f2296c 100644
--- a/ee/spec/policies/project_policy_spec.rb
+++ b/ee/spec/policies/project_policy_spec.rb
@@ -3014,6 +3014,13 @@ def create_member_role(member, abilities = member_role_abilities)
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] }
+
+ it_behaves_like 'custom roles abilities'
+ end
end
describe 'permissions for suggested reviewers bot', :saas do
diff --git a/ee/spec/requests/custom_roles/admin_integrations/request_spec.rb b/ee/spec/requests/custom_roles/admin_integrations/request_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d9b7cc7535fcd02ac532b85339aa34d3ff17f579
--- /dev/null
+++ b/ee/spec/requests/custom_roles/admin_integrations/request_spec.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User with admin_integrations custom role', feature_category: :integrations do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:role) { create(:member_role, :guest, namespace: group, admin_integrations: true) }
+ let_it_be(:membership) { create(:group_member, :guest, member_role: role, user: user, group: group) }
+
+ before do
+ stub_licensed_features(custom_roles: true)
+
+ sign_in(user)
+ end
+
+ describe Groups::Settings::IntegrationsController do
+ let_it_be(:jira_integration) { create(:jira_integration, :group, group: group) }
+
+ describe '#index' do
+ it 'user can access the page via a custom role' do
+ get group_settings_integrations_path(group_id: group)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe '#edit' do
+ it 'user can access the page via a custom role' do
+ get edit_group_settings_integration_path(group_id: group, id: jira_integration.to_param)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe '#update' do
+ include JiraIntegrationHelpers
+
+ let(:params) { { url: 'https://jira.gitlab-example.com', password: 'password' } }
+
+ before do
+ stub_jira_integration_test
+ end
+
+ it 'user can access the page via a custom role' do
+ put group_settings_integration_path(group_id: group, id: jira_integration.to_param, service: params)
+
+ expect(response).to have_gitlab_http_status(:found)
+ end
+ end
+
+ describe '#reset' do
+ let_it_be(:inheriting_integration) { create(:jira_integration, inherit_from_id: jira_integration.id) }
+
+ it 'user can access the page via a custom role' do
+ post reset_group_settings_integration_path(group_id: group, id: jira_integration.to_param)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe '#test' do
+ include JiraIntegrationHelpers
+
+ before do
+ stub_jira_integration_test
+ end
+
+ it 'user can access the page via a custom role' do
+ put test_group_settings_integration_path(group_id: group, id: jira_integration.to_param)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ describe Projects::Settings::IntegrationsController do
+ let_it_be(:jira_integration) { create(:jira_integration, project: project) }
+
+ describe '#index' do
+ it 'user can access the page via a custom role' do
+ get namespace_project_settings_integrations_path(namespace_id: group, project_id: project)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe '#edit' do
+ it 'user can access the page via a custom role' do
+ get edit_namespace_project_settings_integration_path(namespace_id: group, project_id: project,
+ id: jira_integration.to_param)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ describe '#update' do
+ include JiraIntegrationHelpers
+
+ let(:params) { { url: 'https://jira.gitlab-example.com', password: 'password' } }
+
+ before do
+ stub_jira_integration_test
+ end
+
+ it 'user can access the page via a custom role' do
+ put namespace_project_settings_integration_path(namespace_id: group, project_id: project,
+ id: jira_integration.to_param, service: params)
+
+ expect(response).to have_gitlab_http_status(:found)
+ end
+ end
+
+ describe '#test' do
+ include JiraIntegrationHelpers
+
+ before do
+ stub_jira_integration_test
+ end
+
+ it 'user can access the page via a custom role' do
+ put test_namespace_project_settings_integration_path(namespace_id: group, project_id: project,
+ id: jira_integration.to_param)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index e399122cc96a59c3f8df22bfcdee1b3ac99b8800..241ca8cf6fd17443ec12e147044f6fc424027fb2 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -92,6 +92,7 @@
remove_group
view_edit_page
manage_merge_request_settings
+ admin_integrations
]
end
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index fc9aa9275f9fa79baa6d682bf413115f0cc123d2..984584f72a8b92dc1473a605102aa209fd07ffb3 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -71,6 +71,7 @@
admin_wiki create_deploy_token destroy_deploy_token manage_deploy_tokens
push_to_delete_protected_branch read_deploy_token update_snippet
destroy_upload admin_member_access_request rename_project manage_merge_request_settings
+ admin_integrations
]
end