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:
Douglas Barbosa Alexandre 2019-04-05 18:49:48 +00:00
commit 097b63fcb3
33 changed files with 447 additions and 16 deletions

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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'],

View File

@ -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

View File

@ -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

View File

@ -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?

View File

@ -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)

View File

@ -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'

View File

@ -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

View File

@ -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'

View File

@ -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: "" }})

View File

@ -0,0 +1,5 @@
---
title: Move allow developers to create projects in groups to Core
merge_request: 25975
author:
type: added

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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).

View File

@ -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'

View File

@ -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

View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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