diff --git a/app/assets/javascripts/access_level/constants.js b/app/assets/javascripts/access_level/constants.js
index d346f3ed40e011abb778be7248301a70a9779c6b..a2c5701325a56a392d8796db58bbd1e3518f0ab2 100644
--- a/app/assets/javascripts/access_level/constants.js
+++ b/app/assets/javascripts/access_level/constants.js
@@ -6,6 +6,7 @@ export const ACCESS_LEVEL_MINIMAL_ACCESS_INTEGER = 5;
export const ACCESS_LEVEL_GUEST_INTEGER = 10;
export const ACCESS_LEVEL_PLANNER_INTEGER = 15;
export const ACCESS_LEVEL_REPORTER_INTEGER = 20;
+export const ACCESS_LEVEL_SECURITY_MANAGER_INTEGER = 25;
export const ACCESS_LEVEL_DEVELOPER_INTEGER = 30;
export const ACCESS_LEVEL_MAINTAINER_INTEGER = 40;
export const ACCESS_LEVEL_OWNER_INTEGER = 50;
@@ -18,6 +19,7 @@ export const ACCESS_LEVEL_GUEST_STRING = 'GUEST';
export const ACCESS_LEVEL_PLANNER_STRING = 'PLANNER';
export const ACCESS_LEVEL_REPORTER_STRING = 'REPORTER';
export const ACCESS_LEVEL_DEVELOPER_STRING = 'DEVELOPER';
+export const ACCESS_LEVEL_SECURITY_MANAGER_STRING = 'SECURITY_MANAGER';
export const ACCESS_LEVEL_MAINTAINER_STRING = 'MAINTAINER';
export const ACCESS_LEVEL_OWNER_STRING = 'OWNER';
@@ -27,6 +29,7 @@ export const ACCESS_LEVELS_INTEGER_TO_STRING = {
[ACCESS_LEVEL_GUEST_INTEGER]: ACCESS_LEVEL_GUEST_STRING,
[ACCESS_LEVEL_PLANNER_INTEGER]: ACCESS_LEVEL_PLANNER_STRING,
[ACCESS_LEVEL_REPORTER_INTEGER]: ACCESS_LEVEL_REPORTER_STRING,
+ [ACCESS_LEVEL_SECURITY_MANAGER_INTEGER]: ACCESS_LEVEL_SECURITY_MANAGER_STRING,
[ACCESS_LEVEL_DEVELOPER_INTEGER]: ACCESS_LEVEL_DEVELOPER_STRING,
[ACCESS_LEVEL_MAINTAINER_INTEGER]: ACCESS_LEVEL_MAINTAINER_STRING,
[ACCESS_LEVEL_OWNER_INTEGER]: ACCESS_LEVEL_OWNER_STRING,
@@ -38,6 +41,7 @@ export const ACCESS_LEVELS_STRING_TO_INTEGER = {
[ACCESS_LEVEL_GUEST_STRING]: ACCESS_LEVEL_GUEST_INTEGER,
[ACCESS_LEVEL_PLANNER_STRING]: ACCESS_LEVEL_PLANNER_INTEGER,
[ACCESS_LEVEL_REPORTER_STRING]: ACCESS_LEVEL_REPORTER_INTEGER,
+ [ACCESS_LEVEL_SECURITY_MANAGER_STRING]: ACCESS_LEVEL_SECURITY_MANAGER_INTEGER,
[ACCESS_LEVEL_DEVELOPER_STRING]: ACCESS_LEVEL_DEVELOPER_INTEGER,
[ACCESS_LEVEL_MAINTAINER_STRING]: ACCESS_LEVEL_MAINTAINER_INTEGER,
[ACCESS_LEVEL_OWNER_STRING]: ACCESS_LEVEL_OWNER_INTEGER,
@@ -49,6 +53,7 @@ const ACCESS_LEVEL_GUEST = __('Guest');
const ACCESS_LEVEL_PLANNER = __('Planner');
const ACCESS_LEVEL_REPORTER = __('Reporter');
const ACCESS_LEVEL_DEVELOPER = __('Developer');
+const ACCESS_LEVEL_SECURITY_MANAGER = __('Security Manager');
const ACCESS_LEVEL_MAINTAINER = __('Maintainer');
const ACCESS_LEVEL_OWNER = __('Owner');
export const ACCESS_LEVEL_ADMIN = __('Admin');
@@ -91,6 +96,15 @@ export const BASE_ROLES = [
'MemberRole|The Reporter role is suitable for team members who need to stay informed about a project or group but do not actively contribute code.',
),
},
+ {
+ value: 'SECURITY_MANAGER',
+ text: ACCESS_LEVEL_SECURITY_MANAGER,
+ accessLevel: ACCESS_LEVEL_SECURITY_MANAGER_INTEGER,
+ occupiesSeat: true,
+ description: s__(
+ 'MemberRole|The Security Manager role is suitable for team members who need to manage security settings and track security issues.',
+ ),
+ },
{
value: 'DEVELOPER',
text: ACCESS_LEVEL_DEVELOPER,
@@ -100,6 +114,7 @@ export const BASE_ROLES = [
'MemberRole|The Developer role gives users access to contribute code while restricting sensitive administrative actions.',
),
},
+
{
value: 'MAINTAINER',
text: ACCESS_LEVEL_MAINTAINER,
@@ -134,6 +149,7 @@ export const ACCESS_LEVEL_LABELS = {
[ACCESS_LEVEL_GUEST_INTEGER]: ACCESS_LEVEL_GUEST,
[ACCESS_LEVEL_PLANNER_INTEGER]: ACCESS_LEVEL_PLANNER,
[ACCESS_LEVEL_REPORTER_INTEGER]: ACCESS_LEVEL_REPORTER,
+ [ACCESS_LEVEL_SECURITY_MANAGER_INTEGER]: ACCESS_LEVEL_SECURITY_MANAGER,
[ACCESS_LEVEL_DEVELOPER_INTEGER]: ACCESS_LEVEL_DEVELOPER,
[ACCESS_LEVEL_MAINTAINER_INTEGER]: ACCESS_LEVEL_MAINTAINER,
[ACCESS_LEVEL_OWNER_INTEGER]: ACCESS_LEVEL_OWNER,
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index fb82a2d4501c5cd071255ded9769f017d3d0910a..e8ae28900f8cf98e7bea8c30b6a569566bf6653a 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -803,6 +803,7 @@ def localized_access_names
Gitlab::Access::GUEST => _('Guest'),
Gitlab::Access::PLANNER => _('Planner'),
Gitlab::Access::REPORTER => _('Reporter'),
+ Gitlab::Access::SECURITY_MANAGER => _('Security Manager'),
Gitlab::Access::DEVELOPER => _('Developer'),
Gitlab::Access::MAINTAINER => _('Maintainer'),
Gitlab::Access::OWNER => _('Owner')
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index d5f5dea98efa68f2c3b6b15e91b34e5cd3abc47b..54703a26e0031bafa0ae2947af6178f1db107425 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -16,6 +16,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
# This is not a linear condition (some policies available for planner might not be available for higher access levels)
condition(:planner) { access_level == GroupMember::PLANNER }
condition(:developer) { access_level >= GroupMember::DEVELOPER }
+ condition(:security_manager) { access_level == GroupMember::SECURITY_MANAGER }
condition(:owner) { access_level >= GroupMember::OWNER }
condition(:maintainer) { access_level >= GroupMember::MAINTAINER }
condition(:reporter) { access_level >= GroupMember::REPORTER }
@@ -292,6 +293,10 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :read_internal_note
end
+ rule { security_manager }.policy do
+ enable :security_manager_access
+ end
+
rule { admin | organization_owner }.policy do
enable :read_group
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index ecc1d1d90c4c1000be8119a2ef6dd58e3fccaeba..263d0b30a9159aaee98f41e7d183018ab22ab4c5 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -26,6 +26,9 @@ class ProjectPolicy < BasePolicy
desc "User has developer access"
condition(:developer) { team_access_level >= Gitlab::Access::DEVELOPER }
+ desc "User has security manager access"
+ condition(:security_manager) { team_access_level == Gitlab::Access::SECURITY_MANAGER }
+
desc "User has maintainer access"
condition(:maintainer) { team_access_level >= Gitlab::Access::MAINTAINER }
@@ -345,6 +348,7 @@ class ProjectPolicy < BasePolicy
rule { planner }.enable :planner_access
rule { reporter }.enable :reporter_access
rule { developer }.enable :developer_access
+ rule { security_manager }.enable :security_manager_access
rule { maintainer }.enable :maintainer_access
rule { owner | admin | organization_owner }.enable :owner_access
@@ -445,6 +449,10 @@ class ProjectPolicy < BasePolicy
prevent :create_merge_request_in
end
+ rule { security_manager }.policy do
+ # TODO: Add security manager abilities
+ end
+
rule { can?(:reporter_access) & can?(:create_issue) }.enable :create_incident
rule { can?(:reporter_access) & can?(:read_environment) }.enable :read_freeze_period
diff --git a/config/authz/permissions/definitions_todo.txt b/config/authz/permissions/definitions_todo.txt
index f6ab90032f64bd55a0236e9ce9ba88881df93657..595a0dda1047154ae142d6b616d17ac4f8b7bfa2 100644
--- a/config/authz/permissions/definitions_todo.txt
+++ b/config/authz/permissions/definitions_todo.txt
@@ -861,3 +861,4 @@ write_ai_agents
write_model_experiments
write_model_registry
write_observability
+security_manager_access
diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md
index f28d1582e2abb5bf253fec8d5c429c745391666e..cc3922eb59de492ec4b1de8d3ef06fa0b7d3ee59 100644
--- a/doc/api/graphql/reference/_index.md
+++ b/doc/api/graphql/reference/_index.md
@@ -50656,6 +50656,7 @@ Access level to a resource.
| `OWNER` | Owner access. |
| `PLANNER` | Planner access. |
| `REPORTER` | Reporter access. |
+| `SECURITY_MANAGER` | Security manager access. |
### `AccessTokenGranularScopeAccess`
@@ -52495,6 +52496,7 @@ Role of User.
| `OWNER` | Owner. |
| `PLANNER` | Planner. |
| `REPORTER` | Reporter. |
+| `SECURITY_MANAGER` | Security manager. |
### `GitlabSubscriptionsUserSort`
diff --git a/ee/app/assets/javascripts/roles_and_permissions/graphql/group_roles.query.graphql b/ee/app/assets/javascripts/roles_and_permissions/graphql/group_roles.query.graphql
index bb0fe017d8d4d3f8cfd60e61e77cf8255f12dfa8..e510bf393f4cddcd3d7ed2fe51ebaa99e20c5801 100644
--- a/ee/app/assets/javascripts/roles_and_permissions/graphql/group_roles.query.graphql
+++ b/ee/app/assets/javascripts/roles_and_permissions/graphql/group_roles.query.graphql
@@ -4,7 +4,9 @@ query getGroupRoles($fullPath: ID!, $includeCustomRoles: Boolean!) {
group(fullPath: $fullPath) {
id
# Filter out the Minimal Access role (business requirement).
- standardRoles(accessLevel: [GUEST, REPORTER, PLANNER, DEVELOPER, MAINTAINER, OWNER]) {
+ standardRoles(
+ accessLevel: [GUEST, REPORTER, PLANNER, DEVELOPER, SECURITY_MANAGER, MAINTAINER, OWNER]
+ ) {
nodes {
...Role
}
diff --git a/ee/app/assets/javascripts/roles_and_permissions/graphql/instance_roles.query.graphql b/ee/app/assets/javascripts/roles_and_permissions/graphql/instance_roles.query.graphql
index cd3e524edb92f371e95a31ec5c3df4cf134bcef8..5e753b18549798cd57ecbf91eb125a6f40687263 100644
--- a/ee/app/assets/javascripts/roles_and_permissions/graphql/instance_roles.query.graphql
+++ b/ee/app/assets/javascripts/roles_and_permissions/graphql/instance_roles.query.graphql
@@ -2,7 +2,9 @@
query getInstanceRoles($includeCustomRoles: Boolean!, $includeAdminRoles: Boolean!) {
# Filter out the Minimal Access role (business requirement).
- standardRoles(accessLevel: [GUEST, REPORTER, PLANNER, DEVELOPER, MAINTAINER, OWNER]) {
+ standardRoles(
+ accessLevel: [GUEST, REPORTER, PLANNER, DEVELOPER, SECURITY_MANAGER, MAINTAINER, OWNER]
+ ) {
nodes {
...Role
}
diff --git a/ee/app/policies/ee/group_policy.rb b/ee/app/policies/ee/group_policy.rb
index 94b394ccce1ea435e6d6f80379878076a6325ff3..e65cc70d30a6a5729086197ab1603322555b936a 100644
--- a/ee/app/policies/ee/group_policy.rb
+++ b/ee/app/policies/ee/group_policy.rb
@@ -393,6 +393,10 @@ module GroupPolicy
enable :read_security_attribute
end
+ rule { security_manager }.policy do
+ # TODO: Add security manager abilities for EE features
+ end
+
rule { reporter }.policy do
enable :admin_issue_board_list
enable :view_productivity_analytics
diff --git a/ee/bin/custom-ability b/ee/bin/custom-ability
index 1b301cf7d05b19593938d5a74103aa9e01d61cfd..cde9c80954267ad72059d5ff5f06ecbf9657a8c4 100755
--- a/ee/bin/custom-ability
+++ b/ee/bin/custom-ability
@@ -23,6 +23,7 @@ LEVELS = {
'Guest' => 10,
'Planner' => 15,
'Reporter' => 20,
+ 'Security Manager' => 25,
'Developer' => 30,
'Maintainer' => 40,
'Owner' => 50,
diff --git a/ee/spec/features/groups/members/manage_members_spec.rb b/ee/spec/features/groups/members/manage_members_spec.rb
index 211739796ecb58b9fc4fe2d26ced70f724f5dc0a..f01dbcf6a0dae9a6449d81c34bc928664465e888 100644
--- a/ee/spec/features/groups/members/manage_members_spec.rb
+++ b/ee/spec/features/groups/members/manage_members_spec.rb
@@ -474,7 +474,7 @@ def add_user_by_email(role, use_exact_text_match: true)
let_it_be(:group_owner) { create(:user, owner_of: group) }
- let(:access_levels) { %w[Guest Planner Reporter Developer Maintainer Owner] }
+ let(:access_levels) { ['Guest', 'Planner', 'Reporter', 'Security Manager', 'Developer', 'Maintainer', 'Owner'] }
let(:member_role_text) { "#{member_role.name}\n#{member_role.description}" }
before do
diff --git a/ee/spec/frontend/pages/projects/shared/permissions/secrets_manager/components/secrets_manager_permissions_modal_spec.js b/ee/spec/frontend/pages/projects/shared/permissions/secrets_manager/components/secrets_manager_permissions_modal_spec.js
index d330f8530434365e3892d3c27892076cb9b71e15..6701000fa26f31b89831fd1f0689a856ac48d6f4 100644
--- a/ee/spec/frontend/pages/projects/shared/permissions/secrets_manager/components/secrets_manager_permissions_modal_spec.js
+++ b/ee/spec/frontend/pages/projects/shared/permissions/secrets_manager/components/secrets_manager_permissions_modal_spec.js
@@ -140,7 +140,7 @@ describe('SecretsManagerPermissionsModal', () => {
const USER_ITEMS = ['Administrator', 'John Doe'];
const GROUP_ITEMS = ['Organization', 'test-org'];
- const ROLE_ITEMS = ['Reporter', 'Developer', 'Maintainer'];
+ const ROLE_ITEMS = ['Reporter', 'Security Manager', 'Developer', 'Maintainer'];
describe.each`
category | title | fieldItems | selectedItem | principalId
diff --git a/ee/spec/frontend/roles_and_permissions/components/manage_role/role_form_spec.js b/ee/spec/frontend/roles_and_permissions/components/manage_role/role_form_spec.js
index 91fc8bfdb280bcb063463570e279121a99008474..abc6ad7d4aa0690a3760b64e94dadee6b1194c33 100644
--- a/ee/spec/frontend/roles_and_permissions/components/manage_role/role_form_spec.js
+++ b/ee/spec/frontend/roles_and_permissions/components/manage_role/role_form_spec.js
@@ -1,11 +1,11 @@
-import { GlFormInput, GlCollapsibleListbox, GlForm, GlFormGroup } from '@gitlab/ui';
+import { GlCollapsibleListbox, GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
import { nextTick } from 'vue';
-import { createAlert } from '~/alert';
+import PermissionsSelector from 'ee/roles_and_permissions/components/manage_role/permissions_selector.vue';
import RoleForm from 'ee/roles_and_permissions/components/manage_role/role_form.vue';
+import { stubComponent } from 'helpers/stub_component';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { stubComponent } from 'helpers/stub_component';
-import PermissionsSelector from 'ee/roles_and_permissions/components/manage_role/permissions_selector.vue';
+import { createAlert } from '~/alert';
import PageHeading from '~/vue_shared/components/page_heading.vue';
import { mockMemberRole } from '../../mock_data';
@@ -92,6 +92,7 @@ describe('RoleForm', () => {
'GUEST',
'PLANNER',
'REPORTER',
+ 'SECURITY_MANAGER',
'DEVELOPER',
'MAINTAINER',
];
diff --git a/ee/spec/frontend/roles_and_permissions/mock_data.js b/ee/spec/frontend/roles_and_permissions/mock_data.js
index a793c62c8bf24c1bed94b0623eb1ffabd4fff39c..8a0d6f2247cb1289d462e02c69a9108e9640b32f 100644
--- a/ee/spec/frontend/roles_and_permissions/mock_data.js
+++ b/ee/spec/frontend/roles_and_permissions/mock_data.js
@@ -110,6 +110,15 @@ export const standardRoles = [
description:
'The Developer role gives users access to contribute code while restricting sensitive administrative actions.',
},
+ {
+ __typename: 'StandardRole',
+ id: 'gid://gitlab/StandardRole/SECURITY_MANAGER',
+ accessLevel: 30,
+ name: 'Security Manager',
+ detailsPath: 'role/SECURITY_MANAGER',
+ description:
+ 'The Security Manager role is designed for team members who need to oversee security features, including vulnerability management, security dashboards, and policy monitoring, while having restricted access to non-security related administrative functions.',
+ },
{
__typename: 'StandardRole',
id: 'gid://gitlab/StandardRole/MAINTAINER',
diff --git a/ee/spec/policies/project_policy_spec.rb b/ee/spec/policies/project_policy_spec.rb
index 35b6a2e81ccb63a80af373f5c3aad7c5d0bcf830..2164752eb7660e5c5bf3f1ced23db5e250d6bf05 100644
--- a/ee/spec/policies/project_policy_spec.rb
+++ b/ee/spec/policies/project_policy_spec.rb
@@ -1791,6 +1791,7 @@
:planner | nil | true
:reporter | nil | true
:developer | nil | true
+ :security_manager | nil | true
:maintainer | nil | true
:owner | nil | true
:admin | false | false
diff --git a/ee/spec/policies/requirements_management/requirement_policy_spec.rb b/ee/spec/policies/requirements_management/requirement_policy_spec.rb
index c5b2df8ec6f831bab10e599dfeaf7a1df454ecb0..35610e631c787c2bbdac57d7a66653fa9001ea79 100644
--- a/ee/spec/policies/requirements_management/requirement_policy_spec.rb
+++ b/ee/spec/policies/requirements_management/requirement_policy_spec.rb
@@ -7,12 +7,13 @@
let_it_be(:admin) { create(:admin) }
let_it_be(:planner) { create(:user) }
let_it_be(:reporter) { create(:user) }
+ let_it_be(:security_manager) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:project) do
create(:project, :public, namespace: owner.namespace, planners: planner, reporters: reporter, developers: developer,
- maintainers: maintainer, guests: guest)
+ maintainers: maintainer, guests: guest, security_managers: security_manager)
end
let_it_be(:resource, reload: true) { create(:work_item, :requirement, project: project).requirement }
diff --git a/ee/spec/support/shared_examples/policies/requirement_policy_shared_examples.rb b/ee/spec/support/shared_examples/policies/requirement_policy_shared_examples.rb
index 426d951f4a1f10d0cc651384898cdffc0f9eaa67..073af9270f8f549686e371b2aafd12c677f97f2a 100644
--- a/ee/spec/support/shared_examples/policies/requirement_policy_shared_examples.rb
+++ b/ee/spec/support/shared_examples/policies/requirement_policy_shared_examples.rb
@@ -65,6 +65,12 @@
it_behaves_like 'user with manage permissions'
end
+ context 'with security manager' do
+ let(:current_user) { security_manager }
+
+ it_behaves_like 'user with manage permissions'
+ end
+
context 'with guest' do
let(:current_user) { guest }
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b96411c49d666203611447f4c1afbbfe7665f0ee..17026f5efc5337fe34a03a8493fec109f1eda66d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -41489,6 +41489,9 @@ msgstr ""
msgid "MemberRole|The Reporter role is suitable for team members who need to stay informed about a project or group but do not actively contribute code."
msgstr ""
+msgid "MemberRole|The Security Manager role is suitable for team members who need to manage security settings and track security issues."
+msgstr ""
+
msgid "MemberRole|The Security Manager role provides comprehensive visibility and management over security aspects of the group or project."
msgstr ""
@@ -59898,6 +59901,9 @@ msgstr ""
msgid "Security Finding not found"
msgstr ""
+msgid "Security Manager"
+msgstr ""
+
msgid "Security Policy project already exists, but is not linked."
msgstr ""
diff --git a/qa/qa/resource/project_member.rb b/qa/qa/resource/project_member.rb
index dfaa157038ce3cd245cdbed96b7c7409d49154cc..bdd87317540bd3ad7e10c577533f5115175d6708 100644
--- a/qa/qa/resource/project_member.rb
+++ b/qa/qa/resource/project_member.rb
@@ -10,6 +10,7 @@ def initialize
@level = {
guest: 10,
reporter: 20,
+ security_manager: 25,
developer: 30,
maintainer: 40,
owner: 50
diff --git a/spec/factories/user_highest_roles.rb b/spec/factories/user_highest_roles.rb
index 04e3e344b4c4e02ef34d58fd960690676708f8e0..2b97f5670574b16983d43b9a55244b5831a4aa17 100644
--- a/spec/factories/user_highest_roles.rb
+++ b/spec/factories/user_highest_roles.rb
@@ -9,6 +9,7 @@
trait(:planner) { highest_access_level { GroupMember::PLANNER } }
trait(:reporter) { highest_access_level { GroupMember::REPORTER } }
trait(:developer) { highest_access_level { GroupMember::DEVELOPER } }
+ trait(:security_manager) { highest_access_level { GroupMember::SECURITY_MANAGER } }
trait(:maintainer) { highest_access_level { GroupMember::MAINTAINER } }
trait(:owner) { highest_access_level { GroupMember::OWNER } }
end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 86f329c71445a5617e7371b5932f4a32f5c632d4..923190d6e031a3c1abeb19fdbbbf69bf1289ee09 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -227,6 +227,7 @@
reporter_of {}
security_manager_of {}
developer_of {}
+ security_manager_of {}
maintainer_of {}
owner_of {}
# rubocop:enable Lint/EmptyBlock
@@ -238,6 +239,7 @@
Array.wrap(evaluator.reporter_of).each { |target| target.add_reporter(user) }
Array.wrap(evaluator.security_manager_of).each { |target| target.add_security_manager(user) }
Array.wrap(evaluator.developer_of).each { |target| target.add_developer(user) }
+ Array.wrap(evaluator.security_manager_of).each { |target| target.add_security_manager(user) }
Array.wrap(evaluator.maintainer_of).each { |target| target.add_maintainer(user) }
Array.wrap(evaluator.owner_of).each { |target| target.add_owner(user) }
end
diff --git a/spec/features/projects/members/manage_members_spec.rb b/spec/features/projects/members/manage_members_spec.rb
index 7e2cc267ccd817c9f72658c2085a02e9e45a41fd..9de31a341b82489931b5c4b6172a4c5d6783ce90 100644
--- a/spec/features/projects/members/manage_members_spec.rb
+++ b/spec/features/projects/members/manage_members_spec.rb
@@ -103,7 +103,8 @@
page.within role_dropdown_selector do
wait_for_requests
toggle_listbox
- expect_listbox_items(%w[Guest Planner Reporter Developer Maintainer Owner])
+ expect_listbox_items(
+ ['Guest', 'Planner', 'Reporter', 'Security Manager', 'Developer', 'Maintainer', 'Owner'])
end
end
end
diff --git a/spec/frontend/members/mock_data.js b/spec/frontend/members/mock_data.js
index 67ab21cb73646f23388f32b929b84b1a6df5f671..4fddd3c9ff95feae2fa89b0cb8414b9662143040 100644
--- a/spec/frontend/members/mock_data.js
+++ b/spec/frontend/members/mock_data.js
@@ -1,7 +1,7 @@
import {
MEMBERS_TAB_TYPES,
- MEMBER_STATE_CREATED,
MEMBER_MODEL_TYPE_GROUP_MEMBER,
+ MEMBER_STATE_CREATED,
} from '~/members/constants';
export const member = {
@@ -57,6 +57,7 @@ export const member = {
Guest: 10,
Planner: 15,
Reporter: 20,
+ 'Security Manager': 25,
Developer: 30,
Maintainer: 40,
Owner: 50,
diff --git a/spec/models/user_highest_role_spec.rb b/spec/models/user_highest_role_spec.rb
index 234327caac908960420af5c9dc65fdbe22db7f9c..b3e13497808d4e09da8daa66bd58c3c0239c67f7 100644
--- a/spec/models/user_highest_role_spec.rb
+++ b/spec/models/user_highest_role_spec.rb
@@ -35,6 +35,7 @@
Gitlab::Access::REPORTER,
Gitlab::Access::SECURITY_MANAGER,
Gitlab::Access::DEVELOPER,
+ Gitlab::Access::SECURITY_MANAGER,
Gitlab::Access::MAINTAINER,
Gitlab::Access::OWNER
]
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 98c85151e22f4ef8b3a2c9a7322d76e9e9cbb6fc..74cc86bca89431a2b52efb2e191f9b5d307a9710 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -7,6 +7,7 @@
let_it_be_with_reload(:reporter) { create(:user) }
let_it_be_with_reload(:developer) { create(:user) }
let_it_be_with_reload(:maintainer) { create(:user) }
+ let_it_be_with_reload(:security_manager) { create(:user) }
let_it_be_with_reload(:inherited_guest) { create(:user) }
let_it_be_with_reload(:inherited_planner) { create(:user) }
let_it_be_with_reload(:inherited_reporter) { create(:user) }
@@ -142,6 +143,7 @@
project.add_reporter(reporter)
project.add_developer(developer)
project.add_maintainer(maintainer)
+ project.add_security_manager(security_manager)
project.add_owner(owner)
end
end