Merge branch 'move-allow-developers-to-create-projects-in-groups-to-core' into 'master'
Move allow developers to create projects in groups to Core See merge request gitlab-org/gitlab-ce!25975
This commit is contained in:
commit
097b63fcb3
|
@ -89,7 +89,8 @@ class Admin::GroupsController < Admin::ApplicationController
|
||||||
:request_access_enabled,
|
:request_access_enabled,
|
||||||
:visibility_level,
|
:visibility_level,
|
||||||
:require_two_factor_authentication,
|
:require_two_factor_authentication,
|
||||||
:two_factor_grace_period
|
:two_factor_grace_period,
|
||||||
|
:project_creation_level
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -187,7 +187,8 @@ class GroupsController < Groups::ApplicationController
|
||||||
:create_chat_team,
|
:create_chat_team,
|
||||||
:chat_team_name,
|
:chat_team_name,
|
||||||
:require_two_factor_authentication,
|
:require_two_factor_authentication,
|
||||||
:two_factor_grace_period
|
:two_factor_grace_period,
|
||||||
|
:project_creation_level
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ module ApplicationSettingsHelper
|
||||||
:default_artifacts_expire_in,
|
:default_artifacts_expire_in,
|
||||||
:default_branch_protection,
|
:default_branch_protection,
|
||||||
:default_group_visibility,
|
:default_group_visibility,
|
||||||
|
:default_project_creation,
|
||||||
:default_project_visibility,
|
:default_project_visibility,
|
||||||
:default_projects_limit,
|
:default_projects_limit,
|
||||||
:default_snippet_visibility,
|
:default_snippet_visibility,
|
||||||
|
|
|
@ -49,6 +49,13 @@ module NamespacesHelper
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
# Many importers create a temporary Group, so use the real
|
# Many importers create a temporary Group, so use the real
|
||||||
|
|
|
@ -26,6 +26,7 @@ module ApplicationSettingImplementation
|
||||||
default_artifacts_expire_in: '30 days',
|
default_artifacts_expire_in: '30 days',
|
||||||
default_branch_protection: Settings.gitlab['default_branch_protection'],
|
default_branch_protection: Settings.gitlab['default_branch_protection'],
|
||||||
default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
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_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
||||||
default_projects_limit: Settings.gitlab['default_projects_limit'],
|
default_projects_limit: Settings.gitlab['default_projects_limit'],
|
||||||
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
||||||
|
|
|
@ -404,6 +404,10 @@ class Group < Namespace
|
||||||
Feature.enabled?(:group_clusters, root_ancestor, default_enabled: true)
|
Feature.enabled?(:group_clusters, root_ancestor, default_enabled: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def project_creation_level
|
||||||
|
super || ::Gitlab::CurrentSettings.default_project_creation
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def update_two_factor_requirement
|
def update_two_factor_requirement
|
||||||
|
|
|
@ -105,6 +105,7 @@ class User < ApplicationRecord
|
||||||
has_many :groups, through: :group_members
|
has_many :groups, through: :group_members
|
||||||
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
|
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 :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,
|
has_many :owned_or_maintainers_groups,
|
||||||
-> { where(members: { access_level: [Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
|
-> { where(members: { access_level: [Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
|
||||||
through: :group_members,
|
through: :group_members,
|
||||||
|
@ -883,7 +884,12 @@ class User < ApplicationRecord
|
||||||
# rubocop: enable CodeReuse/ServiceClass
|
# rubocop: enable CodeReuse/ServiceClass
|
||||||
|
|
||||||
def several_namespaces?
|
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
|
end
|
||||||
|
|
||||||
def namespace_id
|
def namespace_id
|
||||||
|
@ -1169,12 +1175,24 @@ class User < ApplicationRecord
|
||||||
@manageable_namespaces ||= [namespace] + manageable_groups
|
@manageable_namespaces ||= [namespace] + manageable_groups
|
||||||
end
|
end
|
||||||
|
|
||||||
def manageable_groups
|
def manageable_groups(include_groups_with_developer_maintainer_access: false)
|
||||||
Gitlab::ObjectHierarchy.new(owned_or_maintainers_groups).base_and_descendants
|
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
|
end
|
||||||
|
|
||||||
def manageable_groups_with_routes
|
def manageable_groups_with_routes(include_groups_with_developer_maintainer_access: false)
|
||||||
manageable_groups.eager_load(:route).order('routes.path')
|
manageable_groups(include_groups_with_developer_maintainer_access: include_groups_with_developer_maintainer_access)
|
||||||
|
.eager_load(:route)
|
||||||
|
.order('routes.path')
|
||||||
end
|
end
|
||||||
|
|
||||||
def namespaces
|
def namespaces
|
||||||
|
@ -1573,4 +1591,16 @@ class User < ApplicationRecord
|
||||||
ensure
|
ensure
|
||||||
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
|
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -35,6 +35,14 @@ class GroupPolicy < BasePolicy
|
||||||
with_options scope: :subject, score: 0
|
with_options scope: :subject, score: 0
|
||||||
condition(:request_access_enabled) { @subject.request_access_enabled }
|
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
|
rule { public_group }.policy do
|
||||||
enable :read_group
|
enable :read_group
|
||||||
enable :read_list
|
enable :read_list
|
||||||
|
@ -115,6 +123,9 @@ class GroupPolicy < BasePolicy
|
||||||
|
|
||||||
rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster
|
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
|
def access_level
|
||||||
return GroupMember::NO_ACCESS if @user.nil?
|
return GroupMember::NO_ACCESS if @user.nil?
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
.form-group
|
.form-group
|
||||||
= f.label :default_branch_protection, class: 'label-bold'
|
= 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'
|
= 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
|
.form-group.visibility-level-setting
|
||||||
= f.label :default_project_visibility, class: 'label-bold'
|
= 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)
|
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
|
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
|
||||||
%br/
|
%br/
|
||||||
%span.descr This setting can be overridden in each project.
|
%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
|
.form-group.row
|
||||||
= f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2 pt-0'
|
= f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2 pt-0'
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
%span.descr.text-muted= share_with_group_lock_help_text(@group)
|
%span.descr.text-muted= share_with_group_lock_help_text(@group)
|
||||||
|
|
||||||
= render 'groups/settings/lfs', f: f
|
= 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 'groups/settings/two_factor_auth', f: f
|
||||||
= render_if_exists 'groups/member_lock_setting', f: f, group: @group
|
= render_if_exists 'groups/member_lock_setting', f: f, group: @group
|
||||||
|
|
||||||
|
|
|
@ -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'
|
|
@ -19,7 +19,7 @@
|
||||||
= root_url
|
= root_url
|
||||||
- namespace_id = namespace_id_from(params)
|
- namespace_id = namespace_id_from(params)
|
||||||
= f.select(:namespace_id,
|
= f.select(:namespace_id,
|
||||||
namespaces_options(namespace_id || :current_user,
|
namespaces_options_with_developer_maintainer_access(selected: namespace_id,
|
||||||
display_path: true,
|
display_path: true,
|
||||||
extra_group: namespace_id),
|
extra_group: namespace_id),
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Move allow developers to create projects in groups to Core
|
||||||
|
merge_request: 25975
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -126,6 +126,7 @@ Settings['issues_tracker'] ||= {}
|
||||||
# GitLab
|
# GitLab
|
||||||
#
|
#
|
||||||
Settings['gitlab'] ||= Settingslogic.new({})
|
Settings['gitlab'] ||= Settingslogic.new({})
|
||||||
|
Settings.gitlab['default_project_creation'] ||= ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS
|
||||||
Settings.gitlab['default_projects_limit'] ||= 100000
|
Settings.gitlab['default_projects_limit'] ||= 100000
|
||||||
Settings.gitlab['default_branch_protection'] ||= 2
|
Settings.gitlab['default_branch_protection'] ||= 2
|
||||||
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
|
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -177,6 +177,7 @@ ActiveRecord::Schema.define(version: 20190325165127) do
|
||||||
t.string "runners_registration_token_encrypted"
|
t.string "runners_registration_token_encrypted"
|
||||||
t.integer "local_markdown_version", default: 0, null: false
|
t.integer "local_markdown_version", default: 0, null: false
|
||||||
t.integer "first_day_of_week", 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
|
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1391,6 +1392,7 @@ ActiveRecord::Schema.define(version: 20190325165127) do
|
||||||
t.integer "cached_markdown_version"
|
t.integer "cached_markdown_version"
|
||||||
t.string "runners_token"
|
t.string "runners_token"
|
||||||
t.string "runners_token_encrypted"
|
t.string "runners_token_encrypted"
|
||||||
|
t.integer "project_creation_level"
|
||||||
t.boolean "auto_devops_enabled"
|
t.boolean "auto_devops_enabled"
|
||||||
t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
|
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
|
t.index ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree
|
||||||
|
|
|
@ -151,6 +151,17 @@ There are two different ways to add a new project to a group:
|
||||||
|
|
||||||
![Select group](img/select_group_dropdown.png)
|
![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
|
## Transfer projects into groups
|
||||||
|
|
||||||
Learn how to [transfer a project into a group](../project/settings/index.md#transferring-an-existing-project-into-another-namespace).
|
Learn how to [transfer a project into a group](../project/settings/index.md#transferring-an-existing-project-into-another-namespace).
|
||||||
|
|
|
@ -40,7 +40,8 @@ module API
|
||||||
end
|
end
|
||||||
optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
|
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_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_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_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'
|
optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'
|
||||||
|
|
|
@ -24,6 +24,11 @@ module Gitlab
|
||||||
PROTECTION_FULL = 2
|
PROTECTION_FULL = 2
|
||||||
PROTECTION_DEV_CAN_MERGE = 3
|
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
|
class << self
|
||||||
delegate :values, to: :options
|
delegate :values, to: :options
|
||||||
|
|
||||||
|
@ -85,6 +90,22 @@ module Gitlab
|
||||||
def human_access_with_none(access)
|
def human_access_with_none(access)
|
||||||
options_with_none.key(access)
|
options_with_none.key(access)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def human_access
|
def human_access
|
||||||
|
|
|
@ -6418,6 +6418,21 @@ msgstr ""
|
||||||
msgid "ProjectActivityRSS|Subscribe"
|
msgid "ProjectActivityRSS|Subscribe"
|
||||||
msgstr ""
|
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"
|
msgid "ProjectFileTree|Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,13 @@ describe Admin::ApplicationSettingsController do
|
||||||
expect(response).to redirect_to(admin_application_settings_path)
|
expect(response).to redirect_to(admin_application_settings_path)
|
||||||
expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
|
expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
describe 'PUT #reset_registration_token' do
|
describe 'PUT #reset_registration_token' do
|
||||||
|
|
|
@ -60,5 +60,11 @@ describe Admin::GroupsController do
|
||||||
expect(response).to redirect_to(admin_group_path(group))
|
expect(response).to redirect_to(admin_group_path(group))
|
||||||
expect(group.users).not_to include group_user
|
expect(group.users).not_to include group_user
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -349,6 +349,13 @@ describe GroupsController do
|
||||||
expect(assigns(:group).errors).not_to be_empty
|
expect(assigns(:group).errors).not_to be_empty
|
||||||
expect(assigns(:group).path).not_to eq('new_path')
|
expect(assigns(:group).path).not_to eq('new_path')
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
describe '#ensure_canonical_path' do
|
describe '#ensure_canonical_path' do
|
||||||
|
|
|
@ -4,6 +4,7 @@ FactoryBot.define do
|
||||||
path { name.downcase.gsub(/\s/, '_') }
|
path { name.downcase.gsub(/\s/, '_') }
|
||||||
type 'Group'
|
type 'Group'
|
||||||
owner nil
|
owner nil
|
||||||
|
project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS
|
||||||
|
|
||||||
after(:create) do |group|
|
after(:create) do |group|
|
||||||
if group.owner
|
if group.owner
|
||||||
|
|
|
@ -77,6 +77,14 @@ describe 'Edit group settings' do
|
||||||
end
|
end
|
||||||
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
|
describe 'edit group avatar' do
|
||||||
before do
|
before do
|
||||||
visit edit_group_path(group)
|
visit edit_group_path(group)
|
||||||
|
|
|
@ -252,4 +252,23 @@ describe 'New project' do
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -54,4 +54,31 @@ describe 'User creates a project', :js do
|
||||||
expect(project.namespace).to eq(subgroup)
|
expect(project.namespace).to eq(subgroup)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -1,10 +1,38 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe NamespacesHelper do
|
describe NamespacesHelper, :postgresql do
|
||||||
let!(:admin) { create(:admin) }
|
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) { 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
|
before do
|
||||||
admin_group.add_owner(admin)
|
admin_group.add_owner(admin)
|
||||||
|
@ -105,5 +133,43 @@ describe NamespacesHelper do
|
||||||
helper.namespaces_options
|
helper.namespaces_options
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -959,4 +959,12 @@ describe Group do
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -347,6 +347,120 @@ describe GroupPolicy do
|
||||||
end
|
end
|
||||||
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
|
it_behaves_like 'clusterable policies' do
|
||||||
let(:clusterable) { create(:group) }
|
let(:clusterable) { create(:group) }
|
||||||
let(:cluster) do
|
let(:cluster) do
|
||||||
|
|
|
@ -44,6 +44,7 @@ describe API::Settings, 'Settings' do
|
||||||
put api("/application/settings", admin),
|
put api("/application/settings", admin),
|
||||||
params: {
|
params: {
|
||||||
default_projects_limit: 3,
|
default_projects_limit: 3,
|
||||||
|
default_project_creation: 2,
|
||||||
password_authentication_enabled_for_web: false,
|
password_authentication_enabled_for_web: false,
|
||||||
repository_storages: ['custom'],
|
repository_storages: ['custom'],
|
||||||
plantuml_enabled: true,
|
plantuml_enabled: true,
|
||||||
|
@ -64,12 +65,13 @@ describe API::Settings, 'Settings' do
|
||||||
performance_bar_allowed_group_path: group.full_path,
|
performance_bar_allowed_group_path: group.full_path,
|
||||||
instance_statistics_visibility_private: true,
|
instance_statistics_visibility_private: true,
|
||||||
diff_max_patch_bytes: 150_000,
|
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
|
local_markdown_version: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(200)
|
expect(response).to have_gitlab_http_status(200)
|
||||||
expect(json_response['default_projects_limit']).to eq(3)
|
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['password_authentication_enabled_for_web']).to be_falsey
|
||||||
expect(json_response['repository_storages']).to eq(['custom'])
|
expect(json_response['repository_storages']).to eq(['custom'])
|
||||||
expect(json_response['plantuml_enabled']).to be_truthy
|
expect(json_response['plantuml_enabled']).to be_truthy
|
||||||
|
|
Loading…
Reference in New Issue