Commit be7b5bdc authored by Rémy Coutable's avatar Rémy Coutable

Merge branch '2534-developers-should-be-able-to-create-new-projects-in-group' into 'master'

Resolve "Developers should be able to create new projects in group"

Closes #2534

See merge request gitlab-org/gitlab-ee!4046
parents 8100e98b ef6019e3
...@@ -3,6 +3,7 @@ class GroupsController < Groups::ApplicationController ...@@ -3,6 +3,7 @@ class GroupsController < Groups::ApplicationController
include MergeRequestsAction include MergeRequestsAction
include ParamsBackwardCompatibility include ParamsBackwardCompatibility
include PreviewMarkdown include PreviewMarkdown
prepend EE::GroupsController
respond_to :html respond_to :html
...@@ -118,10 +119,10 @@ class GroupsController < Groups::ApplicationController ...@@ -118,10 +119,10 @@ class GroupsController < Groups::ApplicationController
end end
def group_params def group_params
params.require(:group).permit(group_params_ce << group_params_ee) params.require(:group).permit(group_params_attributes)
end end
def group_params_ce def group_params_attributes
[ [
:avatar, :avatar,
:description, :description,
...@@ -140,13 +141,6 @@ class GroupsController < Groups::ApplicationController ...@@ -140,13 +141,6 @@ class GroupsController < Groups::ApplicationController
] ]
end end
def group_params_ee
[
:membership_lock,
:repository_size_limit
]
end
def load_events def load_events
params[:sort] ||= 'latest_activity_desc' params[:sort] ||= 'latest_activity_desc'
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
= f.label :default_branch_protection, class: 'control-label col-sm-2' = f.label :default_branch_protection, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
= 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 partial: 'admin/application_settings/ee/project_creation_level', locals: { form: f, application_setting: @application_setting }
.form-group.visibility-level-setting .form-group.visibility-level-setting
= f.label :default_project_visibility, class: 'control-label col-sm-2' = f.label :default_project_visibility, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
- return unless License.feature_available?(:project_creation_level)
- form = local_assigns.fetch(:form)
- application_setting = local_assigns.fetch(:application_setting)
.form-group
= form.label s_('ProjectCreationLevel|Default project creation protection'), class: 'control-label col-sm-2'
.col-sm-10
= form.select :default_project_creation, options_for_select(Gitlab::Access.project_creation_options, application_setting.default_project_creation), {}, class: 'form-control'
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
%span.descr This setting can be overridden in each project. %span.descr This setting can be overridden in each project.
- if can? current_user, :admin_group, @group - if can? current_user, :admin_group, @group
= render partial: 'groups/ee/project_creation_level', locals: { form: f, group: @group }
.form-group .form-group
= f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2' = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
- return unless group.feature_available?(:project_creation_level)
- form = local_assigns.fetch(:form)
- group = local_assigns.fetch(:group)
.form-group
= form.label s_('ProjectCreationLevel|Allowed to create projects'), class: 'control-label col-sm-2'
.col-sm-10
= form.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, group.project_creation_level), {}, class: 'form-control'
---
title: "(EEP) Allow developers to create projects in group"
merge_request: 4046
author:
type: added
...@@ -264,6 +264,7 @@ Settings['issues_tracker'] ||= {} ...@@ -264,6 +264,7 @@ Settings['issues_tracker'] ||= {}
# GitLab # GitLab
# #
Settings['gitlab'] ||= Settingslogic.new({}) Settings['gitlab'] ||= Settingslogic.new({})
Settings.gitlab['default_project_creation'] ||= ::EE::Gitlab::Access::DEVELOPER_MASTER_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?
......
class AddDefaultProjectCreationSetting < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:application_settings, :default_project_creation, :integer, default: 2)
end
def down
remove_column(:application_settings, :default_project_creation)
end
end
class AddProjectCreationLevelToGroups < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :namespaces, :project_creation_level, :integer
end
end
...@@ -178,6 +178,7 @@ ActiveRecord::Schema.define(version: 20180115201419) do ...@@ -178,6 +178,7 @@ ActiveRecord::Schema.define(version: 20180115201419) do
t.integer "gitaly_timeout_medium", default: 30, null: false t.integer "gitaly_timeout_medium", default: 30, null: false
t.integer "gitaly_timeout_fast", default: 10, null: false t.integer "gitaly_timeout_fast", default: 10, null: false
t.boolean "mirror_available", default: true, null: false t.boolean "mirror_available", default: true, null: false
t.integer "default_project_creation", default: 2, null: false
end end
create_table "approvals", force: :cascade do |t| create_table "approvals", force: :cascade do |t|
...@@ -1546,6 +1547,7 @@ ActiveRecord::Schema.define(version: 20180115201419) do ...@@ -1546,6 +1547,7 @@ ActiveRecord::Schema.define(version: 20180115201419) do
t.integer "two_factor_grace_period", default: 48, null: false t.integer "two_factor_grace_period", default: 48, null: false
t.integer "cached_markdown_version" t.integer "cached_markdown_version"
t.integer "plan_id" t.integer "plan_id"
t.integer "project_creation_level"
end end
add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree
......
...@@ -152,6 +152,17 @@ There are two different ways to add a new project to a group: ...@@ -152,6 +152,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)
### (EEP) Default project creation level
This feature allows groups to define a default project creation level.
By default, `Developers` and `Masters` are allowed to create projects, but
this can be changed within the group settings for a group, or the default setting
changed within the Admin area (`Settings`, `Visibility and Access Controls`). This
can be `None`, `Masters`, or `Developers + Masters`.
It is available only in [GitLab Enterprise Edition Premium][eep].
## Transfer projects into groups ## Transfer projects into groups
Learn how to [transfer a project into a group](../project/index.md#transfer-an-existing-project-into-a-group). Learn how to [transfer a project into a group](../project/index.md#transfer-an-existing-project-into-a-group).
...@@ -282,4 +293,4 @@ you have an overview of the contributions (pushes, merge requests, ...@@ -282,4 +293,4 @@ you have an overview of the contributions (pushes, merge requests,
and issues) performed my your group members. and issues) performed my your group members.
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/products/
[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition Premium"
...@@ -8,6 +8,10 @@ module EE ...@@ -8,6 +8,10 @@ module EE
attrs += EE::ApplicationSettingsHelper.repository_mirror_attributes attrs += EE::ApplicationSettingsHelper.repository_mirror_attributes
end end
if License.feature_available?(:project_creation_level)
attrs << :default_project_creation
end
attrs attrs
end end
end end
......
...@@ -20,7 +20,9 @@ module EE ...@@ -20,7 +20,9 @@ module EE
:repository_size_limit, :repository_size_limit,
:shared_runners_minutes_limit, :shared_runners_minutes_limit,
:plan_id :plan_id
] ].tap do |params_ee|
params_ee << :project_creation_level if @group&.feature_available?(:project_creation_level)
end
end end
end end
end end
......
module EE
module GroupsController
def group_params_attributes
super + group_params_ee
end
private
def group_params_ee
[
:membership_lock,
:repository_size_limit
].tap do |params_ee|
params_ee << :project_creation_level if current_group&.feature_available?(:project_creation_level)
end
end
def current_group
@group
end
end
end
...@@ -32,6 +32,7 @@ module EE ...@@ -32,6 +32,7 @@ module EE
module ClassMethods module ClassMethods
def defaults def defaults
super.merge( super.merge(
default_project_creation: ::EE::Gitlab::Access::DEVELOPER_MASTER_PROJECT_ACCESS,
elasticsearch_url: ENV['ELASTIC_URL'] || 'http://localhost:9200', elasticsearch_url: ENV['ELASTIC_URL'] || 'http://localhost:9200',
elasticsearch_aws: false, elasticsearch_aws: false,
elasticsearch_aws_region: ENV['ELASTIC_REGION'] || 'us-east-1', elasticsearch_aws_region: ENV['ELASTIC_REGION'] || 'us-east-1',
......
...@@ -58,5 +58,9 @@ module EE ...@@ -58,5 +58,9 @@ module EE
fail_ldap_sync fail_ldap_sync
update_column(:ldap_sync_error, ::Gitlab::UrlSanitizer.sanitize(error_message)) update_column(:ldap_sync_error, ::Gitlab::UrlSanitizer.sanitize(error_message))
end end
def project_creation_level
super || current_application_settings.default_project_creation
end
end end
end end
...@@ -50,6 +50,7 @@ class License < ActiveRecord::Base ...@@ -50,6 +50,7 @@ class License < ActiveRecord::Base
variable_environment_scope variable_environment_scope
reject_unsigned_commits reject_unsigned_commits
commit_committer_check commit_committer_check
project_creation_level
].freeze ].freeze
EEU_FEATURES = EEP_FEATURES + %i[ EEU_FEATURES = EEP_FEATURES + %i[
......
...@@ -7,6 +7,16 @@ module EE ...@@ -7,6 +7,16 @@ module EE
condition(:ldap_synced) { @subject.ldap_synced? } condition(:ldap_synced) { @subject.ldap_synced? }
condition(:epics_disabled) { !@subject.feature_available?(:epics) } condition(:epics_disabled) { !@subject.feature_available?(:epics) }
condition(:project_creation_level_enabled) { @subject.feature_available?(:project_creation_level) }
condition(:create_projects_disabled) do
@subject.project_creation_level == ::EE::Gitlab::Access::NO_ONE_PROJECT_ACCESS
end
condition(:developer_master_access) do
@subject.project_creation_level == ::EE::Gitlab::Access::DEVELOPER_MASTER_PROJECT_ACCESS
end
rule { reporter }.policy do rule { reporter }.policy do
enable :admin_list enable :admin_list
enable :admin_board enable :admin_board
...@@ -55,6 +65,9 @@ module EE ...@@ -55,6 +65,9 @@ module EE
prevent :update_epic prevent :update_epic
prevent :destroy_epic prevent :destroy_epic
end end
rule { project_creation_level_enabled & developer & developer_master_access }.enable :create_projects
rule { project_creation_level_enabled & create_projects_disabled }.prevent :create_projects
end end
end end
end end
# Gitlab::Access module
#
# Define allowed roles that can be used
# in GitLab code to determine authorization level
#
module EE
module Gitlab
module Access
extend self
# Default project creation level
NO_ONE_PROJECT_ACCESS = 0
MASTER_PROJECT_ACCESS = 1
DEVELOPER_MASTER_PROJECT_ACCESS = 2
def project_creation_options
{
s_('ProjectCreationLevel|No one') => NO_ONE_PROJECT_ACCESS,
s_('ProjectCreationLevel|Masters') => MASTER_PROJECT_ACCESS,
s_('ProjectCreationLevel|Developers + Masters') => DEVELOPER_MASTER_PROJECT_ACCESS
}
end
end
end
end
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
# #
module Gitlab module Gitlab
module Access module Access
extend ::EE::Gitlab::Access
AccessDeniedError = Class.new(StandardError) AccessDeniedError = Class.new(StandardError)
NO_ACCESS = 0 NO_ACCESS = 0
......
...@@ -157,6 +157,9 @@ msgstr "" ...@@ -157,6 +157,9 @@ msgstr ""
msgid "All" msgid "All"
msgstr "" msgstr ""
msgid "Allowed to create projects"
msgstr ""
msgid "All changes are committed" msgid "All changes are committed"
msgstr "" msgstr ""
...@@ -259,6 +262,9 @@ msgstr "" ...@@ -259,6 +262,9 @@ msgstr ""
msgid "Avatar will be removed. Are you sure?" msgid "Avatar will be removed. Are you sure?"
msgstr "" msgstr ""
msgid "Avatar will be removed. Are you sure?"
msgstr ""
msgid "Billing" msgid "Billing"
msgstr "" msgstr ""
...@@ -486,6 +492,9 @@ msgstr "" ...@@ -486,6 +492,9 @@ msgstr ""
msgid "Check interval" msgid "Check interval"
msgstr "" msgstr ""
msgid "Check interval"
msgstr ""
msgid "Checking %{text} availability…" msgid "Checking %{text} availability…"
msgstr "" msgstr ""
...@@ -507,6 +516,15 @@ msgstr "" ...@@ -507,6 +516,15 @@ msgstr ""
msgid "Choose file..." msgid "Choose file..."
msgstr "" msgstr ""
msgid "Choose File ..."
msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
msgid "Choose file..."
msgstr ""
msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all." msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
msgstr "" msgstr ""
...@@ -570,6 +588,9 @@ msgstr "" ...@@ -570,6 +588,9 @@ msgstr ""
msgid "Click to expand text" msgid "Click to expand text"
msgstr "" msgstr ""
msgid "Click to expand text"
msgstr ""
msgid "Clone repository" msgid "Clone repository"
msgstr "" msgstr ""
...@@ -603,6 +624,9 @@ msgstr "" ...@@ -603,6 +624,9 @@ msgstr ""
msgid "ClusterIntegration|Are you sure you want to remove this cluster's integration? This will not delete your actual cluster." msgid "ClusterIntegration|Are you sure you want to remove this cluster's integration? This will not delete your actual cluster."
msgstr "" msgstr ""
msgid "ClusterIntegration|Are you sure you want to remove this cluster's integration? This will not delete your actual cluster."
msgstr ""
msgid "ClusterIntegration|CA Certificate" msgid "ClusterIntegration|CA Certificate"
msgstr "" msgstr ""
...@@ -720,6 +744,9 @@ msgstr "" ...@@ -720,6 +744,9 @@ msgstr ""
msgid "ClusterIntegration|Integration status" msgid "ClusterIntegration|Integration status"
msgstr "" msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}" msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr "" msgstr ""
...@@ -834,6 +861,9 @@ msgstr "" ...@@ -834,6 +861,9 @@ msgstr ""
msgid "ClusterIntegration|check the pricing here" msgid "ClusterIntegration|check the pricing here"
msgstr "" msgstr ""
msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|cluster" msgid "ClusterIntegration|cluster"
msgstr "" msgstr ""
...@@ -920,6 +950,27 @@ msgstr "" ...@@ -920,6 +950,27 @@ msgstr ""
msgid "CompareBranches|There isn't anything to compare." msgid "CompareBranches|There isn't anything to compare."
msgstr "" msgstr ""
msgid "Compare Git revisions"
msgstr ""
msgid "Compare Revisions"
msgstr ""
msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
msgstr ""
msgid "CompareBranches|Compare"
msgstr ""
msgid "CompareBranches|Source"
msgstr ""
msgid "CompareBranches|Target"
msgstr ""
msgid "CompareBranches|There isn't anything to compare."
msgstr ""
msgid "Container Registry" msgid "Container Registry"
msgstr "" msgstr ""
...@@ -1126,12 +1177,18 @@ msgstr "" ...@@ -1126,12 +1177,18 @@ msgstr ""
msgid "Disable" msgid "Disable"
msgstr "" msgstr ""
msgid "Disable"
msgstr ""
msgid "Discard changes" msgid "Discard changes"
msgstr "" msgstr ""
msgid "Discover GitLab Geo." msgid "Discover GitLab Geo."
msgstr "" msgstr ""
msgid "Discover GitLab Geo."
msgstr ""
msgid "Dismiss Cycle Analytics introduction box" msgid "Dismiss Cycle Analytics introduction box"
msgstr "" msgstr ""
...@@ -1183,6 +1240,9 @@ msgstr "" ...@@ -1183,6 +1240,9 @@ msgstr ""
msgid "Enable" msgid "Enable"
msgstr "" msgstr ""
msgid "Enable"
msgstr ""
msgid "Environments|An error occurred while fetching the environments." msgid "Environments|An error occurred while fetching the environments."
msgstr "" msgstr ""
...@@ -1651,6 +1711,9 @@ msgstr "" ...@@ -1651,6 +1711,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them." msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr "" msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
msgid "Last %d day" msgid "Last %d day"
msgid_plural "Last %d days" msgid_plural "Last %d days"
msgstr[0] "" msgstr[0] ""
...@@ -1719,6 +1782,9 @@ msgstr "" ...@@ -1719,6 +1782,9 @@ msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos." msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr "" msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
msgid "Mar" msgid "Mar"
msgstr "" msgstr ""
...@@ -1952,6 +2018,9 @@ msgstr "" ...@@ -1952,6 +2018,9 @@ msgstr ""
msgid "Open" msgid "Open"
msgstr "" msgstr ""
msgid "Open"
msgstr ""
msgid "Opened" msgid "Opened"
msgstr "" msgstr ""
...@@ -2096,6 +2165,12 @@ msgstr "" ...@@ -2096,6 +2165,12 @@ msgstr ""
msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a cluster</a>, then try again." msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a cluster</a>, then try again."
msgstr "" msgstr ""
msgid "Play"
msgstr ""
msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a cluster</a>, then try again."
msgstr ""
msgid "Please solve the reCAPTCHA" msgid "Please solve the reCAPTCHA"
msgstr "" msgstr ""
...@@ -2105,6 +2180,9 @@ msgstr "" ...@@ -2105,6 +2180,9 @@ msgstr ""
msgid "Primary" msgid "Primary"
msgstr "" msgstr ""
msgid "Primary"
msgstr ""
msgid "Private - Project access must be granted explicitly to each user." msgid "Private - Project access must be granted explicitly to each user."
msgstr "" msgstr ""
...@@ -2174,6 +2252,15 @@ msgstr "" ...@@ -2174,6 +2252,15 @@ msgstr ""
msgid "Project cache successfully reset." msgid "Project cache successfully reset."
msgstr "" msgstr ""
msgid "Project avatar"
msgstr ""
msgid "Project avatar in repository: %{link}"
msgstr ""
msgid "Project cache successfully reset."
msgstr ""
msgid "Project details" msgid "Project details"
msgstr "" msgstr ""
...@@ -2192,6 +2279,21 @@ msgstr "" ...@@ -2192,6 +2279,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 + Masters"
msgstr ""
msgid "ProjectCreationLevel|Masters"
msgstr ""
msgid "ProjectCreationLevel|No one"
msgstr ""
msgid "ProjectFeature|Disabled" msgid "ProjectFeature|Disabled"
msgstr "" msgstr ""
...@@ -2318,6 +2420,9 @@ msgstr "" ...@@ -2318,6 +2420,9 @@ msgstr ""
msgid "Register / Sign In" msgid "Register / Sign In"
msgstr "" msgstr ""
msgid "Register / Sign In"
msgstr ""
msgid "Registry" msgid "Registry"
msgstr "" msgstr ""
...@@ -2348,12 +2453,21 @@ msgstr "" ...@@ -2348,12 +2453,21 @@ msgstr ""
msgid "Remove avatar" msgid "Remove avatar"
msgstr "" msgstr ""
msgid "Remove"
msgstr ""
msgid "Remove avatar"
msgstr ""
msgid "Remove project" msgid "Remove project"
msgstr "" msgstr ""
msgid "Repair authentication" msgid "Repair authentication"
msgstr "" msgstr ""
msgid "Repair authentication"
msgstr ""
msgid "Repository" msgid "Repository"
msgstr "" msgstr ""
...@@ -2419,6 +2533,9 @@ msgstr "" ...@@ -2419,6 +2533,9 @@ msgstr ""
msgid "Select branch/tag" msgid "Select branch/tag"
msgstr "" msgstr ""
msgid "Select branch/tag"
msgstr ""
msgid "Select target branch" msgid "Select target branch"
msgstr "" msgstr ""
...@@ -2810,6 +2927,12 @@ msgstr "" ...@@ -2810,6 +2927,12 @@ msgstr ""
msgid "There are no merge requests to show" msgid "There are no merge requests to show"
msgstr "" msgstr ""
msgid "There are no issues to show"
msgstr ""
msgid "There are no merge requests to show"
msgstr ""
msgid "There are problems accessing Git storage: " msgid "There are problems accessing Git storage: "
msgstr "" msgstr ""
...@@ -3030,6 +3153,12 @@ msgstr "" ...@@ -3030,6 +3153,12 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON" msgid "ToggleButton|Toggle Status: ON"
msgstr "" msgstr ""
msgid "ToggleButton|Toggle Status: OFF"
msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
msgid "Total Time" msgid "Total Time"
msgstr "" msgstr ""
...@@ -3048,6 +3177,9 @@ msgstr "" ...@@ -3048,6 +3177,9 @@ msgstr ""
msgid "Trigger this manual action" msgid "Trigger this manual action"
msgstr "" msgstr ""
msgid "Trigger this manual action"
msgstr ""
msgid "Turn on Service Desk" msgid "Turn on Service Desk"
msgstr "" msgstr ""
...@@ -3057,6 +3189,12 @@ msgstr "" ...@@ -3057,6 +3189,12 @@ msgstr ""
msgid "Unknown" msgid "Unknown"
msgstr "" msgstr ""
msgid "Unable to reset project cache."
msgstr ""
msgid "Unknown"
msgstr ""
msgid "Unlock" msgid "Unlock"
msgstr "" msgstr ""
...@@ -3093,6 +3231,9 @@ msgstr "" ...@@ -3093,6 +3231,9 @@ msgstr ""
msgid "Upload new avatar" msgid "Upload new avatar"
msgstr "" msgstr ""
msgid "Upload new avatar"
msgstr ""
msgid "UploadLink|click to upload" msgid "UploadLink|click to upload"
msgstr "" msgstr ""
...@@ -3132,6 +3273,9 @@ msgstr "" ...@@ -3132,6 +3273,9 @@ msgstr ""
msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again." msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
msgstr "" msgstr ""
msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
msgstr ""
msgid "We don't have enough data to show this stage." msgid "We don't have enough data to show this stage."
msgstr "" msgstr ""
...@@ -3370,6 +3514,11 @@ msgid_plural "merge requests" ...@@ -3370,6 +3514,11 @@ msgid_plural "merge requests"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "merge request"
msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
msgid "mrWidget|Cancel automatic merge" msgid "mrWidget|Cancel automatic merge"
msgstr "" msgstr ""
......
...@@ -323,6 +323,13 @@ describe GroupsController do ...@@ -323,6 +323,13 @@ describe GroupsController do
expect(controller).to set_flash[:notice] expect(controller).to set_flash[:notice]
end end
it 'updates the project_creation_level successfully' do
post :update, id: group.to_param, group: { project_creation_level: ::EE::Gitlab::Access::MASTER_PROJECT_ACCESS }
expect(response).to have_gitlab_http_status(302)
expect(group.reload.project_creation_level).to eq(::EE::Gitlab::Access::MASTER_PROJECT_ACCESS)
end
it 'does not update the path on error' do it 'does not update the path on error' do
allow_any_instance_of(Group).to receive(:move_dir).and_raise(Gitlab::UpdatePathError) allow_any_instance_of(Group).to receive(:move_dir).and_raise(Gitlab::UpdatePathError)
post :update, id: group.to_param, group: { path: 'new_path' } post :update, id: group.to_param, group: { path: 'new_path' }
......
...@@ -80,5 +80,13 @@ describe Admin::ApplicationSettingsController do ...@@ -80,5 +80,13 @@ describe Admin::ApplicationSettingsController do
expect(ApplicationSetting.current.public_send(setting)).to eq(value) expect(ApplicationSetting.current.public_send(setting)).to eq(value)
end end
end end
it 'updates the default_project_creation for string value' do
stub_licensed_features(project_creation_level: true)
put :update, application_setting: { default_project_creation: ::EE::Gitlab::Access::MASTER_PROJECT_ACCESS }
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.default_project_creation).to eq(::EE::Gitlab::Access::MASTER_PROJECT_ACCESS)
end
end end
end end
...@@ -13,7 +13,7 @@ describe Admin::GroupsController do ...@@ -13,7 +13,7 @@ describe Admin::GroupsController do
before do before do
allow_any_instance_of(ClearNamespaceSharedRunnersMinutesService) allow_any_instance_of(ClearNamespaceSharedRunnersMinutesService)
.to receive(:execute).and_return(clear_runners_minutes_service_result) .to receive(:execute).and_return(clear_runners_minutes_service_result)
end end
context 'when the reset is successful' do context 'when the reset is successful' do
...@@ -38,4 +38,24 @@ describe Admin::GroupsController do ...@@ -38,4 +38,24 @@ describe Admin::GroupsController do
end end
end end
end end
context 'PUT update' do
context 'no license' do
it 'does not update the project_creation_level successfully' do
expect do
post :update, id: group.to_param, group: { project_creation_level: ::EE::Gitlab::Access::NO_ONE_PROJECT_ACCESS }
end.not_to change { group.reload.project_creation_level }
end
end
context 'licensed' do
it 'updates the project_creation_level successfully' do
stub_licensed_features(project_creation_level: true)
expect do
post :update, id: group.to_param, group: { project_creation_level: ::EE::Gitlab::Access::NO_ONE_PROJECT_ACCESS }
end.to change { group.reload.project_creation_level }.to(::EE::Gitlab::Access::NO_ONE_PROJECT_ACCESS)
end
end
end
end end
...@@ -32,4 +32,24 @@ feature 'Edit group settings' do ...@@ -32,4 +32,24 @@ feature 'Edit group settings' do
end end
end end
end end
context 'with project_creation_level feature enabled' do
it 'shows the selection menu' do
stub_licensed_features(project_creation_level: true)
visit edit_group_path(group)
expect(page).to have_content('Allowed to create projects')
end
end
context 'with project_creation_level feature disabled' do
it 'shows the selection menu' do
stub_licensed_features(project_creation_level: false)
visit edit_group_path(group)
expect(page).not_to have_content('Allowed to create projects')
end
end
end end
...@@ -114,4 +114,14 @@ describe Group do ...@@ -114,4 +114,14 @@ describe Group do
expect(group.repository_size_limit).to eql(8.exabytes - 1) expect(group.repository_size_limit).to eql(8.exabytes - 1)
end end
end end
describe 'project_creation_level' do
it 'outputs the default one if it is nil' do
stub_application_setting(default_project_creation: ::EE::Gitlab::Access::MASTER_PROJECT_ACCESS)
group = create(:group, project_creation_level: nil)
expect(group.project_creation_level).to eq(current_application_settings.default_project_creation)
end
end
end end
...@@ -124,4 +124,238 @@ describe GroupPolicy do ...@@ -124,4 +124,238 @@ describe GroupPolicy do
it { is_expected.to be_allowed(:admin_ldap_group_links) } it { is_expected.to be_allowed(:admin_ldap_group_links) }
end end
end end
context "create_projects" do
context 'project_creation_level enabled' do
before do
stub_licensed_features(project_creation_level: true)
end
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 'master' do
let(:current_user) { master }
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: ::EE::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 'master' do
let(:current_user) { master }
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 master only' do
let(:group) { create(:group, project_creation_level: ::EE::Gitlab::Access::MASTER_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 'master' do
let(:current_user) { master }
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 + master' do
let(:group) { create(:group, project_creation_level: ::EE::Gitlab::Access::DEVELOPER_MASTER_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 'master' do
let(:current_user) { master }
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
context 'project_creation_level disabled' 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_disallowed(:create_projects) }
end
context 'master' do
let(:current_user) { master }
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: ::EE::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 'master' do
let(:current_user) { master }
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 master only' do
let(:group) { create(:group, project_creation_level: ::EE::Gitlab::Access::MASTER_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 'master' do
let(:current_user) { master }
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 + master' do
let(:group) { create(:group, project_creation_level: ::EE::Gitlab::Access::DEVELOPER_MASTER_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 'master' do
let(:current_user) { master }
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
end
end end
...@@ -4,6 +4,7 @@ FactoryBot.define 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 ::EE::Gitlab::Access::MASTER_PROJECT_ACCESS
trait :public do trait :public do
visibility_level Gitlab::VisibilityLevel::PUBLIC visibility_level Gitlab::VisibilityLevel::PUBLIC
......
...@@ -3,6 +3,18 @@ require Rails.root.join('db', 'post_migrate', '20171207150343_remove_soft_remove ...@@ -3,6 +3,18 @@ require Rails.root.join('db', 'post_migrate', '20171207150343_remove_soft_remove
describe RemoveSoftRemovedObjects, :migration do describe RemoveSoftRemovedObjects, :migration do
describe '#up' do describe '#up' do
let!(:groups) do
table(:namespaces).tap do |t|
t.inheritance_column = nil
end
end
let!(:routes) do
table(:routes).tap do |t|
t.inheritance_column = nil
end
end
it 'removes various soft removed objects' do it 'removes various soft removed objects' do
5.times do 5.times do
create_with_deleted_at(:issue) create_with_deleted_at(:issue)
...@@ -28,19 +40,21 @@ describe RemoveSoftRemovedObjects, :migration do ...@@ -28,19 +40,21 @@ describe RemoveSoftRemovedObjects, :migration do
it 'removes routes of soft removed personal namespaces' do it 'removes routes of soft removed personal namespaces' do
namespace = create_with_deleted_at(:namespace) namespace = create_with_deleted_at(:namespace)
group = create(:group) group = groups.create!(name: 'group', path: 'group_path', type: 'Group')
routes.create!(source_id: group.id, source_type: 'Group', name: 'group', path: 'group_path')
expect(Route.where(source: namespace).exists?).to eq(true) expect(routes.where(source_id: namespace.id).exists?).to eq(true)
expect(Route.where(source: group).exists?).to eq(true) expect(routes.where(source_id: group.id).exists?).to eq(true)
run_migration run_migration
expect(Route.where(source: namespace).exists?).to eq(false) expect(routes.where(source_id: namespace.id).exists?).to eq(false)
expect(Route.where(source: group).exists?).to eq(true) expect(routes.where(source_id: group.id).exists?).to eq(true)
end end
it 'schedules the removal of soft removed groups' do it 'schedules the removal of soft removed groups' do
group = create_with_deleted_at(:group) group = create_deleted_group
admin = create(:user, admin: true) admin = create(:user, admin: true)
expect_any_instance_of(GroupDestroyWorker) expect_any_instance_of(GroupDestroyWorker)
...@@ -51,7 +65,7 @@ describe RemoveSoftRemovedObjects, :migration do ...@@ -51,7 +65,7 @@ describe RemoveSoftRemovedObjects, :migration do
end end
it 'does not remove soft removed groups when no admin user could be found' do it 'does not remove soft removed groups when no admin user could be found' do
create_with_deleted_at(:group) create_deleted_group
expect_any_instance_of(GroupDestroyWorker) expect_any_instance_of(GroupDestroyWorker)
.not_to receive(:perform) .not_to receive(:perform)
...@@ -74,4 +88,13 @@ describe RemoveSoftRemovedObjects, :migration do ...@@ -74,4 +88,13 @@ describe RemoveSoftRemovedObjects, :migration do
row row
end end
def create_deleted_group
group = groups.create!(name: 'group', path: 'group_path', type: 'Group')
routes.create!(source_id: group.id, source_type: 'Group', name: 'group', path: 'group_path')
groups.where(id: group.id).update_all(deleted_at: 1.year.ago)
group
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment