diff --git a/app/models/group.rb b/app/models/group.rb index 6d6fbaa5cfbb4a124cd906b6cc3407e4b04c5410..2640c26d6f23da1f17148530c209f3a1fc2a6d13 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -246,22 +246,10 @@ def of_ancestors_and_self end scope :project_creation_allowed, ->(user) do - project_creation_allowed_on_levels = [ - ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS, - ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS, - nil - ] - - # When the value of application_settings.default_project_creation is set to `NO_ONE_PROJECT_ACCESS`, - # it means that a `nil` value for `groups.project_creation_level` is telling us: - # do not allow project creation in such groups. - # ie, `nil` is a placeholder value for inheriting the value from the ApplicationSetting. - # So we remove `nil` from the list when the application_setting's value is `NO_ONE_PROJECT_ACCESS` - if ::Gitlab::CurrentSettings.default_project_creation == ::Gitlab::Access::NO_ONE_PROJECT_ACCESS - project_creation_allowed_on_levels.delete(nil) - end + project_creation_levels_for_user = project_creation_levels_for_user(user) - with_project_creation_levels(project_creation_allowed_on_levels).excluding_restricted_visibility_levels_for_user(user) + with_project_creation_levels(project_creation_levels_for_user) + .excluding_restricted_visibility_levels_for_user(user) end scope :shared_into_ancestors, ->(group) do @@ -414,6 +402,42 @@ def with_api_scopes preload(:namespace_settings, :group_feature, :parent) end + # Handle project creation permissions based on application setting and group setting. The `default_project_creation` + # application setting is the default value and can be overridden by the `project_creation_level` group setting. + # `nil` value of namespaces.project_creation_level` means that allowed creation level has not been explicitly set by + # the group owner and is a placeholder value for inheriting the value from the ApplicationSetting. + def project_creation_levels_for_user(user) + project_creation_allowed_on_levels = [ + ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS, + ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS, + nil + ] + + if user.can_admin_all_resources? + project_creation_allowed_on_levels << ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS + end + + default_project_creation = ::Gitlab::CurrentSettings.default_project_creation + prevent_project_creation_by_default = prevent_project_creation?(user, default_project_creation) + + # Remove nil (i.e. inherited `default_project_creation`) when the application setting is: + # 1. NO_ONE_PROJECT_ACCESS + # 2. ADMINISTRATOR_PROJECT_ACCESS and the user is not an admin + # + # To prevent showing groups in the namespaces dropdown on the project creation page that have no explicit group + # setting for `project_creation_level`. + project_creation_allowed_on_levels.delete(nil) if prevent_project_creation_by_default + + project_creation_allowed_on_levels + end + + def prevent_project_creation?(user, project_creation_setting) + return true if project_creation_setting == ::Gitlab::Access::NO_ONE_PROJECT_ACCESS + return false if user.can_admin_all_resources? + + project_creation_setting == ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS + end + private def public_to_user_arel(user) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 643eedbf826913849dd65a7a8810aa5b9b3d29d7..20915e0f42dca054317ba927d92397874ba9cbb3 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -56,7 +56,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy Project.new(namespace: @subject).visibility_level_allowed?(level) end - @subject.project_creation_level == ::Gitlab::Access::NO_ONE_PROJECT_ACCESS || allowed_visibility_levels.empty? + Group.prevent_project_creation?(user, @subject.project_creation_level) || allowed_visibility_levels.empty? end condition(:create_subgroup_disabled, scope: :subject) do diff --git a/config/application_setting_columns/default_project_creation.yml b/config/application_setting_columns/default_project_creation.yml index 198ae170a37414ff331abb5c26d4bac7ad7fd08e..11fb2e8f08f76c4cde9ab338239a028a1f62051c 100644 --- a/config/application_setting_columns/default_project_creation.yml +++ b/config/application_setting_columns/default_project_creation.yml @@ -5,8 +5,8 @@ clusterwide: false column: default_project_creation db_type: integer default: '2' -description: 'Default project creation protection. Can take: `0` _(No one)_, `1` _(Maintainers)_ - or `2` _(Developers + Maintainers)_' +description: 'Default project creation protection. Can take: `0` _(No one)_, `1` _(Maintainers)_, + `2` _(Developers + Maintainers)_ or `3` _(Administrators)_' encrypted: false gitlab_com_different_than_default: false jihu: false diff --git a/doc/administration/settings/visibility_and_access_controls.md b/doc/administration/settings/visibility_and_access_controls.md index 940689a8576e50afe7a9f1796da592cf008e7822..72cf99153e6fc36c7d83e849e2a8a72a0fee6b29 100644 --- a/doc/administration/settings/visibility_and_access_controls.md +++ b/doc/administration/settings/visibility_and_access_controls.md @@ -39,10 +39,15 @@ Prerequisites: 1. Expand **Visibility and access controls**. 1. For **Default project creation protection**, select the desired roles: - No one. + - Administrators. - Maintainers. - Developers and Maintainers. 1. Select **Save changes**. +NOTE: +If you select **Administrators** and [Admin Mode](sign_in_restrictions.md#admin-mode) +is turned on, administrators must enter Admin Mode to create new projects. + ## Restrict project deletion to administrators DETAILS: diff --git a/doc/api/settings.md b/doc/api/settings.md index d5afb8aaad9889e04fc86d1d5eb0018aeebe53eb..0fd62c03a2cfe467a55838c8f75eb2082bad9c70 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -428,7 +428,7 @@ listed in the descriptions of the relevant settings. | `default_ci_config_path` | string | no | Default CI/CD configuration file and path for new projects (`.gitlab-ci.yml` if not set). | | `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131203) in GitLab 16.4: cannot be set to any levels in `restricted_visibility_levels`.| | `default_preferred_language` | string | no | Default preferred language for users who are not logged in. | -| `default_project_creation` | integer | no | Default project creation protection. Can take: `0` _(No one)_, `1` _(Maintainers)_ or `2` _(Developers + Maintainers)_| +| `default_project_creation` | integer | no | Default project creation protection. Can take: `0` _(No one)_, `1` _(Maintainers)_, `2` _(Developers + Maintainers)_, or `3` _(Administrators)_.| | `default_project_visibility` | string | no | What visibility level new projects receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131203) in GitLab 16.4: cannot be set to any levels in `restricted_visibility_levels`.| | `default_projects_limit` | integer | no | Project limit per user. Default is `100000`. | | `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. | diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index a95431b47e7b33aa912d8ffdf1fe5c65eb1d3e37..1ea6d4f106a21fac1323c6746e83ae9c5885b197 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -29,6 +29,7 @@ module Access NO_ONE_PROJECT_ACCESS = 0 MAINTAINER_PROJECT_ACCESS = 1 DEVELOPER_MAINTAINER_PROJECT_ACCESS = 2 + ADMINISTRATOR_PROJECT_ACCESS = 3 # Default subgroup creation level OWNER_SUBGROUP_ACCESS = 0 @@ -140,7 +141,8 @@ def project_creation_options { s_('ProjectCreationLevel|No one') => NO_ONE_PROJECT_ACCESS, s_('ProjectCreationLevel|Maintainers') => MAINTAINER_PROJECT_ACCESS, - s_('ProjectCreationLevel|Developers + Maintainers') => DEVELOPER_MAINTAINER_PROJECT_ACCESS + s_('ProjectCreationLevel|Developers + Maintainers') => DEVELOPER_MAINTAINER_PROJECT_ACCESS, + s_('ProjectCreationLevel|Administrators') => ADMINISTRATOR_PROJECT_ACCESS } end @@ -148,7 +150,8 @@ def project_creation_string_options { 'noone' => NO_ONE_PROJECT_ACCESS, 'maintainer' => MAINTAINER_PROJECT_ACCESS, - 'developer' => DEVELOPER_MAINTAINER_PROJECT_ACCESS + 'developer' => DEVELOPER_MAINTAINER_PROJECT_ACCESS, + 'administrator' => ADMINISTRATOR_PROJECT_ACCESS } end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fd3fdafde19202e633c53744176cd50492e11fa2..6faf8a5a490005221ff2e4f5ce0d69396fbf2f79 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -42749,6 +42749,9 @@ msgstr "" msgid "ProjectActivityRSS|Subscribe" msgstr "" +msgid "ProjectCreationLevel|Administrators" +msgstr "" + msgid "ProjectCreationLevel|Default project creation protection" msgstr "" diff --git a/spec/finders/groups/accepting_project_creations_finder_spec.rb b/spec/finders/groups/accepting_project_creations_finder_spec.rb index 61d673d6a9933c96476aed2e2a028a65ce765bf6..a9b630a836a176ebd3a579a1c030f3a6de4e107f 100644 --- a/spec/finders/groups/accepting_project_creations_finder_spec.rb +++ b/spec/finders/groups/accepting_project_creations_finder_spec.rb @@ -5,6 +5,10 @@ RSpec.describe Groups::AcceptingProjectCreationsFinder, feature_category: :groups_and_projects do let_it_be(:user) { create(:user) } let_it_be(:group_where_direct_owner) { create(:group) } + let_it_be(:group_where_direct_owner_with_admin_project_creation_level) do + create(:group, project_creation_level: Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS) + end + let_it_be(:subgroup_of_group_where_direct_owner) { create(:group, parent: group_where_direct_owner) } let_it_be(:group_where_direct_maintainer) { create(:group) } let_it_be(:group_where_direct_maintainer_but_cant_create_projects) do @@ -42,6 +46,7 @@ before do group_where_direct_owner.add_owner(user) + group_where_direct_owner_with_admin_project_creation_level.add_owner(user) group_where_direct_maintainer.add_maintainer(user) group_where_direct_developer_but_developers_cannot_create_projects.add_developer(user) group_where_direct_developer.add_developer(user) @@ -100,5 +105,26 @@ shared_with_group_where_direct_owner_as_developer ]) end + + context 'with admin user', :enable_admin_mode do + let_it_be(:user) { create(:admin) } + + it 'only returns groups where the user has access to create projects' do + expect(result).to match_array([ + group_where_direct_owner, + group_where_direct_owner_with_admin_project_creation_level, + subgroup_of_group_where_direct_owner, + group_where_direct_maintainer, + group_where_direct_developer, + # groups arising from group shares + shared_with_group_where_direct_owner_as_owner, + shared_with_group_where_direct_owner_as_maintainer, + subgroup_of_shared_with_group_where_direct_owner_as_maintainer, + shared_with_group_where_direct_developer_as_owner, + shared_with_group_where_direct_developer_as_maintainer, + shared_with_group_where_direct_owner_as_developer + ]) + end + end end end diff --git a/spec/finders/groups/accepting_project_transfers_finder_spec.rb b/spec/finders/groups/accepting_project_transfers_finder_spec.rb index 47c293bf74b9849820216cc0c3e030efb86609b6..b71b54447dc40d08009efd3e5cbd46f2993b9fc6 100644 --- a/spec/finders/groups/accepting_project_transfers_finder_spec.rb +++ b/spec/finders/groups/accepting_project_transfers_finder_spec.rb @@ -5,8 +5,13 @@ RSpec.describe Groups::AcceptingProjectTransfersFinder, feature_category: :groups_and_projects do let_it_be(:user) { create(:user) } let_it_be(:group_where_direct_owner) { create(:group) } + let_it_be(:group_where_direct_owner_with_admin_project_creation_level) do + create(:group, project_creation_level: Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS) + end + let_it_be(:subgroup_of_group_where_direct_owner) { create(:group, parent: group_where_direct_owner) } let_it_be(:group_where_direct_maintainer) { create(:group) } + let_it_be(:group_where_direct_maintainer_but_cant_create_projects) do create(:group, project_creation_level: Gitlab::Access::NO_ONE_PROJECT_ACCESS) end @@ -22,6 +27,7 @@ before do group_where_direct_owner.add_owner(user) + group_where_direct_owner_with_admin_project_creation_level.add_owner(user) group_where_direct_maintainer.add_maintainer(user) group_where_direct_developer.add_developer(user) @@ -63,5 +69,21 @@ subgroup_of_shared_with_group_where_direct_owner_as_maintainer ]) end + + context 'with admin user', :enable_admin_mode do + let_it_be(:user) { create(:admin) } + + it 'returns all accessible groups including with admin project creation level' do + expect(result).to match_array([ + group_where_direct_owner, + subgroup_of_group_where_direct_owner, + group_where_direct_maintainer, + shared_with_group_where_direct_owner_as_owner, + shared_with_group_where_direct_owner_as_maintainer, + subgroup_of_shared_with_group_where_direct_owner_as_maintainer, + group_where_direct_owner_with_admin_project_creation_level + ]) + end + end end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 215665a9b585b22afbbe41933de1baa38074c279..7ff9af46645c050541f96db86eeb9581c34d4302 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -5,10 +5,16 @@ RSpec.describe Group, feature_category: :groups_and_projects do include ReloadHelpers include StubGitlabCalls + include AdminModeHelper using RSpec::Parameterized::TableSyntax let!(:group) { create(:group) } + let(:developer_access) { Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS } + let(:maintainer_access) { Gitlab::Access::MAINTAINER_PROJECT_ACCESS } + let(:admin_access) { Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS } + let(:no_one_access) { Gitlab::Access::NO_ONE_PROJECT_ACCESS } + describe 'associations' do it { is_expected.to have_many :projects } it { is_expected.to have_many(:all_group_members).dependent(:destroy) } @@ -141,7 +147,7 @@ end describe '#namespace_members_and_requesters' do - let_it_be(:group) { create(:group) } + let_it_be_with_reload(:group) { create(:group) } let_it_be(:requester) { create(:user) } let_it_be(:developer) { create(:user) } let_it_be(:invited_member) { create(:group_member, :invited, :owner, group: group) } @@ -1207,8 +1213,6 @@ subject { described_class.excluding_restricted_visibility_levels_for_user(user1) } context 'with table syntax' do - using RSpec::Parameterized::TableSyntax - where(:restricted_visibility_levels, :expected_groups) do nil | lazy { [private_group, internal_group, group] } [] | lazy { [private_group, internal_group, group] } @@ -1241,32 +1245,39 @@ let_it_be(:group_1) { create(:group, project_creation_level: Gitlab::Access::NO_ONE_PROJECT_ACCESS) } let_it_be(:group_2) { create(:group, project_creation_level: Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) } let_it_be(:group_3) { create(:group, project_creation_level: Gitlab::Access::MAINTAINER_PROJECT_ACCESS) } - let_it_be(:group_4) { create(:group, project_creation_level: nil) } - - it 'only includes groups where project creation is allowed' do - expect(described_class).to receive(:excluding_restricted_visibility_levels_for_user).and_call_original - - result = described_class.project_creation_allowed(user1) - - expect(result).to include(group_2, group_3, group_4) - expect(result).not_to include(group_1) + let_it_be(:group_4) { create(:group, project_creation_level: Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS) } + let_it_be(:group_5) { create(:group, project_creation_level: nil) } # `nil` inherits `default_project_creation` + let_it_be(:all_groups) { described_class.id_in([group_1, group_2, group_3, group_4, group_5]) } + + where(:admin_user?, :admin_mode, :default_project_creation, :expected_groups) do + false | false | Gitlab::Access::NO_ONE_PROJECT_ACCESS | lazy { [group_2, group_3] } + false | false | Gitlab::Access::MAINTAINER_PROJECT_ACCESS | lazy { [group_2, group_3, group_5] } + false | false | Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS | lazy { [group_2, group_3, group_5] } + false | false | Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS | lazy { [group_2, group_3] } + true | false | Gitlab::Access::NO_ONE_PROJECT_ACCESS | lazy { [group_2, group_3] } + true | false | Gitlab::Access::MAINTAINER_PROJECT_ACCESS | lazy { [group_2, group_3, group_5] } + true | false | Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS | lazy { [group_2, group_3, group_5] } + true | false | Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS | lazy { [group_2, group_3] } + true | true | Gitlab::Access::NO_ONE_PROJECT_ACCESS | lazy { [group_2, group_3, group_4] } + true | true | Gitlab::Access::MAINTAINER_PROJECT_ACCESS | lazy { [group_2, group_3, group_4, group_5] } + true | true | Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS | lazy { [group_2, group_3, group_4, group_5] } + true | true | Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS | lazy { [group_2, group_3, group_4, group_5] } end - context 'when the application_setting is set to `NO_ONE_PROJECT_ACCESS`' do + with_them do + let(:user) { admin_user? ? create(:admin) : create(:user) } + before do - stub_application_setting(default_project_creation: Gitlab::Access::NO_ONE_PROJECT_ACCESS) + enable_admin_mode!(user) if admin_mode + stub_application_setting(default_project_creation: default_project_creation) end - it 'only includes groups where project creation is allowed' do + it 'returns expected groups' do expect(described_class).to receive(:excluding_restricted_visibility_levels_for_user).and_call_original - result = described_class.project_creation_allowed(user1) - - expect(result).to include(group_2, group_3) + result = all_groups.project_creation_allowed(user) - # group_4 won't be included because it has `project_creation_level: nil`, - # and that means it behaves like the value of the application_setting will inherited. - expect(result).not_to include(group_1, group_4) + expect(result).to match_array(expected_groups) end end end @@ -1443,6 +1454,65 @@ end end + describe '.project_creation_levels_for_user' do + where(:admin_user?, :admin_mode, :default_project_creation, :expected_levels) do + false | false | no_one_access | lazy { [developer_access, maintainer_access] } + false | false | admin_access | lazy { [developer_access, maintainer_access] } + false | false | maintainer_access | lazy { [developer_access, maintainer_access, nil] } + false | false | developer_access | lazy { [developer_access, maintainer_access, nil] } + true | false | no_one_access | lazy { [developer_access, maintainer_access] } + true | false | admin_access | lazy { [developer_access, maintainer_access] } + true | false | maintainer_access | lazy { [developer_access, maintainer_access, nil] } + true | false | developer_access | lazy { [developer_access, maintainer_access, nil] } + true | true | no_one_access | lazy { [developer_access, maintainer_access, admin_access] } + true | true | admin_access | lazy { [developer_access, maintainer_access, admin_access, nil] } + true | true | maintainer_access | lazy { [developer_access, maintainer_access, admin_access, nil] } + true | true | developer_access | lazy { [developer_access, maintainer_access, admin_access, nil] } + end + + with_them do + let(:user) { admin_user? ? create(:admin) : create(:user) } + + before do + stub_application_setting(default_project_creation: default_project_creation) + enable_admin_mode!(user) if admin_mode + end + + it 'returns correct project creation levels' do + expect(described_class.project_creation_levels_for_user(user)).to match_array(expected_levels) + end + end + end + + describe '.prevent_project_creation?' do + where(:admin_user?, :admin_mode, :project_creation_setting, :expected_result) do + false | false | lazy { no_one_access } | true + false | false | lazy { admin_access } | true + false | false | lazy { maintainer_access } | false + false | false | lazy { developer_access } | false + true | false | lazy { no_one_access } | true + true | false | lazy { admin_access } | true + true | false | lazy { maintainer_access } | false + true | false | lazy { developer_access } | false + true | true | lazy { no_one_access } | true + true | true | lazy { admin_access } | false + true | true | lazy { maintainer_access } | false + true | true | lazy { developer_access } | false + end + + with_them do + let(:user) { admin_user? ? create(:admin) : create(:user) } + + before do + enable_admin_mode!(user) if admin_mode + end + + subject { described_class.prevent_project_creation?(user, project_creation_setting) } + + it { is_expected.to be(expected_result) } + end + end + describe '#to_reference' do it 'returns a String reference to the object' do expect(group.to_reference).to eq "@#{group.name}" diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 8eb243dc295de78062aa1b3024af68304bf35f41..b2dbe59241ebd28958c71de81a3e61b56ccd9267 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -576,123 +576,42 @@ end context 'create_projects' do - context 'when group has no project creation level set' do - before do - group.update!(project_creation_level: nil) - end - - context 'reporter' do - let(:current_user) { reporter } - - it { is_expected.to be_disallowed(:create_projects) } + context 'without visibility levels restricted' do + where(:project_creation_level, :current_user, :create_projects_allowed?) do + nil | lazy { reporter } | false + nil | lazy { developer } | true + nil | lazy { maintainer } | true + nil | lazy { owner } | true + nil | lazy { admin } | true + ::Gitlab::Access::NO_ONE_PROJECT_ACCESS | lazy { reporter } | false + ::Gitlab::Access::NO_ONE_PROJECT_ACCESS | lazy { developer } | false + ::Gitlab::Access::NO_ONE_PROJECT_ACCESS | lazy { maintainer } | false + ::Gitlab::Access::NO_ONE_PROJECT_ACCESS | lazy { owner } | false + ::Gitlab::Access::NO_ONE_PROJECT_ACCESS | lazy { admin } | false + ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS | lazy { reporter } | false + ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS | lazy { developer } | false + ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS | lazy { maintainer } | true + ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS | lazy { owner } | true + ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS | lazy { admin } | true + ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS | lazy { reporter } | false + ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS | lazy { developer } | true + ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS | lazy { maintainer } | true + ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS | lazy { owner } | true + ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS | lazy { admin } | true + ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS | lazy { reporter } | false + ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS | lazy { developer } | false + ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS | lazy { maintainer } | false + ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS | lazy { owner } | false + ::Gitlab::Access::ADMINISTRATOR_PROJECT_ACCESS | lazy { admin } | true end - context 'developer' do - let(:current_user) { developer } - - it { is_expected.to be_allowed(:create_projects) } - end - - context 'maintainer' do - let(:current_user) { maintainer } - - it { is_expected.to be_allowed(:create_projects) } - end - - context 'owner' do - let(:current_user) { owner } - - it { is_expected.to be_allowed(:create_projects) } - end - end - - context 'when group has project creation level set to no one' do - before do - group.update!(project_creation_level: ::Gitlab::Access::NO_ONE_PROJECT_ACCESS) - end - - context 'reporter' do - let(:current_user) { reporter } - - it { is_expected.to be_disallowed(:create_projects) } - end - - context 'developer' do - let(:current_user) { developer } - - it { is_expected.to be_disallowed(:create_projects) } - end - - context 'maintainer' do - let(:current_user) { maintainer } - - it { is_expected.to be_disallowed(:create_projects) } - end - - context 'owner' do - let(:current_user) { owner } - - it { is_expected.to be_disallowed(:create_projects) } - end - end - - context 'when group has project creation level set to maintainer only' do - before do - group.update!(project_creation_level: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS) - end - - context 'reporter' do - let(:current_user) { reporter } - - it { is_expected.to be_disallowed(:create_projects) } - end - - context 'developer' do - let(:current_user) { developer } - - it { is_expected.to be_disallowed(:create_projects) } - end - - context 'maintainer' do - let(:current_user) { maintainer } - - it { is_expected.to be_allowed(:create_projects) } - end - - context 'owner' do - let(:current_user) { owner } - - it { is_expected.to be_allowed(:create_projects) } - end - end - - context 'when group has project creation level set to developers + maintainer' do - before do - group.update!(project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) - end - - context 'reporter' do - let(:current_user) { reporter } - - it { is_expected.to be_disallowed(:create_projects) } - end - - context 'developer' do - let(:current_user) { developer } - - it { is_expected.to be_allowed(:create_projects) } - end - - context 'maintainer' do - let(:current_user) { maintainer } - - it { is_expected.to be_allowed(:create_projects) } - end - - context 'owner' do - let(:current_user) { owner } + with_them do + before do + group.update!(project_creation_level: project_creation_level) + enable_admin_mode!(current_user) if current_user.admin? + end - it { is_expected.to be_allowed(:create_projects) } + it { is_expected.to(create_projects_allowed? ? be_allowed(:create_projects) : be_disallowed(:create_projects)) } end end