summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGosia Ksionek <mksionek@gitlab.com>2019-04-05 18:49:46 +0000
committerDouglas Barbosa Alexandre <dbalexandre@gmail.com>2019-04-05 18:49:46 +0000
commit64858317adc4f017fe589342155faba9df31f093 (patch)
tree79a7db7e36ca4c6cd5b2b9090030e4a397e94ab4
parent8cdda8f79a1f747e2f8e393ca505d3142a958033 (diff)
downloadgitlab-ce-64858317adc4f017fe589342155faba9df31f093.tar.gz
Add part of needed code
Add columns to store project creation settings Add project creation level column in groups and default project creation column in application settings Remove obsolete line from schema Update migration with project_creation_level column existence check Rename migrations to avoid conflicts Update migration methods Update migration method
-rw-r--r--app/controllers/admin/groups_controller.rb3
-rw-r--r--app/controllers/groups_controller.rb3
-rw-r--r--app/helpers/application_settings_helper.rb1
-rw-r--r--app/helpers/namespaces_helper.rb7
-rw-r--r--app/models/application_setting_implementation.rb1
-rw-r--r--app/models/group.rb4
-rw-r--r--app/models/user.rb40
-rw-r--r--app/policies/group_policy.rb11
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml4
-rw-r--r--app/views/groups/_group_admin_settings.html.haml4
-rw-r--r--app/views/groups/settings/_permissions.html.haml1
-rw-r--r--app/views/groups/settings/_project_creation_level.html.haml3
-rw-r--r--app/views/projects/_new_project_fields.html.haml6
-rw-r--r--changelogs/unreleased/move-allow-developers-to-create-projects-in-groups-to-core.yml5
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--db/migrate/20190311132500_add_default_project_creation_application_setting.rb22
-rw-r--r--db/migrate/20190311132527_add_project_creation_level_to_namespaces.rb22
-rw-r--r--db/schema.rb2
-rw-r--r--doc/user/group/index.md11
-rw-r--r--lib/api/settings.rb3
-rw-r--r--lib/gitlab/access.rb21
-rw-r--r--locale/gitlab.pot15
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb7
-rw-r--r--spec/controllers/admin/groups_controller_spec.rb6
-rw-r--r--spec/controllers/groups_controller_spec.rb7
-rw-r--r--spec/factories/groups.rb1
-rw-r--r--spec/features/groups/group_settings_spec.rb8
-rw-r--r--spec/features/projects/new_project_spec.rb19
-rw-r--r--spec/features/projects/user_creates_project_spec.rb27
-rw-r--r--spec/helpers/namespaces_helper_spec.rb72
-rw-r--r--spec/models/group_spec.rb8
-rw-r--r--spec/policies/group_policy_spec.rb114
-rw-r--r--spec/requests/api/settings_spec.rb4
33 files changed, 447 insertions, 16 deletions
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index e0ecdb0c0e9..15f7ef881c8 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -89,7 +89,8 @@ class Admin::GroupsController < Admin::ApplicationController
:request_access_enabled,
:visibility_level,
:require_two_factor_authentication,
- :two_factor_grace_period
+ :two_factor_grace_period,
+ :project_creation_level
]
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 87b8ef03313..e936d771502 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -187,7 +187,8 @@ class GroupsController < Groups::ApplicationController
:create_chat_team,
:chat_team_name,
:require_two_factor_authentication,
- :two_factor_grace_period
+ :two_factor_grace_period,
+ :project_creation_level
]
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index e635f608237..e275e4278a4 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -137,6 +137,7 @@ module ApplicationSettingsHelper
:default_artifacts_expire_in,
:default_branch_protection,
:default_group_visibility,
+ :default_project_creation,
:default_project_visibility,
:default_projects_limit,
:default_snippet_visibility,
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index ea3bcfc791a..572d68cb4a3 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -49,6 +49,13 @@ module NamespacesHelper
end
end
+ def namespaces_options_with_developer_maintainer_access(options = {})
+ selected = options.delete(:selected) || :current_user
+ options[:groups] = current_user.manageable_groups_with_routes(include_groups_with_developer_maintainer_access: true)
+
+ namespaces_options(selected, options)
+ end
+
private
# Many importers create a temporary Group, so use the real
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 265aa1d4965..b413ffddb9d 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -26,6 +26,7 @@ module ApplicationSettingImplementation
default_artifacts_expire_in: '30 days',
default_branch_protection: Settings.gitlab['default_branch_protection'],
default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
+ default_project_creation: Settings.gitlab['default_project_creation'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
diff --git a/app/models/group.rb b/app/models/group.rb
index ac66815705c..8bc9b75f0a9 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -404,6 +404,10 @@ class Group < Namespace
Feature.enabled?(:group_clusters, root_ancestor, default_enabled: true)
end
+ def project_creation_level
+ super || ::Gitlab::CurrentSettings.default_project_creation
+ end
+
private
def update_two_factor_requirement
diff --git a/app/models/user.rb b/app/models/user.rb
index 259889995d3..d3524bfd6ae 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -105,6 +105,7 @@ class User < ApplicationRecord
has_many :groups, through: :group_members
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
+ has_many :developer_groups, -> { where(members: { access_level: ::Gitlab::Access::DEVELOPER }) }, through: :group_members, source: :group
has_many :owned_or_maintainers_groups,
-> { where(members: { access_level: [Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
through: :group_members,
@@ -883,7 +884,12 @@ class User < ApplicationRecord
# rubocop: enable CodeReuse/ServiceClass
def several_namespaces?
- owned_groups.any? || maintainers_groups.any?
+ union_sql = ::Gitlab::SQL::Union.new(
+ [owned_groups,
+ maintainers_groups,
+ groups_with_developer_maintainer_project_access]).to_sql
+
+ ::Group.from("(#{union_sql}) #{::Group.table_name}").any?
end
def namespace_id
@@ -1169,12 +1175,24 @@ class User < ApplicationRecord
@manageable_namespaces ||= [namespace] + manageable_groups
end
- def manageable_groups
- Gitlab::ObjectHierarchy.new(owned_or_maintainers_groups).base_and_descendants
+ def manageable_groups(include_groups_with_developer_maintainer_access: false)
+ owned_and_maintainer_group_hierarchy = Gitlab::ObjectHierarchy.new(owned_or_maintainers_groups).base_and_descendants
+
+ if include_groups_with_developer_maintainer_access
+ union_sql = ::Gitlab::SQL::Union.new(
+ [owned_and_maintainer_group_hierarchy,
+ groups_with_developer_maintainer_project_access]).to_sql
+
+ ::Group.from("(#{union_sql}) #{::Group.table_name}")
+ else
+ owned_and_maintainer_group_hierarchy
+ end
end
- def manageable_groups_with_routes
- manageable_groups.eager_load(:route).order('routes.path')
+ def manageable_groups_with_routes(include_groups_with_developer_maintainer_access: false)
+ manageable_groups(include_groups_with_developer_maintainer_access: include_groups_with_developer_maintainer_access)
+ .eager_load(:route)
+ .order('routes.path')
end
def namespaces
@@ -1573,4 +1591,16 @@ class User < ApplicationRecord
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end
+
+ def groups_with_developer_maintainer_project_access
+ project_creation_levels = [::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS]
+
+ if ::Gitlab::CurrentSettings.default_project_creation == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS
+ project_creation_levels << nil
+ end
+
+ developer_groups_hierarchy = ::Gitlab::ObjectHierarchy.new(developer_groups).base_and_descendants
+ ::Group.where(id: developer_groups_hierarchy.select(:id),
+ project_creation_level: project_creation_levels)
+ end
end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index db49d3bed9c..eb2e536e8e9 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -35,6 +35,14 @@ class GroupPolicy < BasePolicy
with_options scope: :subject, score: 0
condition(:request_access_enabled) { @subject.request_access_enabled }
+ condition(:create_projects_disabled) do
+ @subject.project_creation_level == ::Gitlab::Access::NO_ONE_PROJECT_ACCESS
+ end
+
+ condition(:developer_maintainer_access) do
+ @subject.project_creation_level == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS
+ end
+
rule { public_group }.policy do
enable :read_group
enable :read_list
@@ -115,6 +123,9 @@ class GroupPolicy < BasePolicy
rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster
+ rule { developer & developer_maintainer_access }.enable :create_projects
+ rule { create_projects_disabled }.prevent :create_projects
+
def access_level
return GroupMember::NO_ACCESS if @user.nil?
diff --git a/app/views/admin/application_settings/_visibility_and_access.html.haml b/app/views/admin/application_settings/_visibility_and_access.html.haml
index 8122d81f578..03ef2924617 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -5,7 +5,9 @@
.form-group
= f.label :default_branch_protection, class: 'label-bold'
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
- = render_if_exists 'admin/application_settings/project_creation_level', form: f, application_setting: @application_setting
+ .form-group
+ = f.label s_('ProjectCreationLevel|Default project creation protection'), class: 'label-bold'
+ = f.select :default_project_creation, options_for_select(Gitlab::Access.project_creation_options, @application_setting.default_project_creation), {}, class: 'form-control'
.form-group.visibility-level-setting
= f.label :default_project_visibility, class: 'label-bold'
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml
index ff59013ed67..7390c42aba2 100644
--- a/app/views/groups/_group_admin_settings.html.haml
+++ b/app/views/groups/_group_admin_settings.html.haml
@@ -9,6 +9,10 @@
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
%br/
%span.descr This setting can be overridden in each project.
+.form-group.row
+ = f.label s_('ProjectCreationLevel|Allowed to create projects'), class: 'col-form-label col-sm-2'
+ .col-sm-10
+ = f.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, @group.project_creation_level), {}, class: 'form-control'
.form-group.row
= f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2 pt-0'
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index 6b0a6e7ed99..0a14830c666 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -18,6 +18,7 @@
%span.descr.text-muted= share_with_group_lock_help_text(@group)
= render 'groups/settings/lfs', f: f
+ = render 'groups/settings/project_creation_level', f: f, group: @group
= render 'groups/settings/two_factor_auth', f: f
= render_if_exists 'groups/member_lock_setting', f: f, group: @group
diff --git a/app/views/groups/settings/_project_creation_level.html.haml b/app/views/groups/settings/_project_creation_level.html.haml
new file mode 100644
index 00000000000..9f711e6aade
--- /dev/null
+++ b/app/views/groups/settings/_project_creation_level.html.haml
@@ -0,0 +1,3 @@
+.form-group
+ = f.label s_('ProjectCreationLevel|Allowed to create projects'), class: 'label-bold'
+ = f.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, group.project_creation_level), {}, class: 'form-control'
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index 5129f11875c..1c1c7d832bd 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -19,9 +19,9 @@
= root_url
- namespace_id = namespace_id_from(params)
= f.select(:namespace_id,
- namespaces_options(namespace_id || :current_user,
- display_path: true,
- extra_group: namespace_id),
+ namespaces_options_with_developer_maintainer_access(selected: namespace_id,
+ display_path: true,
+ extra_group: namespace_id),
{},
{ class: 'select2 js-select-namespace qa-project-namespace-select block-truncated', tabindex: 1, data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_path", track_value: "" }})
diff --git a/changelogs/unreleased/move-allow-developers-to-create-projects-in-groups-to-core.yml b/changelogs/unreleased/move-allow-developers-to-create-projects-in-groups-to-core.yml
new file mode 100644
index 00000000000..34fd0c1b787
--- /dev/null
+++ b/changelogs/unreleased/move-allow-developers-to-create-projects-in-groups-to-core.yml
@@ -0,0 +1,5 @@
+---
+title: Move allow developers to create projects in groups to Core
+merge_request: 25975
+author:
+type: added
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 01ffcade931..3c426cdb969 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -126,6 +126,7 @@ Settings['issues_tracker'] ||= {}
# GitLab
#
Settings['gitlab'] ||= Settingslogic.new({})
+Settings.gitlab['default_project_creation'] ||= ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS
Settings.gitlab['default_projects_limit'] ||= 100000
Settings.gitlab['default_branch_protection'] ||= 2
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
diff --git a/db/migrate/20190311132500_add_default_project_creation_application_setting.rb b/db/migrate/20190311132500_add_default_project_creation_application_setting.rb
new file mode 100644
index 00000000000..87427ad2930
--- /dev/null
+++ b/db/migrate/20190311132500_add_default_project_creation_application_setting.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddDefaultProjectCreationApplicationSetting < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ unless column_exists?(:application_settings, :default_project_creation)
+ add_column(:application_settings, :default_project_creation, :integer, default: 2, null: false)
+ end
+ end
+
+ def down
+ if column_exists?(:application_settings, :default_project_creation)
+ remove_column(:application_settings, :default_project_creation)
+ end
+ end
+end
diff --git a/db/migrate/20190311132527_add_project_creation_level_to_namespaces.rb b/db/migrate/20190311132527_add_project_creation_level_to_namespaces.rb
new file mode 100644
index 00000000000..159e0a95ace
--- /dev/null
+++ b/db/migrate/20190311132527_add_project_creation_level_to_namespaces.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddProjectCreationLevelToNamespaces < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ unless column_exists?(:namespaces, :project_creation_level)
+ add_column :namespaces, :project_creation_level, :integer
+ end
+ end
+
+ def down
+ unless column_exists?(:namespaces, :project_creation_level)
+ remove_column :namespaces, :project_creation_level, :integer
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b20fe4b3d39..1a50c6efbc7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -177,6 +177,7 @@ ActiveRecord::Schema.define(version: 20190325165127) do
t.string "runners_registration_token_encrypted"
t.integer "local_markdown_version", default: 0, null: false
t.integer "first_day_of_week", default: 0, null: false
+ t.integer "default_project_creation", default: 2, null: false
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
end
@@ -1391,6 +1392,7 @@ ActiveRecord::Schema.define(version: 20190325165127) do
t.integer "cached_markdown_version"
t.string "runners_token"
t.string "runners_token_encrypted"
+ t.integer "project_creation_level"
t.boolean "auto_devops_enabled"
t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
t.index ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 3bcfd30079d..2f1cadb2bbc 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -151,6 +151,17 @@ There are two different ways to add a new project to a group:
![Select group](img/select_group_dropdown.png)
+### Default project creation level
+
+Group owners or administrators can allow users with the
+Developer role to create projects under groups.
+
+By default, [Developers and Maintainers](../permissions.md##group-members-permissions) can create projects under agroup, but this can be changed either within the group settings for a group, or
+be set globally by a GitLab administrator in the Admin area
+at **Settings > General > Visibility and access controls**.
+
+Available settings are `No one`, `Maintainers`, or `Developers + Maintainers`.
+
## Transfer projects into groups
Learn how to [transfer a project into a group](../project/settings/index.md#transferring-an-existing-project-into-another-namespace).
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index d742c6c97c1..d96cdc31212 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -40,7 +40,8 @@ module API
end
optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts"
- optional :default_branch_protection, type: Integer, values: Gitlab::Access.protection_values, desc: 'Determine if developers can push to master'
+ optional :default_project_creation, type: Integer, values: ::Gitlab::Access.project_creation_values, desc: 'Determine if developers can create projects in the group'
+ optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to master'
optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index 6c8ca8f219c..6eb08f674c2 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -24,6 +24,11 @@ module Gitlab
PROTECTION_FULL = 2
PROTECTION_DEV_CAN_MERGE = 3
+ # Default project creation level
+ NO_ONE_PROJECT_ACCESS = 0
+ MAINTAINER_PROJECT_ACCESS = 1
+ DEVELOPER_MAINTAINER_PROJECT_ACCESS = 2
+
class << self
delegate :values, to: :options
@@ -85,6 +90,22 @@ module Gitlab
def human_access_with_none(access)
options_with_none.key(access)
end
+
+ 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
+ }
+ end
+
+ def project_creation_values
+ project_creation_options.values
+ end
+
+ def project_creation_level_name(name)
+ project_creation_options.key(name)
+ end
end
def human_access
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 554bb55f9ef..1166134347c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6418,6 +6418,21 @@ msgstr ""
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|Maintainers"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFileTree|Name"
msgstr ""
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index 9af472df74e..1a7be4c9a85 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -85,6 +85,13 @@ describe Admin::ApplicationSettingsController do
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
end
+
+ it 'updates the default_project_creation for string value' do
+ put :update, params: { application_setting: { default_project_creation: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } }
+
+ expect(response).to redirect_to(admin_application_settings_path)
+ expect(ApplicationSetting.current.default_project_creation).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
+ end
end
describe 'PUT #reset_registration_token' do
diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb
index 647fce0ecef..22165faa625 100644
--- a/spec/controllers/admin/groups_controller_spec.rb
+++ b/spec/controllers/admin/groups_controller_spec.rb
@@ -60,5 +60,11 @@ describe Admin::GroupsController do
expect(response).to redirect_to(admin_group_path(group))
expect(group.users).not_to include group_user
end
+
+ it 'updates the project_creation_level successfully' do
+ expect do
+ post :update, params: { id: group.to_param, group: { project_creation_level: ::Gitlab::Access::NO_ONE_PROJECT_ACCESS } }
+ end.to change { group.reload.project_creation_level }.to(::Gitlab::Access::NO_ONE_PROJECT_ACCESS)
+ end
end
end
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 2b803e7151f..4a28a27da79 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -349,6 +349,13 @@ describe GroupsController do
expect(assigns(:group).errors).not_to be_empty
expect(assigns(:group).path).not_to eq('new_path')
end
+
+ it 'updates the project_creation_level successfully' do
+ post :update, params: { id: group.to_param, group: { project_creation_level: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS } }
+
+ expect(response).to have_gitlab_http_status(302)
+ expect(group.reload.project_creation_level).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
+ end
end
describe '#ensure_canonical_path' do
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index dcef8571f41..18a0c2ec731 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -4,6 +4,7 @@ FactoryBot.define do
path { name.downcase.gsub(/\s/, '_') }
type 'Group'
owner nil
+ project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS
after(:create) do |group|
if group.owner
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 378e4d5febc..5cef5f0521f 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -77,6 +77,14 @@ describe 'Edit group settings' do
end
end
+ describe 'project creation level menu' do
+ it 'shows the selection menu' do
+ visit edit_group_path(group)
+
+ expect(page).to have_content('Allowed to create projects')
+ end
+ end
+
describe 'edit group avatar' do
before do
visit edit_group_path(group)
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index 75c72a68069..b54ea929978 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -252,4 +252,23 @@ describe 'New project' do
end
end
end
+
+ context 'Namespace selector' do
+ context 'with group with DEVELOPER_MAINTAINER_PROJECT_ACCESS project_creation_level' do
+ let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
+
+ before do
+ group.add_developer(user)
+ visit new_project_path(namespace_id: group.id)
+ end
+
+ it 'selects the group namespace' do
+ page.within('#blank-project-pane') do
+ namespace = find('#project_namespace_id option[selected]')
+
+ expect(namespace.text).to eq group.full_path
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
index 8d7e2883b2a..c0932539131 100644
--- a/spec/features/projects/user_creates_project_spec.rb
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -54,4 +54,31 @@ describe 'User creates a project', :js do
expect(project.namespace).to eq(subgroup)
end
end
+
+ context 'in a group with DEVELOPER_MAINTAINER_PROJECT_ACCESS project_creation_level' do
+ let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
+
+ before do
+ group.add_developer(user)
+ end
+
+ it 'creates a new project' do
+ visit(new_project_path)
+
+ fill_in :project_name, with: 'a-new-project'
+ fill_in :project_path, with: 'a-new-project'
+
+ page.find('.js-select-namespace').click
+ page.find("div[role='option']", text: group.full_path).click
+
+ page.within('#content-body') do
+ click_button('Create project')
+ end
+
+ expect(page).to have_content("Project 'a-new-project' was successfully created")
+
+ project = Project.find_by(name: 'a-new-project')
+ expect(project.namespace).to eq(group)
+ end
+ end
end
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
index 7ccbdcd1332..601f864ef36 100644
--- a/spec/helpers/namespaces_helper_spec.rb
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -1,10 +1,38 @@
require 'spec_helper'
-describe NamespacesHelper do
+describe NamespacesHelper, :postgresql do
let!(:admin) { create(:admin) }
- let!(:admin_group) { create(:group, :private) }
+ let!(:admin_project_creation_level) { nil }
+ let!(:admin_group) do
+ create(:group,
+ :private,
+ project_creation_level: admin_project_creation_level)
+ end
let!(:user) { create(:user) }
- let!(:user_group) { create(:group, :private) }
+ let!(:user_project_creation_level) { nil }
+ let!(:user_group) do
+ create(:group,
+ :private,
+ project_creation_level: user_project_creation_level)
+ end
+ let!(:subgroup1) do
+ create(:group,
+ :private,
+ parent: admin_group,
+ project_creation_level: nil)
+ end
+ let!(:subgroup2) do
+ create(:group,
+ :private,
+ parent: admin_group,
+ project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS)
+ end
+ let!(:subgroup3) do
+ create(:group,
+ :private,
+ parent: admin_group,
+ project_creation_level: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
+ end
before do
admin_group.add_owner(admin)
@@ -105,5 +133,43 @@ describe NamespacesHelper do
helper.namespaces_options
end
end
+
+ describe 'include_groups_with_developer_maintainer_access parameter' do
+ context 'when DEVELOPER_MAINTAINER_PROJECT_ACCESS is set for a project' do
+ let!(:admin_project_creation_level) { ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS }
+
+ it 'returns groups where user is a developer' do
+ allow(helper).to receive(:current_user).and_return(user)
+ stub_application_setting(default_project_creation: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
+ admin_group.add_user(user, GroupMember::DEVELOPER)
+
+ options = helper.namespaces_options_with_developer_maintainer_access
+
+ expect(options).to include(admin_group.name)
+ expect(options).not_to include(subgroup1.name)
+ expect(options).to include(subgroup2.name)
+ expect(options).not_to include(subgroup3.name)
+ expect(options).to include(user_group.name)
+ expect(options).to include(user.name)
+ end
+ end
+
+ context 'when DEVELOPER_MAINTAINER_PROJECT_ACCESS is set globally' do
+ it 'return groups where default is not overridden' do
+ allow(helper).to receive(:current_user).and_return(user)
+ stub_application_setting(default_project_creation: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS)
+ admin_group.add_user(user, GroupMember::DEVELOPER)
+
+ options = helper.namespaces_options_with_developer_maintainer_access
+
+ expect(options).to include(admin_group.name)
+ expect(options).to include(subgroup1.name)
+ expect(options).to include(subgroup2.name)
+ expect(options).not_to include(subgroup3.name)
+ expect(options).to include(user_group.name)
+ expect(options).to include(user.name)
+ end
+ end
+ end
end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index ad3e3061b9a..e6e7298a043 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -959,4 +959,12 @@ describe Group do
end
end
end
+
+ describe 'project_creation_level' do
+ it 'outputs the default one if it is nil' do
+ group = create(:group, project_creation_level: nil)
+
+ expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation)
+ end
+ end
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index dc98baca6dc..59f3a961d50 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -347,6 +347,120 @@ describe GroupPolicy do
end
end
+ context "create_projects" do
+ context 'when group has no project creation level set' do
+ let(:group) { create(:group, project_creation_level: nil) }
+
+ 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 }
+
+ it { is_expected.to be_allowed(:create_projects) }
+ end
+ end
+
+ context 'when group has project creation level set to no one' do
+ let(:group) { create(:group, project_creation_level: ::Gitlab::Access::NO_ONE_PROJECT_ACCESS) }
+
+ 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
+ let(:group) { create(:group, project_creation_level: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS) }
+
+ 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
+ let(:group) { create(:group, project_creation_level: ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
+
+ 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 }
+
+ it { is_expected.to be_allowed(:create_projects) }
+ end
+ end
+ end
+
it_behaves_like 'clusterable policies' do
let(:clusterable) { create(:group) }
let(:cluster) do
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index f33eb5b9e02..f869325e892 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -44,6 +44,7 @@ describe API::Settings, 'Settings' do
put api("/application/settings", admin),
params: {
default_projects_limit: 3,
+ default_project_creation: 2,
password_authentication_enabled_for_web: false,
repository_storages: ['custom'],
plantuml_enabled: true,
@@ -64,12 +65,13 @@ describe API::Settings, 'Settings' do
performance_bar_allowed_group_path: group.full_path,
instance_statistics_visibility_private: true,
diff_max_patch_bytes: 150_000,
- default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE,
+ default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE,
local_markdown_version: 3
}
expect(response).to have_gitlab_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
+ expect(json_response['default_project_creation']).to eq(::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS)
expect(json_response['password_authentication_enabled_for_web']).to be_falsey
expect(json_response['repository_storages']).to eq(['custom'])
expect(json_response['plantuml_enabled']).to be_truthy