From 56861a19a3466342ed8a489b417c3630c9d61fed Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 14 Jun 2019 15:13:24 -0700 Subject: Add changelog --- changelogs/unreleased/maintainers-can-create-subgroup.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/maintainers-can-create-subgroup.yml diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml new file mode 100644 index 00000000000..b537862c8af --- /dev/null +++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml @@ -0,0 +1,5 @@ +--- +title: Maintainers can crete subgroups +merge_request: 29718 +author: Fabio Papa +type: changed -- cgit v1.2.1 From 1f07faa95a60983e4623845f451e89a5b2c92bbe Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 14 Jun 2019 15:16:55 -0700 Subject: Add failing feature spec detailing a maintainer creating a subgroup - Change the two existing feature examples that create a subgroup to elucidate that the owner is creating the subgroup - Nest two more specs inside the 'subgroup support' context detailing what happens when a maintainer attempts to add a subgroup (one with subgroup support, and one without) --- spec/features/groups/show_spec.rb | 61 ++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 9671a4d8c49..2654d06cd8c 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,32 +56,67 @@ describe 'Group show page' do end context 'subgroup support' do - let(:user) { create(:user) } + let(:owner) { create(:user) } + let(:maintainer) { create(:user) } before do - group.add_owner(user) - sign_in(user) + group.add_owner(owner) + group.add_maintainer(maintainer) end - context 'when subgroups are supported', :js, :nested_groups do + context 'for owners' do before do - allow(Group).to receive(:supports_nested_objects?) { true } - visit path + sign_in(owner) end - it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + context 'when subgroups are supported', :js, :nested_groups do + before do + allow(Group).to receive(:supports_nested_objects?) { true } + visit path + end + + it 'allows creating subgroups' do + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end + end + + context 'when subgroups are not supported' do + before do + allow(Group).to receive(:supports_nested_objects?) { false } + visit path + end + + it 'allows creating subgroups' do + expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + end end end - context 'when subgroups are not supported' do + context 'for maintainers' do before do - allow(Group).to receive(:supports_nested_objects?) { false } - visit path + sign_in(maintainer) + end + + context 'when subgroups are supported', :js, :nested_groups do + before do + allow(Group).to receive(:supports_nested_objects?) { true } + visit path + end + + it 'allows creating subgroups' do + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end end - it 'allows creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + context 'when subgroups are not supported' do + before do + allow(Group).to receive(:supports_nested_objects?) { false } + visit path + end + + it 'allows creating subgroups' do + expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + end end end end -- cgit v1.2.1 From 3cc3cf978f60b1a0f2c627345deef6f5e82254a0 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 11:07:17 -0700 Subject: Add failing unit test specifying a maintainer creating a subgroup --- spec/services/groups/create_service_spec.rb | 9 +++++++++ .../shared_contexts/policies/group_policy_shared_context.rb | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index c5ff6cdbacd..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -87,6 +88,14 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end + + context 'as maintainer' do + before do + group.add_maintainer(user) + end + + it { is_expected.to be_persisted } + end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4808ac0068..b4b09d3295f 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -23,6 +23,15 @@ RSpec.shared_context 'GroupPolicy context' do create_projects read_cluster create_cluster update_cluster admin_cluster add_cluster ] + [ + :create_projects, + :read_cluster, + :create_cluster, + :update_cluster, + :admin_cluster, + :add_cluster, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) + ].compact end let(:owner_permissions) do [ @@ -30,8 +39,7 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) + :set_note_created_at ].compact end -- cgit v1.2.1 From 66b18427755fcc771267a9e3ca87c6d58db4496d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 12:23:56 -0700 Subject: Update the group policy to allow >= maintainer to create subgroups All specs passing --- app/policies/group_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index ea86858181d..d92bcded19d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -109,7 +109,7 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { owner & nested_groups_supported }.enable :create_subgroup + rule { maintainer & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 60a4cdc2e2cdb0a2de061a36ac83ff53e703cb16 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 13:22:12 -0700 Subject: Modify API spec to expect a maintainer to be able to create subgroup --- spec/requests/api/groups_spec.rb | 4 ++-- spec/support/shared_contexts/policies/group_policy_shared_context.rb | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4b09d3295f..a40d3087f6e 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,10 +19,6 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - %i[ - create_projects - read_cluster create_cluster update_cluster admin_cluster add_cluster - ] [ :create_projects, :read_cluster, -- cgit v1.2.1 From a113c5caba4007ff132f6cba01658b9e4cfc64b6 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 18:15:56 +0000 Subject: Apply suggestion to changelogs/unreleased/maintainers-can-create-subgroup.yml --- changelogs/unreleased/maintainers-can-create-subgroup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml index b537862c8af..180f0f7247f 100644 --- a/changelogs/unreleased/maintainers-can-create-subgroup.yml +++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml @@ -1,5 +1,5 @@ --- -title: Maintainers can crete subgroups +title: Maintainers can create subgroups merge_request: 29718 author: Fabio Papa type: changed -- cgit v1.2.1 From a3064967327f7bf3069f3903bd20120d2d3a9ed9 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 25 Jun 2019 21:59:10 -0700 Subject: Add examples specing the setting to choose who can create subgroups This setting is at the group level only. The default is specified to be maintainers and owners. **Specs only**, all failing. --- spec/controllers/admin/groups_controller_spec.rb | 6 ++ spec/factories/groups.rb | 1 + spec/features/groups/group_settings_spec.rb | 8 +++ spec/models/group_spec.rb | 8 +++ spec/policies/group_policy_spec.rb | 70 ++++++++++++++++++++++ .../policies/group_policy_shared_context.rb | 16 ++--- 6 files changed, 99 insertions(+), 10 deletions(-) diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 509d8944e3a..df11321537f 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -68,5 +68,11 @@ describe Admin::GroupsController 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 + + it 'updates the subgroup_creation_level successfully' do + expect do + post :update, params: { id: group.to_param, group: { subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS } } + end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 18a0c2ec731..2f50fbfe2fa 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,6 +5,7 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 5cef5f0521f..95534d5a2ba 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -85,6 +85,14 @@ describe 'Edit group settings' do end end + describe 'subgroup creation level menu' do + it 'shows the selection menu' do + visit edit_group_path(group) + + expect(page).to have_content('Allowed to create subgroups') + end + end + describe 'edit group avatar' do before do visit edit_group_path(group) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index d7accbef6bd..426e2526a01 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1023,4 +1023,12 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end + + describe 'subgroup_creation_level' do + it 'outputs the default one if it is nil' do + group = create(:group, subgroup_creation_level: nil) + + expect(group.subgroup_creation_level).to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 59f3a961d50..aed9a8e34ff 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -163,6 +163,18 @@ describe GroupPolicy do expect_allowed(*updated_owner_permissions) end end + + context 'maintainer' do + let(:current_user) { maintainer } + + it 'allows every maintainer permission except creating subgroups' do + create_subgroup_permission = [:create_subgroup] + updated_maintainer_permissions = maintainer_permissions - create_subgroup_permission + + expect_disallowed(*create_subgroup_permission) + expect_allowed(*updated_maintainer_permissions) + end + end end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do @@ -461,6 +473,64 @@ describe GroupPolicy do end end + context "create_subgroup" do + context 'when group has subgroup creation level set to owner' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + + context 'reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_subgroup) } + end + end + + context 'when group has subgroup creation level set to maintainer' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + context 'reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_allowed(:create_subgroup) } + end + + context 'owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_subgroup) } + end + end + end + it_behaves_like 'clusterable policies' do let(:clusterable) { create(:group) } let(:cluster) do diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index a40d3087f6e..b4808ac0068 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,15 +19,10 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - [ - :create_projects, - :read_cluster, - :create_cluster, - :update_cluster, - :admin_cluster, - :add_cluster, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) - ].compact + %i[ + create_projects + read_cluster create_cluster update_cluster admin_cluster add_cluster + ] end let(:owner_permissions) do [ @@ -35,7 +30,8 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at + :set_note_created_at, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) ].compact end -- cgit v1.2.1 From a1b9ace31ee0d9c602cb266e9bf94f2e37b14ce7 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:46:48 -0700 Subject: Reset group policy to only allow >= owners to create subgroups --- app/policies/group_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index d92bcded19d..ea86858181d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -109,7 +109,7 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { maintainer & nested_groups_supported }.enable :create_subgroup + rule { owner & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 521d4c76db6d18468bdee6d28d5dc408c7c7a09f Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:49:27 -0700 Subject: Add a subgroup_creation_level column to the namespaces table --- ...6175626_add_group_creation_level_to_namespaces.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb new file mode 100644 index 00000000000..b0ea74d4765 --- /dev/null +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + disable_ddl_transaction! + + def up + unless column_exists?(:namespaces, :subgroup_creation_level) + add_column_with_default(:namespaces, :subgroup_creation_level, :integer, default: 0) + end + end + + def down + if column_exists?(:namespaces, :subgroup_creation_level) + remove_column(:namespaces, :subgroup_creation_level) + end + end +end -- cgit v1.2.1 From 58e4c7fea3a4ffcccac77290bd54f1b59475af0e Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:39:04 -0700 Subject: Add constants representing Owner and Maintainer group access levels --- lib/gitlab/access.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 6eb08f674c2..77076ead47a 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -29,6 +29,10 @@ module Gitlab MAINTAINER_PROJECT_ACCESS = 1 DEVELOPER_MAINTAINER_PROJECT_ACCESS = 2 + # Default subgroup creation level + OWNER_SUBGROUP_ACCESS = 0 + MAINTAINER_SUBGROUP_ACCESS = 1 + class << self delegate :values, to: :options -- cgit v1.2.1 From 5f9bc7992d042eebd425ae3e0a6a3f35b73a7804 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:45:58 -0700 Subject: Add subgroup_creation_level to the list of allowed group params For both groups_controller and admin/groups_controller --- app/controllers/admin/groups_controller.rb | 3 ++- app/controllers/groups_controller.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 15f7ef881c8..6317fa7c8d1 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -90,7 +90,8 @@ class Admin::GroupsController < Admin::ApplicationController :visibility_level, :require_two_factor_authentication, :two_factor_grace_period, - :project_creation_level + :project_creation_level, + :subgroup_creation_level ] end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index e936d771502..6bb72476959 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -188,7 +188,8 @@ class GroupsController < Groups::ApplicationController :chat_team_name, :require_two_factor_authentication, :two_factor_grace_period, - :project_creation_level + :project_creation_level, + :subgroup_creation_level ] end -- cgit v1.2.1 From 50bd9ddf2ec67594eb8c28f2c8fb5b262be2c515 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:11:09 -0700 Subject: Add "allowed to create subgroups" dropdown to group settings form --- app/views/groups/settings/_permissions.html.haml | 1 + app/views/groups/settings/_subgroup_creation_level.html.haml | 3 +++ lib/gitlab/access.rb | 7 +++++++ 3 files changed, 11 insertions(+) create mode 100644 app/views/groups/settings/_subgroup_creation_level.html.haml diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index 0a14830c666..b5562198984 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -19,6 +19,7 @@ = render 'groups/settings/lfs', f: f = render 'groups/settings/project_creation_level', f: f, group: @group + = render 'groups/settings/subgroup_creation_level', f: f, group: @group = render 'groups/settings/two_factor_auth', f: f = render_if_exists 'groups/member_lock_setting', f: f, group: @group diff --git a/app/views/groups/settings/_subgroup_creation_level.html.haml b/app/views/groups/settings/_subgroup_creation_level.html.haml new file mode 100644 index 00000000000..f36ad192bad --- /dev/null +++ b/app/views/groups/settings/_subgroup_creation_level.html.haml @@ -0,0 +1,3 @@ +.form-group + = f.label s_('SubgroupCreationLevel|Allowed to create subgroups'), class: 'label-bold' + = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, group.subgroup_creation_level), {}, class: 'form-control' diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 77076ead47a..7ef9f7ef630 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -110,6 +110,13 @@ module Gitlab def project_creation_level_name(name) project_creation_options.key(name) end + + def subgroup_creation_options + { + s_('SubgroupCreationlevel|Owners') => OWNER_SUBGROUP_ACCESS, + s_('SubgroupCreationlevel|Maintainers') => MAINTAINER_SUBGROUP_ACCESS + } + end end def human_access -- cgit v1.2.1 From 7bf0ce2283334752bd88a8eb63aa16e5b4b33864 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:17:24 -0700 Subject: Make the group model return maintainer level when it is not set --- app/models/group.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/group.rb b/app/models/group.rb index dbec211935d..f32312c6f43 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -418,6 +418,10 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end + def subgroup_creation_level + super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + private def update_two_factor_requirement -- cgit v1.2.1 From 075a6328813ee3733e23254d8e7540b59ec789c0 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 15:53:46 -0700 Subject: Add policy to allow maintainers to create subgroups when enabled --- app/policies/group_policy.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index ea86858181d..bd1eb02ca1f 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -43,6 +43,10 @@ class GroupPolicy < BasePolicy @subject.project_creation_level == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS end + condition(:maintainer_can_create_group) do + @subject.subgroup_creation_level == ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + rule { public_group }.policy do enable :read_group enable :read_list @@ -110,6 +114,7 @@ class GroupPolicy < BasePolicy end rule { owner & nested_groups_supported }.enable :create_subgroup + rule { maintainer & maintainer_can_create_group & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 9caf32698110f9c3b7a861db86b994ddab8308a7 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 08:35:09 -0700 Subject: Add feature examples specing maintainers creating subgroups Both with subgroup_creation_level set to owners and to maintainers. Also fixed the naming of some other examples in the same spec as they were contradicting what they were actually performing in the test. These examples were probably copy/pasted, and not renamed. --- spec/features/groups/show_spec.rb | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 2654d06cd8c..68fa3f4e817 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -86,7 +86,7 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do + it 'does not allow creating subgroups' do expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) end end @@ -103,8 +103,22 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + context 'when subgroup_creation_level is set to maintainer' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + it 'allows creating subgroups' do + visit path + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end + end + + context 'when subgroup_creation_level is set to owners' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + + it 'does not allow creating subgroups' do + visit path + expect(page).not_to have_css("li[data-text='New subgroup']", visible: false) + end end end @@ -114,7 +128,7 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do + it 'does not allow creating subgroups' do expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) end end -- cgit v1.2.1 From e1d0a30b57ea8c225e719670a6a89f48e413ce9d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 09:22:10 -0700 Subject: Update schema --- db/schema.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index 4ed7c0cb248..29367551efb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20190611161641) do +ActiveRecord::Schema.define(version: 20190626175626) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1442,6 +1442,7 @@ ActiveRecord::Schema.define(version: 20190611161641) do t.integer "project_creation_level" t.boolean "auto_devops_enabled" t.datetime_with_timezone "last_ci_minutes_notification_at" + t.integer "subgroup_creation_level", default: 0, null: false 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"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} -- cgit v1.2.1 From 65d4843421c7bdc1b571f2b72f8db1a64cf0834f Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 13:31:47 -0700 Subject: Style rules; Revert some examples --- ...75626_add_group_creation_level_to_namespaces.rb | 7 +++++-- spec/controllers/admin/groups_controller_spec.rb | 9 ++++++-- spec/features/groups/group_settings_spec.rb | 2 +- spec/features/groups/show_spec.rb | 24 +++++++++++++++------- spec/models/group_spec.rb | 3 ++- spec/policies/group_policy_spec.rb | 21 ++++++++++++++----- spec/requests/api/groups_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 4 ++-- 8 files changed, 52 insertions(+), 22 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index b0ea74d4765..eed0ba25f27 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -5,10 +5,13 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] DOWNTIME = false disable_ddl_transaction! - + def up unless column_exists?(:namespaces, :subgroup_creation_level) - add_column_with_default(:namespaces, :subgroup_creation_level, :integer, default: 0) + add_column_with_default(:namespaces, + :subgroup_creation_level, + :integer, + default: 0) end end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index df11321537f..72f389513f8 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,9 +70,14 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do + MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + expect do - post :update, params: { id: group.to_param, group: { subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS } } - end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + post :update, + params: { id: group.to_param, + group: { subgroup_creation_level: MAINTAINER } } + end.to change { group.reload.subgroup_creation_level } + .to(MAINTAINER) end end end diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 95534d5a2ba..676769c25fe 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -92,7 +92,7 @@ describe 'Edit group settings' do expect(page).to have_content('Allowed to create subgroups') end end - + describe 'edit group avatar' do before do visit edit_group_path(group) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 68fa3f4e817..163906010fa 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -76,7 +76,8 @@ describe 'Group show page' do end it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + expect(page) + .to have_css("li[data-text='New subgroup']", visible: false) end end @@ -87,7 +88,8 @@ describe 'Group show page' do end it 'does not allow creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_selector("li[data-text='New subgroup']", visible: false) end end end @@ -104,8 +106,11 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to maintainer' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } - + let(:group) do + create(:group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + it 'allows creating subgroups' do visit path expect(page).to have_css("li[data-text='New subgroup']", visible: false) @@ -113,11 +118,15 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to owners' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:group) do + create(:group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end it 'does not allow creating subgroups' do visit path - expect(page).not_to have_css("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_css("li[data-text='New subgroup']", visible: false) end end end @@ -129,7 +138,8 @@ describe 'Group show page' do end it 'does not allow creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_selector("li[data-text='New subgroup']", visible: false) end end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 426e2526a01..2b85b281d33 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1028,7 +1028,8 @@ describe Group do it 'outputs the default one if it is nil' do group = create(:group, subgroup_creation_level: nil) - expect(group.subgroup_creation_level).to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + expect(group.subgroup_creation_level) + .to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index aed9a8e34ff..da186f63eca 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -145,7 +145,8 @@ describe GroupPolicy do it 'allows every owner permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = owner_permissions - create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_owner_permissions) @@ -157,7 +158,8 @@ describe GroupPolicy do it 'allows every owner permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = owner_permissions - create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_owner_permissions) @@ -169,7 +171,8 @@ describe GroupPolicy do it 'allows every maintainer permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_maintainer_permissions = maintainer_permissions - create_subgroup_permission + updated_maintainer_permissions = + maintainer_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_maintainer_permissions) @@ -475,7 +478,11 @@ describe GroupPolicy do context "create_subgroup" do context 'when group has subgroup creation level set to owner' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:group) do + create( + :group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end context 'reporter' do let(:current_user) { reporter } @@ -503,7 +510,11 @@ describe GroupPolicy do end context 'when group has subgroup creation level set to maintainer' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:group) do + create( + :group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end context 'reporter' do let(:current_user) { reporter } diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 52d926d5484..c41408fba65 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'can create subgroups' do + it 'cannot create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(201) + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..267ad529d3b 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as maintainer' do + context 'as Owner' do before do - group.add_maintainer(user) + group.add_owner(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 3a1d9ce3a1ed86eb169c48896d290b9bf9a91af9 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 14:06:50 -0700 Subject: Remove an example that is no longer necessary --- spec/models/group_spec.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 2b85b281d33..d7accbef6bd 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1023,13 +1023,4 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end - - describe 'subgroup_creation_level' do - it 'outputs the default one if it is nil' do - group = create(:group, subgroup_creation_level: nil) - - expect(group.subgroup_creation_level) - .to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) - end - end end -- cgit v1.2.1 From d9c6efceda827de6fa0b23bd9b4940b7914d646b Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 16:12:54 -0700 Subject: Make maintainers the default setting for creating subgroups --- app/models/group.rb | 10 ++++++---- spec/models/group_spec.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index f32312c6f43..f8be5f33be8 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,6 +58,8 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } + before_create :default_subgroup_creation_level_to_maintainers + after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -418,10 +420,6 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end - def subgroup_creation_level - super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end - private def update_two_factor_requirement @@ -451,4 +449,8 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end + + def default_subgroup_creation_level_to_maintainers + self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index d7accbef6bd..3a5ae14ab46 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1023,4 +1023,12 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end + + describe 'subgroup_creation_level' do + it 'defaults to maintainers' do + group = create (:group) + + expect(group.subgroup_creation_level).to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + end end -- cgit v1.2.1 From f21c03c8e27932d957086a1ec2465d468fb387da Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 30 Jun 2019 14:40:23 -0700 Subject: Make subgroup_creation_level default to maintainer at SQL level - Migration updates existing groups to "owner", then sets default to "maintainer" so that new groups will default to that - Update spec examples --- ...75626_add_group_creation_level_to_namespaces.rb | 1 + db/schema.rb | 2 +- spec/controllers/admin/groups_controller_spec.rb | 7 ++-- spec/factories/groups.rb | 5 ++- spec/features/groups/show_spec.rb | 17 ++++++--- spec/models/group_spec.rb | 5 +-- spec/policies/group_policy_spec.rb | 43 ++++++++++++++++++---- spec/requests/api/groups_spec.rb | 4 +- .../policies/group_policy_shared_context.rb | 2 +- 9 files changed, 61 insertions(+), 25 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index eed0ba25f27..85ac89af46e 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -12,6 +12,7 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] :subgroup_creation_level, :integer, default: 0) + change_column_default(:namespaces, :subgroup_creation_level, 1) end end diff --git a/db/schema.rb b/db/schema.rb index 29367551efb..683ee685372 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1442,7 +1442,7 @@ ActiveRecord::Schema.define(version: 20190626175626) do t.integer "project_creation_level" t.boolean "auto_devops_enabled" t.datetime_with_timezone "last_ci_minutes_notification_at" - t.integer "subgroup_creation_level", default: 0, null: false + t.integer "subgroup_creation_level", default: 1, null: false 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"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 72f389513f8..398f587bafe 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,14 +70,13 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: MAINTAINER } } - end.to change { group.reload.subgroup_creation_level } - .to(MAINTAINER) + group: { subgroup_creation_level: OWNER } } + end.to change { group.reload.subgroup_creation_level }.to(OWNER) end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 2f50fbfe2fa..b67ab955ffc 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,7 +5,6 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS - subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner @@ -46,5 +45,9 @@ FactoryBot.define do trait :auto_devops_disabled do auto_devops_enabled false end + + trait :owner_subgroup_creation_only do + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS + end end end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 163906010fa..48ba9064327 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -72,10 +72,11 @@ describe 'Group show page' do context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -84,10 +85,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -102,10 +104,9 @@ describe 'Group show page' do context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end - context 'when subgroup_creation_level is set to maintainer' do + context 'when subgroup_creation_level is set to maintainers' do let(:group) do create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) @@ -113,7 +114,9 @@ describe 'Group show page' do it 'allows creating subgroups' do visit path - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + + expect(page) + .to have_css("li[data-text='New subgroup']", visible: false) end end @@ -125,6 +128,7 @@ describe 'Group show page' do it 'does not allow creating subgroups' do visit path + expect(page) .not_to have_css("li[data-text='New subgroup']", visible: false) end @@ -134,10 +138,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 3a5ae14ab46..12b3bfdf015 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1026,9 +1026,8 @@ describe Group do describe 'subgroup_creation_level' do it 'defaults to maintainers' do - group = create (:group) - - expect(group.subgroup_creation_level).to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + expect(group.subgroup_creation_level) + .to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index da186f63eca..7fba62d2aa8 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -98,12 +98,38 @@ describe GroupPolicy do context 'maintainer' do let(:current_user) { maintainer } - it do - expect_allowed(*guest_permissions) - expect_allowed(*reporter_permissions) - expect_allowed(*developer_permissions) - expect_allowed(*maintainer_permissions) - expect_disallowed(*owner_permissions) + context 'with subgroup_creation level set to maintainer' do + let(:group) { create(:group, + :private, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + it do + allow(Group).to receive(:supports_nested_objects?).and_return(true) + + create_subgroup_permission = [:create_subgroup] + updated_maintainer_permissions = + maintainer_permissions + create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission + + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*updated_maintainer_permissions) + expect_disallowed(*updated_owner_permissions) + end + end + + context 'with subgroup_creation_level set to owner' do + it do + allow(Group).to receive(:supports_nested_objects?).and_return(true) + + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) + end end end @@ -181,7 +207,10 @@ describe GroupPolicy do end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do - let(:nested_group) { create(:group, :private, parent: group) } + let(:nested_group) { create(:group, + :private, + :owner_subgroup_creation_only, + parent: group) } before do nested_group.add_guest(guest) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4808ac0068..74389c4d82b 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -7,7 +7,7 @@ RSpec.shared_context 'GroupPolicy context' do let(:maintainer) { create(:user) } let(:owner) { create(:user) } let(:admin) { create(:admin) } - let(:group) { create(:group, :private) } + let(:group) { create(:group, :private, :owner_subgroup_creation_only) } let(:guest_permissions) do %i[ -- cgit v1.2.1 From 8309221555347d537cef74aad83ede4afc855074 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:00:34 -0700 Subject: Remove AR hook to set the default subgroup_creation_level --- app/models/group.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index f8be5f33be8..dbec211935d 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,8 +58,6 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } - before_create :default_subgroup_creation_level_to_maintainers - after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -449,8 +447,4 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end - - def default_subgroup_creation_level_to_maintainers - self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end end -- cgit v1.2.1 From fe438d0287ead7f07d9281261b5b079fd11147be Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:17:26 -0700 Subject: Clean up the show_spec examples previously added --- spec/features/groups/show_spec.rb | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 48ba9064327..ef0e885ee5f 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,27 +56,28 @@ describe 'Group show page' do end context 'subgroup support' do + let(:restricted_group) { create(:group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:relaxed_group) { create(:group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } let(:owner) { create(:user) } let(:maintainer) { create(:user) } - before do - group.add_owner(owner) - group.add_maintainer(maintainer) - end - context 'for owners' do + let(:path) { group_path(restricted_group) } + before do + restricted_group.add_owner(owner) sign_in(owner) end context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -85,11 +86,10 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } + visit path end it 'does not allow creating subgroups' do - visit path - expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -107,30 +107,30 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to maintainers' do - let(:group) do - create(:group, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + before do + relaxed_group.add_maintainer(maintainer) + path = group_path(relaxed_group) + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end end context 'when subgroup_creation_level is set to owners' do - let(:group) do - create(:group, - subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + before do + restricted_group.add_maintainer(maintainer) end it 'does not allow creating subgroups' do + path = group_path(restricted_group) visit path expect(page) - .not_to have_css("li[data-text='New subgroup']", visible: false) + .not_to have_selector("li[data-text='New subgroup']", + visible: false) end end end -- cgit v1.2.1 From a5175d618c11a62c97f16e484fd26cb5bae8153d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:55:00 -0700 Subject: Fix group creat_service_spec to contain maintainer context --- spec/services/groups/create_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 267ad529d3b..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as Owner' do + context 'as maintainer' do before do - group.add_owner(user) + group.add_maintainer(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From ca6640d2b28e1ec1049576fe2ae4a954cc598988 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:56:02 -0700 Subject: Add descriptions to examples --- spec/policies/group_policy_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 7fba62d2aa8..020b51f776e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -103,7 +103,7 @@ describe GroupPolicy do :private, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } - it do + it 'allows every maintainer permission plus creating subgroups' do allow(Group).to receive(:supports_nested_objects?).and_return(true) create_subgroup_permission = [:create_subgroup] @@ -121,7 +121,7 @@ describe GroupPolicy do end context 'with subgroup_creation_level set to owner' do - it do + it 'allows every maintainer permission' do allow(Group).to receive(:supports_nested_objects?).and_return(true) expect_allowed(*guest_permissions) -- cgit v1.2.1 From f72757f808fc818da6faa5ae38e2cbab56558e2d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 14 Jun 2019 15:13:24 -0700 Subject: Add changelog --- changelogs/unreleased/maintainers-can-create-subgroup.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/maintainers-can-create-subgroup.yml diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml new file mode 100644 index 00000000000..b537862c8af --- /dev/null +++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml @@ -0,0 +1,5 @@ +--- +title: Maintainers can crete subgroups +merge_request: 29718 +author: Fabio Papa +type: changed -- cgit v1.2.1 From c32b477ed876950593c82f06a6ed9d8cea69c170 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 14 Jun 2019 15:16:55 -0700 Subject: Add failing feature spec detailing a maintainer creating a subgroup - Change the two existing feature examples that create a subgroup to elucidate that the owner is creating the subgroup - Nest two more specs inside the 'subgroup support' context detailing what happens when a maintainer attempts to add a subgroup (one with subgroup support, and one without) --- spec/features/groups/show_spec.rb | 61 ++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 9671a4d8c49..2654d06cd8c 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,32 +56,67 @@ describe 'Group show page' do end context 'subgroup support' do - let(:user) { create(:user) } + let(:owner) { create(:user) } + let(:maintainer) { create(:user) } before do - group.add_owner(user) - sign_in(user) + group.add_owner(owner) + group.add_maintainer(maintainer) end - context 'when subgroups are supported', :js, :nested_groups do + context 'for owners' do before do - allow(Group).to receive(:supports_nested_objects?) { true } - visit path + sign_in(owner) end - it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + context 'when subgroups are supported', :js, :nested_groups do + before do + allow(Group).to receive(:supports_nested_objects?) { true } + visit path + end + + it 'allows creating subgroups' do + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end + end + + context 'when subgroups are not supported' do + before do + allow(Group).to receive(:supports_nested_objects?) { false } + visit path + end + + it 'allows creating subgroups' do + expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + end end end - context 'when subgroups are not supported' do + context 'for maintainers' do before do - allow(Group).to receive(:supports_nested_objects?) { false } - visit path + sign_in(maintainer) + end + + context 'when subgroups are supported', :js, :nested_groups do + before do + allow(Group).to receive(:supports_nested_objects?) { true } + visit path + end + + it 'allows creating subgroups' do + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end end - it 'allows creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + context 'when subgroups are not supported' do + before do + allow(Group).to receive(:supports_nested_objects?) { false } + visit path + end + + it 'allows creating subgroups' do + expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + end end end end -- cgit v1.2.1 From 97404196e0d89a2a72d96c127c0d6b9e8e450822 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 11:07:17 -0700 Subject: Add failing unit test specifying a maintainer creating a subgroup --- spec/services/groups/create_service_spec.rb | 9 +++++++++ .../shared_contexts/policies/group_policy_shared_context.rb | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index c5ff6cdbacd..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -87,6 +88,14 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end + + context 'as maintainer' do + before do + group.add_maintainer(user) + end + + it { is_expected.to be_persisted } + end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4808ac0068..b4b09d3295f 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -23,6 +23,15 @@ RSpec.shared_context 'GroupPolicy context' do create_projects read_cluster create_cluster update_cluster admin_cluster add_cluster ] + [ + :create_projects, + :read_cluster, + :create_cluster, + :update_cluster, + :admin_cluster, + :add_cluster, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) + ].compact end let(:owner_permissions) do [ @@ -30,8 +39,7 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) + :set_note_created_at ].compact end -- cgit v1.2.1 From eb27c2b164418da3ed75052657b364b740505b51 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 12:23:56 -0700 Subject: Update the group policy to allow >= maintainer to create subgroups All specs passing --- app/policies/group_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index ea86858181d..d92bcded19d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -109,7 +109,7 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { owner & nested_groups_supported }.enable :create_subgroup + rule { maintainer & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From e5f7ef0e77c5ab11f753347711088c8117b5b5dd Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 13:22:12 -0700 Subject: Modify API spec to expect a maintainer to be able to create subgroup --- spec/requests/api/groups_spec.rb | 4 ++-- spec/support/shared_contexts/policies/group_policy_shared_context.rb | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4b09d3295f..a40d3087f6e 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,10 +19,6 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - %i[ - create_projects - read_cluster create_cluster update_cluster admin_cluster add_cluster - ] [ :create_projects, :read_cluster, -- cgit v1.2.1 From 0239fb86ae4ca77ce93555ca60a09ba6abd61b7d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 25 Jun 2019 21:59:10 -0700 Subject: Add examples specing the setting to choose who can create subgroups This setting is at the group level only. The default is specified to be maintainers and owners. **Specs only**, all failing. --- spec/controllers/admin/groups_controller_spec.rb | 6 ++ spec/factories/groups.rb | 1 + spec/features/groups/group_settings_spec.rb | 8 +++ spec/models/group_spec.rb | 8 +++ spec/policies/group_policy_spec.rb | 70 ++++++++++++++++++++++ .../policies/group_policy_shared_context.rb | 16 ++--- 6 files changed, 99 insertions(+), 10 deletions(-) diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 509d8944e3a..df11321537f 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -68,5 +68,11 @@ describe Admin::GroupsController 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 + + it 'updates the subgroup_creation_level successfully' do + expect do + post :update, params: { id: group.to_param, group: { subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS } } + end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 18a0c2ec731..2f50fbfe2fa 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,6 +5,7 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 5cef5f0521f..95534d5a2ba 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -85,6 +85,14 @@ describe 'Edit group settings' do end end + describe 'subgroup creation level menu' do + it 'shows the selection menu' do + visit edit_group_path(group) + + expect(page).to have_content('Allowed to create subgroups') + end + end + describe 'edit group avatar' do before do visit edit_group_path(group) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 470ce65707d..fd40061dd3a 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,4 +994,12 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end + + describe 'subgroup_creation_level' do + it 'outputs the default one if it is nil' do + group = create(:group, subgroup_creation_level: nil) + + expect(group.subgroup_creation_level).to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 59f3a961d50..aed9a8e34ff 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -163,6 +163,18 @@ describe GroupPolicy do expect_allowed(*updated_owner_permissions) end end + + context 'maintainer' do + let(:current_user) { maintainer } + + it 'allows every maintainer permission except creating subgroups' do + create_subgroup_permission = [:create_subgroup] + updated_maintainer_permissions = maintainer_permissions - create_subgroup_permission + + expect_disallowed(*create_subgroup_permission) + expect_allowed(*updated_maintainer_permissions) + end + end end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do @@ -461,6 +473,64 @@ describe GroupPolicy do end end + context "create_subgroup" do + context 'when group has subgroup creation level set to owner' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + + context 'reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_subgroup) } + end + end + + context 'when group has subgroup creation level set to maintainer' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + context 'reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_allowed(:create_subgroup) } + end + + context 'owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_subgroup) } + end + end + end + it_behaves_like 'clusterable policies' do let(:clusterable) { create(:group) } let(:cluster) do diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index a40d3087f6e..b4808ac0068 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,15 +19,10 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - [ - :create_projects, - :read_cluster, - :create_cluster, - :update_cluster, - :admin_cluster, - :add_cluster, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) - ].compact + %i[ + create_projects + read_cluster create_cluster update_cluster admin_cluster add_cluster + ] end let(:owner_permissions) do [ @@ -35,7 +30,8 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at + :set_note_created_at, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) ].compact end -- cgit v1.2.1 From 58c627c2dbba1b2e1bd80d688a77872dc341caeb Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:46:48 -0700 Subject: Reset group policy to only allow >= owners to create subgroups --- app/policies/group_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index d92bcded19d..ea86858181d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -109,7 +109,7 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { maintainer & nested_groups_supported }.enable :create_subgroup + rule { owner & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From e55c85af81e9002ac48e7f7ea8235edc559b09ed Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:49:27 -0700 Subject: Add a subgroup_creation_level column to the namespaces table --- ...6175626_add_group_creation_level_to_namespaces.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb new file mode 100644 index 00000000000..b0ea74d4765 --- /dev/null +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + disable_ddl_transaction! + + def up + unless column_exists?(:namespaces, :subgroup_creation_level) + add_column_with_default(:namespaces, :subgroup_creation_level, :integer, default: 0) + end + end + + def down + if column_exists?(:namespaces, :subgroup_creation_level) + remove_column(:namespaces, :subgroup_creation_level) + end + end +end -- cgit v1.2.1 From 1dbcc55b10ebba2a71afa0071128139b2118e576 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:39:04 -0700 Subject: Add constants representing Owner and Maintainer group access levels --- lib/gitlab/access.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 6eb08f674c2..77076ead47a 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -29,6 +29,10 @@ module Gitlab MAINTAINER_PROJECT_ACCESS = 1 DEVELOPER_MAINTAINER_PROJECT_ACCESS = 2 + # Default subgroup creation level + OWNER_SUBGROUP_ACCESS = 0 + MAINTAINER_SUBGROUP_ACCESS = 1 + class << self delegate :values, to: :options -- cgit v1.2.1 From 3991a992f49ddd6abd1f0ca317bfd21b190b9422 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:45:58 -0700 Subject: Add subgroup_creation_level to the list of allowed group params For both groups_controller and admin/groups_controller --- app/controllers/admin/groups_controller.rb | 3 ++- app/controllers/groups_controller.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 15f7ef881c8..6317fa7c8d1 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -90,7 +90,8 @@ class Admin::GroupsController < Admin::ApplicationController :visibility_level, :require_two_factor_authentication, :two_factor_grace_period, - :project_creation_level + :project_creation_level, + :subgroup_creation_level ] end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 797833e3f91..aff418faae5 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -192,7 +192,8 @@ class GroupsController < Groups::ApplicationController :chat_team_name, :require_two_factor_authentication, :two_factor_grace_period, - :project_creation_level + :project_creation_level, + :subgroup_creation_level ] end -- cgit v1.2.1 From 26c6e75d9ef48217eef4ce9ce3b09642e5a73a28 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:11:09 -0700 Subject: Add "allowed to create subgroups" dropdown to group settings form --- app/views/groups/settings/_permissions.html.haml | 1 + app/views/groups/settings/_subgroup_creation_level.html.haml | 3 +++ lib/gitlab/access.rb | 7 +++++++ 3 files changed, 11 insertions(+) create mode 100644 app/views/groups/settings/_subgroup_creation_level.html.haml diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index 0da1f1ba7f5..d3375e00bad 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -20,6 +20,7 @@ = render_if_exists 'groups/settings/ip_restriction', f: f, group: @group = render 'groups/settings/lfs', f: f = render 'groups/settings/project_creation_level', f: f, group: @group + = render 'groups/settings/subgroup_creation_level', f: f, group: @group = render 'groups/settings/two_factor_auth', f: f = render_if_exists 'groups/member_lock_setting', f: f, group: @group diff --git a/app/views/groups/settings/_subgroup_creation_level.html.haml b/app/views/groups/settings/_subgroup_creation_level.html.haml new file mode 100644 index 00000000000..f36ad192bad --- /dev/null +++ b/app/views/groups/settings/_subgroup_creation_level.html.haml @@ -0,0 +1,3 @@ +.form-group + = f.label s_('SubgroupCreationLevel|Allowed to create subgroups'), class: 'label-bold' + = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, group.subgroup_creation_level), {}, class: 'form-control' diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 77076ead47a..7ef9f7ef630 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -110,6 +110,13 @@ module Gitlab def project_creation_level_name(name) project_creation_options.key(name) end + + def subgroup_creation_options + { + s_('SubgroupCreationlevel|Owners') => OWNER_SUBGROUP_ACCESS, + s_('SubgroupCreationlevel|Maintainers') => MAINTAINER_SUBGROUP_ACCESS + } + end end def human_access -- cgit v1.2.1 From d4b2ff2e2295d8a40b019c4c3bc1e9669c89ed82 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:17:24 -0700 Subject: Make the group model return maintainer level when it is not set --- app/models/group.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/group.rb b/app/models/group.rb index 8e89c7ecfb1..44bc6c8288a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -414,6 +414,10 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end + def subgroup_creation_level + super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + private def update_two_factor_requirement -- cgit v1.2.1 From 6d6897219af7bb3bea66c012c33e2588248cecb8 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 15:53:46 -0700 Subject: Add policy to allow maintainers to create subgroups when enabled --- app/policies/group_policy.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index ea86858181d..bd1eb02ca1f 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -43,6 +43,10 @@ class GroupPolicy < BasePolicy @subject.project_creation_level == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS end + condition(:maintainer_can_create_group) do + @subject.subgroup_creation_level == ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + rule { public_group }.policy do enable :read_group enable :read_list @@ -110,6 +114,7 @@ class GroupPolicy < BasePolicy end rule { owner & nested_groups_supported }.enable :create_subgroup + rule { maintainer & maintainer_can_create_group & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 9a6cf8338ab8a98d49cc1c0612b04bb5f746a146 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 18:15:56 +0000 Subject: Apply suggestion to changelogs/unreleased/maintainers-can-create-subgroup.yml --- changelogs/unreleased/maintainers-can-create-subgroup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml index b537862c8af..180f0f7247f 100644 --- a/changelogs/unreleased/maintainers-can-create-subgroup.yml +++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml @@ -1,5 +1,5 @@ --- -title: Maintainers can crete subgroups +title: Maintainers can create subgroups merge_request: 29718 author: Fabio Papa type: changed -- cgit v1.2.1 From 6dd6f98143f29695e7e4c767149f3a99beac7573 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 08:35:09 -0700 Subject: Add feature examples specing maintainers creating subgroups Both with subgroup_creation_level set to owners and to maintainers. Also fixed the naming of some other examples in the same spec as they were contradicting what they were actually performing in the test. These examples were probably copy/pasted, and not renamed. --- spec/features/groups/show_spec.rb | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 2654d06cd8c..68fa3f4e817 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -86,7 +86,7 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do + it 'does not allow creating subgroups' do expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) end end @@ -103,8 +103,22 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + context 'when subgroup_creation_level is set to maintainer' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + it 'allows creating subgroups' do + visit path + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end + end + + context 'when subgroup_creation_level is set to owners' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + + it 'does not allow creating subgroups' do + visit path + expect(page).not_to have_css("li[data-text='New subgroup']", visible: false) + end end end @@ -114,7 +128,7 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do + it 'does not allow creating subgroups' do expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) end end -- cgit v1.2.1 From 582495df08c4e8ba5efb0b63a96741da9a0b1869 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 09:22:10 -0700 Subject: Update schema --- db/schema.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/schema.rb b/db/schema.rb index 8876be1cb34..4b2bb95d413 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2112,6 +2112,7 @@ ActiveRecord::Schema.define(version: 20190628145246) do t.integer "extra_shared_runners_minutes_limit" t.string "ldap_sync_status", default: "ready", null: false t.boolean "membership_lock", default: false + t.integer "subgroup_creation_level", default: 0, null: false t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree -- cgit v1.2.1 From 9470dc25d8639499dafbbd366eda00b359cbd417 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 13:31:47 -0700 Subject: Style rules; Revert some examples --- ...75626_add_group_creation_level_to_namespaces.rb | 7 +++++-- spec/controllers/admin/groups_controller_spec.rb | 9 ++++++-- spec/features/groups/group_settings_spec.rb | 2 +- spec/features/groups/show_spec.rb | 24 +++++++++++++++------- spec/models/group_spec.rb | 3 ++- spec/policies/group_policy_spec.rb | 21 ++++++++++++++----- spec/requests/api/groups_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 4 ++-- 8 files changed, 52 insertions(+), 22 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index b0ea74d4765..eed0ba25f27 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -5,10 +5,13 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] DOWNTIME = false disable_ddl_transaction! - + def up unless column_exists?(:namespaces, :subgroup_creation_level) - add_column_with_default(:namespaces, :subgroup_creation_level, :integer, default: 0) + add_column_with_default(:namespaces, + :subgroup_creation_level, + :integer, + default: 0) end end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index df11321537f..72f389513f8 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,9 +70,14 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do + MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + expect do - post :update, params: { id: group.to_param, group: { subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS } } - end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + post :update, + params: { id: group.to_param, + group: { subgroup_creation_level: MAINTAINER } } + end.to change { group.reload.subgroup_creation_level } + .to(MAINTAINER) end end end diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 95534d5a2ba..676769c25fe 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -92,7 +92,7 @@ describe 'Edit group settings' do expect(page).to have_content('Allowed to create subgroups') end end - + describe 'edit group avatar' do before do visit edit_group_path(group) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 68fa3f4e817..163906010fa 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -76,7 +76,8 @@ describe 'Group show page' do end it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + expect(page) + .to have_css("li[data-text='New subgroup']", visible: false) end end @@ -87,7 +88,8 @@ describe 'Group show page' do end it 'does not allow creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_selector("li[data-text='New subgroup']", visible: false) end end end @@ -104,8 +106,11 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to maintainer' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } - + let(:group) do + create(:group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + it 'allows creating subgroups' do visit path expect(page).to have_css("li[data-text='New subgroup']", visible: false) @@ -113,11 +118,15 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to owners' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:group) do + create(:group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end it 'does not allow creating subgroups' do visit path - expect(page).not_to have_css("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_css("li[data-text='New subgroup']", visible: false) end end end @@ -129,7 +138,8 @@ describe 'Group show page' do end it 'does not allow creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_selector("li[data-text='New subgroup']", visible: false) end end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index fd40061dd3a..6627177ad61 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -999,7 +999,8 @@ describe Group do it 'outputs the default one if it is nil' do group = create(:group, subgroup_creation_level: nil) - expect(group.subgroup_creation_level).to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + expect(group.subgroup_creation_level) + .to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index aed9a8e34ff..da186f63eca 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -145,7 +145,8 @@ describe GroupPolicy do it 'allows every owner permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = owner_permissions - create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_owner_permissions) @@ -157,7 +158,8 @@ describe GroupPolicy do it 'allows every owner permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = owner_permissions - create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_owner_permissions) @@ -169,7 +171,8 @@ describe GroupPolicy do it 'allows every maintainer permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_maintainer_permissions = maintainer_permissions - create_subgroup_permission + updated_maintainer_permissions = + maintainer_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_maintainer_permissions) @@ -475,7 +478,11 @@ describe GroupPolicy do context "create_subgroup" do context 'when group has subgroup creation level set to owner' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:group) do + create( + :group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end context 'reporter' do let(:current_user) { reporter } @@ -503,7 +510,11 @@ describe GroupPolicy do end context 'when group has subgroup creation level set to maintainer' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:group) do + create( + :group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end context 'reporter' do let(:current_user) { reporter } diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 52d926d5484..c41408fba65 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'can create subgroups' do + it 'cannot create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(201) + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..267ad529d3b 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as maintainer' do + context 'as Owner' do before do - group.add_maintainer(user) + group.add_owner(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 23c549d4a38efc15a54ff7cc57e38a9aefb632f8 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 14:06:50 -0700 Subject: Remove an example that is no longer necessary --- spec/models/group_spec.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 6627177ad61..470ce65707d 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,13 +994,4 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end - - describe 'subgroup_creation_level' do - it 'outputs the default one if it is nil' do - group = create(:group, subgroup_creation_level: nil) - - expect(group.subgroup_creation_level) - .to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) - end - end end -- cgit v1.2.1 From a781822664a4f9c8edffaa1d75fec856b13bbb1a Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 16:12:54 -0700 Subject: Make maintainers the default setting for creating subgroups --- app/models/group.rb | 10 ++++++---- spec/models/group_spec.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 44bc6c8288a..47e7bf34ec8 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,6 +58,8 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } + before_create :default_subgroup_creation_level_to_maintainers + after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -414,10 +416,6 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end - def subgroup_creation_level - super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end - private def update_two_factor_requirement @@ -447,4 +445,8 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end + + def default_subgroup_creation_level_to_maintainers + self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 470ce65707d..9ae18d7bab7 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,4 +994,12 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end + + describe 'subgroup_creation_level' do + it 'defaults to maintainers' do + group = create (:group) + + expect(group.subgroup_creation_level).to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + end end -- cgit v1.2.1 From e93df68a442486dc08887773c4e96b055f013c57 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 30 Jun 2019 14:40:23 -0700 Subject: Make subgroup_creation_level default to maintainer at SQL level - Migration updates existing groups to "owner", then sets default to "maintainer" so that new groups will default to that - Update spec examples --- ...75626_add_group_creation_level_to_namespaces.rb | 1 + db/schema.rb | 2 +- spec/controllers/admin/groups_controller_spec.rb | 7 ++-- spec/factories/groups.rb | 5 ++- spec/features/groups/show_spec.rb | 17 ++++++--- spec/models/group_spec.rb | 5 +-- spec/policies/group_policy_spec.rb | 43 ++++++++++++++++++---- spec/requests/api/groups_spec.rb | 4 +- .../policies/group_policy_shared_context.rb | 2 +- 9 files changed, 61 insertions(+), 25 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index eed0ba25f27..85ac89af46e 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -12,6 +12,7 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] :subgroup_creation_level, :integer, default: 0) + change_column_default(:namespaces, :subgroup_creation_level, 1) end end diff --git a/db/schema.rb b/db/schema.rb index 4b2bb95d413..e777cc575c0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2112,7 +2112,7 @@ ActiveRecord::Schema.define(version: 20190628145246) do t.integer "extra_shared_runners_minutes_limit" t.string "ldap_sync_status", default: "ready", null: false t.boolean "membership_lock", default: false - t.integer "subgroup_creation_level", default: 0, null: false + t.integer "subgroup_creation_level", default: 1, null: false t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 72f389513f8..398f587bafe 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,14 +70,13 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: MAINTAINER } } - end.to change { group.reload.subgroup_creation_level } - .to(MAINTAINER) + group: { subgroup_creation_level: OWNER } } + end.to change { group.reload.subgroup_creation_level }.to(OWNER) end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 2f50fbfe2fa..b67ab955ffc 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,7 +5,6 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS - subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner @@ -46,5 +45,9 @@ FactoryBot.define do trait :auto_devops_disabled do auto_devops_enabled false end + + trait :owner_subgroup_creation_only do + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS + end end end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 163906010fa..48ba9064327 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -72,10 +72,11 @@ describe 'Group show page' do context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -84,10 +85,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -102,10 +104,9 @@ describe 'Group show page' do context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end - context 'when subgroup_creation_level is set to maintainer' do + context 'when subgroup_creation_level is set to maintainers' do let(:group) do create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) @@ -113,7 +114,9 @@ describe 'Group show page' do it 'allows creating subgroups' do visit path - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + + expect(page) + .to have_css("li[data-text='New subgroup']", visible: false) end end @@ -125,6 +128,7 @@ describe 'Group show page' do it 'does not allow creating subgroups' do visit path + expect(page) .not_to have_css("li[data-text='New subgroup']", visible: false) end @@ -134,10 +138,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 9ae18d7bab7..c7fb0f51075 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -997,9 +997,8 @@ describe Group do describe 'subgroup_creation_level' do it 'defaults to maintainers' do - group = create (:group) - - expect(group.subgroup_creation_level).to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + expect(group.subgroup_creation_level) + .to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index da186f63eca..7fba62d2aa8 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -98,12 +98,38 @@ describe GroupPolicy do context 'maintainer' do let(:current_user) { maintainer } - it do - expect_allowed(*guest_permissions) - expect_allowed(*reporter_permissions) - expect_allowed(*developer_permissions) - expect_allowed(*maintainer_permissions) - expect_disallowed(*owner_permissions) + context 'with subgroup_creation level set to maintainer' do + let(:group) { create(:group, + :private, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + it do + allow(Group).to receive(:supports_nested_objects?).and_return(true) + + create_subgroup_permission = [:create_subgroup] + updated_maintainer_permissions = + maintainer_permissions + create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission + + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*updated_maintainer_permissions) + expect_disallowed(*updated_owner_permissions) + end + end + + context 'with subgroup_creation_level set to owner' do + it do + allow(Group).to receive(:supports_nested_objects?).and_return(true) + + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) + end end end @@ -181,7 +207,10 @@ describe GroupPolicy do end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do - let(:nested_group) { create(:group, :private, parent: group) } + let(:nested_group) { create(:group, + :private, + :owner_subgroup_creation_only, + parent: group) } before do nested_group.add_guest(guest) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4808ac0068..74389c4d82b 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -7,7 +7,7 @@ RSpec.shared_context 'GroupPolicy context' do let(:maintainer) { create(:user) } let(:owner) { create(:user) } let(:admin) { create(:admin) } - let(:group) { create(:group, :private) } + let(:group) { create(:group, :private, :owner_subgroup_creation_only) } let(:guest_permissions) do %i[ -- cgit v1.2.1 From 832a598cf05b70cdff2eb081cd02e410849c39e2 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:00:34 -0700 Subject: Remove AR hook to set the default subgroup_creation_level --- app/models/group.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 47e7bf34ec8..8e89c7ecfb1 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,8 +58,6 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } - before_create :default_subgroup_creation_level_to_maintainers - after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -445,8 +443,4 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end - - def default_subgroup_creation_level_to_maintainers - self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end end -- cgit v1.2.1 From 128b5e33dbd4729f25bd3146c967861e814be9e6 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:17:26 -0700 Subject: Clean up the show_spec examples previously added --- spec/features/groups/show_spec.rb | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 48ba9064327..ef0e885ee5f 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,27 +56,28 @@ describe 'Group show page' do end context 'subgroup support' do + let(:restricted_group) { create(:group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:relaxed_group) { create(:group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } let(:owner) { create(:user) } let(:maintainer) { create(:user) } - before do - group.add_owner(owner) - group.add_maintainer(maintainer) - end - context 'for owners' do + let(:path) { group_path(restricted_group) } + before do + restricted_group.add_owner(owner) sign_in(owner) end context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -85,11 +86,10 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } + visit path end it 'does not allow creating subgroups' do - visit path - expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -107,30 +107,30 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to maintainers' do - let(:group) do - create(:group, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + before do + relaxed_group.add_maintainer(maintainer) + path = group_path(relaxed_group) + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end end context 'when subgroup_creation_level is set to owners' do - let(:group) do - create(:group, - subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + before do + restricted_group.add_maintainer(maintainer) end it 'does not allow creating subgroups' do + path = group_path(restricted_group) visit path expect(page) - .not_to have_css("li[data-text='New subgroup']", visible: false) + .not_to have_selector("li[data-text='New subgroup']", + visible: false) end end end -- cgit v1.2.1 From 849e87e8d2c955d91f55c38911b20574c839035a Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:55:00 -0700 Subject: Fix group creat_service_spec to contain maintainer context --- spec/services/groups/create_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 267ad529d3b..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as Owner' do + context 'as maintainer' do before do - group.add_owner(user) + group.add_maintainer(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 71822fc5de60f073e6f43507b0419c6d52540c8d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:56:02 -0700 Subject: Add descriptions to examples --- spec/policies/group_policy_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 7fba62d2aa8..020b51f776e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -103,7 +103,7 @@ describe GroupPolicy do :private, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } - it do + it 'allows every maintainer permission plus creating subgroups' do allow(Group).to receive(:supports_nested_objects?).and_return(true) create_subgroup_permission = [:create_subgroup] @@ -121,7 +121,7 @@ describe GroupPolicy do end context 'with subgroup_creation_level set to owner' do - it do + it 'allows every maintainer permission' do allow(Group).to receive(:supports_nested_objects?).and_return(true) expect_allowed(*guest_permissions) -- cgit v1.2.1 From b0196e855a6c82ffa96b384e1063df77f2b42249 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 13:41:32 -0700 Subject: Apply changes recomended by merge request coach --- app/models/group.rb | 4 ++++ app/views/groups/_group_admin_settings.html.haml | 6 ++++++ ...190626175626_add_group_creation_level_to_namespaces.rb | 15 +++++---------- db/schema.rb | 2 +- spec/controllers/admin/groups_controller_spec.rb | 6 ++---- spec/features/admin/admin_groups_spec.rb | 9 +++++++++ spec/features/groups/show_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 9 --------- 8 files changed, 29 insertions(+), 26 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 8e89c7ecfb1..44bc6c8288a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -414,6 +414,10 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end + def subgroup_creation_level + super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + private def update_two_factor_requirement diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml index b8f632d11d3..733cb36cc3d 100644 --- a/app/views/groups/_group_admin_settings.html.haml +++ b/app/views/groups/_group_admin_settings.html.haml @@ -16,6 +16,12 @@ .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 + .col-sm-2.col-form-label + = f.label s_('SubgroupCreationlevel|Allowed to create subgroups') + .col-sm-10 + = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, @group.subgroup_creation_level), {}, class: 'form-control' + .form-group.row .col-sm-2.col-form-label.pt-0 = f.label :require_two_factor_authentication, 'Two-factor authentication' diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index 85ac89af46e..867ec3b7c91 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -7,18 +7,13 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] disable_ddl_transaction! def up - unless column_exists?(:namespaces, :subgroup_creation_level) - add_column_with_default(:namespaces, - :subgroup_creation_level, - :integer, - default: 0) - change_column_default(:namespaces, :subgroup_creation_level, 1) - end + add_column(:namespaces, :subgroup_creation_level, :integer) + change_column_default(:namespaces, + :subgroup_creation_level, + ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end def down - if column_exists?(:namespaces, :subgroup_creation_level) - remove_column(:namespaces, :subgroup_creation_level) - end + remove_column(:namespaces, :subgroup_creation_level) end end diff --git a/db/schema.rb b/db/schema.rb index e777cc575c0..56f66b45595 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2112,7 +2112,7 @@ ActiveRecord::Schema.define(version: 20190628145246) do t.integer "extra_shared_runners_minutes_limit" t.string "ldap_sync_status", default: "ready", null: false t.boolean "membership_lock", default: false - t.integer "subgroup_creation_level", default: 1, null: false + t.integer "subgroup_creation_level", default: 1 t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 398f587bafe..1123563c1e3 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,13 +70,11 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS - expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: OWNER } } - end.to change { group.reload.subgroup_creation_level }.to(OWNER) + group: { subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS } } + end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end end end diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 735ca60f7da..35c384dd458 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -102,6 +102,15 @@ describe 'Admin Groups' do expect_selected_visibility(group.visibility_level) end + it 'shows the subgroup creation level dropdown populated with the group subgroup_creation_level value' do + group = create(:group, :private, :owner_subgroup_creation_only) + + visit admin_group_edit_path(group) + + expect(page).to have_select("group_subgroup_creation_level", + selected: ::Gitlab::Access.subgroup_creation_options.keys[group.subgroup_creation_level]) + end + it 'edit group path does not change group name', :js do group = create(:group, :private) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index ef0e885ee5f..5096abadb79 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -71,7 +71,7 @@ describe 'Group show page' do sign_in(owner) end - context 'when subgroups are supported', :js, :nested_groups do + context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } visit path @@ -101,7 +101,7 @@ describe 'Group show page' do sign_in(maintainer) end - context 'when subgroups are supported', :js, :nested_groups do + context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..c5ff6cdbacd 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,4 +1,3 @@ -# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -88,14 +87,6 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - - context 'as maintainer' do - before do - group.add_maintainer(user) - end - - it { is_expected.to be_persisted } - end end end -- cgit v1.2.1 From 142a39437eb76c957a984204bc79b0bf5db5be35 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 15:29:54 -0700 Subject: Adjust the documentation on subgroups --- doc/user/group/subgroups/index.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index 1c6cca049c5..e3f7539a9b6 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -75,13 +75,16 @@ structure. ## Creating a subgroup NOTE: **Note:** -You must be an Owner of a group to create a subgroup. For -more information check the [permissions table](../../permissions.md#group-members-permissions). +In order to create a subgroup you must either be an Owner or a Maintainer of the +group, depending on the group's setting. By default groups allow both Owners and +Maintainers to create subgroups, but this can be changed by an Owner or +Administrator to only allow Owners to create subgroups. For more information +check the [permissions table](../../permissions.md#group-members-permissions). For a list of words that are not allowed to be used as group names see the -[reserved names](../../reserved_names.md). -Users can always create subgroups if they are explicitly added as an Owner to -a parent group, even if group creation is disabled by an administrator in their -settings. +[reserved names](../../reserved_names.md). Users can always create subgroups if +they are explicitly added as an Owner (or Maintainer, if that setting is +enabled) to a parent group, even if group creation is disabled by an +administrator in their settings. To create a subgroup: -- cgit v1.2.1 From 650b88fc41d51f3d313e74bb94cbd211ca54003b Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 15:39:43 -0700 Subject: Adjust the documentation on subgroup permissions --- doc/user/permissions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 03abef9fc62..20b23b75cb9 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -209,13 +209,15 @@ group. | Create project in group | | | ✓ | ✓ | ✓ | | Create/edit/delete group milestones | | | ✓ | ✓ | ✓ | | Enable/disable a dependency proxy **[PREMIUM]** | | | ✓ | ✓ | ✓ | +| Create subgroup | | | | ✓* | ✓ | | Edit group | | | | | ✓ | -| Create subgroup | | | | | ✓ | | Manage group members | | | | | ✓ | | Remove group | | | | | ✓ | | Delete group epic **[ULTIMATE]** | | | | | ✓ | | View group Audit Events | | | | | ✓ | +*Groups can be set to allow either Owners or Owners and maintainers to create subgroups + ### Subgroup permissions When you add a member to a subgroup, they inherit the membership and -- cgit v1.2.1 From 0f659a0da634bf61378766d6ec247cc49595becf Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 16:27:49 -0700 Subject: Fix some code style issues --- spec/features/groups/show_spec.rb | 12 ++++++++---- spec/policies/group_policy_spec.rb | 13 ++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 5096abadb79..f1501181432 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,10 +56,14 @@ describe 'Group show page' do end context 'subgroup support' do - let(:restricted_group) { create(:group, - subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } - let(:relaxed_group) { create(:group, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:restricted_group) do + create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end + + let(:relaxed_group) do + create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + let(:owner) { create(:user) } let(:maintainer) { create(:user) } diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 020b51f776e..dc3675a7b9e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -99,9 +99,9 @@ describe GroupPolicy do let(:current_user) { maintainer } context 'with subgroup_creation level set to maintainer' do - let(:group) { create(:group, - :private, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:group) do + create(:group, :private, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end it 'allows every maintainer permission plus creating subgroups' do allow(Group).to receive(:supports_nested_objects?).and_return(true) @@ -207,10 +207,9 @@ describe GroupPolicy do end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do - let(:nested_group) { create(:group, - :private, - :owner_subgroup_creation_only, - parent: group) } + let(:nested_group) do + create(:group, :private, :owner_subgroup_creation_only, parent: group) + end before do nested_group.add_guest(guest) -- cgit v1.2.1 From a0485e75005e7930acba832089d830744d6cbb14 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 2 Jul 2019 08:38:38 -0700 Subject: Apply recomended changes from merge coach --- ...190626175626_add_group_creation_level_to_namespaces.rb | 1 - spec/features/admin/admin_groups_spec.rb | 3 +-- spec/features/groups/show_spec.rb | 15 +++++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index 867ec3b7c91..3b75c92e518 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -4,7 +4,6 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] include Gitlab::Database::MigrationHelpers DOWNTIME = false - disable_ddl_transaction! def up add_column(:namespaces, :subgroup_creation_level, :integer) diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 35c384dd458..c1ad73779c9 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -107,8 +107,7 @@ describe 'Admin Groups' do visit admin_group_edit_path(group) - expect(page).to have_select("group_subgroup_creation_level", - selected: ::Gitlab::Access.subgroup_creation_options.keys[group.subgroup_creation_level]) + expect(page).to have_content('Allowed to create subgroups') end it 'edit group path does not change group name', :js do diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index f1501181432..bed998a0859 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -57,11 +57,11 @@ describe 'Group show page' do context 'subgroup support' do let(:restricted_group) do - create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end let(:relaxed_group) do - create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end let(:owner) { create(:user) } @@ -78,10 +78,11 @@ describe 'Group show page' do context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -90,10 +91,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -113,11 +115,12 @@ describe 'Group show page' do context 'when subgroup_creation_level is set to maintainers' do before do relaxed_group.add_maintainer(maintainer) - path = group_path(relaxed_group) - visit path end it 'allows creating subgroups' do + path = group_path(relaxed_group) + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end -- cgit v1.2.1 From bab77650eae67d40ec4070597f83bcc1d406697a Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 14 Jun 2019 15:13:24 -0700 Subject: Add changelog --- changelogs/unreleased/maintainers-can-create-subgroup.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/maintainers-can-create-subgroup.yml diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml new file mode 100644 index 00000000000..b537862c8af --- /dev/null +++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml @@ -0,0 +1,5 @@ +--- +title: Maintainers can crete subgroups +merge_request: 29718 +author: Fabio Papa +type: changed -- cgit v1.2.1 From 467ed9d4e79b27557f6407ed4fd567473b83ab76 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 14 Jun 2019 15:16:55 -0700 Subject: Add failing feature spec detailing a maintainer creating a subgroup - Change the two existing feature examples that create a subgroup to elucidate that the owner is creating the subgroup - Nest two more specs inside the 'subgroup support' context detailing what happens when a maintainer attempts to add a subgroup (one with subgroup support, and one without) --- spec/features/groups/show_spec.rb | 61 ++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 9671a4d8c49..2654d06cd8c 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,32 +56,67 @@ describe 'Group show page' do end context 'subgroup support' do - let(:user) { create(:user) } + let(:owner) { create(:user) } + let(:maintainer) { create(:user) } before do - group.add_owner(user) - sign_in(user) + group.add_owner(owner) + group.add_maintainer(maintainer) end - context 'when subgroups are supported', :js, :nested_groups do + context 'for owners' do before do - allow(Group).to receive(:supports_nested_objects?) { true } - visit path + sign_in(owner) end - it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + context 'when subgroups are supported', :js, :nested_groups do + before do + allow(Group).to receive(:supports_nested_objects?) { true } + visit path + end + + it 'allows creating subgroups' do + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end + end + + context 'when subgroups are not supported' do + before do + allow(Group).to receive(:supports_nested_objects?) { false } + visit path + end + + it 'allows creating subgroups' do + expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + end end end - context 'when subgroups are not supported' do + context 'for maintainers' do before do - allow(Group).to receive(:supports_nested_objects?) { false } - visit path + sign_in(maintainer) + end + + context 'when subgroups are supported', :js, :nested_groups do + before do + allow(Group).to receive(:supports_nested_objects?) { true } + visit path + end + + it 'allows creating subgroups' do + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end end - it 'allows creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + context 'when subgroups are not supported' do + before do + allow(Group).to receive(:supports_nested_objects?) { false } + visit path + end + + it 'allows creating subgroups' do + expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + end end end end -- cgit v1.2.1 From 6d50040c9469d2b1b6c137897b5d7403f3e3b7d5 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 11:07:17 -0700 Subject: Add failing unit test specifying a maintainer creating a subgroup --- spec/services/groups/create_service_spec.rb | 9 +++++++++ .../shared_contexts/policies/group_policy_shared_context.rb | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index c5ff6cdbacd..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -87,6 +88,14 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end + + context 'as maintainer' do + before do + group.add_maintainer(user) + end + + it { is_expected.to be_persisted } + end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4808ac0068..b4b09d3295f 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -23,6 +23,15 @@ RSpec.shared_context 'GroupPolicy context' do create_projects read_cluster create_cluster update_cluster admin_cluster add_cluster ] + [ + :create_projects, + :read_cluster, + :create_cluster, + :update_cluster, + :admin_cluster, + :add_cluster, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) + ].compact end let(:owner_permissions) do [ @@ -30,8 +39,7 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) + :set_note_created_at ].compact end -- cgit v1.2.1 From e520893b3450d6b9da4fbe74da943fbfcc24fedd Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 12:23:56 -0700 Subject: Update the group policy to allow >= maintainer to create subgroups All specs passing --- app/policies/group_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index ea86858181d..d92bcded19d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -109,7 +109,7 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { owner & nested_groups_supported }.enable :create_subgroup + rule { maintainer & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 90abe7189177e9d6ecc41c3a3d77a3181904ae8e Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 13:22:12 -0700 Subject: Modify API spec to expect a maintainer to be able to create subgroup --- spec/requests/api/groups_spec.rb | 4 ++-- spec/support/shared_contexts/policies/group_policy_shared_context.rb | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4b09d3295f..a40d3087f6e 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,10 +19,6 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - %i[ - create_projects - read_cluster create_cluster update_cluster admin_cluster add_cluster - ] [ :create_projects, :read_cluster, -- cgit v1.2.1 From ae703dbc4a8cc58ee44a4f99e789636c867c7267 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 25 Jun 2019 21:59:10 -0700 Subject: Add examples specing the setting to choose who can create subgroups This setting is at the group level only. The default is specified to be maintainers and owners. **Specs only**, all failing. --- spec/controllers/admin/groups_controller_spec.rb | 6 ++ spec/factories/groups.rb | 1 + spec/features/groups/group_settings_spec.rb | 8 +++ spec/models/group_spec.rb | 8 +++ spec/policies/group_policy_spec.rb | 70 ++++++++++++++++++++++ .../policies/group_policy_shared_context.rb | 16 ++--- 6 files changed, 99 insertions(+), 10 deletions(-) diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 509d8944e3a..df11321537f 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -68,5 +68,11 @@ describe Admin::GroupsController 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 + + it 'updates the subgroup_creation_level successfully' do + expect do + post :update, params: { id: group.to_param, group: { subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS } } + end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 18a0c2ec731..2f50fbfe2fa 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,6 +5,7 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 5cef5f0521f..95534d5a2ba 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -85,6 +85,14 @@ describe 'Edit group settings' do end end + describe 'subgroup creation level menu' do + it 'shows the selection menu' do + visit edit_group_path(group) + + expect(page).to have_content('Allowed to create subgroups') + end + end + describe 'edit group avatar' do before do visit edit_group_path(group) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 470ce65707d..fd40061dd3a 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,4 +994,12 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end + + describe 'subgroup_creation_level' do + it 'outputs the default one if it is nil' do + group = create(:group, subgroup_creation_level: nil) + + expect(group.subgroup_creation_level).to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 59f3a961d50..aed9a8e34ff 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -163,6 +163,18 @@ describe GroupPolicy do expect_allowed(*updated_owner_permissions) end end + + context 'maintainer' do + let(:current_user) { maintainer } + + it 'allows every maintainer permission except creating subgroups' do + create_subgroup_permission = [:create_subgroup] + updated_maintainer_permissions = maintainer_permissions - create_subgroup_permission + + expect_disallowed(*create_subgroup_permission) + expect_allowed(*updated_maintainer_permissions) + end + end end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do @@ -461,6 +473,64 @@ describe GroupPolicy do end end + context "create_subgroup" do + context 'when group has subgroup creation level set to owner' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + + context 'reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_subgroup) } + end + end + + context 'when group has subgroup creation level set to maintainer' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + context 'reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_allowed(:create_subgroup) } + end + + context 'owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_subgroup) } + end + end + end + it_behaves_like 'clusterable policies' do let(:clusterable) { create(:group) } let(:cluster) do diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index a40d3087f6e..b4808ac0068 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,15 +19,10 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - [ - :create_projects, - :read_cluster, - :create_cluster, - :update_cluster, - :admin_cluster, - :add_cluster, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) - ].compact + %i[ + create_projects + read_cluster create_cluster update_cluster admin_cluster add_cluster + ] end let(:owner_permissions) do [ @@ -35,7 +30,8 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at + :set_note_created_at, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) ].compact end -- cgit v1.2.1 From b6ce065f4b0476933845ba265e0fe92fb24d1e6e Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:46:48 -0700 Subject: Reset group policy to only allow >= owners to create subgroups --- app/policies/group_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index d92bcded19d..ea86858181d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -109,7 +109,7 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { maintainer & nested_groups_supported }.enable :create_subgroup + rule { owner & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 1f4d2ab096284187d92d00f40ebea4df961b2c8e Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:49:27 -0700 Subject: Add a subgroup_creation_level column to the namespaces table --- ...6175626_add_group_creation_level_to_namespaces.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb new file mode 100644 index 00000000000..b0ea74d4765 --- /dev/null +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + disable_ddl_transaction! + + def up + unless column_exists?(:namespaces, :subgroup_creation_level) + add_column_with_default(:namespaces, :subgroup_creation_level, :integer, default: 0) + end + end + + def down + if column_exists?(:namespaces, :subgroup_creation_level) + remove_column(:namespaces, :subgroup_creation_level) + end + end +end -- cgit v1.2.1 From 83a7545554fc3ccd44cd6c6d620b860d1d4aeb08 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:39:04 -0700 Subject: Add constants representing Owner and Maintainer group access levels --- lib/gitlab/access.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 6eb08f674c2..77076ead47a 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -29,6 +29,10 @@ module Gitlab MAINTAINER_PROJECT_ACCESS = 1 DEVELOPER_MAINTAINER_PROJECT_ACCESS = 2 + # Default subgroup creation level + OWNER_SUBGROUP_ACCESS = 0 + MAINTAINER_SUBGROUP_ACCESS = 1 + class << self delegate :values, to: :options -- cgit v1.2.1 From 65e9fd31d5ca0e0e6eb2b44599f18c333a366a47 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:45:58 -0700 Subject: Add subgroup_creation_level to the list of allowed group params For both groups_controller and admin/groups_controller --- app/controllers/admin/groups_controller.rb | 3 ++- app/controllers/groups_controller.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 15f7ef881c8..6317fa7c8d1 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -90,7 +90,8 @@ class Admin::GroupsController < Admin::ApplicationController :visibility_level, :require_two_factor_authentication, :two_factor_grace_period, - :project_creation_level + :project_creation_level, + :subgroup_creation_level ] end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 797833e3f91..aff418faae5 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -192,7 +192,8 @@ class GroupsController < Groups::ApplicationController :chat_team_name, :require_two_factor_authentication, :two_factor_grace_period, - :project_creation_level + :project_creation_level, + :subgroup_creation_level ] end -- cgit v1.2.1 From 84c1455c5b26017ad28ad0f48433d8f41d8b95d3 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:11:09 -0700 Subject: Add "allowed to create subgroups" dropdown to group settings form --- app/views/groups/settings/_permissions.html.haml | 1 + app/views/groups/settings/_subgroup_creation_level.html.haml | 3 +++ lib/gitlab/access.rb | 7 +++++++ 3 files changed, 11 insertions(+) create mode 100644 app/views/groups/settings/_subgroup_creation_level.html.haml diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index 0da1f1ba7f5..d3375e00bad 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -20,6 +20,7 @@ = render_if_exists 'groups/settings/ip_restriction', f: f, group: @group = render 'groups/settings/lfs', f: f = render 'groups/settings/project_creation_level', f: f, group: @group + = render 'groups/settings/subgroup_creation_level', f: f, group: @group = render 'groups/settings/two_factor_auth', f: f = render_if_exists 'groups/member_lock_setting', f: f, group: @group diff --git a/app/views/groups/settings/_subgroup_creation_level.html.haml b/app/views/groups/settings/_subgroup_creation_level.html.haml new file mode 100644 index 00000000000..f36ad192bad --- /dev/null +++ b/app/views/groups/settings/_subgroup_creation_level.html.haml @@ -0,0 +1,3 @@ +.form-group + = f.label s_('SubgroupCreationLevel|Allowed to create subgroups'), class: 'label-bold' + = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, group.subgroup_creation_level), {}, class: 'form-control' diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 77076ead47a..7ef9f7ef630 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -110,6 +110,13 @@ module Gitlab def project_creation_level_name(name) project_creation_options.key(name) end + + def subgroup_creation_options + { + s_('SubgroupCreationlevel|Owners') => OWNER_SUBGROUP_ACCESS, + s_('SubgroupCreationlevel|Maintainers') => MAINTAINER_SUBGROUP_ACCESS + } + end end def human_access -- cgit v1.2.1 From 6175ff7a9b6bc97929a0b81bfe70ff39eaffb58c Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:17:24 -0700 Subject: Make the group model return maintainer level when it is not set --- app/models/group.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/group.rb b/app/models/group.rb index 8e89c7ecfb1..44bc6c8288a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -414,6 +414,10 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end + def subgroup_creation_level + super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + private def update_two_factor_requirement -- cgit v1.2.1 From 806088d72e9ec7b1d1ef97db43d76b248dd19186 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 15:53:46 -0700 Subject: Add policy to allow maintainers to create subgroups when enabled --- app/policies/group_policy.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index ea86858181d..bd1eb02ca1f 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -43,6 +43,10 @@ class GroupPolicy < BasePolicy @subject.project_creation_level == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS end + condition(:maintainer_can_create_group) do + @subject.subgroup_creation_level == ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + rule { public_group }.policy do enable :read_group enable :read_list @@ -110,6 +114,7 @@ class GroupPolicy < BasePolicy end rule { owner & nested_groups_supported }.enable :create_subgroup + rule { maintainer & maintainer_can_create_group & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 7c7a94f2088ee2687bc21434371c6b27ae56bd1b Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 18:15:56 +0000 Subject: Apply suggestion to changelogs/unreleased/maintainers-can-create-subgroup.yml --- changelogs/unreleased/maintainers-can-create-subgroup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml index b537862c8af..180f0f7247f 100644 --- a/changelogs/unreleased/maintainers-can-create-subgroup.yml +++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml @@ -1,5 +1,5 @@ --- -title: Maintainers can crete subgroups +title: Maintainers can create subgroups merge_request: 29718 author: Fabio Papa type: changed -- cgit v1.2.1 From 770e3de7d613d2c87c33bbc993ca5cec1eda4635 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 08:35:09 -0700 Subject: Add feature examples specing maintainers creating subgroups Both with subgroup_creation_level set to owners and to maintainers. Also fixed the naming of some other examples in the same spec as they were contradicting what they were actually performing in the test. These examples were probably copy/pasted, and not renamed. --- spec/features/groups/show_spec.rb | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 2654d06cd8c..68fa3f4e817 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -86,7 +86,7 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do + it 'does not allow creating subgroups' do expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) end end @@ -103,8 +103,22 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + context 'when subgroup_creation_level is set to maintainer' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + it 'allows creating subgroups' do + visit path + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end + end + + context 'when subgroup_creation_level is set to owners' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + + it 'does not allow creating subgroups' do + visit path + expect(page).not_to have_css("li[data-text='New subgroup']", visible: false) + end end end @@ -114,7 +128,7 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do + it 'does not allow creating subgroups' do expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) end end -- cgit v1.2.1 From 8547d6d2b4840bca951d708b3f949f665b2760e5 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 09:22:10 -0700 Subject: Update schema --- db/schema.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/schema.rb b/db/schema.rb index 7948f919c57..2c6f577c784 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2112,6 +2112,7 @@ ActiveRecord::Schema.define(version: 20190628145246) do t.integer "extra_shared_runners_minutes_limit" t.string "ldap_sync_status", default: "ready", null: false t.boolean "membership_lock", default: false + t.integer "subgroup_creation_level", default: 0, null: false t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree -- cgit v1.2.1 From ba836a046df0f805686f95cc73598db9211612e2 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 13:31:47 -0700 Subject: Style rules; Revert some examples --- ...75626_add_group_creation_level_to_namespaces.rb | 7 +++++-- spec/controllers/admin/groups_controller_spec.rb | 9 ++++++-- spec/features/groups/group_settings_spec.rb | 2 +- spec/features/groups/show_spec.rb | 24 +++++++++++++++------- spec/models/group_spec.rb | 3 ++- spec/policies/group_policy_spec.rb | 21 ++++++++++++++----- spec/requests/api/groups_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 4 ++-- 8 files changed, 52 insertions(+), 22 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index b0ea74d4765..eed0ba25f27 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -5,10 +5,13 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] DOWNTIME = false disable_ddl_transaction! - + def up unless column_exists?(:namespaces, :subgroup_creation_level) - add_column_with_default(:namespaces, :subgroup_creation_level, :integer, default: 0) + add_column_with_default(:namespaces, + :subgroup_creation_level, + :integer, + default: 0) end end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index df11321537f..72f389513f8 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,9 +70,14 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do + MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + expect do - post :update, params: { id: group.to_param, group: { subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS } } - end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + post :update, + params: { id: group.to_param, + group: { subgroup_creation_level: MAINTAINER } } + end.to change { group.reload.subgroup_creation_level } + .to(MAINTAINER) end end end diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 95534d5a2ba..676769c25fe 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -92,7 +92,7 @@ describe 'Edit group settings' do expect(page).to have_content('Allowed to create subgroups') end end - + describe 'edit group avatar' do before do visit edit_group_path(group) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 68fa3f4e817..163906010fa 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -76,7 +76,8 @@ describe 'Group show page' do end it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + expect(page) + .to have_css("li[data-text='New subgroup']", visible: false) end end @@ -87,7 +88,8 @@ describe 'Group show page' do end it 'does not allow creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_selector("li[data-text='New subgroup']", visible: false) end end end @@ -104,8 +106,11 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to maintainer' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } - + let(:group) do + create(:group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + it 'allows creating subgroups' do visit path expect(page).to have_css("li[data-text='New subgroup']", visible: false) @@ -113,11 +118,15 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to owners' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:group) do + create(:group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end it 'does not allow creating subgroups' do visit path - expect(page).not_to have_css("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_css("li[data-text='New subgroup']", visible: false) end end end @@ -129,7 +138,8 @@ describe 'Group show page' do end it 'does not allow creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_selector("li[data-text='New subgroup']", visible: false) end end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index fd40061dd3a..6627177ad61 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -999,7 +999,8 @@ describe Group do it 'outputs the default one if it is nil' do group = create(:group, subgroup_creation_level: nil) - expect(group.subgroup_creation_level).to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + expect(group.subgroup_creation_level) + .to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index aed9a8e34ff..da186f63eca 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -145,7 +145,8 @@ describe GroupPolicy do it 'allows every owner permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = owner_permissions - create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_owner_permissions) @@ -157,7 +158,8 @@ describe GroupPolicy do it 'allows every owner permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = owner_permissions - create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_owner_permissions) @@ -169,7 +171,8 @@ describe GroupPolicy do it 'allows every maintainer permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_maintainer_permissions = maintainer_permissions - create_subgroup_permission + updated_maintainer_permissions = + maintainer_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_maintainer_permissions) @@ -475,7 +478,11 @@ describe GroupPolicy do context "create_subgroup" do context 'when group has subgroup creation level set to owner' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:group) do + create( + :group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end context 'reporter' do let(:current_user) { reporter } @@ -503,7 +510,11 @@ describe GroupPolicy do end context 'when group has subgroup creation level set to maintainer' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:group) do + create( + :group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end context 'reporter' do let(:current_user) { reporter } diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 52d926d5484..c41408fba65 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'can create subgroups' do + it 'cannot create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(201) + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..267ad529d3b 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as maintainer' do + context 'as Owner' do before do - group.add_maintainer(user) + group.add_owner(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 13d3123425d2575773550015240509e297c1177a Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 14:06:50 -0700 Subject: Remove an example that is no longer necessary --- spec/models/group_spec.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 6627177ad61..470ce65707d 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,13 +994,4 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end - - describe 'subgroup_creation_level' do - it 'outputs the default one if it is nil' do - group = create(:group, subgroup_creation_level: nil) - - expect(group.subgroup_creation_level) - .to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) - end - end end -- cgit v1.2.1 From 4601b2d17927ca80f2e93a0211bca809dd264b98 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 16:12:54 -0700 Subject: Make maintainers the default setting for creating subgroups --- app/models/group.rb | 10 ++++++---- spec/models/group_spec.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 44bc6c8288a..47e7bf34ec8 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,6 +58,8 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } + before_create :default_subgroup_creation_level_to_maintainers + after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -414,10 +416,6 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end - def subgroup_creation_level - super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end - private def update_two_factor_requirement @@ -447,4 +445,8 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end + + def default_subgroup_creation_level_to_maintainers + self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 470ce65707d..9ae18d7bab7 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,4 +994,12 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end + + describe 'subgroup_creation_level' do + it 'defaults to maintainers' do + group = create (:group) + + expect(group.subgroup_creation_level).to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + end end -- cgit v1.2.1 From 2497a1e1cf0f52347684552184a24bd246f7bcc8 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 30 Jun 2019 14:40:23 -0700 Subject: Make subgroup_creation_level default to maintainer at SQL level - Migration updates existing groups to "owner", then sets default to "maintainer" so that new groups will default to that - Update spec examples --- ...75626_add_group_creation_level_to_namespaces.rb | 1 + db/schema.rb | 2 +- spec/controllers/admin/groups_controller_spec.rb | 7 ++-- spec/factories/groups.rb | 5 ++- spec/features/groups/show_spec.rb | 17 ++++++--- spec/models/group_spec.rb | 5 +-- spec/policies/group_policy_spec.rb | 43 ++++++++++++++++++---- spec/requests/api/groups_spec.rb | 4 +- .../policies/group_policy_shared_context.rb | 2 +- 9 files changed, 61 insertions(+), 25 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index eed0ba25f27..85ac89af46e 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -12,6 +12,7 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] :subgroup_creation_level, :integer, default: 0) + change_column_default(:namespaces, :subgroup_creation_level, 1) end end diff --git a/db/schema.rb b/db/schema.rb index 2c6f577c784..272f0fe747b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2112,7 +2112,7 @@ ActiveRecord::Schema.define(version: 20190628145246) do t.integer "extra_shared_runners_minutes_limit" t.string "ldap_sync_status", default: "ready", null: false t.boolean "membership_lock", default: false - t.integer "subgroup_creation_level", default: 0, null: false + t.integer "subgroup_creation_level", default: 1, null: false t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 72f389513f8..398f587bafe 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,14 +70,13 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: MAINTAINER } } - end.to change { group.reload.subgroup_creation_level } - .to(MAINTAINER) + group: { subgroup_creation_level: OWNER } } + end.to change { group.reload.subgroup_creation_level }.to(OWNER) end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 2f50fbfe2fa..b67ab955ffc 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,7 +5,6 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS - subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner @@ -46,5 +45,9 @@ FactoryBot.define do trait :auto_devops_disabled do auto_devops_enabled false end + + trait :owner_subgroup_creation_only do + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS + end end end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 163906010fa..48ba9064327 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -72,10 +72,11 @@ describe 'Group show page' do context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -84,10 +85,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -102,10 +104,9 @@ describe 'Group show page' do context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end - context 'when subgroup_creation_level is set to maintainer' do + context 'when subgroup_creation_level is set to maintainers' do let(:group) do create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) @@ -113,7 +114,9 @@ describe 'Group show page' do it 'allows creating subgroups' do visit path - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + + expect(page) + .to have_css("li[data-text='New subgroup']", visible: false) end end @@ -125,6 +128,7 @@ describe 'Group show page' do it 'does not allow creating subgroups' do visit path + expect(page) .not_to have_css("li[data-text='New subgroup']", visible: false) end @@ -134,10 +138,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 9ae18d7bab7..c7fb0f51075 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -997,9 +997,8 @@ describe Group do describe 'subgroup_creation_level' do it 'defaults to maintainers' do - group = create (:group) - - expect(group.subgroup_creation_level).to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + expect(group.subgroup_creation_level) + .to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index da186f63eca..7fba62d2aa8 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -98,12 +98,38 @@ describe GroupPolicy do context 'maintainer' do let(:current_user) { maintainer } - it do - expect_allowed(*guest_permissions) - expect_allowed(*reporter_permissions) - expect_allowed(*developer_permissions) - expect_allowed(*maintainer_permissions) - expect_disallowed(*owner_permissions) + context 'with subgroup_creation level set to maintainer' do + let(:group) { create(:group, + :private, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + it do + allow(Group).to receive(:supports_nested_objects?).and_return(true) + + create_subgroup_permission = [:create_subgroup] + updated_maintainer_permissions = + maintainer_permissions + create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission + + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*updated_maintainer_permissions) + expect_disallowed(*updated_owner_permissions) + end + end + + context 'with subgroup_creation_level set to owner' do + it do + allow(Group).to receive(:supports_nested_objects?).and_return(true) + + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) + end end end @@ -181,7 +207,10 @@ describe GroupPolicy do end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do - let(:nested_group) { create(:group, :private, parent: group) } + let(:nested_group) { create(:group, + :private, + :owner_subgroup_creation_only, + parent: group) } before do nested_group.add_guest(guest) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4808ac0068..74389c4d82b 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -7,7 +7,7 @@ RSpec.shared_context 'GroupPolicy context' do let(:maintainer) { create(:user) } let(:owner) { create(:user) } let(:admin) { create(:admin) } - let(:group) { create(:group, :private) } + let(:group) { create(:group, :private, :owner_subgroup_creation_only) } let(:guest_permissions) do %i[ -- cgit v1.2.1 From c440275520cbe3863c7b70b3893d2013f04f3f70 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:00:34 -0700 Subject: Remove AR hook to set the default subgroup_creation_level --- app/models/group.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 47e7bf34ec8..8e89c7ecfb1 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,8 +58,6 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } - before_create :default_subgroup_creation_level_to_maintainers - after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -445,8 +443,4 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end - - def default_subgroup_creation_level_to_maintainers - self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end end -- cgit v1.2.1 From 6913cfa9aa9c9c4bda85c2c3a5c2e4c0bcdc1e48 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:17:26 -0700 Subject: Clean up the show_spec examples previously added --- spec/features/groups/show_spec.rb | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 48ba9064327..ef0e885ee5f 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,27 +56,28 @@ describe 'Group show page' do end context 'subgroup support' do + let(:restricted_group) { create(:group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:relaxed_group) { create(:group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } let(:owner) { create(:user) } let(:maintainer) { create(:user) } - before do - group.add_owner(owner) - group.add_maintainer(maintainer) - end - context 'for owners' do + let(:path) { group_path(restricted_group) } + before do + restricted_group.add_owner(owner) sign_in(owner) end context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -85,11 +86,10 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } + visit path end it 'does not allow creating subgroups' do - visit path - expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -107,30 +107,30 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to maintainers' do - let(:group) do - create(:group, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + before do + relaxed_group.add_maintainer(maintainer) + path = group_path(relaxed_group) + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end end context 'when subgroup_creation_level is set to owners' do - let(:group) do - create(:group, - subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + before do + restricted_group.add_maintainer(maintainer) end it 'does not allow creating subgroups' do + path = group_path(restricted_group) visit path expect(page) - .not_to have_css("li[data-text='New subgroup']", visible: false) + .not_to have_selector("li[data-text='New subgroup']", + visible: false) end end end -- cgit v1.2.1 From 6e56a915304603a3cc7716823c2d98f108707611 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:55:00 -0700 Subject: Fix group creat_service_spec to contain maintainer context --- spec/services/groups/create_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 267ad529d3b..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as Owner' do + context 'as maintainer' do before do - group.add_owner(user) + group.add_maintainer(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 3bb1539f24ed642971b16bd9dbb75d7cbb71b08a Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:56:02 -0700 Subject: Add descriptions to examples --- spec/policies/group_policy_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 7fba62d2aa8..020b51f776e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -103,7 +103,7 @@ describe GroupPolicy do :private, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } - it do + it 'allows every maintainer permission plus creating subgroups' do allow(Group).to receive(:supports_nested_objects?).and_return(true) create_subgroup_permission = [:create_subgroup] @@ -121,7 +121,7 @@ describe GroupPolicy do end context 'with subgroup_creation_level set to owner' do - it do + it 'allows every maintainer permission' do allow(Group).to receive(:supports_nested_objects?).and_return(true) expect_allowed(*guest_permissions) -- cgit v1.2.1 From 0cacb35552e6c30f9cb03f17d1953063fc551aa5 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 13:41:32 -0700 Subject: Apply changes recomended by merge request coach --- app/models/group.rb | 4 ++++ app/views/groups/_group_admin_settings.html.haml | 6 ++++++ ...190626175626_add_group_creation_level_to_namespaces.rb | 15 +++++---------- db/schema.rb | 2 +- spec/controllers/admin/groups_controller_spec.rb | 6 ++---- spec/features/admin/admin_groups_spec.rb | 9 +++++++++ spec/features/groups/show_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 9 --------- 8 files changed, 29 insertions(+), 26 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 8e89c7ecfb1..44bc6c8288a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -414,6 +414,10 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end + def subgroup_creation_level + super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + private def update_two_factor_requirement diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml index b8f632d11d3..733cb36cc3d 100644 --- a/app/views/groups/_group_admin_settings.html.haml +++ b/app/views/groups/_group_admin_settings.html.haml @@ -16,6 +16,12 @@ .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 + .col-sm-2.col-form-label + = f.label s_('SubgroupCreationlevel|Allowed to create subgroups') + .col-sm-10 + = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, @group.subgroup_creation_level), {}, class: 'form-control' + .form-group.row .col-sm-2.col-form-label.pt-0 = f.label :require_two_factor_authentication, 'Two-factor authentication' diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index 85ac89af46e..867ec3b7c91 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -7,18 +7,13 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] disable_ddl_transaction! def up - unless column_exists?(:namespaces, :subgroup_creation_level) - add_column_with_default(:namespaces, - :subgroup_creation_level, - :integer, - default: 0) - change_column_default(:namespaces, :subgroup_creation_level, 1) - end + add_column(:namespaces, :subgroup_creation_level, :integer) + change_column_default(:namespaces, + :subgroup_creation_level, + ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end def down - if column_exists?(:namespaces, :subgroup_creation_level) - remove_column(:namespaces, :subgroup_creation_level) - end + remove_column(:namespaces, :subgroup_creation_level) end end diff --git a/db/schema.rb b/db/schema.rb index 272f0fe747b..d121ad8b372 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2112,7 +2112,7 @@ ActiveRecord::Schema.define(version: 20190628145246) do t.integer "extra_shared_runners_minutes_limit" t.string "ldap_sync_status", default: "ready", null: false t.boolean "membership_lock", default: false - t.integer "subgroup_creation_level", default: 1, null: false + t.integer "subgroup_creation_level", default: 1 t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 398f587bafe..1123563c1e3 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,13 +70,11 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS - expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: OWNER } } - end.to change { group.reload.subgroup_creation_level }.to(OWNER) + group: { subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS } } + end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end end end diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 735ca60f7da..35c384dd458 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -102,6 +102,15 @@ describe 'Admin Groups' do expect_selected_visibility(group.visibility_level) end + it 'shows the subgroup creation level dropdown populated with the group subgroup_creation_level value' do + group = create(:group, :private, :owner_subgroup_creation_only) + + visit admin_group_edit_path(group) + + expect(page).to have_select("group_subgroup_creation_level", + selected: ::Gitlab::Access.subgroup_creation_options.keys[group.subgroup_creation_level]) + end + it 'edit group path does not change group name', :js do group = create(:group, :private) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index ef0e885ee5f..5096abadb79 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -71,7 +71,7 @@ describe 'Group show page' do sign_in(owner) end - context 'when subgroups are supported', :js, :nested_groups do + context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } visit path @@ -101,7 +101,7 @@ describe 'Group show page' do sign_in(maintainer) end - context 'when subgroups are supported', :js, :nested_groups do + context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..c5ff6cdbacd 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,4 +1,3 @@ -# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -88,14 +87,6 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - - context 'as maintainer' do - before do - group.add_maintainer(user) - end - - it { is_expected.to be_persisted } - end end end -- cgit v1.2.1 From 4834d9359334edd40bf44cbe6f9fc9d10ce7265e Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 11:07:17 -0700 Subject: Add failing unit test specifying a maintainer creating a subgroup --- spec/services/groups/create_service_spec.rb | 9 +++++++++ .../shared_contexts/policies/group_policy_shared_context.rb | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index c5ff6cdbacd..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -87,6 +88,14 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end + + context 'as maintainer' do + before do + group.add_maintainer(user) + end + + it { is_expected.to be_persisted } + end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index 74389c4d82b..d596317462a 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -23,6 +23,15 @@ RSpec.shared_context 'GroupPolicy context' do create_projects read_cluster create_cluster update_cluster admin_cluster add_cluster ] + [ + :create_projects, + :read_cluster, + :create_cluster, + :update_cluster, + :admin_cluster, + :add_cluster, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) + ].compact end let(:owner_permissions) do [ @@ -30,8 +39,7 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) + :set_note_created_at ].compact end -- cgit v1.2.1 From 4ab48e3374590e5300e86565bc47691c30316125 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 13:22:12 -0700 Subject: Modify API spec to expect a maintainer to be able to create subgroup --- spec/support/shared_contexts/policies/group_policy_shared_context.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index d596317462a..5a55bbac788 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,10 +19,6 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - %i[ - create_projects - read_cluster create_cluster update_cluster admin_cluster add_cluster - ] [ :create_projects, :read_cluster, -- cgit v1.2.1 From 1c68fd4ad4dc588440cf58e21be5c2c844e02648 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 15:29:54 -0700 Subject: Adjust the documentation on subgroups --- doc/user/group/subgroups/index.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index 1c6cca049c5..e3f7539a9b6 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -75,13 +75,16 @@ structure. ## Creating a subgroup NOTE: **Note:** -You must be an Owner of a group to create a subgroup. For -more information check the [permissions table](../../permissions.md#group-members-permissions). +In order to create a subgroup you must either be an Owner or a Maintainer of the +group, depending on the group's setting. By default groups allow both Owners and +Maintainers to create subgroups, but this can be changed by an Owner or +Administrator to only allow Owners to create subgroups. For more information +check the [permissions table](../../permissions.md#group-members-permissions). For a list of words that are not allowed to be used as group names see the -[reserved names](../../reserved_names.md). -Users can always create subgroups if they are explicitly added as an Owner to -a parent group, even if group creation is disabled by an administrator in their -settings. +[reserved names](../../reserved_names.md). Users can always create subgroups if +they are explicitly added as an Owner (or Maintainer, if that setting is +enabled) to a parent group, even if group creation is disabled by an +administrator in their settings. To create a subgroup: -- cgit v1.2.1 From df5f65af715f310fc90e5d0e9311be29ca3f959f Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 15:39:43 -0700 Subject: Adjust the documentation on subgroup permissions --- doc/user/permissions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 03abef9fc62..20b23b75cb9 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -209,13 +209,15 @@ group. | Create project in group | | | ✓ | ✓ | ✓ | | Create/edit/delete group milestones | | | ✓ | ✓ | ✓ | | Enable/disable a dependency proxy **[PREMIUM]** | | | ✓ | ✓ | ✓ | +| Create subgroup | | | | ✓* | ✓ | | Edit group | | | | | ✓ | -| Create subgroup | | | | | ✓ | | Manage group members | | | | | ✓ | | Remove group | | | | | ✓ | | Delete group epic **[ULTIMATE]** | | | | | ✓ | | View group Audit Events | | | | | ✓ | +*Groups can be set to allow either Owners or Owners and maintainers to create subgroups + ### Subgroup permissions When you add a member to a subgroup, they inherit the membership and -- cgit v1.2.1 From df96ef8b9754998756afc06a507d708813e1d84b Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 16:27:49 -0700 Subject: Fix some code style issues --- spec/features/groups/show_spec.rb | 12 ++++++++---- spec/policies/group_policy_spec.rb | 13 ++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 5096abadb79..f1501181432 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,10 +56,14 @@ describe 'Group show page' do end context 'subgroup support' do - let(:restricted_group) { create(:group, - subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } - let(:relaxed_group) { create(:group, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:restricted_group) do + create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end + + let(:relaxed_group) do + create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + let(:owner) { create(:user) } let(:maintainer) { create(:user) } diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 020b51f776e..dc3675a7b9e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -99,9 +99,9 @@ describe GroupPolicy do let(:current_user) { maintainer } context 'with subgroup_creation level set to maintainer' do - let(:group) { create(:group, - :private, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:group) do + create(:group, :private, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end it 'allows every maintainer permission plus creating subgroups' do allow(Group).to receive(:supports_nested_objects?).and_return(true) @@ -207,10 +207,9 @@ describe GroupPolicy do end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do - let(:nested_group) { create(:group, - :private, - :owner_subgroup_creation_only, - parent: group) } + let(:nested_group) do + create(:group, :private, :owner_subgroup_creation_only, parent: group) + end before do nested_group.add_guest(guest) -- cgit v1.2.1 From 44fabdaade148caed637d0ea387830100eb15fbc Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 2 Jul 2019 08:38:38 -0700 Subject: Apply recomended changes from merge coach --- ...190626175626_add_group_creation_level_to_namespaces.rb | 1 - spec/features/admin/admin_groups_spec.rb | 3 +-- spec/features/groups/show_spec.rb | 15 +++++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index 867ec3b7c91..3b75c92e518 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -4,7 +4,6 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] include Gitlab::Database::MigrationHelpers DOWNTIME = false - disable_ddl_transaction! def up add_column(:namespaces, :subgroup_creation_level, :integer) diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 35c384dd458..c1ad73779c9 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -107,8 +107,7 @@ describe 'Admin Groups' do visit admin_group_edit_path(group) - expect(page).to have_select("group_subgroup_creation_level", - selected: ::Gitlab::Access.subgroup_creation_options.keys[group.subgroup_creation_level]) + expect(page).to have_content('Allowed to create subgroups') end it 'edit group path does not change group name', :js do diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index f1501181432..bed998a0859 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -57,11 +57,11 @@ describe 'Group show page' do context 'subgroup support' do let(:restricted_group) do - create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end let(:relaxed_group) do - create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end let(:owner) { create(:user) } @@ -78,10 +78,11 @@ describe 'Group show page' do context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -90,10 +91,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -113,11 +115,12 @@ describe 'Group show page' do context 'when subgroup_creation_level is set to maintainers' do before do relaxed_group.add_maintainer(maintainer) - path = group_path(relaxed_group) - visit path end it 'allows creating subgroups' do + path = group_path(relaxed_group) + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end -- cgit v1.2.1 From 9c8f5b7192986918322d23efc2acec7b189fd69b Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 05:55:20 +0000 Subject: Apply suggestion to doc/user/group/subgroups/index.md --- doc/user/group/subgroups/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index e3f7539a9b6..fad7541bdc4 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -81,7 +81,9 @@ Maintainers to create subgroups, but this can be changed by an Owner or Administrator to only allow Owners to create subgroups. For more information check the [permissions table](../../permissions.md#group-members-permissions). For a list of words that are not allowed to be used as group names see the -[reserved names](../../reserved_names.md). Users can always create subgroups if +[reserved names](../../reserved_names.md). + +Users can always create subgroups if they are explicitly added as an Owner (or Maintainer, if that setting is enabled) to a parent group, even if group creation is disabled by an administrator in their settings. -- cgit v1.2.1 From 78e3a413b8d3779bfd462b4864ab3d9813344366 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 05:55:49 +0000 Subject: Apply suggestion to doc/user/permissions.md --- doc/user/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 20b23b75cb9..216e88d0113 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -209,7 +209,7 @@ group. | Create project in group | | | ✓ | ✓ | ✓ | | Create/edit/delete group milestones | | | ✓ | ✓ | ✓ | | Enable/disable a dependency proxy **[PREMIUM]** | | | ✓ | ✓ | ✓ | -| Create subgroup | | | | ✓* | ✓ | +| Create subgroup | | | | ✓ (1) | ✓ | | Edit group | | | | | ✓ | | Manage group members | | | | | ✓ | | Remove group | | | | | ✓ | -- cgit v1.2.1 From 43858b4badc786fc8254937e0cefad3798481d9f Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 05:56:03 +0000 Subject: Apply suggestion to doc/user/permissions.md --- doc/user/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 216e88d0113..190f8bc8cbc 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -216,7 +216,7 @@ group. | Delete group epic **[ULTIMATE]** | | | | | ✓ | | View group Audit Events | | | | | ✓ | -*Groups can be set to allow either Owners or Owners and maintainers to create subgroups +- (1): Groups can be set to allow either Owners or Owners and maintainers to create subgroups ### Subgroup permissions -- cgit v1.2.1 From cea16e03d639ea8ba32cba32c07a47d05220a363 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 05:56:53 +0000 Subject: Apply suggestion to doc/user/group/subgroups/index.md --- doc/user/group/subgroups/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index fad7541bdc4..4e88ec5ee76 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -74,7 +74,6 @@ structure. ## Creating a subgroup -NOTE: **Note:** In order to create a subgroup you must either be an Owner or a Maintainer of the group, depending on the group's setting. By default groups allow both Owners and Maintainers to create subgroups, but this can be changed by an Owner or -- cgit v1.2.1 From e8e3fdf7c7f6d0696538ff5819c03d0ea5e9217b Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 16:43:56 +0000 Subject: Update index.md --- doc/user/group/subgroups/index.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index 4e88ec5ee76..b9313ccdb1e 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -74,18 +74,19 @@ structure. ## Creating a subgroup -In order to create a subgroup you must either be an Owner or a Maintainer of the -group, depending on the group's setting. By default groups allow both Owners and +To create a subgroup you must either be an Owner or a Maintainer of the group, +depending on the group's setting. By default groups allow both Owners and Maintainers to create subgroups, but this can be changed by an Owner or -Administrator to only allow Owners to create subgroups. For more information -check the [permissions table](../../permissions.md#group-members-permissions). -For a list of words that are not allowed to be used as group names see the +Administrator to only allow Owners to create subgroups. + +For more information check the +[permissions table](../../permissions.md#group-members-permissions). For a list +of words that are not allowed to be used as group names see the [reserved names](../../reserved_names.md). -Users can always create subgroups if -they are explicitly added as an Owner (or Maintainer, if that setting is -enabled) to a parent group, even if group creation is disabled by an -administrator in their settings. +Users can always create subgroups if they are explicitly added as an Owner (or +Maintainer, if that setting is enabled) to a parent group, even if group +creation is disabled by an administrator in their settings. To create a subgroup: -- cgit v1.2.1 -- cgit v1.2.1 From c93ce836930a875452432ccc0c92733fb8adda29 Mon Sep 17 00:00:00 2001 From: manojmj Date: Thu, 27 Jun 2019 14:44:01 +0530 Subject: Do not allow localhost url redirection in GitHub Integration --- .../unreleased/security-github-ssrf-redirect.yml | 5 ++ config/initializers/octokit.rb | 1 + lib/gitlab/github_import/client.rb | 4 +- lib/gitlab/legacy_github_import/client.rb | 2 +- lib/gitlab/octokit/middleware.rb | 23 ++++++++ spec/lib/gitlab/octokit/middleware_spec.rb | 68 ++++++++++++++++++++++ 6 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/security-github-ssrf-redirect.yml create mode 100644 config/initializers/octokit.rb create mode 100644 lib/gitlab/octokit/middleware.rb create mode 100644 spec/lib/gitlab/octokit/middleware_spec.rb diff --git a/changelogs/unreleased/security-github-ssrf-redirect.yml b/changelogs/unreleased/security-github-ssrf-redirect.yml new file mode 100644 index 00000000000..36a36de3eb0 --- /dev/null +++ b/changelogs/unreleased/security-github-ssrf-redirect.yml @@ -0,0 +1,5 @@ +--- +title: Do not allow localhost url redirection in GitHub Integration +merge_request: +author: +type: security diff --git a/config/initializers/octokit.rb b/config/initializers/octokit.rb new file mode 100644 index 00000000000..b3749258ec5 --- /dev/null +++ b/config/initializers/octokit.rb @@ -0,0 +1 @@ +Octokit.middleware.insert_after Octokit::Middleware::FollowRedirects, Gitlab::Octokit::Middleware diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb index a61beafae0d..826b35d685c 100644 --- a/lib/gitlab/github_import/client.rb +++ b/lib/gitlab/github_import/client.rb @@ -40,7 +40,7 @@ module Gitlab # otherwise hitting the rate limit will result in a thread # being blocked in a `sleep()` call for up to an hour. def initialize(token, per_page: 100, parallel: true) - @octokit = Octokit::Client.new( + @octokit = ::Octokit::Client.new( access_token: token, per_page: per_page, api_endpoint: api_endpoint @@ -139,7 +139,7 @@ module Gitlab begin yield - rescue Octokit::TooManyRequests + rescue ::Octokit::TooManyRequests raise_or_wait_for_rate_limit # This retry will only happen when running in sequential mode as we'll diff --git a/lib/gitlab/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb index bbdd094e33b..b23efd64dee 100644 --- a/lib/gitlab/legacy_github_import/client.rb +++ b/lib/gitlab/legacy_github_import/client.rb @@ -101,7 +101,7 @@ module Gitlab # GitHub Rate Limit API returns 404 when the rate limit is # disabled. In this case we just want to return gracefully # instead of spitting out an error. - rescue Octokit::NotFound + rescue ::Octokit::NotFound nil end diff --git a/lib/gitlab/octokit/middleware.rb b/lib/gitlab/octokit/middleware.rb new file mode 100644 index 00000000000..2f762957d1b --- /dev/null +++ b/lib/gitlab/octokit/middleware.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Octokit + class Middleware + def initialize(app) + @app = app + end + + def call(env) + Gitlab::UrlBlocker.validate!(env[:url], { allow_localhost: allow_local_requests?, allow_local_network: allow_local_requests? }) + + @app.call(env) + end + + private + + def allow_local_requests? + Gitlab::CurrentSettings.allow_local_requests_from_hooks_and_services? + end + end + end +end diff --git a/spec/lib/gitlab/octokit/middleware_spec.rb b/spec/lib/gitlab/octokit/middleware_spec.rb new file mode 100644 index 00000000000..7f2b523f5b7 --- /dev/null +++ b/spec/lib/gitlab/octokit/middleware_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe Gitlab::Octokit::Middleware do + let(:app) { double(:app) } + let(:middleware) { described_class.new(app) } + + shared_examples 'Public URL' do + it 'does not raise an error' do + expect(app).to receive(:call).with(env) + + expect { middleware.call(env) }.not_to raise_error + end + end + + shared_examples 'Local URL' do + it 'raises an error' do + expect { middleware.call(env) }.to raise_error(Gitlab::UrlBlocker::BlockedUrlError) + end + end + + describe '#call' do + context 'when the URL is a public URL' do + let(:env) { { url: 'https://public-url.com' } } + + it_behaves_like 'Public URL' + end + + context 'when the URL is a localhost adresss' do + let(:env) { { url: 'http://127.0.0.1' } } + + context 'when localhost requests are not allowed' do + before do + stub_application_setting(allow_local_requests_from_hooks_and_services: false) + end + + it_behaves_like 'Local URL' + end + + context 'when localhost requests are allowed' do + before do + stub_application_setting(allow_local_requests_from_hooks_and_services: true) + end + + it_behaves_like 'Public URL' + end + end + + context 'when the URL is a local network address' do + let(:env) { { url: 'http://172.16.0.0' } } + + context 'when local network requests are not allowed' do + before do + stub_application_setting(allow_local_requests_from_hooks_and_services: false) + end + + it_behaves_like 'Local URL' + end + + context 'when local network requests are allowed' do + before do + stub_application_setting(allow_local_requests_from_hooks_and_services: true) + end + + it_behaves_like 'Public URL' + end + end + end +end -- cgit v1.2.1 From 51fa8a5e778ff6909fd7f70fc8062bb33940a822 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 5 Jul 2019 14:52:06 +0000 Subject: Apply suggestion to doc/user/permissions.md --- doc/user/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 190f8bc8cbc..10184de8784 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -216,7 +216,7 @@ group. | Delete group epic **[ULTIMATE]** | | | | | ✓ | | View group Audit Events | | | | | ✓ | -- (1): Groups can be set to allow either Owners or Owners and maintainers to create subgroups +- (1): Groups can be set to allow either Owners or Owners and Maintainers to create subgroups ### Subgroup permissions -- cgit v1.2.1 From 019caa8de59f0ca701d4f099a4068605b17e3b93 Mon Sep 17 00:00:00 2001 From: drew cimino Date: Fri, 28 Jun 2019 10:40:34 -0400 Subject: Use MergeRequest#source_project as permissions reference for MergeRequest#all_pipelines MergeRequest#all_pipelines fetches Ci::Pipeline records from the source project, so we should specifically check that project for permissions. This was already happening for intra-project merge requests, but in the event that the target and source projects both have private builds, we should ensure that the project permissions are respected. --- .../merge_requests/application_controller.rb | 2 +- .../projects/merge_requests_controller.rb | 3 +- .../security-mr-pipeline-permissions.yml | 5 ++ .../projects/merge_requests_controller_spec.rb | 98 +++++++++++++++++++++- 4 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/security-mr-pipeline-permissions.yml diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb index dcc272aecff..006731c0e66 100644 --- a/app/controllers/projects/merge_requests/application_controller.rb +++ b/app/controllers/projects/merge_requests/application_controller.rb @@ -45,7 +45,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont def set_pipeline_variables @pipelines = - if can?(current_user, :read_pipeline, @project) + if can?(current_user, :read_pipeline, @merge_request.source_project) @merge_request.all_pipelines else Ci::Pipeline.none diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 7ee8e0ea8f8..7f87fc3792a 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -82,7 +82,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end def pipelines - @pipelines = @merge_request.all_pipelines.page(params[:page]).per(30) + set_pipeline_variables + @pipelines = @pipelines.page(params[:page]).per(30) Gitlab::PollingInterval.set_header(response, interval: 10_000) diff --git a/changelogs/unreleased/security-mr-pipeline-permissions.yml b/changelogs/unreleased/security-mr-pipeline-permissions.yml new file mode 100644 index 00000000000..a317c93228c --- /dev/null +++ b/changelogs/unreleased/security-mr-pipeline-permissions.yml @@ -0,0 +1,5 @@ +--- +title: Use source project as permissions reference for MergeRequestsController#pipelines +merge_request: +author: +type: security diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 0eca663a683..64a89205e2f 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -623,10 +623,100 @@ describe Projects::MergeRequestsController do format: :json end - it 'responds with serialized pipelines' do - expect(json_response['pipelines']).not_to be_empty - expect(json_response['count']['all']).to eq 1 - expect(response).to include_pagination_headers + context 'with "enabled" builds on a public project' do + let(:project) { create(:project, :repository, :public) } + + context 'for a project owner' do + it 'responds with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + + context 'for an unassociated user' do + let(:user) { create :user } + + it 'responds with no pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + end + + context 'with private builds on a public project' do + let(:project) { create(:project, :repository, :public, :builds_private) } + + context 'for a project owner' do + it 'responds with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + + context 'for an unassociated user' do + let(:user) { create :user } + + it 'responds with no pipelines' do + expect(json_response['pipelines']).to be_empty + expect(json_response['count']['all']).to eq(0) + expect(response).to include_pagination_headers + end + end + + context 'from a project fork' do + let(:fork_user) { create :user } + let(:forked_project) { fork_project(project, fork_user, repository: true) } # Forked project carries over :builds_private + let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: forked_project) } + + context 'with private builds' do + context 'for the target project member' do + it 'does not respond with serialized pipelines' do + expect(json_response['pipelines']).to be_empty + expect(json_response['count']['all']).to eq(0) + expect(response).to include_pagination_headers + end + end + + context 'for the source project member' do + let(:user) { fork_user } + + it 'responds with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + end + + context 'with public builds' do + let(:forked_project) do + fork_project(project, fork_user, repository: true).tap do |new_project| + new_project.project_feature.update(builds_access_level: ProjectFeature::ENABLED) + end + end + + context 'for the target project member' do + it 'does not respond with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + + context 'for the source project member' do + let(:user) { fork_user } + + it 'responds with serialized pipelines' do + expect(json_response['pipelines']).to be_present + expect(json_response['count']['all']).to eq(1) + expect(response).to include_pagination_headers + end + end + end + end end end -- cgit v1.2.1 From 11e2eb79f332f4fd5533b8e0b16ce5c7600b4833 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 5 Jul 2019 08:27:46 -0700 Subject: Fixed a failing test --- .../policies/group_policy_shared_context.rb | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index 5a55bbac788..599c912ce00 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,15 +19,10 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - [ - :create_projects, - :read_cluster, - :create_cluster, - :update_cluster, - :admin_cluster, - :add_cluster, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) - ].compact + %i[ + create_projects read_cluster create_cluster update_cluster + admin_cluster add_cluster + ] end let(:owner_permissions) do [ @@ -35,7 +30,8 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at + :set_note_created_at, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) ].compact end -- cgit v1.2.1 From b7602279ab79cd69daf731bd160d7a00ff5fe2f0 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 14 Jun 2019 15:13:24 -0700 Subject: Add changelog --- changelogs/unreleased/maintainers-can-create-subgroup.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/maintainers-can-create-subgroup.yml diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml new file mode 100644 index 00000000000..b537862c8af --- /dev/null +++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml @@ -0,0 +1,5 @@ +--- +title: Maintainers can crete subgroups +merge_request: 29718 +author: Fabio Papa +type: changed -- cgit v1.2.1 From 976572489da756e410ba3b7d741d57e2e04aa9db Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 14 Jun 2019 15:16:55 -0700 Subject: Add failing feature spec detailing a maintainer creating a subgroup - Change the two existing feature examples that create a subgroup to elucidate that the owner is creating the subgroup - Nest two more specs inside the 'subgroup support' context detailing what happens when a maintainer attempts to add a subgroup (one with subgroup support, and one without) --- spec/features/groups/show_spec.rb | 61 ++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 9671a4d8c49..2654d06cd8c 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,32 +56,67 @@ describe 'Group show page' do end context 'subgroup support' do - let(:user) { create(:user) } + let(:owner) { create(:user) } + let(:maintainer) { create(:user) } before do - group.add_owner(user) - sign_in(user) + group.add_owner(owner) + group.add_maintainer(maintainer) end - context 'when subgroups are supported', :js, :nested_groups do + context 'for owners' do before do - allow(Group).to receive(:supports_nested_objects?) { true } - visit path + sign_in(owner) end - it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + context 'when subgroups are supported', :js, :nested_groups do + before do + allow(Group).to receive(:supports_nested_objects?) { true } + visit path + end + + it 'allows creating subgroups' do + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end + end + + context 'when subgroups are not supported' do + before do + allow(Group).to receive(:supports_nested_objects?) { false } + visit path + end + + it 'allows creating subgroups' do + expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + end end end - context 'when subgroups are not supported' do + context 'for maintainers' do before do - allow(Group).to receive(:supports_nested_objects?) { false } - visit path + sign_in(maintainer) + end + + context 'when subgroups are supported', :js, :nested_groups do + before do + allow(Group).to receive(:supports_nested_objects?) { true } + visit path + end + + it 'allows creating subgroups' do + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end end - it 'allows creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + context 'when subgroups are not supported' do + before do + allow(Group).to receive(:supports_nested_objects?) { false } + visit path + end + + it 'allows creating subgroups' do + expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + end end end end -- cgit v1.2.1 From f5957cf5f5fdb633ea5c7a6cc968667370951e45 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 11:07:17 -0700 Subject: Add failing unit test specifying a maintainer creating a subgroup --- spec/services/groups/create_service_spec.rb | 9 +++++++++ .../shared_contexts/policies/group_policy_shared_context.rb | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index c5ff6cdbacd..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -87,6 +88,14 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end + + context 'as maintainer' do + before do + group.add_maintainer(user) + end + + it { is_expected.to be_persisted } + end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4808ac0068..b4b09d3295f 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -23,6 +23,15 @@ RSpec.shared_context 'GroupPolicy context' do create_projects read_cluster create_cluster update_cluster admin_cluster add_cluster ] + [ + :create_projects, + :read_cluster, + :create_cluster, + :update_cluster, + :admin_cluster, + :add_cluster, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) + ].compact end let(:owner_permissions) do [ @@ -30,8 +39,7 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) + :set_note_created_at ].compact end -- cgit v1.2.1 From cc80e306d571d8881d06d0d04831ddd0433fd518 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 12:23:56 -0700 Subject: Update the group policy to allow >= maintainer to create subgroups All specs passing --- app/policies/group_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index ea86858181d..d92bcded19d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -109,7 +109,7 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { owner & nested_groups_supported }.enable :create_subgroup + rule { maintainer & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From a664c1b7a825f9aab96a8dc92ef6f667ba7a8882 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 13:22:12 -0700 Subject: Modify API spec to expect a maintainer to be able to create subgroup --- spec/requests/api/groups_spec.rb | 4 ++-- spec/support/shared_contexts/policies/group_policy_shared_context.rb | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4b09d3295f..a40d3087f6e 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,10 +19,6 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - %i[ - create_projects - read_cluster create_cluster update_cluster admin_cluster add_cluster - ] [ :create_projects, :read_cluster, -- cgit v1.2.1 From c53d34b918954e83ef1ab12e578840899791f24e Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 25 Jun 2019 21:59:10 -0700 Subject: Add examples specing the setting to choose who can create subgroups This setting is at the group level only. The default is specified to be maintainers and owners. **Specs only**, all failing. --- spec/controllers/admin/groups_controller_spec.rb | 6 ++ spec/factories/groups.rb | 1 + spec/features/groups/group_settings_spec.rb | 8 +++ spec/models/group_spec.rb | 8 +++ spec/policies/group_policy_spec.rb | 70 ++++++++++++++++++++++ .../policies/group_policy_shared_context.rb | 16 ++--- 6 files changed, 99 insertions(+), 10 deletions(-) diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 509d8944e3a..df11321537f 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -68,5 +68,11 @@ describe Admin::GroupsController 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 + + it 'updates the subgroup_creation_level successfully' do + expect do + post :update, params: { id: group.to_param, group: { subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS } } + end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 18a0c2ec731..2f50fbfe2fa 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,6 +5,7 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 5cef5f0521f..95534d5a2ba 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -85,6 +85,14 @@ describe 'Edit group settings' do end end + describe 'subgroup creation level menu' do + it 'shows the selection menu' do + visit edit_group_path(group) + + expect(page).to have_content('Allowed to create subgroups') + end + end + describe 'edit group avatar' do before do visit edit_group_path(group) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 470ce65707d..fd40061dd3a 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,4 +994,12 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end + + describe 'subgroup_creation_level' do + it 'outputs the default one if it is nil' do + group = create(:group, subgroup_creation_level: nil) + + expect(group.subgroup_creation_level).to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 59f3a961d50..aed9a8e34ff 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -163,6 +163,18 @@ describe GroupPolicy do expect_allowed(*updated_owner_permissions) end end + + context 'maintainer' do + let(:current_user) { maintainer } + + it 'allows every maintainer permission except creating subgroups' do + create_subgroup_permission = [:create_subgroup] + updated_maintainer_permissions = maintainer_permissions - create_subgroup_permission + + expect_disallowed(*create_subgroup_permission) + expect_allowed(*updated_maintainer_permissions) + end + end end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do @@ -461,6 +473,64 @@ describe GroupPolicy do end end + context "create_subgroup" do + context 'when group has subgroup creation level set to owner' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + + context 'reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_subgroup) } + end + end + + context 'when group has subgroup creation level set to maintainer' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + context 'reporter' do + let(:current_user) { reporter } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'developer' do + let(:current_user) { developer } + + it { is_expected.to be_disallowed(:create_subgroup) } + end + + context 'maintainer' do + let(:current_user) { maintainer } + + it { is_expected.to be_allowed(:create_subgroup) } + end + + context 'owner' do + let(:current_user) { owner } + + it { is_expected.to be_allowed(:create_subgroup) } + end + end + end + it_behaves_like 'clusterable policies' do let(:clusterable) { create(:group) } let(:cluster) do diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index a40d3087f6e..b4808ac0068 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,15 +19,10 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - [ - :create_projects, - :read_cluster, - :create_cluster, - :update_cluster, - :admin_cluster, - :add_cluster, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) - ].compact + %i[ + create_projects + read_cluster create_cluster update_cluster admin_cluster add_cluster + ] end let(:owner_permissions) do [ @@ -35,7 +30,8 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at + :set_note_created_at, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) ].compact end -- cgit v1.2.1 From 73fa819b793302b3cee1d06033ac8e94fa5b784b Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:46:48 -0700 Subject: Reset group policy to only allow >= owners to create subgroups --- app/policies/group_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index d92bcded19d..ea86858181d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -109,7 +109,7 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { maintainer & nested_groups_supported }.enable :create_subgroup + rule { owner & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 582ff9dbb6dc223163e1ba674cbc72f292520981 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:49:27 -0700 Subject: Add a subgroup_creation_level column to the namespaces table --- ...6175626_add_group_creation_level_to_namespaces.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb new file mode 100644 index 00000000000..b0ea74d4765 --- /dev/null +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + disable_ddl_transaction! + + def up + unless column_exists?(:namespaces, :subgroup_creation_level) + add_column_with_default(:namespaces, :subgroup_creation_level, :integer, default: 0) + end + end + + def down + if column_exists?(:namespaces, :subgroup_creation_level) + remove_column(:namespaces, :subgroup_creation_level) + end + end +end -- cgit v1.2.1 From 3ae905d67d96cdff2e855b97ae44a617284c91dc Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:39:04 -0700 Subject: Add constants representing Owner and Maintainer group access levels --- lib/gitlab/access.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 6eb08f674c2..77076ead47a 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -29,6 +29,10 @@ module Gitlab MAINTAINER_PROJECT_ACCESS = 1 DEVELOPER_MAINTAINER_PROJECT_ACCESS = 2 + # Default subgroup creation level + OWNER_SUBGROUP_ACCESS = 0 + MAINTAINER_SUBGROUP_ACCESS = 1 + class << self delegate :values, to: :options -- cgit v1.2.1 From d37e8dd7cbec015d32c94ab88a51517e4c76d57f Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 13:45:58 -0700 Subject: Add subgroup_creation_level to the list of allowed group params For both groups_controller and admin/groups_controller --- app/controllers/admin/groups_controller.rb | 3 ++- app/controllers/groups_controller.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 15f7ef881c8..6317fa7c8d1 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -90,7 +90,8 @@ class Admin::GroupsController < Admin::ApplicationController :visibility_level, :require_two_factor_authentication, :two_factor_grace_period, - :project_creation_level + :project_creation_level, + :subgroup_creation_level ] end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 797833e3f91..aff418faae5 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -192,7 +192,8 @@ class GroupsController < Groups::ApplicationController :chat_team_name, :require_two_factor_authentication, :two_factor_grace_period, - :project_creation_level + :project_creation_level, + :subgroup_creation_level ] end -- cgit v1.2.1 From 0a2a6aae44c6d06bb8ebc24f5f94195ab43a5e92 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:11:09 -0700 Subject: Add "allowed to create subgroups" dropdown to group settings form --- app/views/groups/settings/_permissions.html.haml | 1 + app/views/groups/settings/_subgroup_creation_level.html.haml | 3 +++ lib/gitlab/access.rb | 7 +++++++ 3 files changed, 11 insertions(+) create mode 100644 app/views/groups/settings/_subgroup_creation_level.html.haml diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index 0da1f1ba7f5..d3375e00bad 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -20,6 +20,7 @@ = render_if_exists 'groups/settings/ip_restriction', f: f, group: @group = render 'groups/settings/lfs', f: f = render 'groups/settings/project_creation_level', f: f, group: @group + = render 'groups/settings/subgroup_creation_level', f: f, group: @group = render 'groups/settings/two_factor_auth', f: f = render_if_exists 'groups/member_lock_setting', f: f, group: @group diff --git a/app/views/groups/settings/_subgroup_creation_level.html.haml b/app/views/groups/settings/_subgroup_creation_level.html.haml new file mode 100644 index 00000000000..f36ad192bad --- /dev/null +++ b/app/views/groups/settings/_subgroup_creation_level.html.haml @@ -0,0 +1,3 @@ +.form-group + = f.label s_('SubgroupCreationLevel|Allowed to create subgroups'), class: 'label-bold' + = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, group.subgroup_creation_level), {}, class: 'form-control' diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb index 77076ead47a..7ef9f7ef630 100644 --- a/lib/gitlab/access.rb +++ b/lib/gitlab/access.rb @@ -110,6 +110,13 @@ module Gitlab def project_creation_level_name(name) project_creation_options.key(name) end + + def subgroup_creation_options + { + s_('SubgroupCreationlevel|Owners') => OWNER_SUBGROUP_ACCESS, + s_('SubgroupCreationlevel|Maintainers') => MAINTAINER_SUBGROUP_ACCESS + } + end end def human_access -- cgit v1.2.1 From 610b3fedafda079b33e37d39f3e0f3b71ab5ecab Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 14:17:24 -0700 Subject: Make the group model return maintainer level when it is not set --- app/models/group.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/group.rb b/app/models/group.rb index 9520db1bc0a..3f80c1373f1 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -416,6 +416,10 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end + def subgroup_creation_level + super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + private def update_two_factor_requirement -- cgit v1.2.1 From 843f06d3a8a76d144481c6d98f059ca7071d6db7 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 15:53:46 -0700 Subject: Add policy to allow maintainers to create subgroups when enabled --- app/policies/group_policy.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index ea86858181d..bd1eb02ca1f 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -43,6 +43,10 @@ class GroupPolicy < BasePolicy @subject.project_creation_level == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS end + condition(:maintainer_can_create_group) do + @subject.subgroup_creation_level == ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + rule { public_group }.policy do enable :read_group enable :read_list @@ -110,6 +114,7 @@ class GroupPolicy < BasePolicy end rule { owner & nested_groups_supported }.enable :create_subgroup + rule { maintainer & maintainer_can_create_group & nested_groups_supported }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally -- cgit v1.2.1 From 412c86b989cac27fec9e0b2f51c601db18977268 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Thu, 27 Jun 2019 18:15:56 +0000 Subject: Apply suggestion to changelogs/unreleased/maintainers-can-create-subgroup.yml --- changelogs/unreleased/maintainers-can-create-subgroup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/maintainers-can-create-subgroup.yml b/changelogs/unreleased/maintainers-can-create-subgroup.yml index b537862c8af..180f0f7247f 100644 --- a/changelogs/unreleased/maintainers-can-create-subgroup.yml +++ b/changelogs/unreleased/maintainers-can-create-subgroup.yml @@ -1,5 +1,5 @@ --- -title: Maintainers can crete subgroups +title: Maintainers can create subgroups merge_request: 29718 author: Fabio Papa type: changed -- cgit v1.2.1 From 308769d19d46837980576bc7544f3b987b62c0f0 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 08:35:09 -0700 Subject: Add feature examples specing maintainers creating subgroups Both with subgroup_creation_level set to owners and to maintainers. Also fixed the naming of some other examples in the same spec as they were contradicting what they were actually performing in the test. These examples were probably copy/pasted, and not renamed. --- spec/features/groups/show_spec.rb | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 2654d06cd8c..68fa3f4e817 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -86,7 +86,7 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do + it 'does not allow creating subgroups' do expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) end end @@ -103,8 +103,22 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + context 'when subgroup_creation_level is set to maintainer' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + it 'allows creating subgroups' do + visit path + expect(page).to have_css("li[data-text='New subgroup']", visible: false) + end + end + + context 'when subgroup_creation_level is set to owners' do + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + + it 'does not allow creating subgroups' do + visit path + expect(page).not_to have_css("li[data-text='New subgroup']", visible: false) + end end end @@ -114,7 +128,7 @@ describe 'Group show page' do visit path end - it 'allows creating subgroups' do + it 'does not allow creating subgroups' do expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) end end -- cgit v1.2.1 From 34f8469d572af9162760c2532e337dff4c865e77 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 09:22:10 -0700 Subject: Update schema --- db/schema.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/schema.rb b/db/schema.rb index 9a8b64689bd..4b1b946a100 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2117,6 +2117,7 @@ ActiveRecord::Schema.define(version: 20190703130053) do t.string "ldap_sync_status", default: "ready", null: false t.boolean "membership_lock", default: false t.integer "last_ci_minutes_usage_notification_level" + t.integer "subgroup_creation_level", default: 0, null: false t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree -- cgit v1.2.1 From 2a7830f5abf329c7d68bd71efab25d6134832f1d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 13:31:47 -0700 Subject: Style rules; Revert some examples --- ...75626_add_group_creation_level_to_namespaces.rb | 7 +++++-- spec/controllers/admin/groups_controller_spec.rb | 9 ++++++-- spec/features/groups/group_settings_spec.rb | 2 +- spec/features/groups/show_spec.rb | 24 +++++++++++++++------- spec/models/group_spec.rb | 3 ++- spec/policies/group_policy_spec.rb | 21 ++++++++++++++----- spec/requests/api/groups_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 4 ++-- 8 files changed, 52 insertions(+), 22 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index b0ea74d4765..eed0ba25f27 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -5,10 +5,13 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] DOWNTIME = false disable_ddl_transaction! - + def up unless column_exists?(:namespaces, :subgroup_creation_level) - add_column_with_default(:namespaces, :subgroup_creation_level, :integer, default: 0) + add_column_with_default(:namespaces, + :subgroup_creation_level, + :integer, + default: 0) end end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index df11321537f..72f389513f8 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,9 +70,14 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do + MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + expect do - post :update, params: { id: group.to_param, group: { subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS } } - end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + post :update, + params: { id: group.to_param, + group: { subgroup_creation_level: MAINTAINER } } + end.to change { group.reload.subgroup_creation_level } + .to(MAINTAINER) end end end diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 95534d5a2ba..676769c25fe 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -92,7 +92,7 @@ describe 'Edit group settings' do expect(page).to have_content('Allowed to create subgroups') end end - + describe 'edit group avatar' do before do visit edit_group_path(group) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 68fa3f4e817..163906010fa 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -76,7 +76,8 @@ describe 'Group show page' do end it 'allows creating subgroups' do - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + expect(page) + .to have_css("li[data-text='New subgroup']", visible: false) end end @@ -87,7 +88,8 @@ describe 'Group show page' do end it 'does not allow creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_selector("li[data-text='New subgroup']", visible: false) end end end @@ -104,8 +106,11 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to maintainer' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } - + let(:group) do + create(:group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + it 'allows creating subgroups' do visit path expect(page).to have_css("li[data-text='New subgroup']", visible: false) @@ -113,11 +118,15 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to owners' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:group) do + create(:group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end it 'does not allow creating subgroups' do visit path - expect(page).not_to have_css("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_css("li[data-text='New subgroup']", visible: false) end end end @@ -129,7 +138,8 @@ describe 'Group show page' do end it 'does not allow creating subgroups' do - expect(page).not_to have_selector("li[data-text='New subgroup']", visible: false) + expect(page) + .not_to have_selector("li[data-text='New subgroup']", visible: false) end end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index fd40061dd3a..6627177ad61 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -999,7 +999,8 @@ describe Group do it 'outputs the default one if it is nil' do group = create(:group, subgroup_creation_level: nil) - expect(group.subgroup_creation_level).to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + expect(group.subgroup_creation_level) + .to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index aed9a8e34ff..da186f63eca 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -145,7 +145,8 @@ describe GroupPolicy do it 'allows every owner permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = owner_permissions - create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_owner_permissions) @@ -157,7 +158,8 @@ describe GroupPolicy do it 'allows every owner permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = owner_permissions - create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_owner_permissions) @@ -169,7 +171,8 @@ describe GroupPolicy do it 'allows every maintainer permission except creating subgroups' do create_subgroup_permission = [:create_subgroup] - updated_maintainer_permissions = maintainer_permissions - create_subgroup_permission + updated_maintainer_permissions = + maintainer_permissions - create_subgroup_permission expect_disallowed(*create_subgroup_permission) expect_allowed(*updated_maintainer_permissions) @@ -475,7 +478,11 @@ describe GroupPolicy do context "create_subgroup" do context 'when group has subgroup creation level set to owner' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:group) do + create( + :group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end context 'reporter' do let(:current_user) { reporter } @@ -503,7 +510,11 @@ describe GroupPolicy do end context 'when group has subgroup creation level set to maintainer' do - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:group) do + create( + :group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end context 'reporter' do let(:current_user) { reporter } diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 52d926d5484..c41408fba65 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'can create subgroups' do + it 'cannot create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(201) + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..267ad529d3b 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as maintainer' do + context 'as Owner' do before do - group.add_maintainer(user) + group.add_owner(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 884bf503ef09fbcc80da1ced0ec46ac9d918ed66 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 14:06:50 -0700 Subject: Remove an example that is no longer necessary --- spec/models/group_spec.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 6627177ad61..470ce65707d 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,13 +994,4 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end - - describe 'subgroup_creation_level' do - it 'outputs the default one if it is nil' do - group = create(:group, subgroup_creation_level: nil) - - expect(group.subgroup_creation_level) - .to eq(::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) - end - end end -- cgit v1.2.1 From a743db19830c84c3dfe6e23f22578de3c70301ca Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 16:12:54 -0700 Subject: Make maintainers the default setting for creating subgroups --- app/models/group.rb | 10 ++++++---- spec/models/group_spec.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 3f80c1373f1..8f39c3a658f 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,6 +58,8 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } + before_create :default_subgroup_creation_level_to_maintainers + after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -416,10 +418,6 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end - def subgroup_creation_level - super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end - private def update_two_factor_requirement @@ -449,4 +447,8 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end + + def default_subgroup_creation_level_to_maintainers + self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 470ce65707d..9ae18d7bab7 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -994,4 +994,12 @@ describe Group do expect(group.project_creation_level).to eq(Gitlab::CurrentSettings.default_project_creation) end end + + describe 'subgroup_creation_level' do + it 'defaults to maintainers' do + group = create (:group) + + expect(group.subgroup_creation_level).to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + end end -- cgit v1.2.1 From f7361e678cad995097730473973e612000f4bcf7 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 30 Jun 2019 14:40:23 -0700 Subject: Make subgroup_creation_level default to maintainer at SQL level - Migration updates existing groups to "owner", then sets default to "maintainer" so that new groups will default to that - Update spec examples --- ...75626_add_group_creation_level_to_namespaces.rb | 1 + spec/controllers/admin/groups_controller_spec.rb | 7 ++-- spec/factories/groups.rb | 5 ++- spec/features/groups/show_spec.rb | 17 ++++++--- spec/models/group_spec.rb | 5 +-- spec/policies/group_policy_spec.rb | 43 ++++++++++++++++++---- spec/requests/api/groups_spec.rb | 4 +- .../policies/group_policy_shared_context.rb | 2 +- 8 files changed, 60 insertions(+), 24 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index eed0ba25f27..85ac89af46e 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -12,6 +12,7 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] :subgroup_creation_level, :integer, default: 0) + change_column_default(:namespaces, :subgroup_creation_level, 1) end end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 72f389513f8..398f587bafe 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,14 +70,13 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: MAINTAINER } } - end.to change { group.reload.subgroup_creation_level } - .to(MAINTAINER) + group: { subgroup_creation_level: OWNER } } + end.to change { group.reload.subgroup_creation_level }.to(OWNER) end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 2f50fbfe2fa..b67ab955ffc 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,7 +5,6 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS - subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner @@ -46,5 +45,9 @@ FactoryBot.define do trait :auto_devops_disabled do auto_devops_enabled false end + + trait :owner_subgroup_creation_only do + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS + end end end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 163906010fa..48ba9064327 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -72,10 +72,11 @@ describe 'Group show page' do context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -84,10 +85,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -102,10 +104,9 @@ describe 'Group show page' do context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end - context 'when subgroup_creation_level is set to maintainer' do + context 'when subgroup_creation_level is set to maintainers' do let(:group) do create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) @@ -113,7 +114,9 @@ describe 'Group show page' do it 'allows creating subgroups' do visit path - expect(page).to have_css("li[data-text='New subgroup']", visible: false) + + expect(page) + .to have_css("li[data-text='New subgroup']", visible: false) end end @@ -125,6 +128,7 @@ describe 'Group show page' do it 'does not allow creating subgroups' do visit path + expect(page) .not_to have_css("li[data-text='New subgroup']", visible: false) end @@ -134,10 +138,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 9ae18d7bab7..c7fb0f51075 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -997,9 +997,8 @@ describe Group do describe 'subgroup_creation_level' do it 'defaults to maintainers' do - group = create (:group) - - expect(group.subgroup_creation_level).to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + expect(group.subgroup_creation_level) + .to eq(Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index da186f63eca..7fba62d2aa8 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -98,12 +98,38 @@ describe GroupPolicy do context 'maintainer' do let(:current_user) { maintainer } - it do - expect_allowed(*guest_permissions) - expect_allowed(*reporter_permissions) - expect_allowed(*developer_permissions) - expect_allowed(*maintainer_permissions) - expect_disallowed(*owner_permissions) + context 'with subgroup_creation level set to maintainer' do + let(:group) { create(:group, + :private, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + + it do + allow(Group).to receive(:supports_nested_objects?).and_return(true) + + create_subgroup_permission = [:create_subgroup] + updated_maintainer_permissions = + maintainer_permissions + create_subgroup_permission + updated_owner_permissions = + owner_permissions - create_subgroup_permission + + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*updated_maintainer_permissions) + expect_disallowed(*updated_owner_permissions) + end + end + + context 'with subgroup_creation_level set to owner' do + it do + allow(Group).to receive(:supports_nested_objects?).and_return(true) + + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*developer_permissions) + expect_allowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) + end end end @@ -181,7 +207,10 @@ describe GroupPolicy do end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do - let(:nested_group) { create(:group, :private, parent: group) } + let(:nested_group) { create(:group, + :private, + :owner_subgroup_creation_only, + parent: group) } before do nested_group.add_guest(guest) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index b4808ac0068..74389c4d82b 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -7,7 +7,7 @@ RSpec.shared_context 'GroupPolicy context' do let(:maintainer) { create(:user) } let(:owner) { create(:user) } let(:admin) { create(:admin) } - let(:group) { create(:group, :private) } + let(:group) { create(:group, :private, :owner_subgroup_creation_only) } let(:guest_permissions) do %i[ -- cgit v1.2.1 From c6284c4db82fdbfec177fe5366e71289d6041925 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:00:34 -0700 Subject: Remove AR hook to set the default subgroup_creation_level --- app/models/group.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 8f39c3a658f..9520db1bc0a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,8 +58,6 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } - before_create :default_subgroup_creation_level_to_maintainers - after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -447,8 +445,4 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end - - def default_subgroup_creation_level_to_maintainers - self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end end -- cgit v1.2.1 From 85d1ef270a0b87784cf96955ef782c1a664b99c9 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:17:26 -0700 Subject: Clean up the show_spec examples previously added --- spec/features/groups/show_spec.rb | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 48ba9064327..ef0e885ee5f 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,27 +56,28 @@ describe 'Group show page' do end context 'subgroup support' do + let(:restricted_group) { create(:group, + subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } + let(:relaxed_group) { create(:group, + subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } let(:owner) { create(:user) } let(:maintainer) { create(:user) } - before do - group.add_owner(owner) - group.add_maintainer(maintainer) - end - context 'for owners' do + let(:path) { group_path(restricted_group) } + before do + restricted_group.add_owner(owner) sign_in(owner) end context 'when subgroups are supported', :js, :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -85,11 +86,10 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } + visit path end it 'does not allow creating subgroups' do - visit path - expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -107,30 +107,30 @@ describe 'Group show page' do end context 'when subgroup_creation_level is set to maintainers' do - let(:group) do - create(:group, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + before do + relaxed_group.add_maintainer(maintainer) + path = group_path(relaxed_group) + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end end context 'when subgroup_creation_level is set to owners' do - let(:group) do - create(:group, - subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + before do + restricted_group.add_maintainer(maintainer) end it 'does not allow creating subgroups' do + path = group_path(restricted_group) visit path expect(page) - .not_to have_css("li[data-text='New subgroup']", visible: false) + .not_to have_selector("li[data-text='New subgroup']", + visible: false) end end end -- cgit v1.2.1 From 44a1812ec7dc8cf3a743ddac6d7e95c393f1b83b Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:55:00 -0700 Subject: Fix group creat_service_spec to contain maintainer context --- spec/services/groups/create_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 267ad529d3b..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as Owner' do + context 'as maintainer' do before do - group.add_owner(user) + group.add_maintainer(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 7de9a6781b9f7dbacd7c909f4b7361f663bb9eaa Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:56:02 -0700 Subject: Add descriptions to examples --- spec/policies/group_policy_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 7fba62d2aa8..020b51f776e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -103,7 +103,7 @@ describe GroupPolicy do :private, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } - it do + it 'allows every maintainer permission plus creating subgroups' do allow(Group).to receive(:supports_nested_objects?).and_return(true) create_subgroup_permission = [:create_subgroup] @@ -121,7 +121,7 @@ describe GroupPolicy do end context 'with subgroup_creation_level set to owner' do - it do + it 'allows every maintainer permission' do allow(Group).to receive(:supports_nested_objects?).and_return(true) expect_allowed(*guest_permissions) -- cgit v1.2.1 From cd5259d5b17bf4caccbc246215903adc7ebab0e5 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 13:41:32 -0700 Subject: Apply changes recomended by merge request coach --- app/models/group.rb | 4 ++++ app/views/groups/_group_admin_settings.html.haml | 6 ++++++ ...190626175626_add_group_creation_level_to_namespaces.rb | 15 +++++---------- db/schema.rb | 2 +- spec/controllers/admin/groups_controller_spec.rb | 6 ++---- spec/features/admin/admin_groups_spec.rb | 9 +++++++++ spec/features/groups/show_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 9 --------- 8 files changed, 29 insertions(+), 26 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 9520db1bc0a..3f80c1373f1 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -416,6 +416,10 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end + def subgroup_creation_level + super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + private def update_two_factor_requirement diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml index b8f632d11d3..733cb36cc3d 100644 --- a/app/views/groups/_group_admin_settings.html.haml +++ b/app/views/groups/_group_admin_settings.html.haml @@ -16,6 +16,12 @@ .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 + .col-sm-2.col-form-label + = f.label s_('SubgroupCreationlevel|Allowed to create subgroups') + .col-sm-10 + = f.select :subgroup_creation_level, options_for_select(::Gitlab::Access.subgroup_creation_options, @group.subgroup_creation_level), {}, class: 'form-control' + .form-group.row .col-sm-2.col-form-label.pt-0 = f.label :require_two_factor_authentication, 'Two-factor authentication' diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index 85ac89af46e..867ec3b7c91 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -7,18 +7,13 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] disable_ddl_transaction! def up - unless column_exists?(:namespaces, :subgroup_creation_level) - add_column_with_default(:namespaces, - :subgroup_creation_level, - :integer, - default: 0) - change_column_default(:namespaces, :subgroup_creation_level, 1) - end + add_column(:namespaces, :subgroup_creation_level, :integer) + change_column_default(:namespaces, + :subgroup_creation_level, + ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end def down - if column_exists?(:namespaces, :subgroup_creation_level) - remove_column(:namespaces, :subgroup_creation_level) - end + remove_column(:namespaces, :subgroup_creation_level) end end diff --git a/db/schema.rb b/db/schema.rb index 4b1b946a100..8c085d64e27 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2117,7 +2117,7 @@ ActiveRecord::Schema.define(version: 20190703130053) do t.string "ldap_sync_status", default: "ready", null: false t.boolean "membership_lock", default: false t.integer "last_ci_minutes_usage_notification_level" - t.integer "subgroup_creation_level", default: 0, null: false + t.integer "subgroup_creation_level", default: 1 t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 398f587bafe..1123563c1e3 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,13 +70,11 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS - expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: OWNER } } - end.to change { group.reload.subgroup_creation_level }.to(OWNER) + group: { subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS } } + end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end end end diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 735ca60f7da..35c384dd458 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -102,6 +102,15 @@ describe 'Admin Groups' do expect_selected_visibility(group.visibility_level) end + it 'shows the subgroup creation level dropdown populated with the group subgroup_creation_level value' do + group = create(:group, :private, :owner_subgroup_creation_only) + + visit admin_group_edit_path(group) + + expect(page).to have_select("group_subgroup_creation_level", + selected: ::Gitlab::Access.subgroup_creation_options.keys[group.subgroup_creation_level]) + end + it 'edit group path does not change group name', :js do group = create(:group, :private) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index ef0e885ee5f..5096abadb79 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -71,7 +71,7 @@ describe 'Group show page' do sign_in(owner) end - context 'when subgroups are supported', :js, :nested_groups do + context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } visit path @@ -101,7 +101,7 @@ describe 'Group show page' do sign_in(maintainer) end - context 'when subgroups are supported', :js, :nested_groups do + context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..c5ff6cdbacd 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,4 +1,3 @@ -# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -88,14 +87,6 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - - context 'as maintainer' do - before do - group.add_maintainer(user) - end - - it { is_expected.to be_persisted } - end end end -- cgit v1.2.1 From de5c188e0911fb8f275f26a7551cd0bdbd84422d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 11:07:17 -0700 Subject: Add failing unit test specifying a maintainer creating a subgroup --- spec/services/groups/create_service_spec.rb | 9 +++++++++ .../shared_contexts/policies/group_policy_shared_context.rb | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index c5ff6cdbacd..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -87,6 +88,14 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end + + context 'as maintainer' do + before do + group.add_maintainer(user) + end + + it { is_expected.to be_persisted } + end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index 74389c4d82b..d596317462a 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -23,6 +23,15 @@ RSpec.shared_context 'GroupPolicy context' do create_projects read_cluster create_cluster update_cluster admin_cluster add_cluster ] + [ + :create_projects, + :read_cluster, + :create_cluster, + :update_cluster, + :admin_cluster, + :add_cluster, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) + ].compact end let(:owner_permissions) do [ @@ -30,8 +39,7 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) + :set_note_created_at ].compact end -- cgit v1.2.1 From f6e7ffa7250299c1c0f9fc866eb78c5f746d58a6 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 13:22:12 -0700 Subject: Modify API spec to expect a maintainer to be able to create subgroup --- spec/support/shared_contexts/policies/group_policy_shared_context.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index d596317462a..5a55bbac788 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,10 +19,6 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - %i[ - create_projects - read_cluster create_cluster update_cluster admin_cluster add_cluster - ] [ :create_projects, :read_cluster, -- cgit v1.2.1 From fe47ec447fe125af43245c237c38c7da9775c5c0 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 15:29:54 -0700 Subject: Adjust the documentation on subgroups --- doc/user/group/subgroups/index.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index 1c6cca049c5..e3f7539a9b6 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -75,13 +75,16 @@ structure. ## Creating a subgroup NOTE: **Note:** -You must be an Owner of a group to create a subgroup. For -more information check the [permissions table](../../permissions.md#group-members-permissions). +In order to create a subgroup you must either be an Owner or a Maintainer of the +group, depending on the group's setting. By default groups allow both Owners and +Maintainers to create subgroups, but this can be changed by an Owner or +Administrator to only allow Owners to create subgroups. For more information +check the [permissions table](../../permissions.md#group-members-permissions). For a list of words that are not allowed to be used as group names see the -[reserved names](../../reserved_names.md). -Users can always create subgroups if they are explicitly added as an Owner to -a parent group, even if group creation is disabled by an administrator in their -settings. +[reserved names](../../reserved_names.md). Users can always create subgroups if +they are explicitly added as an Owner (or Maintainer, if that setting is +enabled) to a parent group, even if group creation is disabled by an +administrator in their settings. To create a subgroup: -- cgit v1.2.1 From 58e4b654e98eabcfaf267f41ce42df04adbfcc10 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 15:39:43 -0700 Subject: Adjust the documentation on subgroup permissions --- doc/user/permissions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 33fff0fed74..92635ae484b 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -209,13 +209,15 @@ group. | Create project in group | | | ✓ | ✓ | ✓ | | Create/edit/delete group milestones | | | ✓ | ✓ | ✓ | | Enable/disable a dependency proxy **[PREMIUM]** | | | ✓ | ✓ | ✓ | +| Create subgroup | | | | ✓* | ✓ | | Edit group | | | | | ✓ | -| Create subgroup | | | | | ✓ | | Manage group members | | | | | ✓ | | Remove group | | | | | ✓ | | Delete group epic **[ULTIMATE]** | | | | | ✓ | | View group Audit Events | | | | | ✓ | +*Groups can be set to allow either Owners or Owners and maintainers to create subgroups + ### Subgroup permissions When you add a member to a subgroup, they inherit the membership and -- cgit v1.2.1 From a8165fec41f6343c296ec9bcc8f57a099f2da983 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 16:27:49 -0700 Subject: Fix some code style issues --- spec/features/groups/show_spec.rb | 12 ++++++++---- spec/policies/group_policy_spec.rb | 13 ++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 5096abadb79..f1501181432 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -56,10 +56,14 @@ describe 'Group show page' do end context 'subgroup support' do - let(:restricted_group) { create(:group, - subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } - let(:relaxed_group) { create(:group, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:restricted_group) do + create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + end + + let(:relaxed_group) do + create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end + let(:owner) { create(:user) } let(:maintainer) { create(:user) } diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 020b51f776e..dc3675a7b9e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -99,9 +99,9 @@ describe GroupPolicy do let(:current_user) { maintainer } context 'with subgroup_creation level set to maintainer' do - let(:group) { create(:group, - :private, - subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } + let(:group) do + create(:group, :private, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + end it 'allows every maintainer permission plus creating subgroups' do allow(Group).to receive(:supports_nested_objects?).and_return(true) @@ -207,10 +207,9 @@ describe GroupPolicy do end describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do - let(:nested_group) { create(:group, - :private, - :owner_subgroup_creation_only, - parent: group) } + let(:nested_group) do + create(:group, :private, :owner_subgroup_creation_only, parent: group) + end before do nested_group.add_guest(guest) -- cgit v1.2.1 From c822fdb9bfd73aa8e2189781e336b43344940023 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 2 Jul 2019 08:38:38 -0700 Subject: Apply recomended changes from merge coach --- ...190626175626_add_group_creation_level_to_namespaces.rb | 1 - spec/features/admin/admin_groups_spec.rb | 3 +-- spec/features/groups/show_spec.rb | 15 +++++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb index 867ec3b7c91..3b75c92e518 100644 --- a/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb +++ b/db/migrate/20190626175626_add_group_creation_level_to_namespaces.rb @@ -4,7 +4,6 @@ class AddGroupCreationLevelToNamespaces < ActiveRecord::Migration[5.1] include Gitlab::Database::MigrationHelpers DOWNTIME = false - disable_ddl_transaction! def up add_column(:namespaces, :subgroup_creation_level, :integer) diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index 35c384dd458..c1ad73779c9 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -107,8 +107,7 @@ describe 'Admin Groups' do visit admin_group_edit_path(group) - expect(page).to have_select("group_subgroup_creation_level", - selected: ::Gitlab::Access.subgroup_creation_options.keys[group.subgroup_creation_level]) + expect(page).to have_content('Allowed to create subgroups') end it 'edit group path does not change group name', :js do diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index f1501181432..bed998a0859 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -57,11 +57,11 @@ describe 'Group show page' do context 'subgroup support' do let(:restricted_group) do - create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end let(:relaxed_group) do - create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) + create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end let(:owner) { create(:user) } @@ -78,10 +78,11 @@ describe 'Group show page' do context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -90,10 +91,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end @@ -113,11 +115,12 @@ describe 'Group show page' do context 'when subgroup_creation_level is set to maintainers' do before do relaxed_group.add_maintainer(maintainer) - path = group_path(relaxed_group) - visit path end it 'allows creating subgroups' do + path = group_path(relaxed_group) + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end -- cgit v1.2.1 From b16e6b93c433b3e2e93118934b355480a50581df Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 25 Jun 2019 21:59:10 -0700 Subject: Add examples specing the setting to choose who can create subgroups This setting is at the group level only. The default is specified to be maintainers and owners. **Specs only**, all failing. --- spec/factories/groups.rb | 1 + .../policies/group_policy_shared_context.rb | 16 ++++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index b67ab955ffc..947392b4fbc 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,6 +5,7 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index 5a55bbac788..74389c4d82b 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,15 +19,10 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - [ - :create_projects, - :read_cluster, - :create_cluster, - :update_cluster, - :admin_cluster, - :add_cluster, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) - ].compact + %i[ + create_projects + read_cluster create_cluster update_cluster admin_cluster add_cluster + ] end let(:owner_permissions) do [ @@ -35,7 +30,8 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at + :set_note_created_at, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) ].compact end -- cgit v1.2.1 From 6b9b0f150688e44cbaa27f97062a773963624a61 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 13:31:47 -0700 Subject: Style rules; Revert some examples --- spec/controllers/admin/groups_controller_spec.rb | 2 ++ spec/requests/api/groups_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 1123563c1e3..451367f58e1 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,6 +70,8 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do + MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + expect do post :update, params: { id: group.to_param, diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 52d926d5484..c41408fba65 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'can create subgroups' do + it 'cannot create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(201) + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..267ad529d3b 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as maintainer' do + context 'as Owner' do before do - group.add_maintainer(user) + group.add_owner(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 79d0f5d736d6dfb82fa00677994377dfd87a6f07 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 16:12:54 -0700 Subject: Make maintainers the default setting for creating subgroups --- app/models/group.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 3f80c1373f1..8f39c3a658f 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,6 +58,8 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } + before_create :default_subgroup_creation_level_to_maintainers + after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -416,10 +418,6 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end - def subgroup_creation_level - super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end - private def update_two_factor_requirement @@ -449,4 +447,8 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end + + def default_subgroup_creation_level_to_maintainers + self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end end -- cgit v1.2.1 From 5c94c5592e8853df95f34ae522eebeeb51705012 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 30 Jun 2019 14:40:23 -0700 Subject: Make subgroup_creation_level default to maintainer at SQL level - Migration updates existing groups to "owner", then sets default to "maintainer" so that new groups will default to that - Update spec examples --- spec/controllers/admin/groups_controller_spec.rb | 2 +- spec/factories/groups.rb | 1 - spec/requests/api/groups_spec.rb | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 451367f58e1..34cd0b324b5 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,7 +70,7 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS expect do post :update, diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 947392b4fbc..b67ab955ffc 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,7 +5,6 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS - subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end -- cgit v1.2.1 From 4e427390feb4bf74ae91c84f18ab2ee38e917a51 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:00:34 -0700 Subject: Remove AR hook to set the default subgroup_creation_level --- app/models/group.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 8f39c3a658f..9520db1bc0a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,8 +58,6 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } - before_create :default_subgroup_creation_level_to_maintainers - after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -447,8 +445,4 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end - - def default_subgroup_creation_level_to_maintainers - self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end end -- cgit v1.2.1 From a58d7be102f4cd78fbac0399698548e50275f573 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:17:26 -0700 Subject: Clean up the show_spec examples previously added --- spec/features/groups/show_spec.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index bed998a0859..9cf5e7f9bb6 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -78,11 +78,10 @@ describe 'Group show page' do context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -91,11 +90,10 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } + visit path end it 'does not allow creating subgroups' do - visit path - expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end -- cgit v1.2.1 From f1fa1323afe37632ecd729b9c51d494381e79285 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:55:00 -0700 Subject: Fix group creat_service_spec to contain maintainer context --- spec/services/groups/create_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 267ad529d3b..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as Owner' do + context 'as maintainer' do before do - group.add_owner(user) + group.add_maintainer(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From a79e48758d312eeacef2f3ae174010b574670eee Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 13:41:32 -0700 Subject: Apply changes recomended by merge request coach --- app/models/group.rb | 4 ++++ spec/controllers/admin/groups_controller_spec.rb | 2 -- spec/services/groups/create_service_spec.rb | 9 --------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 9520db1bc0a..3f80c1373f1 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -416,6 +416,10 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end + def subgroup_creation_level + super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end + private def update_two_factor_requirement diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 34cd0b324b5..1123563c1e3 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,8 +70,6 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS - expect do post :update, params: { id: group.to_param, diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..c5ff6cdbacd 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,4 +1,3 @@ -# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -88,14 +87,6 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - - context 'as maintainer' do - before do - group.add_maintainer(user) - end - - it { is_expected.to be_persisted } - end end end -- cgit v1.2.1 From c90fba28408ddc72e17b511b617b00e01f3269ba Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 11:07:17 -0700 Subject: Add failing unit test specifying a maintainer creating a subgroup --- spec/services/groups/create_service_spec.rb | 9 +++++++++ .../shared_contexts/policies/group_policy_shared_context.rb | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index c5ff6cdbacd..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # frozen_string_literal: true require 'spec_helper' @@ -87,6 +88,14 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end + + context 'as maintainer' do + before do + group.add_maintainer(user) + end + + it { is_expected.to be_persisted } + end end end diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index 74389c4d82b..d596317462a 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -23,6 +23,15 @@ RSpec.shared_context 'GroupPolicy context' do create_projects read_cluster create_cluster update_cluster admin_cluster add_cluster ] + [ + :create_projects, + :read_cluster, + :create_cluster, + :update_cluster, + :admin_cluster, + :add_cluster, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) + ].compact end let(:owner_permissions) do [ @@ -30,8 +39,7 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) + :set_note_created_at ].compact end -- cgit v1.2.1 From 22444c8d045f1c9eba8c467de0fb65e2ca684a8d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 16 Jun 2019 13:22:12 -0700 Subject: Modify API spec to expect a maintainer to be able to create subgroup --- spec/support/shared_contexts/policies/group_policy_shared_context.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index d596317462a..5a55bbac788 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,10 +19,6 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - %i[ - create_projects - read_cluster create_cluster update_cluster admin_cluster add_cluster - ] [ :create_projects, :read_cluster, -- cgit v1.2.1 From 8f0d0d59938b8e1ac48e6f02161eb1b6a134b8ec Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 25 Jun 2019 21:59:10 -0700 Subject: Add examples specing the setting to choose who can create subgroups This setting is at the group level only. The default is specified to be maintainers and owners. **Specs only**, all failing. --- spec/factories/groups.rb | 1 + spec/policies/group_policy_spec.rb | 8 ++++++++ .../policies/group_policy_shared_context.rb | 16 ++++++---------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index b67ab955ffc..947392b4fbc 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,6 +5,7 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS + subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index dc3675a7b9e..893b686da43 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -506,11 +506,15 @@ describe GroupPolicy do context "create_subgroup" do context 'when group has subgroup creation level set to owner' do +<<<<<<< HEAD let(:group) do create( :group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end +======= + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } +>>>>>>> Add examples specing the setting to choose who can create subgroups context 'reporter' do let(:current_user) { reporter } @@ -538,11 +542,15 @@ describe GroupPolicy do end context 'when group has subgroup creation level set to maintainer' do +<<<<<<< HEAD let(:group) do create( :group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end +======= + let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } +>>>>>>> Add examples specing the setting to choose who can create subgroups context 'reporter' do let(:current_user) { reporter } diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index 5a55bbac788..74389c4d82b 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -19,15 +19,10 @@ RSpec.shared_context 'GroupPolicy context' do let(:reporter_permissions) { [:admin_label] } let(:developer_permissions) { [:admin_milestone] } let(:maintainer_permissions) do - [ - :create_projects, - :read_cluster, - :create_cluster, - :update_cluster, - :admin_cluster, - :add_cluster, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) - ].compact + %i[ + create_projects + read_cluster create_cluster update_cluster admin_cluster add_cluster + ] end let(:owner_permissions) do [ @@ -35,7 +30,8 @@ RSpec.shared_context 'GroupPolicy context' do :admin_namespace, :admin_group_member, :change_visibility_level, - :set_note_created_at + :set_note_created_at, + (Gitlab::Database.postgresql? ? :create_subgroup : nil) ].compact end -- cgit v1.2.1 From fed434e433063ce5bced7813cdae4ae3540535ca Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 13:31:47 -0700 Subject: Style rules; Revert some examples --- spec/controllers/admin/groups_controller_spec.rb | 7 +++++-- spec/policies/group_policy_spec.rb | 8 -------- spec/requests/api/groups_spec.rb | 4 ++-- spec/services/groups/create_service_spec.rb | 4 ++-- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 1123563c1e3..72f389513f8 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,11 +70,14 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do + MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS } } - end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::OWNER_SUBGROUP_ACCESS) + group: { subgroup_creation_level: MAINTAINER } } + end.to change { group.reload.subgroup_creation_level } + .to(MAINTAINER) end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 893b686da43..dc3675a7b9e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -506,15 +506,11 @@ describe GroupPolicy do context "create_subgroup" do context 'when group has subgroup creation level set to owner' do -<<<<<<< HEAD let(:group) do create( :group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) end -======= - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS) } ->>>>>>> Add examples specing the setting to choose who can create subgroups context 'reporter' do let(:current_user) { reporter } @@ -542,15 +538,11 @@ describe GroupPolicy do end context 'when group has subgroup creation level set to maintainer' do -<<<<<<< HEAD let(:group) do create( :group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) end -======= - let(:group) { create(:group, subgroup_creation_level: ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS) } ->>>>>>> Add examples specing the setting to choose who can create subgroups context 'reporter' do let(:current_user) { reporter } diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 52d926d5484..c41408fba65 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'can create subgroups' do + it 'cannot create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(201) + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index b4e6ddddfac..267ad529d3b 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as maintainer' do + context 'as Owner' do before do - group.add_maintainer(user) + group.add_owner(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 8b8172c1e4738fece547492dee6e3a7a61e8d50e Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 28 Jun 2019 16:12:54 -0700 Subject: Make maintainers the default setting for creating subgroups --- app/models/group.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 3f80c1373f1..8f39c3a658f 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,6 +58,8 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } + before_create :default_subgroup_creation_level_to_maintainers + after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -416,10 +418,6 @@ class Group < Namespace super || ::Gitlab::CurrentSettings.default_project_creation end - def subgroup_creation_level - super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end - private def update_two_factor_requirement @@ -449,4 +447,8 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end + + def default_subgroup_creation_level_to_maintainers + self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + end end -- cgit v1.2.1 From a39a4e4509f5272bc5b4001cded198ebd7d98614 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Sun, 30 Jun 2019 14:40:23 -0700 Subject: Make subgroup_creation_level default to maintainer at SQL level - Migration updates existing groups to "owner", then sets default to "maintainer" so that new groups will default to that - Update spec examples --- spec/controllers/admin/groups_controller_spec.rb | 7 +++---- spec/factories/groups.rb | 1 - spec/features/groups/show_spec.rb | 6 ++++-- spec/requests/api/groups_spec.rb | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 72f389513f8..398f587bafe 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -70,14 +70,13 @@ describe Admin::GroupsController do end it 'updates the subgroup_creation_level successfully' do - MAINTAINER = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + OWNER = ::Gitlab::Access::OWNER_SUBGROUP_ACCESS expect do post :update, params: { id: group.to_param, - group: { subgroup_creation_level: MAINTAINER } } - end.to change { group.reload.subgroup_creation_level } - .to(MAINTAINER) + group: { subgroup_creation_level: OWNER } } + end.to change { group.reload.subgroup_creation_level }.to(OWNER) end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 947392b4fbc..b67ab955ffc 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -5,7 +5,6 @@ FactoryBot.define do type 'Group' owner nil project_creation_level ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS - subgroup_creation_level ::Gitlab::Access::OWNER_SUBGROUP_ACCESS after(:create) do |group| if group.owner diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 9cf5e7f9bb6..bed998a0859 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -78,10 +78,11 @@ describe 'Group show page' do context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -90,10 +91,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c41408fba65..52d926d5484 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -803,10 +803,10 @@ describe API::Groups do group2.add_maintainer(user1) end - it 'cannot create subgroups' do + it 'can create subgroups' do post api("/groups", user1), params: { parent_id: group2.id, name: 'foo', path: 'foo' } - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(201) end end end -- cgit v1.2.1 From 5015e23d6abcf2d3797e7dea76d3d3db9f3d6219 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:00:34 -0700 Subject: Remove AR hook to set the default subgroup_creation_level --- app/models/group.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 8f39c3a658f..9520db1bc0a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -58,8 +58,6 @@ class Group < Namespace add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } - before_create :default_subgroup_creation_level_to_maintainers - after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement @@ -447,8 +445,4 @@ class Group < Namespace errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.") end - - def default_subgroup_creation_level_to_maintainers - self.subgroup_creation_level = ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS - end end -- cgit v1.2.1 From 2b96af35062dcc2492cfad7f6e6f6d6fd8f76ff6 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:17:26 -0700 Subject: Clean up the show_spec examples previously added --- spec/features/groups/show_spec.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index bed998a0859..9cf5e7f9bb6 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -78,11 +78,10 @@ describe 'Group show page' do context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } + visit path end it 'allows creating subgroups' do - visit path - expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -91,11 +90,10 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } + visit path end it 'does not allow creating subgroups' do - visit path - expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end -- cgit v1.2.1 From b40d4ecdb992b9dad2f222e67e5ab54d8ed38e5d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Mon, 1 Jul 2019 09:55:00 -0700 Subject: Fix group creat_service_spec to contain maintainer context --- spec/services/groups/create_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 267ad529d3b..b4e6ddddfac 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -89,9 +89,9 @@ describe Groups::CreateService, '#execute' do it { is_expected.to be_persisted } end - context 'as Owner' do + context 'as maintainer' do before do - group.add_owner(user) + group.add_maintainer(user) end it { is_expected.to be_persisted } -- cgit v1.2.1 From 862b0289716bafa174dd765950f2854ac0419fc7 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Tue, 2 Jul 2019 08:38:38 -0700 Subject: Apply recomended changes from merge coach --- spec/features/groups/show_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 9cf5e7f9bb6..bed998a0859 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -78,10 +78,11 @@ describe 'Group show page' do context 'when subgroups are supported', :nested_groups do before do allow(Group).to receive(:supports_nested_objects?) { true } - visit path end it 'allows creating subgroups' do + visit path + expect(page) .to have_css("li[data-text='New subgroup']", visible: false) end @@ -90,10 +91,11 @@ describe 'Group show page' do context 'when subgroups are not supported' do before do allow(Group).to receive(:supports_nested_objects?) { false } - visit path end it 'does not allow creating subgroups' do + visit path + expect(page) .not_to have_selector("li[data-text='New subgroup']", visible: false) end -- cgit v1.2.1 From 1cc2f2786398461d6b68dff442c3f46c0d4eaa8d Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 05:55:20 +0000 Subject: Apply suggestion to doc/user/group/subgroups/index.md --- doc/user/group/subgroups/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index e3f7539a9b6..fad7541bdc4 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -81,7 +81,9 @@ Maintainers to create subgroups, but this can be changed by an Owner or Administrator to only allow Owners to create subgroups. For more information check the [permissions table](../../permissions.md#group-members-permissions). For a list of words that are not allowed to be used as group names see the -[reserved names](../../reserved_names.md). Users can always create subgroups if +[reserved names](../../reserved_names.md). + +Users can always create subgroups if they are explicitly added as an Owner (or Maintainer, if that setting is enabled) to a parent group, even if group creation is disabled by an administrator in their settings. -- cgit v1.2.1 From d67a36f9b8ec3559bc7f531962c4d73a36155e8e Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 05:55:49 +0000 Subject: Apply suggestion to doc/user/permissions.md --- doc/user/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 92635ae484b..8ff778e7494 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -209,7 +209,7 @@ group. | Create project in group | | | ✓ | ✓ | ✓ | | Create/edit/delete group milestones | | | ✓ | ✓ | ✓ | | Enable/disable a dependency proxy **[PREMIUM]** | | | ✓ | ✓ | ✓ | -| Create subgroup | | | | ✓* | ✓ | +| Create subgroup | | | | ✓ (1) | ✓ | | Edit group | | | | | ✓ | | Manage group members | | | | | ✓ | | Remove group | | | | | ✓ | -- cgit v1.2.1 From e8497339eeedb4df0d1aea817874849c645cdc77 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 05:56:03 +0000 Subject: Apply suggestion to doc/user/permissions.md --- doc/user/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 8ff778e7494..c992f581a82 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -216,7 +216,7 @@ group. | Delete group epic **[ULTIMATE]** | | | | | ✓ | | View group Audit Events | | | | | ✓ | -*Groups can be set to allow either Owners or Owners and maintainers to create subgroups +- (1): Groups can be set to allow either Owners or Owners and maintainers to create subgroups ### Subgroup permissions -- cgit v1.2.1 From 8b70c1cf8ca82b3c4b34740c65734fc32e3e01ee Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 05:56:53 +0000 Subject: Apply suggestion to doc/user/group/subgroups/index.md --- doc/user/group/subgroups/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index fad7541bdc4..4e88ec5ee76 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -74,7 +74,6 @@ structure. ## Creating a subgroup -NOTE: **Note:** In order to create a subgroup you must either be an Owner or a Maintainer of the group, depending on the group's setting. By default groups allow both Owners and Maintainers to create subgroups, but this can be changed by an Owner or -- cgit v1.2.1 From fa6504d6354fdabedd24589e7242ba457302aaa6 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 3 Jul 2019 16:43:56 +0000 Subject: Update index.md --- doc/user/group/subgroups/index.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md index 4e88ec5ee76..b9313ccdb1e 100644 --- a/doc/user/group/subgroups/index.md +++ b/doc/user/group/subgroups/index.md @@ -74,18 +74,19 @@ structure. ## Creating a subgroup -In order to create a subgroup you must either be an Owner or a Maintainer of the -group, depending on the group's setting. By default groups allow both Owners and +To create a subgroup you must either be an Owner or a Maintainer of the group, +depending on the group's setting. By default groups allow both Owners and Maintainers to create subgroups, but this can be changed by an Owner or -Administrator to only allow Owners to create subgroups. For more information -check the [permissions table](../../permissions.md#group-members-permissions). -For a list of words that are not allowed to be used as group names see the +Administrator to only allow Owners to create subgroups. + +For more information check the +[permissions table](../../permissions.md#group-members-permissions). For a list +of words that are not allowed to be used as group names see the [reserved names](../../reserved_names.md). -Users can always create subgroups if -they are explicitly added as an Owner (or Maintainer, if that setting is -enabled) to a parent group, even if group creation is disabled by an -administrator in their settings. +Users can always create subgroups if they are explicitly added as an Owner (or +Maintainer, if that setting is enabled) to a parent group, even if group +creation is disabled by an administrator in their settings. To create a subgroup: -- cgit v1.2.1 From e9f9cc8937bc86b582424929fc8e961f003caf96 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 5 Jul 2019 14:52:06 +0000 Subject: Apply suggestion to doc/user/permissions.md --- doc/user/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index c992f581a82..aac548020be 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -216,7 +216,7 @@ group. | Delete group epic **[ULTIMATE]** | | | | | ✓ | | View group Audit Events | | | | | ✓ | -- (1): Groups can be set to allow either Owners or Owners and maintainers to create subgroups +- (1): Groups can be set to allow either Owners or Owners and Maintainers to create subgroups ### Subgroup permissions -- cgit v1.2.1 From 476da097cdd1c866bdd84de050b05a9508f13c14 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 5 Jul 2019 12:43:58 -0700 Subject: Regenerate locale strings --- locale/gitlab.pot | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 78bf48d66fc..f8de4a5ac71 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9994,6 +9994,18 @@ msgstr "" msgid "StorageSize|Unknown" msgstr "" +msgid "SubgroupCreationLevel|Allowed to create subgroups" +msgstr "" + +msgid "SubgroupCreationlevel|Allowed to create subgroups" +msgstr "" + +msgid "SubgroupCreationlevel|Maintainers" +msgstr "" + +msgid "SubgroupCreationlevel|Owners" +msgstr "" + msgid "Subgroups" msgstr "" -- cgit v1.2.1 From a7821dd910fd385a66cfe6c840c37c7b11263410 Mon Sep 17 00:00:00 2001 From: Fabio Pitino Date: Fri, 28 Jun 2019 17:27:07 +0100 Subject: Drop feature to take ownership of a trigger token Removing API and frontend interactions that allowed users to take ownership of a trigger token. Removed mentions from the documentation. --- app/controllers/projects/triggers_controller.rb | 12 +-------- app/views/projects/triggers/_trigger.html.haml | 3 --- ...urity-remove-take-trigger-ownership-feature.yml | 5 ++++ config/routes/project.rb | 6 +---- doc/api/pipeline_triggers.md | 29 ---------------------- doc/ci/triggers/README.md | 14 +---------- doc/user/project/new_ci_build_permissions_model.md | 3 +-- lib/api/triggers.rb | 21 ---------------- locale/gitlab.pot | 6 ----- spec/features/triggers_spec.rb | 23 ----------------- spec/requests/api/triggers_spec.rb | 28 --------------------- 11 files changed, 9 insertions(+), 141 deletions(-) create mode 100644 changelogs/unreleased/security-remove-take-trigger-ownership-feature.yml diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb index 284e119ca06..7159d0243a3 100644 --- a/app/controllers/projects/triggers_controller.rb +++ b/app/controllers/projects/triggers_controller.rb @@ -4,7 +4,7 @@ class Projects::TriggersController < Projects::ApplicationController before_action :authorize_admin_build! before_action :authorize_manage_trigger!, except: [:index, :create] before_action :authorize_admin_trigger!, only: [:edit, :update] - before_action :trigger, only: [:take_ownership, :edit, :update, :destroy] + before_action :trigger, only: [:edit, :update, :destroy] layout 'project_settings' @@ -24,16 +24,6 @@ class Projects::TriggersController < Projects::ApplicationController redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers') end - def take_ownership - if trigger.update(owner: current_user) - flash[:notice] = _('Trigger was re-assigned.') - else - flash[:alert] = _('You could not take ownership of trigger.') - end - - redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers') - end - def edit end diff --git a/app/views/projects/triggers/_trigger.html.haml b/app/views/projects/triggers/_trigger.html.haml index 6f6f1e5e0c5..7367a96f8e4 100644 --- a/app/views/projects/triggers/_trigger.html.haml +++ b/app/views/projects/triggers/_trigger.html.haml @@ -30,10 +30,7 @@ Never %td.text-right.trigger-actions - - take_ownership_confirmation = "By taking ownership you will bind this trigger to your user account. With this the trigger will have access to all your projects as if it was you. Are you sure?" - revoke_trigger_confirmation = "By revoking a trigger you will break any processes making use of it. Are you sure?" - - if trigger.owner != current_user && can?(current_user, :manage_trigger, trigger) - = link_to 'Take ownership', take_ownership_project_trigger_path(@project, trigger), data: { confirm: take_ownership_confirmation }, method: :post, class: "btn btn-default btn-sm btn-trigger-take-ownership" - if can?(current_user, :admin_trigger, trigger) = link_to edit_project_trigger_path(@project, trigger), method: :get, title: "Edit", class: "btn btn-default btn-sm" do %i.fa.fa-pencil diff --git a/changelogs/unreleased/security-remove-take-trigger-ownership-feature.yml b/changelogs/unreleased/security-remove-take-trigger-ownership-feature.yml new file mode 100644 index 00000000000..201f66e1f18 --- /dev/null +++ b/changelogs/unreleased/security-remove-take-trigger-ownership-feature.yml @@ -0,0 +1,5 @@ +--- +title: Drop feature to take ownership of trigger token. +merge_request: +author: +type: security diff --git a/config/routes/project.rb b/config/routes/project.rb index c202463dadb..1f632765317 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -339,11 +339,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resource :variables, only: [:show, :update] - resources :triggers, only: [:index, :create, :edit, :update, :destroy] do - member do - post :take_ownership - end - end + resources :triggers, only: [:index, :create, :edit, :update, :destroy] resource :mirror, only: [:show, :update] do member do diff --git a/doc/api/pipeline_triggers.md b/doc/api/pipeline_triggers.md index 736312df116..e207ff8e98a 100644 --- a/doc/api/pipeline_triggers.md +++ b/doc/api/pipeline_triggers.md @@ -120,35 +120,6 @@ curl --request PUT --header "PRIVATE-TOKEN: " --form descript } ``` -## Take ownership of a project trigger - -Update an owner of a project trigger. - -``` -POST /projects/:id/triggers/:trigger_id/take_ownership -``` - -| Attribute | Type | required | Description | -|---------------|---------|----------|--------------------------| -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | -| `trigger_id` | integer | yes | The trigger id | - -``` -curl --request POST --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/1/triggers/10/take_ownership" -``` - -```json -{ - "id": 10, - "description": "my trigger", - "created_at": "2016-01-07T09:53:58.235Z", - "last_used": null, - "token": "6d056f63e50fe6f8c5f8f4aa10edb7", - "updated_at": "2016-01-07T09:53:58.235Z", - "owner": null -} -``` - ## Remove a project trigger Remove a project's build trigger. diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md index d1f9aa03b6b..2a382f18038 100644 --- a/doc/ci/triggers/README.md +++ b/doc/ci/triggers/README.md @@ -97,17 +97,6 @@ overview of the time the triggers were last used. ![Triggers page overview](img/triggers_page.png) -## Taking ownership of a trigger - -> **Note**: -GitLab 9.0 introduced a trigger ownership to solve permission problems. - -Each created trigger when run will impersonate their associated user including -their access to projects and their project permissions. - -You can take ownership of existing triggers by clicking *Take ownership*. -From now on the trigger will be run as you. - ## Revoking a trigger You can revoke a trigger any time by going at your project's @@ -282,8 +271,7 @@ Old triggers, created before GitLab 9.0 will be marked as legacy. Triggers with the legacy label do not have an associated user and only have access to the current project. They are considered deprecated and will be -removed with one of the future versions of GitLab. You are advised to -[take ownership](#taking-ownership-of-a-trigger) of any legacy triggers. +removed with one of the future versions of GitLab. [ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017 [ee-2346]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2346 diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index d35a8568394..5d7d9991091 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -92,8 +92,7 @@ to steal the tokens of other jobs. Since 9.0 [pipeline triggers][triggers] do support the new permission model. The new triggers do impersonate their associated user including their access -to projects and their project permissions. To migrate trigger to use new permission -model use **Take ownership**. +to projects and their project permissions. ## Before GitLab 8.12 diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 0e829c5699b..eeecc390256 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -112,27 +112,6 @@ module API end end - desc 'Take ownership of trigger' do - success Entities::Trigger - end - params do - requires :trigger_id, type: Integer, desc: 'The trigger ID' - end - post ':id/triggers/:trigger_id/take_ownership' do - authenticate! - authorize! :admin_build, user_project - - trigger = user_project.triggers.find(params.delete(:trigger_id)) - break not_found!('Trigger') unless trigger - - if trigger.update(owner: current_user) - status :ok - present trigger, with: Entities::Trigger, current_user: current_user - else - render_validation_error!(trigger) - end - end - desc 'Delete a trigger' do success Entities::Trigger end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9b6e8d8c8a4..47dca877c2f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -11377,9 +11377,6 @@ msgstr "" msgid "Trigger was created successfully." msgstr "" -msgid "Trigger was re-assigned." -msgstr "" - msgid "Trigger was successfully updated." msgstr "" @@ -12321,9 +12318,6 @@ msgstr "" msgid "You could not create a new trigger." msgstr "" -msgid "You could not take ownership of trigger." -msgstr "" - msgid "You do not have any subscriptions yet" msgstr "" diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 919859c145a..41b640bb53a 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -81,29 +81,6 @@ describe 'Triggers', :js do end end - describe 'trigger "Take ownership" workflow' do - before do - create(:ci_trigger, owner: user2, project: @project, description: trigger_title) - visit project_settings_ci_cd_path(@project) - end - - it 'button "Take ownership" has correct alert' do - expected_alert = 'By taking ownership you will bind this trigger to your user account. With this the trigger will have access to all your projects as if it was you. Are you sure?' - expect(page.find('a.btn-trigger-take-ownership')['data-confirm']).to eq expected_alert - end - - it 'take trigger ownership' do - # See if "Take ownership" on trigger works post trigger creation - page.accept_confirm do - first(:link, "Take ownership").send_keys(:return) - end - - expect(page.find('.flash-notice')).to have_content 'Trigger was re-assigned.' - expect(page.find('.triggers-list')).to have_content trigger_title - expect(page.find('.triggers-list .trigger-owner')).to have_content user.name - end - end - describe 'trigger "Revoke" workflow' do before do create(:ci_trigger, owner: user2, project: @project, description: trigger_title) diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index f0f01e97f1d..8ea3d16a41f 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -270,34 +270,6 @@ describe API::Triggers do end end - describe 'POST /projects/:id/triggers/:trigger_id/take_ownership' do - context 'authenticated user with valid permissions' do - it 'updates owner' do - post api("/projects/#{project.id}/triggers/#{trigger.id}/take_ownership", user) - - expect(response).to have_gitlab_http_status(200) - expect(json_response).to include('owner') - expect(trigger.reload.owner).to eq(user) - end - end - - context 'authenticated user with invalid permissions' do - it 'does not update owner' do - post api("/projects/#{project.id}/triggers/#{trigger.id}/take_ownership", user2) - - expect(response).to have_gitlab_http_status(403) - end - end - - context 'unauthenticated user' do - it 'does not update owner' do - post api("/projects/#{project.id}/triggers/#{trigger.id}/take_ownership") - - expect(response).to have_gitlab_http_status(401) - end - end - end - describe 'DELETE /projects/:id/triggers/:trigger_id' do context 'authenticated user with valid permissions' do it 'deletes trigger' do -- cgit v1.2.1 From 9b176c65159e4186f79eae2107af80e69132ba09 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Wed, 10 Jul 2019 12:27:12 -0700 Subject: Make Group#subgroup_creation_level return Owner if it is nil in DB --- app/models/group.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/group.rb b/app/models/group.rb index 3f80c1373f1..37f30552b39 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -417,7 +417,7 @@ class Group < Namespace end def subgroup_creation_level - super || ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS + super || ::Gitlab::Access::OWNER_SUBGROUP_ACCESS end private -- cgit v1.2.1 From 1c42f748f8cac88692d3dca5470f74331e48b8d1 Mon Sep 17 00:00:00 2001 From: Fabio Pitino Date: Fri, 21 Jun 2019 17:56:03 +0100 Subject: Don't display badges when builds are restricted Badges were leaked to unauthorized users even when Public Builds project setting is disabled. Added guard clause to the controller to check if user can read build. --- app/controllers/projects/badges_controller.rb | 3 +- ...ity-fix-badges-leaked-to-unauthorized-users.yml | 5 + .../controllers/projects/badges_controller_spec.rb | 124 ++++++++++++++++----- 3 files changed, 101 insertions(+), 31 deletions(-) create mode 100644 changelogs/unreleased/security-fix-badges-leaked-to-unauthorized-users.yml diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb index 09a384e89ab..66b51b17790 100644 --- a/app/controllers/projects/badges_controller.rb +++ b/app/controllers/projects/badges_controller.rb @@ -3,7 +3,8 @@ class Projects::BadgesController < Projects::ApplicationController layout 'project_settings' before_action :authorize_admin_project!, only: [:index] - before_action :no_cache_headers, except: [:index] + before_action :no_cache_headers, only: [:pipeline, :coverage] + before_action :authorize_read_build!, only: [:pipeline, :coverage] def pipeline pipeline_status = Gitlab::Badge::Pipeline::Status diff --git a/changelogs/unreleased/security-fix-badges-leaked-to-unauthorized-users.yml b/changelogs/unreleased/security-fix-badges-leaked-to-unauthorized-users.yml new file mode 100644 index 00000000000..9526f3c559f --- /dev/null +++ b/changelogs/unreleased/security-fix-badges-leaked-to-unauthorized-users.yml @@ -0,0 +1,5 @@ +--- +title: Show badges if pipelines are public otherwise default to project permissions. +erge_request: +author: +type: security diff --git a/spec/controllers/projects/badges_controller_spec.rb b/spec/controllers/projects/badges_controller_spec.rb index 5ec8d8d41d7..4ae29ba7f54 100644 --- a/spec/controllers/projects/badges_controller_spec.rb +++ b/spec/controllers/projects/badges_controller_spec.rb @@ -7,51 +7,115 @@ describe Projects::BadgesController do let!(:pipeline) { create(:ci_empty_pipeline) } let(:user) { create(:user) } - before do - project.add_maintainer(user) - sign_in(user) - end + shared_examples 'a badge resource' do |badge_type| + context 'when pipelines are public' do + before do + project.update!(public_builds: true) + end + + context 'when project is public' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + end + + it "returns the #{badge_type} badge to unauthenticated users" do + get_badge(badge_type) + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when project is restricted' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + project.add_guest(user) + sign_in(user) + end + + it "returns the #{badge_type} badge to guest users" do + get_badge(badge_type) + + expect(response).to have_gitlab_http_status(:ok) + end + end + end - it 'requests the pipeline badge successfully' do - get_badge(:pipeline) + context 'format' do + before do + project.add_maintainer(user) + sign_in(user) + end - expect(response).to have_gitlab_http_status(:ok) - end + it 'renders the `flat` badge layout by default' do + get_badge(badge_type) - it 'requests the coverage badge successfully' do - get_badge(:coverage) + expect(response).to render_template('projects/badges/badge') + end - expect(response).to have_gitlab_http_status(:ok) - end + context 'when style param is set to `flat`' do + it 'renders the `flat` badge layout' do + get_badge(badge_type, 'flat') - it 'renders the `flat` badge layout by default' do - get_badge(:coverage) + expect(response).to render_template('projects/badges/badge') + end + end - expect(response).to render_template('projects/badges/badge') - end + context 'when style param is set to an invalid type' do + it 'renders the `flat` (default) badge layout' do + get_badge(badge_type, 'xxx') + + expect(response).to render_template('projects/badges/badge') + end + end - context 'when style param is set to `flat`' do - it 'renders the `flat` badge layout' do - get_badge(:coverage, 'flat') + context 'when style param is set to `flat-square`' do + it 'renders the `flat-square` badge layout' do + get_badge(badge_type, 'flat-square') - expect(response).to render_template('projects/badges/badge') + expect(response).to render_template('projects/badges/badge_flat-square') + end + end end - end - context 'when style param is set to an invalid type' do - it 'renders the `flat` (default) badge layout' do - get_badge(:coverage, 'xxx') + context 'when pipelines are not public' do + before do + project.update!(public_builds: false) + end - expect(response).to render_template('projects/badges/badge') + context 'when project is public' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + end + + it 'returns 404 to unauthenticated users' do + get_badge(badge_type) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when project is restricted to the user' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + project.add_guest(user) + sign_in(user) + end + + it 'defaults to project permissions' do + get_badge(:coverage) + + expect(response).to have_gitlab_http_status(:not_found) + end + end end end - context 'when style param is set to `flat-square`' do - it 'renders the `flat-square` badge layout' do - get_badge(:coverage, 'flat-square') + describe '#pipeline' do + it_behaves_like 'a badge resource', :pipeline + end - expect(response).to render_template('projects/badges/badge_flat-square') - end + describe '#coverage' do + it_behaves_like 'a badge resource', :coverage end def get_badge(badge, style = nil) -- cgit v1.2.1 From 4f72293aaf36de46a0bd1615e7a4b2da92414be8 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Thu, 11 Jul 2019 14:15:34 +0100 Subject: Danger asks for throughput labels --- danger/metadata/Dangerfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/danger/metadata/Dangerfile b/danger/metadata/Dangerfile index 1adca152736..f2d68e64eb6 100644 --- a/danger/metadata/Dangerfile +++ b/danger/metadata/Dangerfile @@ -1,5 +1,13 @@ # rubocop:disable Style/SignalException +THROUGHPUT_LABELS = [ + 'Community contribution', + 'security', + 'bug', + 'feature', + 'backstage' +].freeze + if gitlab.mr_body.size < 5 fail "Please provide a proper merge request description." end @@ -8,6 +16,10 @@ if gitlab.mr_labels.empty? fail "Please add labels to this merge request." end +if (THROUGHPUT_LABELS & gitlab.mr_labels).empty? + warn 'Please add a [throughput label](https://about.gitlab.com/handbook/engineering/management/throughput/#implementation) to this merge request.' +end + unless gitlab.mr_json["assignee"] warn "This merge request does not have any assignee yet. Setting an assignee clarifies who needs to take action on the merge request at any given time." end -- cgit v1.2.1 From dfe906209e2238b82c84c9fb435498cae2f3d43e Mon Sep 17 00:00:00 2001 From: Adam Hegyi Date: Thu, 11 Jul 2019 06:48:50 +0200 Subject: Queries for Upload should be scoped by model --- app/controllers/concerns/uploads_actions.rb | 2 +- app/uploaders/records_uploads.rb | 2 +- .../unreleased/security-60551-fix-upload-scope.yml | 5 +++++ spec/controllers/groups/uploads_controller_spec.rb | 5 +++++ .../controllers/projects/uploads_controller_spec.rb | 5 +++++ .../controllers/uploads_actions_shared_examples.rb | 10 ++++++++++ spec/uploaders/records_uploads_spec.rb | 21 +++++++++++++++++++++ 7 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/security-60551-fix-upload-scope.yml diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index 59f6d3452a3..f5d35379e10 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -90,7 +90,7 @@ module UploadsActions return unless uploader = build_uploader upload_paths = uploader.upload_paths(params[:filename]) - upload = Upload.find_by(uploader: uploader_class.to_s, path: upload_paths) + upload = Upload.find_by(model: model, uploader: uploader_class.to_s, path: upload_paths) upload&.build_uploader end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/uploaders/records_uploads.rb b/app/uploaders/records_uploads.rb index 00b51f92b12..d7a859ebd5f 100644 --- a/app/uploaders/records_uploads.rb +++ b/app/uploaders/records_uploads.rb @@ -35,7 +35,7 @@ module RecordsUploads end def readd_upload - uploads.where(path: upload_path).delete_all + uploads.where(model: model, path: upload_path).delete_all upload.delete if upload self.upload = build_upload.tap(&:save!) diff --git a/changelogs/unreleased/security-60551-fix-upload-scope.yml b/changelogs/unreleased/security-60551-fix-upload-scope.yml new file mode 100644 index 00000000000..7d7096833a7 --- /dev/null +++ b/changelogs/unreleased/security-60551-fix-upload-scope.yml @@ -0,0 +1,5 @@ +--- +title: Queries for Upload should be scoped by model +merge_request: +author: +type: security diff --git a/spec/controllers/groups/uploads_controller_spec.rb b/spec/controllers/groups/uploads_controller_spec.rb index 0f99a957581..60342bf8e3d 100644 --- a/spec/controllers/groups/uploads_controller_spec.rb +++ b/spec/controllers/groups/uploads_controller_spec.rb @@ -10,6 +10,11 @@ describe Groups::UploadsController do { group_id: model } end + let(:other_model) { create(:group, :public) } + let(:other_params) do + { group_id: other_model } + end + it_behaves_like 'handle uploads' do let(:uploader_class) { NamespaceFileUploader } end diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index 776c1270977..661ed9840b1 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -10,6 +10,11 @@ describe Projects::UploadsController do { namespace_id: model.namespace.to_param, project_id: model } end + let(:other_model) { create(:project, :public) } + let(:other_params) do + { namespace_id: other_model.namespace.to_param, project_id: other_model } + end + it_behaves_like 'handle uploads' context 'when the URL the old style, without /-/system' do diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index 59708173716..9036838e50a 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -74,6 +74,16 @@ shared_examples 'handle uploads' do UploadService.new(model, jpg, uploader_class).execute end + context 'when accessing a specific upload via different model' do + it 'responds with status 404' do + params.merge!(other_params) + + show_upload + + expect(response).to have_gitlab_http_status(404) + end + end + context "when the model is public" do before do model.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) diff --git a/spec/uploaders/records_uploads_spec.rb b/spec/uploaders/records_uploads_spec.rb index 42352f9b9f8..6134137d2b7 100644 --- a/spec/uploaders/records_uploads_spec.rb +++ b/spec/uploaders/records_uploads_spec.rb @@ -85,6 +85,27 @@ describe RecordsUploads do expect { existing.reload }.to raise_error(ActiveRecord::RecordNotFound) expect(Upload.count).to eq(1) end + + it 'does not affect other uploads with different model but the same path' do + project = create(:project) + other_project = create(:project) + + uploader = RecordsUploadsExampleUploader.new(other_project) + + upload_for_project = Upload.create!( + path: File.join('uploads', 'rails_sample.jpg'), + size: 512.kilobytes, + model: project, + uploader: uploader.class.to_s + ) + + uploader.store!(upload_fixture('rails_sample.jpg')) + + upload_for_project_fresh = Upload.find(upload_for_project.id) + + expect(upload_for_project).to eq(upload_for_project_fresh) + expect(Upload.count).to eq(2) + end end describe '#destroy_upload callback' do -- cgit v1.2.1 From ff0654b0b46df4d143c3ec6bb4fa92da34745e44 Mon Sep 17 00:00:00 2001 From: John Cai Date: Thu, 11 Jul 2019 09:53:38 -0700 Subject: Add unset_rugged rake task Adds an unset_rugged rake task that unsets all rugged feature flags. Also fixes the existing disable_rugged task to have it explicitly disable feature flags instead of just unsetting them. --- lib/tasks/gitlab/features.rake | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/tasks/gitlab/features.rake b/lib/tasks/gitlab/features.rake index d88bcca0819..9cf568c07fe 100644 --- a/lib/tasks/gitlab/features.rake +++ b/lib/tasks/gitlab/features.rake @@ -10,14 +10,22 @@ namespace :gitlab do set_rugged_feature_flags(false) puts 'All Rugged feature flags were disabled.' end + + task unset_rugged: :environment do + set_rugged_feature_flags(nil) + puts 'All Rugged feature flags were unset.' + end end def set_rugged_feature_flags(status) Gitlab::Git::RuggedImpl::Repository::FEATURE_FLAGS.each do |flag| - if status - Feature.enable(flag) - else + case status + when nil Feature.get(flag).remove + when true + Feature.enable(flag) + when false + Feature.disable(flag) end end end -- cgit v1.2.1 From 43830eca33b6be5d59685be5c2f3270ed81bf751 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 10 Jul 2019 17:04:02 -0300 Subject: Do not show moved issue ids for user not authorized Do not show moved issue id for users that cannot read issue --- app/serializers/issue_entity.rb | 7 ++++- .../unreleased/security-hide_moved_issue_id.yml | 5 ++++ spec/serializers/issue_entity_spec.rb | 33 ++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/security-hide_moved_issue_id.yml diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index 36e601f45c5..82139855760 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -16,9 +16,14 @@ class IssueEntity < IssuableEntity expose :discussion_locked expose :assignees, using: API::Entities::UserBasic expose :due_date - expose :moved_to_id expose :project_id + expose :moved_to_id do |issue| + if issue.moved_to_id.present? && can?(request.current_user, :read_issue, issue.moved_to) + issue.moved_to_id + end + end + expose :web_url do |issue| project_issue_path(issue.project, issue) end diff --git a/changelogs/unreleased/security-hide_moved_issue_id.yml b/changelogs/unreleased/security-hide_moved_issue_id.yml new file mode 100644 index 00000000000..24353d797c9 --- /dev/null +++ b/changelogs/unreleased/security-hide_moved_issue_id.yml @@ -0,0 +1,5 @@ +--- +title: Do not show moved issue id for users that cannot read issue +merge_request: +author: +type: security diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb index caa3e41402b..0e05b3c84f4 100644 --- a/spec/serializers/issue_entity_spec.rb +++ b/spec/serializers/issue_entity_spec.rb @@ -17,4 +17,37 @@ describe IssueEntity do it 'has time estimation attributes' do expect(subject).to include(:time_estimate, :total_time_spent, :human_time_estimate, :human_total_time_spent) end + + context 'when issue got moved' do + let(:public_project) { create(:project, :public) } + let(:member) { create(:user) } + let(:non_member) { create(:user) } + let(:issue) { create(:issue, project: public_project) } + + before do + project.add_developer(member) + public_project.add_developer(member) + Issues::MoveService.new(public_project, member).execute(issue, project) + end + + context 'when user cannot read target project' do + it 'does not return moved_to_id' do + request = double('request', current_user: non_member) + + response = described_class.new(issue, request: request).as_json + + expect(response[:moved_to_id]).to be_nil + end + end + + context 'when user can read target project' do + it 'returns moved moved_to_id' do + request = double('request', current_user: member) + + response = described_class.new(issue, request: request).as_json + + expect(response[:moved_to_id]).to eq(issue.moved_to_id) + end + end + end end -- cgit v1.2.1 From b9d43e27b294e0ac18421e4e0540ba2c5fd80d15 Mon Sep 17 00:00:00 2001 From: drew cimino Date: Tue, 25 Jun 2019 17:05:18 -0400 Subject: Added specs for Ci::Pipeline::Seed::Build - #included? when only: and except: both match --- spec/lib/gitlab/ci/pipeline/seed/build_spec.rb | 57 ++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index 7991e2f48b5..633000ec8fe 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -3,14 +3,9 @@ require 'spec_helper' describe Gitlab::Ci::Pipeline::Seed::Build do let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_empty_pipeline, project: project) } + let(:attributes) { { name: 'rspec', ref: 'master' } } - let(:attributes) do - { name: 'rspec', ref: 'master' } - end - - subject do - described_class.new(pipeline, attributes) - end + subject { described_class.new(pipeline, attributes) } describe '#attributes' do it 'returns hash attributes of a build' do @@ -76,7 +71,7 @@ describe Gitlab::Ci::Pipeline::Seed::Build do end end - describe 'applying only/except policies' do + describe 'applying job inclusion policies' do context 'when no branch policy is specified' do let(:attributes) { { name: 'rspec' } } @@ -95,6 +90,12 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.to be_included } end + + context 'with both only and except policies' do + let(:attributes) { { name: 'rspec', only: { refs: %w(deploy) }, except: { refs: %(deploy) } } } + + it { is_expected.not_to be_included } + end end context 'when branch regexp policy does not match' do @@ -109,6 +110,12 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.to be_included } end + + context 'with both only and except policies' do + let(:attributes) { { name: 'rspec', only: { refs: %w(/^deploy$/) }, except: { refs: %w(/^deploy$/) } } } + + it { is_expected.not_to be_included } + end end context 'when branch policy matches' do @@ -123,6 +130,12 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.not_to be_included } end + + context 'when using both only and except policies' do + let(:attributes) { { name: 'rspec', only: { refs: %w(deploy master) }, except: { refs: %w(deploy master) } } } + + it { is_expected.not_to be_included } + end end context 'when keyword policy matches' do @@ -137,6 +150,12 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.not_to be_included } end + + context 'when using both only and except policies' do + let(:attributes) { { name: 'rspec', only: { refs: %w(branches) }, except: { refs: %(branches) } } } + + it { is_expected.not_to be_included } + end end context 'when keyword policy does not match' do @@ -151,6 +170,12 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.to be_included } end + + context 'when using both only and except policies' do + let(:attributes) { { name: 'rspec', only: { refs: %(tags) }, except: { refs: %(tags) } } } + + it { is_expected.not_to be_included } + end end context 'with source-keyword policy' do @@ -239,6 +264,14 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.not_to be_included } end + + context 'when using both only and except policies' do + let(:attributes) do + { name: 'rspec', only: { refs: ["branches@#{pipeline.project_full_path}"] }, except: { refs: ["branches@#{pipeline.project_full_path}"] } } + end + + it { is_expected.not_to be_included } + end end context 'when repository path does not matches' do @@ -257,6 +290,14 @@ describe Gitlab::Ci::Pipeline::Seed::Build do it { is_expected.to be_included } end + + context 'when using both only and except policies' do + let(:attributes) do + { name: 'rspec', only: { refs: ['branches@fork'] }, except: { refs: ['branches@fork'] } } + end + + it { is_expected.not_to be_included } + end end end end -- cgit v1.2.1 From 24941101483421f2661c040be0fb7612df40d375 Mon Sep 17 00:00:00 2001 From: drew cimino Date: Tue, 25 Jun 2019 18:46:28 -0400 Subject: Polish for Ci::Pipeline::Seed::Build specs --- spec/lib/gitlab/ci/pipeline/seed/build_spec.rb | 205 +++++++++++++++++-------- 1 file changed, 143 insertions(+), 62 deletions(-) diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index 633000ec8fe..e0bff638233 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Gitlab::Ci::Pipeline::Seed::Build do @@ -5,23 +7,24 @@ describe Gitlab::Ci::Pipeline::Seed::Build do let(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:attributes) { { name: 'rspec', ref: 'master' } } - subject { described_class.new(pipeline, attributes) } + let(:seed_build) { described_class.new(pipeline, attributes) } describe '#attributes' do - it 'returns hash attributes of a build' do - expect(subject.attributes).to be_a Hash - expect(subject.attributes) - .to include(:name, :project, :ref) - end + subject { seed_build.attributes } + + it { is_expected.to be_a(Hash) } + it { is_expected.to include(:name, :project, :ref) } end describe '#bridge?' do + subject { seed_build.bridge? } + context 'when job is a bridge' do let(:attributes) do { name: 'rspec', ref: 'master', options: { trigger: 'my/project' } } end - it { is_expected.to be_bridge } + it { is_expected.to be_truthy } end context 'when trigger definition is empty' do @@ -29,20 +32,20 @@ describe Gitlab::Ci::Pipeline::Seed::Build do { name: 'rspec', ref: 'master', options: { trigger: '' } } end - it { is_expected.not_to be_bridge } + it { is_expected.to be_falsey } end context 'when job is not a bridge' do - it { is_expected.not_to be_bridge } + it { is_expected.to be_falsey } end end describe '#to_resource' do + subject { seed_build.to_resource } + context 'when job is not a bridge' do - it 'returns a valid build resource' do - expect(subject.to_resource).to be_a(::Ci::Build) - expect(subject.to_resource).to be_valid - end + it { is_expected.to be_a(::Ci::Build) } + it { is_expected.to be_valid } end context 'when job is a bridge' do @@ -50,49 +53,57 @@ describe Gitlab::Ci::Pipeline::Seed::Build do { name: 'rspec', ref: 'master', options: { trigger: 'my/project' } } end - it 'returns a valid bridge resource' do - expect(subject.to_resource).to be_a(::Ci::Bridge) - expect(subject.to_resource).to be_valid - end + it { is_expected.to be_a(::Ci::Bridge) } + it { is_expected.to be_valid } end it 'memoizes a resource object' do - build = subject.to_resource - - expect(build.object_id).to eq subject.to_resource.object_id + expect(subject.object_id).to eq seed_build.to_resource.object_id end it 'can not be persisted without explicit assignment' do - build = subject.to_resource - pipeline.save! - expect(build).not_to be_persisted + expect(subject).not_to be_persisted end end describe 'applying job inclusion policies' do + subject { seed_build } + context 'when no branch policy is specified' do - let(:attributes) { { name: 'rspec' } } + let(:attributes) do + { name: 'rspec' } + end it { is_expected.to be_included } end context 'when branch policy does not match' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: ['deploy'] } } } + let(:attributes) do + { name: 'rspec', only: { refs: ['deploy'] } } + end it { is_expected.not_to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: ['deploy'] } } } + let(:attributes) do + { name: 'rspec', except: { refs: ['deploy'] } } + end it { is_expected.to be_included } end context 'with both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: %w(deploy) }, except: { refs: %(deploy) } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[deploy] }, + except: { refs: %w[deploy] } + } + end it { is_expected.not_to be_included } end @@ -100,19 +111,29 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when branch regexp policy does not match' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: ['/^deploy$/'] } } } + let(:attributes) do + { name: 'rspec', only: { refs: %w[/^deploy$/] } } + end it { is_expected.not_to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: ['/^deploy$/'] } } } + let(:attributes) do + { name: 'rspec', except: { refs: %w[/^deploy$/] } } + end it { is_expected.to be_included } end context 'with both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: %w(/^deploy$/) }, except: { refs: %w(/^deploy$/) } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[/^deploy$/] }, + except: { refs: %w[/^deploy$/] } + } + end it { is_expected.not_to be_included } end @@ -120,19 +141,29 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when branch policy matches' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: %w[deploy master] } } } + let(:attributes) do + { name: 'rspec', only: { refs: %w[deploy master] } } + end it { is_expected.to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: %w[deploy master] } } } + let(:attributes) do + { name: 'rspec', except: { refs: %w[deploy master] } } + end it { is_expected.not_to be_included } end context 'when using both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: %w(deploy master) }, except: { refs: %w(deploy master) } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[deploy master] }, + except: { refs: %w[deploy master] } + } + end it { is_expected.not_to be_included } end @@ -140,19 +171,29 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when keyword policy matches' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: ['branches'] } } } + let(:attributes) do + { name: 'rspec', only: { refs: %w[branches] } } + end it { is_expected.to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: ['branches'] } } } + let(:attributes) do + { name: 'rspec', except: { refs: %w[branches] } } + end it { is_expected.not_to be_included } end context 'when using both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: %w(branches) }, except: { refs: %(branches) } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[branches] }, + except: { refs: %w[branches] } + } + end it { is_expected.not_to be_included } end @@ -160,19 +201,29 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when keyword policy does not match' do context 'when using only' do - let(:attributes) { { name: 'rspec', only: { refs: ['tags'] } } } + let(:attributes) do + { name: 'rspec', only: { refs: %w[tags] } } + end it { is_expected.not_to be_included } end context 'when using except' do - let(:attributes) { { name: 'rspec', except: { refs: ['tags'] } } } + let(:attributes) do + { name: 'rspec', except: { refs: %w[tags] } } + end it { is_expected.to be_included } end context 'when using both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: %(tags) }, except: { refs: %(tags) } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: %w[tags] }, + except: { refs: %w[tags] } + } + end it { is_expected.not_to be_included } end @@ -181,35 +232,47 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'with source-keyword policy' do using RSpec::Parameterized - let(:pipeline) { build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source) } + let(:pipeline) do + create(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source) + end context 'matches' do where(:keyword, :source) do [ - %w(pushes push), - %w(web web), - %w(triggers trigger), - %w(schedules schedule), - %w(api api), - %w(external external) + %w[pushes push], + %w[web web], + %w[triggers trigger], + %w[schedules schedule], + %w[api api], + %w[external external] ] end with_them do context 'using an only policy' do - let(:attributes) { { name: 'rspec', only: { refs: [keyword] } } } + let(:attributes) do + { name: 'rspec', only: { refs: [keyword] } } + end it { is_expected.to be_included } end context 'using an except policy' do - let(:attributes) { { name: 'rspec', except: { refs: [keyword] } } } + let(:attributes) do + { name: 'rspec', except: { refs: [keyword] } } + end it { is_expected.not_to be_included } end context 'using both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: [keyword] }, except: { refs: [keyword] } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: [keyword] }, + except: { refs: [keyword] } + } + end it { is_expected.not_to be_included } end @@ -218,29 +281,39 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'non-matches' do where(:keyword, :source) do - %w(web trigger schedule api external).map { |source| ['pushes', source] } + - %w(push trigger schedule api external).map { |source| ['web', source] } + - %w(push web schedule api external).map { |source| ['triggers', source] } + - %w(push web trigger api external).map { |source| ['schedules', source] } + - %w(push web trigger schedule external).map { |source| ['api', source] } + - %w(push web trigger schedule api).map { |source| ['external', source] } + %w[web trigger schedule api external].map { |source| ['pushes', source] } + + %w[push trigger schedule api external].map { |source| ['web', source] } + + %w[push web schedule api external].map { |source| ['triggers', source] } + + %w[push web trigger api external].map { |source| ['schedules', source] } + + %w[push web trigger schedule external].map { |source| ['api', source] } + + %w[push web trigger schedule api].map { |source| ['external', source] } end with_them do context 'using an only policy' do - let(:attributes) { { name: 'rspec', only: { refs: [keyword] } } } + let(:attributes) do + { name: 'rspec', only: { refs: [keyword] } } + end it { is_expected.not_to be_included } end context 'using an except policy' do - let(:attributes) { { name: 'rspec', except: { refs: [keyword] } } } + let(:attributes) do + { name: 'rspec', except: { refs: [keyword] } } + end it { is_expected.to be_included } end context 'using both only and except policies' do - let(:attributes) { { name: 'rspec', only: { refs: [keyword] }, except: { refs: [keyword] } } } + let(:attributes) do + { + name: 'rspec', + only: { refs: [keyword] }, + except: { refs: [keyword] } + } + end it { is_expected.not_to be_included } end @@ -267,7 +340,11 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when using both only and except policies' do let(:attributes) do - { name: 'rspec', only: { refs: ["branches@#{pipeline.project_full_path}"] }, except: { refs: ["branches@#{pipeline.project_full_path}"] } } + { + name: 'rspec', + only: { refs: ["branches@#{pipeline.project_full_path}"] }, + except: { refs: ["branches@#{pipeline.project_full_path}"] } + } end it { is_expected.not_to be_included } @@ -277,7 +354,7 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when repository path does not matches' do context 'when using only' do let(:attributes) do - { name: 'rspec', only: { refs: ['branches@fork'] } } + { name: 'rspec', only: { refs: %w[branches@fork] } } end it { is_expected.not_to be_included } @@ -285,7 +362,7 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when using except' do let(:attributes) do - { name: 'rspec', except: { refs: ['branches@fork'] } } + { name: 'rspec', except: { refs: %w[branches@fork] } } end it { is_expected.to be_included } @@ -293,7 +370,11 @@ describe Gitlab::Ci::Pipeline::Seed::Build do context 'when using both only and except policies' do let(:attributes) do - { name: 'rspec', only: { refs: ['branches@fork'] }, except: { refs: ['branches@fork'] } } + { + name: 'rspec', + only: { refs: %w[branches@fork] }, + except: { refs: %w[branches@fork] } + } end it { is_expected.not_to be_included } -- cgit v1.2.1 From 20ad0cb330ab972fd840c4e188d29f6caaa0e43e Mon Sep 17 00:00:00 2001 From: Matej Latin Date: Thu, 27 Jun 2019 11:23:08 +0000 Subject: Removes the warning style from the paragraph In U2F device message, removes warning style --- app/views/u2f/_register.html.haml | 2 +- .../unreleased/61776-fixing-the-U2F-warning-message-text-colour.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/61776-fixing-the-U2F-warning-message-text-colour.yml diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index f6724f72307..ef3835332a7 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -16,7 +16,7 @@ .col-md-4 %button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true }= _("Set up new U2F device") .col-md-8 - %p.text-warning= _("You need to register a two-factor authentication app before you can set up a U2F device.") + %p= _("You need to register a two-factor authentication app before you can set up a U2F device.") %script#js-register-u2f-in-progress{ type: "text/template" } %p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.") diff --git a/changelogs/unreleased/61776-fixing-the-U2F-warning-message-text-colour.yml b/changelogs/unreleased/61776-fixing-the-U2F-warning-message-text-colour.yml new file mode 100644 index 00000000000..988eb77db12 --- /dev/null +++ b/changelogs/unreleased/61776-fixing-the-U2F-warning-message-text-colour.yml @@ -0,0 +1,5 @@ +--- +title: Remove the warning style from the U2F device message in user settings > account +merge_request: 30119 +author: matejlatin +type: other -- cgit v1.2.1 From a8a7d70558d02e86eab3767dff5ba0ad59225303 Mon Sep 17 00:00:00 2001 From: Fabio Papa Date: Fri, 12 Jul 2019 10:48:38 -0700 Subject: Add a link from the permissions table to the subgroups/creating a subgroup section --- doc/user/permissions.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index d496bc39f9a..e9b61b9c995 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -209,14 +209,15 @@ group. | Create project in group | | | ✓ | ✓ | ✓ | | Create/edit/delete group milestones | | | ✓ | ✓ | ✓ | | Enable/disable a dependency proxy **[PREMIUM]** | | | ✓ | ✓ | ✓ | -| Create subgroup | | | | ✓ (1) | ✓ | +| Create subgroup | | | | ✓ (1) | ✓ | | Edit group | | | | | ✓ | | Manage group members | | | | | ✓ | | Remove group | | | | | ✓ | | Delete group epic **(ULTIMATE)** | | | | | ✓ | | View group Audit Events | | | | | ✓ | -- (1): Groups can be set to allow either Owners or Owners and Maintainers to create subgroups +- (1): Groups can be set to [allow either Owners or Owners and + Maintainers to create subgroups](group/subgroups/index.md#creating-a-subgroup) ### Subgroup permissions -- cgit v1.2.1 From 3d171dc27163274b181fc0481065c681abae3186 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 15 Jul 2019 09:26:18 +0300 Subject: Remove old ignore_column in AR models Next models are affected: * Project * Namespace * Issue * Merge request * CI Trigger * CI Pipeline schedule Signed-off-by: Dmitriy Zaporozhets --- app/models/ci/pipeline_schedule.rb | 3 --- app/models/ci/trigger.rb | 3 --- app/models/issue.rb | 3 --- app/models/merge_request.rb | 5 ----- app/models/namespace.rb | 3 --- app/models/project.rb | 3 --- 6 files changed, 20 deletions(-) diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb index ba8cea0cea9..42d4e86fe8d 100644 --- a/app/models/ci/pipeline_schedule.rb +++ b/app/models/ci/pipeline_schedule.rb @@ -4,11 +4,8 @@ module Ci class PipelineSchedule < ApplicationRecord extend Gitlab::Ci::Model include Importable - include IgnorableColumn include StripAttribute - ignore_column :deleted_at - belongs_to :project belongs_to :owner, class_name: 'User' has_one :last_pipeline, -> { order(id: :desc) }, class_name: 'Ci::Pipeline' diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 8927bb9bc18..c7bfb9900c4 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -3,11 +3,8 @@ module Ci class Trigger < ApplicationRecord extend Gitlab::Ci::Model - include IgnorableColumn include Presentable - ignore_column :deleted_at - belongs_to :project belongs_to :owner, class_name: "User" diff --git a/app/models/issue.rb b/app/models/issue.rb index 982a94315bd..12d30389910 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -13,11 +13,8 @@ class Issue < ApplicationRecord include RelativePositioning include TimeTrackable include ThrottledTouch - include IgnorableColumn include LabelEventable - ignore_column :assignee_id, :branch_name, :deleted_at - DueDateStruct = Struct.new(:title, :name).freeze NoDueDate = DueDateStruct.new('No Due Date', '0').freeze AnyDueDate = DueDateStruct.new('Any Due Date', '').freeze diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 53977748c30..34dca665852 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -7,7 +7,6 @@ class MergeRequest < ApplicationRecord include Noteable include Referable include Presentable - include IgnorableColumn include TimeTrackable include ManualInverseAssociation include EachBatch @@ -24,10 +23,6 @@ class MergeRequest < ApplicationRecord SORTING_PREFERENCE_FIELD = :merge_requests_sort - ignore_column :locked_at, - :ref_fetched, - :deleted_at - belongs_to :target_project, class_name: "Project" belongs_to :source_project, class_name: "Project" belongs_to :merge_user, class_name: "User" diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 1d95590bac9..3ed4bf8e64a 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -8,13 +8,10 @@ class Namespace < ApplicationRecord include AfterCommitQueue include Storage::LegacyNamespace include Gitlab::SQL::Pattern - include IgnorableColumn include FeatureGate include FromUnion include Gitlab::Utils::StrongMemoize - ignore_column :deleted_at - # Prevent users from creating unreasonably deep level of nesting. # The number 20 was taken based on maximum nesting level of # Android repo (15) + some extra backup. diff --git a/app/models/project.rb b/app/models/project.rb index f6f7d373f91..2906aca75fc 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -31,7 +31,6 @@ class Project < ApplicationRecord include FeatureGate include OptionallySearch include FromUnion - include IgnorableColumn extend Gitlab::Cache::RequestCache extend Gitlab::ConfigHelper @@ -56,8 +55,6 @@ class Project < ApplicationRecord VALID_MIRROR_PORTS = [22, 80, 443].freeze VALID_MIRROR_PROTOCOLS = %w(http https ssh git).freeze - ignore_column :import_status, :import_jid, :import_error - cache_markdown_field :description, pipeline: :description delegate :feature_available?, :builds_enabled?, :wiki_enabled?, -- cgit v1.2.1 From f5c1cd489834e824c83f2ae909cd0dd41fb95dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Tue, 2 Jul 2019 18:38:23 +0200 Subject: Fix Server Side Request Forgery mitigation bypass When we can't resolve the hostname or it is invalid, we shouldn't even perform the request. This fix also fixes the problem the SSRF rebinding attack. We can't stub feature flags outside example blocks. Nevertheless, there are some actions that calls the UrlBlocker, that are performed outside example blocks, ie: `set` instruction. That's why we have to use some signalign mechanism outside the scope of the specs. --- changelogs/unreleased/security-dns-ssrf-bypass.yml | 5 +++ lib/gitlab/url_blocker.rb | 13 ++++++- spec/lib/gitlab/url_blocker_spec.rb | 44 ++++++++++++++++------ spec/spec_helper.rb | 1 + 4 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 changelogs/unreleased/security-dns-ssrf-bypass.yml diff --git a/changelogs/unreleased/security-dns-ssrf-bypass.yml b/changelogs/unreleased/security-dns-ssrf-bypass.yml new file mode 100644 index 00000000000..e48696ce5bd --- /dev/null +++ b/changelogs/unreleased/security-dns-ssrf-bypass.yml @@ -0,0 +1,5 @@ +--- +title: Fix Server Side Request Forgery mitigation bypass +merge_request: +author: +type: security diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb index f6b2e2acf16..cc937cbb3cf 100644 --- a/lib/gitlab/url_blocker.rb +++ b/lib/gitlab/url_blocker.rb @@ -85,9 +85,9 @@ module Gitlab # we'll be making the request to the IP address, instead of using the hostname. def enforce_uri_hostname(addrs_info, uri, hostname, dns_rebind_protection) address = addrs_info.first - ip_address = address&.ip_address + ip_address = address.ip_address - return [uri, nil] unless dns_rebind_protection && ip_address && ip_address != hostname + return [uri, nil] unless dns_rebind_protection && ip_address != hostname uri = uri.dup uri.hostname = ip_address @@ -111,6 +111,15 @@ module Gitlab addr.ipv6_v4mapped? ? addr.ipv6_to_ipv4 : addr end rescue SocketError + # In the test suite we use a lot of mocked urls that are either invalid or + # don't exist. In order to avoid modifying a ton of tests and factories + # we allow invalid urls unless the environment variable RSPEC_ALLOW_INVALID_URLS + # is not true + return if Rails.env.test? && ENV['RSPEC_ALLOW_INVALID_URLS'] == 'true' + + # If the addr can't be resolved or the url is invalid (i.e http://1.1.1.1.1) + # we block the url + raise BlockedUrlError, "Host cannot be resolved or invalid" end def validate_local_request(address_info:, allow_localhost:, allow_local_network:) diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index f8b0cbfb6f6..0fab890978a 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -68,6 +68,16 @@ describe Gitlab::UrlBlocker do expect(uri).to eq(Addressable::URI.parse('https://example.org')) expect(hostname).to eq(nil) end + + context 'when it cannot be resolved' do + let(:import_url) { 'http://foobar.x' } + + it 'raises error' do + stub_env('RSPEC_ALLOW_INVALID_URLS', 'false') + + expect { described_class.validate!(import_url) }.to raise_error(described_class::BlockedUrlError) + end + end end context 'when the URL hostname is an IP address' do @@ -79,6 +89,16 @@ describe Gitlab::UrlBlocker do expect(uri).to eq(Addressable::URI.parse('https://93.184.216.34')) expect(hostname).to be(nil) end + + context 'when it is invalid' do + let(:import_url) { 'http://1.1.1.1.1' } + + it 'raises an error' do + stub_env('RSPEC_ALLOW_INVALID_URLS', 'false') + + expect { described_class.validate!(import_url) }.to raise_error(described_class::BlockedUrlError) + end + end end end end @@ -180,8 +200,6 @@ describe Gitlab::UrlBlocker do end it 'returns true for a non-alphanumeric hostname' do - stub_resolv - aggregate_failures do expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami/a') @@ -300,10 +318,6 @@ describe Gitlab::UrlBlocker do end context 'when enforce_user is' do - before do - stub_resolv - end - context 'false (default)' do it 'does not block urls with a non-alphanumeric username' do expect(described_class).not_to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a') @@ -351,6 +365,18 @@ describe Gitlab::UrlBlocker do expect(described_class.blocked_url?('https://git‌lab.com/foo/foo.bar', ascii_only: true)).to be true end end + + it 'blocks urls with invalid ip address' do + stub_env('RSPEC_ALLOW_INVALID_URLS', 'false') + + expect(described_class).to be_blocked_url('http://8.8.8.8.8') + end + + it 'blocks urls whose hostname cannot be resolved' do + stub_env('RSPEC_ALLOW_INVALID_URLS', 'false') + + expect(described_class).to be_blocked_url('http://foobar.x') + end end describe '#validate_hostname' do @@ -382,10 +408,4 @@ describe Gitlab::UrlBlocker do end end end - - # Resolv does not support resolving UTF-8 domain names - # See https://bugs.ruby-lang.org/issues/4270 - def stub_resolv - allow(Resolv).to receive(:getaddresses).and_return([]) - end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 95e0d8858b9..089dbc09aa3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,6 +3,7 @@ SimpleCovEnv.start! ENV["RAILS_ENV"] = 'test' ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true' +ENV["RSPEC_ALLOW_INVALID_URLS"] = 'true' require File.expand_path('../config/environment', __dir__) require 'rspec/rails' -- cgit v1.2.1 From 25cc54cacd4eb8f997dba2290e9d09818bd6a269 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 15 Jul 2019 14:55:59 +0200 Subject: Deploy serverless apps with `gitlabktl` --- lib/gitlab/ci/templates/Serverless.gitlab-ci.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml index a3db2705bf6..280e75d46f5 100644 --- a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml @@ -8,26 +8,23 @@ stages: - deploy .serverless:build:image: - stage: build image: registry.gitlab.com/gitlab-org/gitlabktl:latest + stage: build script: /usr/bin/gitlabktl app build .serverless:deploy:image: + image: registry.gitlab.com/gitlab-org/gitlabktl:latest stage: deploy - image: gcr.io/triggermesh/tm@sha256:3cfdd470a66b741004fb02354319d79f1598c70117ce79978d2e07e192bfb336 # v0.0.11 environment: development - script: - - echo "$CI_REGISTRY_IMAGE" - - tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait + script: /usr/bin/gitlabktl app deploy .serverless:build:functions: - stage: build - environment: development image: registry.gitlab.com/gitlab-org/gitlabktl:latest + stage: build script: /usr/bin/gitlabktl serverless build .serverless:deploy:functions: + image: registry.gitlab.com/gitlab-org/gitlabktl:latest stage: deploy environment: development - image: registry.gitlab.com/gitlab-org/gitlabktl:latest script: /usr/bin/gitlabktl serverless deploy -- cgit v1.2.1 From beaa63530669d10c7244d187fa386144bc5da7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C5=82gorzata=20Ksionek?= Date: Thu, 4 Jul 2019 16:42:31 +0200 Subject: Add class for group level analytics Add specs for group level Update entities Update base classes Add groups-centric changes Update plan and review stage Add summary classes Add summary spec Update specs files Add to specs test cases for group Add changelog entry Add group serializer Fix typo Fix typo Add fetching namespace in sql query Update specs Add rubocop fix Add rubocop fix Modify method to be in sync with code review Add counting deploys from subgroup To group summary stage Add subgroups handling In group stage summary Add additional spec Add additional specs Add more precise inheritance Add attr reader to group level Fix rubocop offence Fix problems with specs Add cr remarks Renaming median method and a lot of calls in specs Move spec setup Rename method in specs Add code review remarks regarding module Add proper module name --- app/models/cycle_analytics/base.rb | 27 ------- app/models/cycle_analytics/base_methods.rb | 27 +++++++ app/models/cycle_analytics/group_level.rb | 30 +++++++ app/models/cycle_analytics/project_level.rb | 5 +- app/serializers/analytics_issue_entity.rb | 6 +- app/serializers/analytics_merge_request_entity.rb | 2 +- app/serializers/analytics_stage_entity.rb | 4 +- app/serializers/group_analytics_stage_entity.rb | 16 ++++ .../group_analytics_stage_serializer.rb | 5 ++ .../adjust-cycle-analytics-to-group-level.yml | 5 ++ lib/gitlab/cycle_analytics/base_event_fetcher.rb | 21 +++-- lib/gitlab/cycle_analytics/base_query.rb | 14 +++- lib/gitlab/cycle_analytics/base_stage.rb | 24 ++++-- lib/gitlab/cycle_analytics/code_event_fetcher.rb | 6 +- lib/gitlab/cycle_analytics/group_stage_summary.rb | 24 ++++++ lib/gitlab/cycle_analytics/issue_event_fetcher.rb | 6 +- lib/gitlab/cycle_analytics/issue_helper.rb | 3 + lib/gitlab/cycle_analytics/metrics_tables.rb | 8 ++ lib/gitlab/cycle_analytics/permissions.rb | 2 +- lib/gitlab/cycle_analytics/plan_event_fetcher.rb | 6 +- lib/gitlab/cycle_analytics/plan_helper.rb | 13 ++- .../cycle_analytics/production_event_fetcher.rb | 6 +- lib/gitlab/cycle_analytics/review_event_fetcher.rb | 6 +- lib/gitlab/cycle_analytics/summary/group/base.rb | 24 ++++++ lib/gitlab/cycle_analytics/summary/group/deploy.rb | 29 +++++++ lib/gitlab/cycle_analytics/summary/group/issue.rb | 25 ++++++ .../cycle_analytics/base_event_fetcher_spec.rb | 6 +- spec/lib/gitlab/cycle_analytics/code_stage_spec.rb | 94 +++++++++++++++++++--- .../cycle_analytics/group_stage_summary_spec.rb | 65 +++++++++++++++ .../lib/gitlab/cycle_analytics/issue_stage_spec.rb | 75 +++++++++++++++-- spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb | 74 ++++++++++++++++- .../gitlab/cycle_analytics/review_stage_spec.rb | 61 +++++++++++--- .../gitlab/cycle_analytics/shared_event_spec.rb | 2 +- .../gitlab/cycle_analytics/shared_stage_spec.rb | 4 +- .../gitlab/cycle_analytics/staging_stage_spec.rb | 64 ++++++++++++--- spec/lib/gitlab/cycle_analytics/usage_data_spec.rb | 2 +- spec/models/cycle_analytics/code_spec.rb | 4 +- spec/models/cycle_analytics/group_level_spec.rb | 46 +++++++++++ spec/models/cycle_analytics/issue_spec.rb | 2 +- spec/models/cycle_analytics/plan_spec.rb | 2 +- spec/models/cycle_analytics/production_spec.rb | 4 +- spec/models/cycle_analytics/project_level_spec.rb | 2 +- spec/models/cycle_analytics/review_spec.rb | 2 +- spec/models/cycle_analytics/staging_spec.rb | 4 +- spec/models/cycle_analytics/test_spec.rb | 8 +- spec/serializers/analytics_issue_entity_spec.rb | 6 +- .../serializers/analytics_issue_serializer_spec.rb | 6 +- .../analytics_merge_request_serializer_spec.rb | 6 +- .../serializers/analytics_stage_serializer_spec.rb | 10 +-- .../cycle_analytics_helpers/test_generation.rb | 12 +-- 50 files changed, 768 insertions(+), 137 deletions(-) delete mode 100644 app/models/cycle_analytics/base.rb create mode 100644 app/models/cycle_analytics/base_methods.rb create mode 100644 app/models/cycle_analytics/group_level.rb create mode 100644 app/serializers/group_analytics_stage_entity.rb create mode 100644 app/serializers/group_analytics_stage_serializer.rb create mode 100644 changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml create mode 100644 lib/gitlab/cycle_analytics/group_stage_summary.rb create mode 100644 lib/gitlab/cycle_analytics/summary/group/base.rb create mode 100644 lib/gitlab/cycle_analytics/summary/group/deploy.rb create mode 100644 lib/gitlab/cycle_analytics/summary/group/issue.rb create mode 100644 spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb create mode 100644 spec/models/cycle_analytics/group_level_spec.rb diff --git a/app/models/cycle_analytics/base.rb b/app/models/cycle_analytics/base.rb deleted file mode 100644 index d7b28cd1b67..00000000000 --- a/app/models/cycle_analytics/base.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module CycleAnalytics - class Base - STAGES = %i[issue plan code test review staging production].freeze - - def all_medians_by_stage - STAGES.each_with_object({}) do |stage_name, medians_per_stage| - medians_per_stage[stage_name] = self[stage_name].median - end - end - - def stats - @stats ||= STAGES.map do |stage_name| - self[stage_name].as_json - end - end - - def no_stats? - stats.all? { |hash| hash[:value].nil? } - end - - def [](stage_name) - Gitlab::CycleAnalytics::Stage[stage_name].new(project: @project, options: @options) - end - end -end diff --git a/app/models/cycle_analytics/base_methods.rb b/app/models/cycle_analytics/base_methods.rb new file mode 100644 index 00000000000..1c2a0854769 --- /dev/null +++ b/app/models/cycle_analytics/base_methods.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module CycleAnalytics + module BaseMethods + STAGES = %i[issue plan code test review staging production].freeze + + def all_medians_by_stage + STAGES.each_with_object({}) do |stage_name, medians_per_stage| + medians_per_stage[stage_name] = self[stage_name].project_median + end + end + + def stats + @stats ||= STAGES.map do |stage_name| + self[stage_name].as_json + end + end + + def no_stats? + stats.all? { |hash| hash[:value].nil? } + end + + def [](stage_name) + Gitlab::CycleAnalytics::Stage[stage_name].new(options: options) + end + end +end diff --git a/app/models/cycle_analytics/group_level.rb b/app/models/cycle_analytics/group_level.rb new file mode 100644 index 00000000000..58bd2eb0d9a --- /dev/null +++ b/app/models/cycle_analytics/group_level.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module CycleAnalytics + class GroupLevel + include BaseMethods + attr_reader :options + + def initialize(options:) + @options = options + end + + def summary + @summary ||= ::Gitlab::CycleAnalytics::GroupStageSummary.new(options[:group], + from: options[:from], + current_user: options[:current_user]).data + end + + def permissions(user: nil) + STAGES.each_with_object({}) do |stage, obj| + obj[stage] = true + end + end + + def stats + @stats ||= STAGES.map do |stage_name| + self[stage_name].as_json(serializer: GroupAnalyticsStageSerializer) + end + end + end +end diff --git a/app/models/cycle_analytics/project_level.rb b/app/models/cycle_analytics/project_level.rb index 22631cc7d41..abe1be11eae 100644 --- a/app/models/cycle_analytics/project_level.rb +++ b/app/models/cycle_analytics/project_level.rb @@ -1,12 +1,13 @@ # frozen_string_literal: true module CycleAnalytics - class ProjectLevel < Base + class ProjectLevel + include BaseMethods attr_reader :project, :options def initialize(project, options:) @project = project - @options = options + @options = options.merge(project: project) end def summary diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb index ab15bd0ac7a..29d4a6ae1d0 100644 --- a/app/serializers/analytics_issue_entity.rb +++ b/app/serializers/analytics_issue_entity.rb @@ -20,12 +20,12 @@ class AnalyticsIssueEntity < Grape::Entity end expose :url do |object| - url_to(:namespace_project_issue, id: object[:iid].to_s) + url_to(:namespace_project_issue, object) end private - def url_to(route, id) - public_send("#{route}_url", request.project.namespace, request.project, id) # rubocop:disable GitlabSecurity/PublicSend + def url_to(route, object) + public_send("#{route}_url", object[:path], object[:name], object[:iid].to_s) # rubocop:disable GitlabSecurity/PublicSend end end diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb index b7134da9461..21d7eeb81b0 100644 --- a/app/serializers/analytics_merge_request_entity.rb +++ b/app/serializers/analytics_merge_request_entity.rb @@ -4,6 +4,6 @@ class AnalyticsMergeRequestEntity < AnalyticsIssueEntity expose :state expose :url do |object| - url_to(:namespace_project_merge_request, id: object[:iid].to_s) + url_to(:namespace_project_merge_request, object) end end diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb index 8bc6da5aeeb..eb38b90fb18 100644 --- a/app/serializers/analytics_stage_entity.rb +++ b/app/serializers/analytics_stage_entity.rb @@ -8,9 +8,9 @@ class AnalyticsStageEntity < Grape::Entity expose :legend expose :description - expose :median, as: :value do |stage| + expose :project_median, as: :value do |stage| # median returns a BatchLoader instance which we first have to unwrap by using to_f # we use to_f to make sure results below 1 are presented to the end-user - stage.median.to_f.nonzero? ? distance_of_time_in_words(stage.median) : nil + stage.project_median.to_f.nonzero? ? distance_of_time_in_words(stage.project_median) : nil end end diff --git a/app/serializers/group_analytics_stage_entity.rb b/app/serializers/group_analytics_stage_entity.rb new file mode 100644 index 00000000000..019a3086f68 --- /dev/null +++ b/app/serializers/group_analytics_stage_entity.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class GroupAnalyticsStageEntity < Grape::Entity + include EntityDateHelper + + expose :title + expose :name + expose :legend + expose :description + + expose :group_median, as: :value do |stage| + # median returns a BatchLoader instance which we first have to unwrap by using to_f + # we use to_f to make sure results below 1 are presented to the end-user + stage.group_median.to_f.nonzero? ? distance_of_time_in_words(stage.group_median) : nil + end +end diff --git a/app/serializers/group_analytics_stage_serializer.rb b/app/serializers/group_analytics_stage_serializer.rb new file mode 100644 index 00000000000..ec448dea602 --- /dev/null +++ b/app/serializers/group_analytics_stage_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class GroupAnalyticsStageSerializer < BaseSerializer + entity GroupAnalyticsStageEntity +end diff --git a/changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml b/changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml new file mode 100644 index 00000000000..49f558a6c9c --- /dev/null +++ b/changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml @@ -0,0 +1,5 @@ +--- +title: Adjust cycle analytics to group level +merge_request: 30391 +author: +type: added diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index 0cacef5b278..96aa799e864 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -5,12 +5,11 @@ module Gitlab class BaseEventFetcher include BaseQuery - attr_reader :projections, :query, :stage, :order, :project, :options + attr_reader :projections, :query, :stage, :order, :options MAX_EVENTS = 50 - def initialize(project: nil, stage:, options:) - @project = project + def initialize(stage:, options:) @stage = stage @options = options end @@ -68,11 +67,23 @@ module Gitlab end def allowed_ids_source - { project_id: project.id } + group ? { group_id: group.id, include_subgroups: true } : { project_id: project.id } + end + + def serialization_context + {} end def projects - [project] + group ? Project.inside_path(group.full_path) : [project] + end + + def group + @group ||= options.fetch(:group, nil) + end + + def project + @project ||= options.fetch(:project, nil) end end end diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb index 39fc1759cfc..9c98c0bfbf2 100644 --- a/lib/gitlab/cycle_analytics/base_query.rb +++ b/lib/gitlab/cycle_analytics/base_query.rb @@ -16,17 +16,25 @@ module Gitlab def stage_query(project_ids) query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id])) .join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])) + .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id])) + .join(routes_table).on(projects_table[:namespace_id].eq(routes_table[:source_id])) .project(issue_table[:project_id].as("project_id")) .where(issue_table[:project_id].in(project_ids)) + .where(routes_table[:source_type].eq('Namespace')) .where(issue_table[:created_at].gteq(options[:from])) # Load merge_requests - query = query.join(mr_table, Arel::Nodes::OuterJoin) + + query = load_merge_requests(query) + + query + end + + def load_merge_requests(query) + query.join(mr_table, Arel::Nodes::OuterJoin) .on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id])) .join(mr_metrics_table) .on(mr_table[:id].eq(mr_metrics_table[:merge_request_id])) - - query end end end diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 98b86e54340..678a891e941 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -5,10 +5,9 @@ module Gitlab class BaseStage include BaseQuery - attr_reader :project, :options + attr_reader :options - def initialize(project: nil, options:) - @project = project + def initialize(options:) @options = options end @@ -24,7 +23,7 @@ module Gitlab raise NotImplementedError.new("Expected #{self.name} to implement title") end - def median + def project_median return if project.nil? BatchLoader.for(project.id).batch(key: name) do |project_ids, loader| @@ -42,6 +41,10 @@ module Gitlab end end + def group_median + median_query(projects.map(&:id)) + end + def median_query(project_ids) # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). @@ -67,8 +70,7 @@ module Gitlab private def event_fetcher - @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: project, - stage: name, + @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(stage: name, options: event_options) end @@ -77,7 +79,15 @@ module Gitlab end def projects - [project] + group ? Project.inside_path(group.full_path) : [project] + end + + def group + @group ||= options.fetch(:group, nil) + end + + def project + @project ||= options.fetch(:project, nil) end end end diff --git a/lib/gitlab/cycle_analytics/code_event_fetcher.rb b/lib/gitlab/cycle_analytics/code_event_fetcher.rb index 9e7ca529579..1e4e9b9e02c 100644 --- a/lib/gitlab/cycle_analytics/code_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/code_event_fetcher.rb @@ -11,7 +11,9 @@ module Gitlab mr_table[:id], mr_table[:created_at], mr_table[:state], - mr_table[:author_id]] + mr_table[:author_id], + projects_table[:name], + routes_table[:path]] @order = mr_table[:created_at] super(*args) @@ -20,7 +22,7 @@ module Gitlab private def serialize(event) - AnalyticsMergeRequestSerializer.new(project: project).represent(event) + AnalyticsMergeRequestSerializer.new(serialization_context).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/group_stage_summary.rb b/lib/gitlab/cycle_analytics/group_stage_summary.rb new file mode 100644 index 00000000000..7b5c74e1a1b --- /dev/null +++ b/lib/gitlab/cycle_analytics/group_stage_summary.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Gitlab + module CycleAnalytics + class GroupStageSummary + def initialize(group, from:, current_user:) + @group = group + @from = from + @current_user = current_user + end + + def data + [serialize(Summary::Group::Issue.new(group: @group, from: @from, current_user: @current_user)), + serialize(Summary::Group::Deploy.new(group: @group, from: @from))] + end + + private + + def serialize(summary_object) + AnalyticsSummarySerializer.new.represent(summary_object) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb index bb3520ae920..2d03e425a6a 100644 --- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb @@ -10,7 +10,9 @@ module Gitlab issue_table[:iid], issue_table[:id], issue_table[:created_at], - issue_table[:author_id]] + issue_table[:author_id], + projects_table[:name], + routes_table[:path]] super(*args) end @@ -18,7 +20,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: project).represent(event) + AnalyticsIssueSerializer.new(serialization_context).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/issue_helper.rb b/lib/gitlab/cycle_analytics/issue_helper.rb index ac836b8bf0f..0fc4f1dd41a 100644 --- a/lib/gitlab/cycle_analytics/issue_helper.rb +++ b/lib/gitlab/cycle_analytics/issue_helper.rb @@ -5,8 +5,11 @@ module Gitlab module IssueHelper def stage_query(project_ids) query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])) + .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id])) + .join(routes_table).on(projects_table[:namespace_id].eq(routes_table[:source_id])) .project(issue_table[:project_id].as("project_id")) .where(issue_table[:project_id].in(project_ids)) + .where(routes_table[:source_type].eq('Namespace')) .where(issue_table[:created_at].gteq(options[:from])) .where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil))) diff --git a/lib/gitlab/cycle_analytics/metrics_tables.rb b/lib/gitlab/cycle_analytics/metrics_tables.rb index 3e0302d308d..015f7bfde24 100644 --- a/lib/gitlab/cycle_analytics/metrics_tables.rb +++ b/lib/gitlab/cycle_analytics/metrics_tables.rb @@ -35,6 +35,14 @@ module Gitlab User.arel_table end + def projects_table + Project.arel_table + end + + def routes_table + Route.arel_table + end + def build_table ::CommitStatus.arel_table end diff --git a/lib/gitlab/cycle_analytics/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb index 03ba98b4dfb..0da041f8950 100644 --- a/lib/gitlab/cycle_analytics/permissions.rb +++ b/lib/gitlab/cycle_analytics/permissions.rb @@ -23,7 +23,7 @@ module Gitlab end def get - ::CycleAnalytics::Base::STAGES.each do |stage| + ::CycleAnalytics::BaseMethods::STAGES.each do |stage| @stage_permission_hash[stage] = authorized_stage?(stage) end diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb index 49a6b099f34..77cc358daa9 100644 --- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb @@ -10,7 +10,9 @@ module Gitlab issue_table[:iid], issue_table[:id], issue_table[:created_at], - issue_table[:author_id]] + issue_table[:author_id], + projects_table[:name], + routes_table[:path]] super(*args) end @@ -18,7 +20,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: project).represent(event) + AnalyticsIssueSerializer.new(serialization_context).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/plan_helper.rb b/lib/gitlab/cycle_analytics/plan_helper.rb index ae578d45ad5..c3f742503a9 100644 --- a/lib/gitlab/cycle_analytics/plan_helper.rb +++ b/lib/gitlab/cycle_analytics/plan_helper.rb @@ -5,14 +5,21 @@ module Gitlab module PlanHelper def stage_query(project_ids) query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])) + .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id])) + .join(routes_table).on(projects_table[:namespace_id].eq(routes_table[:source_id])) .project(issue_table[:project_id].as("project_id")) .where(issue_table[:project_id].in(project_ids)) - .where(issue_table[:created_at].gteq(options[:from])) - .where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil))) - .where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil)) + .where(routes_table[:source_type].eq('Namespace')) + query = add_conditions_to_query(query) query end + + def add_conditions_to_query(query) + query.where(issue_table[:created_at].gteq(options[:from])) + .where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil))) + .where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil)) + end end end end diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb index 949119d69a0..404b2460814 100644 --- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb @@ -10,7 +10,9 @@ module Gitlab issue_table[:iid], issue_table[:id], issue_table[:created_at], - issue_table[:author_id]] + issue_table[:author_id], + projects_table[:name], + routes_table[:path]] super(*args) end @@ -18,7 +20,7 @@ module Gitlab private def serialize(event) - AnalyticsIssueSerializer.new(project: project).represent(event) + AnalyticsIssueSerializer.new(serialization_context).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/review_event_fetcher.rb b/lib/gitlab/cycle_analytics/review_event_fetcher.rb index d31736e755d..6acd12517fa 100644 --- a/lib/gitlab/cycle_analytics/review_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/review_event_fetcher.rb @@ -11,7 +11,9 @@ module Gitlab mr_table[:id], mr_table[:created_at], mr_table[:state], - mr_table[:author_id]] + mr_table[:author_id], + projects_table[:name], + routes_table[:path]] super(*args) end @@ -19,7 +21,7 @@ module Gitlab private def serialize(event) - AnalyticsMergeRequestSerializer.new(project: project).represent(event) + AnalyticsMergeRequestSerializer.new(serialization_context).represent(event) end def allowed_ids_finder_class diff --git a/lib/gitlab/cycle_analytics/summary/group/base.rb b/lib/gitlab/cycle_analytics/summary/group/base.rb new file mode 100644 index 00000000000..7f18b61d309 --- /dev/null +++ b/lib/gitlab/cycle_analytics/summary/group/base.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Gitlab + module CycleAnalytics + module Summary + module Group + class Base + def initialize(group:, from:) + @group = group + @from = from + end + + def title + raise NotImplementedError.new("Expected #{self.name} to implement title") + end + + def value + raise NotImplementedError.new("Expected #{self.name} to implement value") + end + end + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/summary/group/deploy.rb b/lib/gitlab/cycle_analytics/summary/group/deploy.rb new file mode 100644 index 00000000000..d8fcd8f2ce4 --- /dev/null +++ b/lib/gitlab/cycle_analytics/summary/group/deploy.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Gitlab + module CycleAnalytics + module Summary + module Group + class Deploy < Group::Base + def title + n_('Deploy', 'Deploys', value) + end + + def value + @value ||= Deployment.joins(:project) + .where(projects: { id: projects }) + .where("deployments.created_at > ?", @from) + .success + .count + end + + private + + def projects + Project.inside_path(@group.full_path).ids + end + end + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/summary/group/issue.rb b/lib/gitlab/cycle_analytics/summary/group/issue.rb new file mode 100644 index 00000000000..a5188056cb7 --- /dev/null +++ b/lib/gitlab/cycle_analytics/summary/group/issue.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Gitlab + module CycleAnalytics + module Summary + module Group + class Issue < Group::Base + def initialize(group:, from:, current_user:) + @group = group + @from = from + @current_user = current_user + end + + def title + n_('New Issue', 'New Issues', value) + end + + def value + @value ||= IssuesFinder.new(@current_user, group_id: @group.id, include_subgroups: true).execute.created_after(@from).count + end + end + end + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb index 8b07da11c5d..b7a64adc2ff 100644 --- a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb @@ -9,12 +9,12 @@ describe Gitlab::CycleAnalytics::BaseEventFetcher do let(:options) do { start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs, - from: 30.days.ago } + from: 30.days.ago, + project: project } end subject do - described_class.new(project: project, - stage: :issue, + described_class.new(stage: :issue, options: options).fetch end diff --git a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb index c738cc49c1f..68f73ba3c93 100644 --- a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb @@ -5,31 +5,31 @@ describe Gitlab::CycleAnalytics::CodeStage do let(:stage_name) { :code } let(:project) { create(:project) } - let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } - let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:mr_1) { create(:merge_request, source_project: project, created_at: 15.minutes.ago) } - let!(:mr_2) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'A') } - let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:mr_1) { create(:merge_request, source_project: project, created_at: 15.minutes.ago) } + let(:mr_2) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'A') } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago) issue_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) issue_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1) create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2) end it_behaves_like 'base stage' - describe '#median' do + describe '#project_median' do around do |example| Timecop.freeze { example.run } end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end @@ -41,4 +41,80 @@ describe Gitlab::CycleAnalytics::CodeStage do expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) end end + + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:mr_2_1) { create(:merge_request, source_project: project_2, created_at: 15.minutes.ago) } + let(:mr_2_2) { create(:merge_request, source_project: project_3, created_at: 10.minutes.ago, source_branch: 'A') } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago) + issue_2_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + issue_2_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + create(:merge_requests_closing_issues, merge_request: mr_2_1, issue: issue_2_1) + create(:merge_requests_closing_issues, merge_request: mr_2_2, issue: issue_2_2) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title) + end + end + + context 'when subgroup is given' do + let(:subgroup) { create(:group, parent: group) } + let(:project_4) { create(:project, group: subgroup) } + let(:project_5) { create(:project, group: subgroup) } + let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) } + let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + let(:mr_3_1) { create(:merge_request, source_project: project_4, created_at: 15.minutes.ago) } + let(:mr_3_2) { create(:merge_request, source_project: project_5, created_at: 10.minutes.ago, source_branch: 'A') } + + before do + issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 45.minutes.ago) + issue_3_2.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + issue_3_3.metrics.update!(first_added_to_board_at: 60.minutes.ago, first_mentioned_in_commit_at: 40.minutes.ago) + create(:merge_requests_closing_issues, merge_request: mr_3_1, issue: issue_3_1) + create(:merge_requests_closing_issues, merge_request: mr_3_2, issue: issue_3_2) + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(4) + expect(result.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title, mr_3_1.title, mr_3_2.title) + end + + it 'exposes merge requests that close issues with full path for subgroup' do + result = stage.events + + expect(result.count).to eq(4) + expect(result.find { |event| event[:title] == mr_3_1.title }[:url]).to include("#{subgroup.full_path}") + end + end + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb new file mode 100644 index 00000000000..8e552b23283 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe Gitlab::CycleAnalytics::GroupStageSummary do + let(:group) { create(:group) } + let(:project) { create(:project, :repository, namespace: group) } + let(:project_2) { create(:project, :repository, namespace: group) } + let(:from) { 1.day.ago } + let(:user) { create(:user, :admin) } + subject { described_class.new(group, from: Time.now, current_user: user).data } + + describe "#new_issues" do + it "finds the number of issues created after the 'from date'" do + Timecop.freeze(5.days.ago) { create(:issue, project: project) } + Timecop.freeze(5.days.ago) { create(:issue, project: project_2) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + + expect(subject.first[:value]).to eq(2) + end + + it "doesn't find issues from other projects" do + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group))) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + + expect(subject.first[:value]).to eq(2) + end + + it "finds issues from subgroups" do + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group, parent: group))) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + + expect(subject.first[:value]).to eq(3) + end + end + + describe "#deploys" do + it "finds the number of deploys made created after the 'from date'" do + Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project) } + Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project) } + Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project_2) } + Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project_2) } + + expect(subject.second[:value]).to eq(2) + end + + it "doesn't find deploys from other projects" do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: create(:project, :repository, namespace: create(:group))) + end + + expect(subject.second[:value]).to eq(0) + end + + it "finds deploys from subgroups" do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: create(:project, :repository, namespace: create(:group, parent: group))) + end + + expect(subject.second[:value]).to eq(1) + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb index 3b6af9cbaed..64ac9df52b2 100644 --- a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb @@ -4,11 +4,11 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::IssueStage do let(:stage_name) { :issue } let(:project) { create(:project) } - let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } - let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) } + let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) } let!(:issue_without_milestone) { create(:issue, project: project, created_at: 1.minute.ago) } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago ) @@ -24,7 +24,7 @@ describe Gitlab::CycleAnalytics::IssueStage do end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end @@ -36,4 +36,69 @@ describe Gitlab::CycleAnalytics::IssueStage do expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title, issue_3.title) end end + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago) + issue_2_2.metrics.update!(first_added_to_board_at: 30.minutes.ago) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title) + end + end + + context 'when subgroup is given' do + let(:subgroup) { create(:group, parent: group) } + let(:project_4) { create(:project, group: subgroup) } + let(:project_5) { create(:project, group: subgroup) } + let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) } + let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + + before do + issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago) + issue_3_2.metrics.update!(first_added_to_board_at: 30.minutes.ago) + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(4) + expect(result.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title) + end + + it 'exposes merge requests that close issues with full path for subgroup' do + result = stage.events + + expect(result.count).to eq(4) + expect(result.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}") + end + end + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb index 506a8160412..55de6192af1 100644 --- a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::CycleAnalytics::PlanStage do let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } let!(:issue_3) { create(:issue, project: project, created_at: 30.minutes.ago) } let!(:issue_without_milestone) { create(:issue, project: project, created_at: 1.minute.ago) } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do issue_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago) @@ -18,13 +18,13 @@ describe Gitlab::CycleAnalytics::PlanStage do it_behaves_like 'base stage' - describe '#median' do + describe '#project_median' do around do |example| Timecop.freeze { example.run } end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end @@ -36,4 +36,72 @@ describe Gitlab::CycleAnalytics::PlanStage do expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title) end end + + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + issue_2_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago) + issue_2_2.metrics.update!(first_added_to_board_at: 30.minutes.ago, first_mentioned_in_commit_at: 20.minutes.ago) + issue_2_3.metrics.update!(first_added_to_board_at: 15.minutes.ago) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title) + end + end + + context 'when subgroup is given' do + let(:subgroup) { create(:group, parent: group) } + let(:project_4) { create(:project, group: subgroup) } + let(:project_5) { create(:project, group: subgroup) } + let(:issue_3_1) { create(:issue, project: project_4, created_at: 90.minutes.ago) } + let(:issue_3_2) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + let(:issue_3_3) { create(:issue, project: project_5, created_at: 60.minutes.ago) } + + before do + issue_3_1.metrics.update!(first_associated_with_milestone_at: 60.minutes.ago, first_mentioned_in_commit_at: 10.minutes.ago) + issue_3_2.metrics.update!(first_added_to_board_at: 30.minutes.ago, first_mentioned_in_commit_at: 20.minutes.ago) + issue_3_3.metrics.update!(first_added_to_board_at: 15.minutes.ago) + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(4) + expect(result.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title) + end + + it 'exposes merge requests that close issues with full path for subgroup' do + result = stage.events + + expect(result.count).to eq(4) + expect(result.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}") + end + end + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb index f072a9644e8..9c7cb5811d0 100644 --- a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb @@ -4,14 +4,14 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::ReviewStage do let(:stage_name) { :review } let(:project) { create(:project) } - let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } - let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } - let!(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } - let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } + let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } + let(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } + let(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } let!(:mr_4) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'C') } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do mr_1.metrics.update!(merged_at: 30.minutes.ago) @@ -24,13 +24,13 @@ describe Gitlab::CycleAnalytics::ReviewStage do it_behaves_like 'base stage' - describe '#median' do + describe '#project_median' do around do |example| Timecop.freeze { example.run } end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end @@ -42,4 +42,47 @@ describe Gitlab::CycleAnalytics::ReviewStage do expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) end end + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:mr_2_1) { create(:merge_request, :closed, source_project: project_2, created_at: 60.minutes.ago) } + let(:mr_2_2) { create(:merge_request, :closed, source_project: project_3, created_at: 40.minutes.ago, source_branch: 'A') } + let(:mr_2_3) { create(:merge_request, source_project: project_2, created_at: 10.minutes.ago, source_branch: 'B') } + let!(:mr_2_4) { create(:merge_request, source_project: project_3, created_at: 10.minutes.ago, source_branch: 'C') } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + mr_2_1.metrics.update!(merged_at: 30.minutes.ago) + mr_2_2.metrics.update!(merged_at: 10.minutes.ago) + + create(:merge_requests_closing_issues, merge_request: mr_2_1, issue: issue_2_1) + create(:merge_requests_closing_issues, merge_request: mr_2_2, issue: issue_2_2) + create(:merge_requests_closing_issues, merge_request: mr_2_3, issue: issue_2_3) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title) + end + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb index c22d27f60d6..b001a46001e 100644 --- a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' shared_examples 'default query config' do let(:project) { create(:project) } - let(:event) { described_class.new(project: project, stage: stage_name, options: { from: 1.day.ago }) } + let(:event) { described_class.new(stage: stage_name, options: { from: 1.day.ago, project: project }) } it 'has the stage attribute' do expect(event.stage).not_to be_nil diff --git a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb index 1a4b572cc11..c146146723f 100644 --- a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' shared_examples 'base stage' do ISSUES_MEDIAN = 30.minutes.to_i - let(:stage) { described_class.new(project: double, options: {}) } + let(:stage) { described_class.new(options: { project: double }) } before do - allow(stage).to receive(:median).and_return(1.12) + allow(stage).to receive(:project_median).and_return(1.12) allow_any_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher).to receive(:event_result).and_return({}) end diff --git a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb index 17d5fbb9733..3e2d748396f 100644 --- a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb @@ -5,16 +5,16 @@ describe Gitlab::CycleAnalytics::StagingStage do let(:stage_name) { :staging } let(:project) { create(:project) } - let!(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } - let!(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } - let!(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } - let!(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } - let!(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } + let(:issue_1) { create(:issue, project: project, created_at: 90.minutes.ago) } + let(:issue_2) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:issue_3) { create(:issue, project: project, created_at: 60.minutes.ago) } + let(:mr_1) { create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago) } + let(:mr_2) { create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A') } + let(:mr_3) { create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B') } let(:build_1) { create(:ci_build, project: project) } let(:build_2) { create(:ci_build, project: project) } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } before do mr_1.metrics.update!(merged_at: 80.minutes.ago, first_deployed_to_production_at: 50.minutes.ago, pipeline_id: build_1.commit_id) @@ -28,13 +28,13 @@ describe Gitlab::CycleAnalytics::StagingStage do it_behaves_like 'base stage' - describe '#median' do + describe '#project_median' do around do |example| Timecop.freeze { example.run } end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end @@ -46,4 +46,50 @@ describe Gitlab::CycleAnalytics::StagingStage do expect(result.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) end end + + context 'when group is given' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_2) { create(:project, group: group) } + let(:project_3) { create(:project, group: group) } + let(:issue_2_1) { create(:issue, project: project_2, created_at: 90.minutes.ago) } + let(:issue_2_2) { create(:issue, project: project_3, created_at: 60.minutes.ago) } + let(:issue_2_3) { create(:issue, project: project_2, created_at: 60.minutes.ago) } + let(:mr_1) { create(:merge_request, :closed, source_project: project_2, created_at: 60.minutes.ago) } + let(:mr_2) { create(:merge_request, :closed, source_project: project_3, created_at: 40.minutes.ago, source_branch: 'A') } + let(:mr_3) { create(:merge_request, source_project: project_2, created_at: 10.minutes.ago, source_branch: 'B') } + let(:build_1) { create(:ci_build, project: project_2) } + let(:build_2) { create(:ci_build, project: project_3) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: user, group: group }) } + + before do + group.add_owner(user) + mr_1.metrics.update!(merged_at: 80.minutes.ago, first_deployed_to_production_at: 50.minutes.ago, pipeline_id: build_1.commit_id) + mr_2.metrics.update!(merged_at: 60.minutes.ago, first_deployed_to_production_at: 30.minutes.ago, pipeline_id: build_2.commit_id) + mr_3.metrics.update!(merged_at: 10.minutes.ago, first_deployed_to_production_at: 3.days.ago, pipeline_id: create(:ci_build, project: project_2).commit_id) + + create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_2_1) + create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2_2) + create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_2_3) + end + + describe '#group_median' do + around do |example| + Timecop.freeze { example.run } + end + + it 'counts median from issues with metrics' do + expect(stage.group_median).to eq(ISSUES_MEDIAN) + end + end + + describe '#events' do + it 'exposes merge requests that close issues' do + result = stage.events + + expect(result.count).to eq(2) + expect(result.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) + end + end + end end diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb index 8122e85a981..4ef33ff6e2b 100644 --- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb @@ -34,7 +34,7 @@ describe Gitlab::CycleAnalytics::UsageData do expect(result).to have_key(:avg_cycle_analytics) - CycleAnalytics::Base::STAGES.each do |stage| + CycleAnalytics::BaseMethods::STAGES.each do |stage| expect(result[:avg_cycle_analytics]).to have_key(stage) stage_values = result[:avg_cycle_analytics][stage] diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb index db6e70973ae..808659552ff 100644 --- a/spec/models/cycle_analytics/code_spec.rb +++ b/spec/models/cycle_analytics/code_spec.rb @@ -38,7 +38,7 @@ describe 'CycleAnalytics#code' do merge_merge_requests_closing_issue(user, project, issue) deploy_master(user, project) - expect(subject[:code].median).to be_nil + expect(subject[:code].project_median).to be_nil end end end @@ -68,7 +68,7 @@ describe 'CycleAnalytics#code' do merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:code].median).to be_nil + expect(subject[:code].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/group_level_spec.rb b/spec/models/cycle_analytics/group_level_spec.rb new file mode 100644 index 00000000000..70c370bc39d --- /dev/null +++ b/spec/models/cycle_analytics/group_level_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe CycleAnalytics::GroupLevel do + let(:group) { create(:group)} + let(:project) { create(:project, :repository, namespace: group) } + let(:from_date) { 10.days.ago } + let(:user) { create(:user, :admin) } + let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } + let(:milestone) { create(:milestone, project: project) } + let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") } + let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) } + + subject { described_class.new(options: { from: from_date, group: group, current_user: user }) } + + describe '#permissions' do + it 'returns permissions' do + expect(subject.permissions.values.uniq).to eq([true]) + end + end + + describe '#stats' do + before do + allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue]) + + create_cycle(user, project, issue, mr, milestone, pipeline) + deploy_master(user, project) + end + + it 'returns medians for each stage for a specific group' do + expect(subject.no_stats?).to eq(false) + end + end + + describe '#summary' do + before do + create_cycle(user, project, issue, mr, milestone, pipeline) + deploy_master(user, project) + end + + it 'returns medians for each stage for a specific group' do + expect(subject.summary.map { |summary| summary[:value] }).to contain_exactly(1, 1) + end + end +end diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb index 4ccbdf29df6..8cdf83b1292 100644 --- a/spec/models/cycle_analytics/issue_spec.rb +++ b/spec/models/cycle_analytics/issue_spec.rb @@ -43,7 +43,7 @@ describe 'CycleAnalytics#issue' do create_merge_request_closing_issue(user, project, issue) merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:issue].median).to be_nil + expect(subject[:issue].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb index c99c38e9cf3..28ad9bd194d 100644 --- a/spec/models/cycle_analytics/plan_spec.rb +++ b/spec/models/cycle_analytics/plan_spec.rb @@ -47,7 +47,7 @@ describe 'CycleAnalytics#plan' do create_merge_request_closing_issue(user, project, issue, source_branch: branch_name) merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:issue].median).to be_nil + expect(subject[:issue].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb index ddd199362d1..613c1786540 100644 --- a/spec/models/cycle_analytics/production_spec.rb +++ b/spec/models/cycle_analytics/production_spec.rb @@ -41,7 +41,7 @@ describe 'CycleAnalytics#production' do MergeRequests::MergeService.new(project, user).execute(merge_request) deploy_master(user, project) - expect(subject[:production].median).to be_nil + expect(subject[:production].project_median).to be_nil end end @@ -52,7 +52,7 @@ describe 'CycleAnalytics#production' do MergeRequests::MergeService.new(project, user).execute(merge_request) deploy_master(user, project, environment: 'staging') - expect(subject[:production].median).to be_nil + expect(subject[:production].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/project_level_spec.rb b/spec/models/cycle_analytics/project_level_spec.rb index 77bd0bfeb9c..4de01b1c679 100644 --- a/spec/models/cycle_analytics/project_level_spec.rb +++ b/spec/models/cycle_analytics/project_level_spec.rb @@ -23,7 +23,7 @@ describe CycleAnalytics::ProjectLevel do it 'returns every median for each stage for a specific project' do values = described_class::STAGES.each_with_object({}) do |stage_name, hsh| - hsh[stage_name] = subject[stage_name].median.presence + hsh[stage_name] = subject[stage_name].project_median.presence end expect(subject.all_medians_by_stage).to eq(values) diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb index 63c481ed465..ef88fd86340 100644 --- a/spec/models/cycle_analytics/review_spec.rb +++ b/spec/models/cycle_analytics/review_spec.rb @@ -28,7 +28,7 @@ describe 'CycleAnalytics#review' do it "returns nil" do MergeRequests::MergeService.new(project, user).execute(create(:merge_request)) - expect(subject[:review].median).to be_nil + expect(subject[:review].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index c134b97553f..571792559d8 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -45,7 +45,7 @@ describe 'CycleAnalytics#staging' do MergeRequests::MergeService.new(project, user).execute(merge_request) deploy_master(user, project) - expect(subject[:staging].median).to be_nil + expect(subject[:staging].project_median).to be_nil end end @@ -56,7 +56,7 @@ describe 'CycleAnalytics#staging' do MergeRequests::MergeService.new(project, user).execute(merge_request) deploy_master(user, project, environment: 'staging') - expect(subject[:staging].median).to be_nil + expect(subject[:staging].project_median).to be_nil end end end diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb index a6ea73b2699..7b3001d2bd8 100644 --- a/spec/models/cycle_analytics/test_spec.rb +++ b/spec/models/cycle_analytics/test_spec.rb @@ -36,7 +36,7 @@ describe 'CycleAnalytics#test' do merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:test].median).to be_nil + expect(subject[:test].project_median).to be_nil end end @@ -47,7 +47,7 @@ describe 'CycleAnalytics#test' do pipeline.run! pipeline.succeed! - expect(subject[:test].median).to be_nil + expect(subject[:test].project_median).to be_nil end end @@ -62,7 +62,7 @@ describe 'CycleAnalytics#test' do merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:test].median).to be_nil + expect(subject[:test].project_median).to be_nil end end @@ -77,7 +77,7 @@ describe 'CycleAnalytics#test' do merge_merge_requests_closing_issue(user, project, issue) - expect(subject[:test].median).to be_nil + expect(subject[:test].project_median).to be_nil end end end diff --git a/spec/serializers/analytics_issue_entity_spec.rb b/spec/serializers/analytics_issue_entity_spec.rb index 89588b4df2b..dd5e43a4b62 100644 --- a/spec/serializers/analytics_issue_entity_spec.rb +++ b/spec/serializers/analytics_issue_entity_spec.rb @@ -9,12 +9,14 @@ describe AnalyticsIssueEntity do iid: "1", id: "1", created_at: "2016-11-12 15:04:02.948604", - author: user + author: user, + name: project.name, + path: project.namespace } end let(:project) { create(:project) } - let(:request) { EntityRequest.new(project: project, entity: :merge_request) } + let(:request) { EntityRequest.new(entity: :merge_request) } let(:entity) do described_class.new(entity_hash, request: request, project: project) diff --git a/spec/serializers/analytics_issue_serializer_spec.rb b/spec/serializers/analytics_issue_serializer_spec.rb index 5befc28f4fa..c9ffe1c5dad 100644 --- a/spec/serializers/analytics_issue_serializer_spec.rb +++ b/spec/serializers/analytics_issue_serializer_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe AnalyticsIssueSerializer do subject do described_class - .new(project: project, entity: :merge_request) + .new(entity: :merge_request) .represent(resource) end @@ -16,7 +16,9 @@ describe AnalyticsIssueSerializer do iid: "1", id: "1", created_at: "2016-11-12 15:04:02.948604", - author: user + author: user, + name: project.name, + path: project.namespace } end diff --git a/spec/serializers/analytics_merge_request_serializer_spec.rb b/spec/serializers/analytics_merge_request_serializer_spec.rb index 62067cc0ef2..123d7d795ce 100644 --- a/spec/serializers/analytics_merge_request_serializer_spec.rb +++ b/spec/serializers/analytics_merge_request_serializer_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe AnalyticsMergeRequestSerializer do subject do described_class - .new(project: project, entity: :merge_request) + .new(entity: :merge_request) .represent(resource) end @@ -17,7 +17,9 @@ describe AnalyticsMergeRequestSerializer do id: "1", state: 'open', created_at: "2016-11-12 15:04:02.948604", - author: user + author: user, + name: project.name, + path: project.namespace } end diff --git a/spec/serializers/analytics_stage_serializer_spec.rb b/spec/serializers/analytics_stage_serializer_spec.rb index 5b05c2f2ef3..86a796a2d94 100644 --- a/spec/serializers/analytics_stage_serializer_spec.rb +++ b/spec/serializers/analytics_stage_serializer_spec.rb @@ -6,11 +6,11 @@ describe AnalyticsStageSerializer do end let(:resource) do - Gitlab::CycleAnalytics::CodeStage.new(project: double, options: {}) + Gitlab::CycleAnalytics::CodeStage.new(options: { project: double }) end before do - allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(1.12) + allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(1.12) allow_any_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher).to receive(:event_result).and_return({}) end @@ -24,7 +24,7 @@ describe AnalyticsStageSerializer do context 'when median is equal 0' do before do - allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(0) + allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(0) end it 'sets the value to nil' do @@ -34,7 +34,7 @@ describe AnalyticsStageSerializer do context 'when median is below 1' do before do - allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(0.12) + allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(0.12) end it 'sets the value to equal to median' do @@ -44,7 +44,7 @@ describe AnalyticsStageSerializer do context 'when median is above 1' do before do - allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:median).and_return(60.12) + allow_any_instance_of(Gitlab::CycleAnalytics::BaseStage).to receive(:project_median).and_return(60.12) end it 'sets the value to equal to median' do diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb index 19b32c84d81..be1c2bc3046 100644 --- a/spec/support/cycle_analytics_helpers/test_generation.rb +++ b/spec/support/cycle_analytics_helpers/test_generation.rb @@ -50,7 +50,7 @@ module CycleAnalyticsHelpers end median_time_difference = time_differences.sort[2] - expect(subject[phase].median).to be_within(5).of(median_time_difference) + expect(subject[phase].project_median).to be_within(5).of(median_time_difference) end context "when the data belongs to another project" do @@ -80,7 +80,7 @@ module CycleAnalyticsHelpers # Turn off the stub before checking assertions allow(self).to receive(:project).and_call_original - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end @@ -103,7 +103,7 @@ module CycleAnalyticsHelpers Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end end @@ -121,7 +121,7 @@ module CycleAnalyticsHelpers Timecop.freeze(end_time + 1.day) { post_fn[self, data] } if post_fn - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end end @@ -138,7 +138,7 @@ module CycleAnalyticsHelpers post_fn[self, data] if post_fn - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end end @@ -146,7 +146,7 @@ module CycleAnalyticsHelpers context "when none of the start / end conditions are matched" do it "returns nil" do - expect(subject[phase].median).to be_nil + expect(subject[phase].project_median).to be_nil end end end -- cgit v1.2.1 From 382a13c015538783ca90b27362ad3c821644ef55 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Mon, 15 Jul 2019 14:27:35 +0200 Subject: Update example Prometheus scrape config Use a full real-world example that is compatible with the omnibus dashboards and follows Prometheus best practices. --- changelogs/unreleased/bjk-fix_prom_example.yml | 5 +++ doc/administration/monitoring/prometheus/index.md | 54 ++++++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/bjk-fix_prom_example.yml diff --git a/changelogs/unreleased/bjk-fix_prom_example.yml b/changelogs/unreleased/bjk-fix_prom_example.yml new file mode 100644 index 00000000000..2f81bc6196b --- /dev/null +++ b/changelogs/unreleased/bjk-fix_prom_example.yml @@ -0,0 +1,5 @@ +--- +title: Update example Prometheus scrape config +merge_request: 30739 +author: +type: other diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md index 341ea3330d7..c8968c51393 100644 --- a/doc/administration/monitoring/prometheus/index.md +++ b/doc/administration/monitoring/prometheus/index.md @@ -134,17 +134,57 @@ To use an external Prometheus server: ```yaml scrape_configs: - - job_name: 'gitlab_exporters' + - job_name: nginx static_configs: - - targets: ['1.1.1.1:9168', '1.1.1.1:9236', '1.1.1.1:9236', '1.1.1.1:9100', '1.1.1.1:9121', '1.1.1.1:9187'] - - - job_name: 'gitlab_metrics' - metrics_path: /-/metrics + - targets: + - 1.1.1.1:8060 + - job_name: redis + static_configs: + - targets: + - 1.1.1.1:9121 + - job_name: postgres + static_configs: + - targets: + - 1.1.1.1:9187 + - job_name: node + static_configs: + - targets: + - 1.1.1.1:9100 + - job_name: gitlab-workhorse + static_configs: + - targets: + - 1.1.1.1:9229 + - job_name: gitlab-rails + metrics_path: "/-/metrics" + static_configs: + - targets: + - 1.1.1.1:8080 + - job_name: gitlab-sidekiq + static_configs: + - targets: + - 1.1.1.1:8082 + - job_name: gitlab_monitor_database + metrics_path: "/database" + static_configs: + - targets: + - 1.1.1.1:9168 + - job_name: gitlab_monitor_sidekiq + metrics_path: "/sidekiq" + static_configs: + - targets: + - 1.1.1.1:9168 + - job_name: gitlab_monitor_process + metrics_path: "/process" + static_configs: + - targets: + - 1.1.1.1:9168 + - job_name: gitaly static_configs: - - targets: ['1.1.1.1:443'] + - targets: + - 1.1.1.1:9236 ``` -1. Restart the Prometheus server. +1. Reload the Prometheus server. ## Viewing performance metrics -- cgit v1.2.1 From 70d74f4730d69d61b07362fbe953c37494a732ca Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 15 Jul 2019 15:18:50 +0200 Subject: Update docs on serverless apps deployments --- doc/user/project/clusters/serverless/index.md | 37 ++++++++++++--------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md index 14ee6303bf9..a32759c7bdc 100644 --- a/doc/user/project/clusters/serverless/index.md +++ b/doc/user/project/clusters/serverless/index.md @@ -41,8 +41,7 @@ To run Knative on Gitlab, you will need: external IP address or hostname for that domain. 1. **`.gitlab-ci.yml`:** GitLab uses [Kaniko](https://github.com/GoogleContainerTools/kaniko) to build the application. We also use [gitlabktl](https://gitlab.com/gitlab-org/gitlabktl) - and [TriggerMesh CLI](https://github.com/triggermesh/tm) CLIs to simplify the - deployment of services and functions to Knative. + CLI to simplify the deployment of services and functions to Knative. 1. **`serverless.yml`** (for [functions only](#deploying-functions)): When using serverless to deploy functions, the `serverless.yml` file will contain the information for all the functions being hosted in the repository as well as a reference to the runtime being used. @@ -249,7 +248,7 @@ Explanation of the fields used above: | Parameter | Description | |-----------|-------------| -| `name` | Indicates which provider is used to execute the `serverless.yml` file. In this case, the TriggerMesh `tm` CLI. | +| `name` | Indicates which provider is used to execute the `serverless.yml` file. In this case, the TriggerMesh middleware. | | `environment` | Includes the environment variables to be passed as part of function execution for **all** functions in the file, where `FOO` is the variable name and `BAR` are he variable contents. You may replace this with you own variables. | ### `functions` @@ -343,27 +342,23 @@ Go to the **CI/CD > Pipelines** and click on the pipeline that deployed your app The output will look like this: ```bash -Running with gitlab-runner 11.5.0~beta.844.g96d88322 (96d88322) - on docker-auto-scale 72989761 -Using Docker executor with image gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ... -Pulling docker image gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ... -Using docker image sha256:6b3f6590a9b30bd7aafb9573f047d930c70066e43955b4beb18a1eee175f6de1 for gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5 ... -Running on runner-72989761-project-4342902-concurrent-0 via runner-72989761-stg-srm-1541795796-27929c96... -Cloning repository... -Cloning into '/builds/danielgruesso/knative'... -Checking out 8671ad20 as master... -Skipping Git submodules setup -$ echo "$CI_REGISTRY_IMAGE" -registry.staging.gitlab.com/danielgruesso/knative -$ tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait -Deployment started. Run "tm -n knative-4342902 describe service knative" to see the details -Waiting for ready state....... -Service domain: knative.knative-4342902.example.com +Running with gitlab-runner 12.1.0-rc1 (6da35412) + on prm-com-gitlab-org ae3bfce3 +Using Docker executor with image registry.gitlab.com/gitlab-org/gitlabktl:latest ... +Running on runner-ae3bfc-concurrent-0 via runner-ae3bfc ... +Fetching changes... +Authenticating with credentials from job payload (GitLab Registry) +$ /usr/bin/gitlabktl application deploy +Welcome to gitlabktl tool +time="2019-07-15T10:51:07Z" level=info msg="deploying registry credentials" +Creating app-hello function +Waiting for app-hello ready state +Service app-hello URL: http://app-hello.serverless.example.com Job succeeded ``` -The second to last line, labeled **Service domain** contains the URL for the deployment. Copy and paste the domain into your -browser to see the app live. +The second to last line, labeled **Service domain** contains the URL for the +deployment. Copy and paste the domain into your browser to see the app live. ![knative app](img/knative-app.png) -- cgit v1.2.1 From d2400a990822dc291382761753e3fc08250fc7f2 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 15 Jul 2019 15:20:10 +0200 Subject: Add changelog entry for serverless apps deployment --- .../unreleased/feature-gb-serverless-app-deployment-template.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/feature-gb-serverless-app-deployment-template.yml diff --git a/changelogs/unreleased/feature-gb-serverless-app-deployment-template.yml b/changelogs/unreleased/feature-gb-serverless-app-deployment-template.yml new file mode 100644 index 00000000000..bd9001bd671 --- /dev/null +++ b/changelogs/unreleased/feature-gb-serverless-app-deployment-template.yml @@ -0,0 +1,5 @@ +--- +title: Deploy serverless apps with gitlabktl +merge_request: 30740 +author: +type: added -- cgit v1.2.1 From 72cddf5a1f7e851864661ad97a611b1ef8d5563a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C5=82gorzata=20Ksionek?= Date: Mon, 15 Jul 2019 16:22:15 +0200 Subject: Fix test stage specs --- spec/lib/gitlab/cycle_analytics/test_stage_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb index 8633a63849f..41028c44a00 100644 --- a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb @@ -4,7 +4,7 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec' describe Gitlab::CycleAnalytics::TestStage do let(:stage_name) { :test } let(:project) { create(:project) } - let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) } + let(:stage) { described_class.new(options: { from: 2.days.ago, current_user: project.creator, project: project }) } it_behaves_like 'base stage' @@ -36,7 +36,7 @@ describe Gitlab::CycleAnalytics::TestStage do end it 'counts median from issues with metrics' do - expect(stage.median).to eq(ISSUES_MEDIAN) + expect(stage.project_median).to eq(ISSUES_MEDIAN) end end end -- cgit v1.2.1 From 1e3877a9c86a3651b12c2f01310e68599b15f8b8 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 10 Jul 2019 13:17:45 +0100 Subject: Added add to tree dropdown to Vue files breadcrumb --- .../repository/components/breadcrumbs.vue | 186 +++++++++++++++++++++ app/assets/javascripts/repository/index.js | 50 ++++-- .../queries/getPermissions.query.graphql | 9 + app/helpers/tree_helper.rb | 32 ++++ app/views/projects/_files.html.haml | 3 + app/views/projects/tree/_tree_header.html.haml | 2 +- locale/gitlab.pot | 9 + .../repository/components/breadcrumbs_spec.js | 20 ++- 8 files changed, 296 insertions(+), 15 deletions(-) create mode 100644 app/assets/javascripts/repository/queries/getPermissions.query.graphql diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue index 67963dc1923..afb58a60155 100644 --- a/app/assets/javascripts/repository/components/breadcrumbs.vue +++ b/app/assets/javascripts/repository/components/breadcrumbs.vue @@ -1,12 +1,41 @@ @@ -56,6 +228,20 @@ export default { {{ link.name }} + diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js index ea051eaa414..f9727960040 100644 --- a/app/assets/javascripts/repository/index.js +++ b/app/assets/javascripts/repository/index.js @@ -5,6 +5,7 @@ import Breadcrumbs from './components/breadcrumbs.vue'; import LastCommit from './components/last_commit.vue'; import apolloProvider from './graphql'; import { setTitle } from './utils/title'; +import { parseBoolean } from '../lib/utils/common_utils'; export default function setupVueRepositoryList() { const el = document.getElementById('js-tree-list'); @@ -36,19 +37,42 @@ export default function setupVueRepositoryList() { .forEach(elem => elem.classList.toggle('hidden', !isRoot)); }); - // eslint-disable-next-line no-new - new Vue({ - el: document.getElementById('js-repo-breadcrumb'), - router, - apolloProvider, - render(h) { - return h(Breadcrumbs, { - props: { - currentPath: this.$route.params.pathMatch, - }, - }); - }, - }); + const breadcrumbEl = document.getElementById('js-repo-breadcrumb'); + + if (breadcrumbEl) { + const { + canCollaborate, + canEditTree, + newBranchPath, + newTagPath, + newBlobPath, + forkNewBlobPath, + forkNewDirectoryPath, + forkUploadBlobPath, + } = breadcrumbEl.dataset; + + // eslint-disable-next-line no-new + new Vue({ + el: breadcrumbEl, + router, + apolloProvider, + render(h) { + return h(Breadcrumbs, { + props: { + currentPath: this.$route.params.pathMatch, + canCollaborate: parseBoolean(canCollaborate), + canEditTree: parseBoolean(canEditTree), + newBranchPath, + newTagPath, + newBlobPath, + forkNewBlobPath, + forkNewDirectoryPath, + forkUploadBlobPath, + }, + }); + }, + }); + } // eslint-disable-next-line no-new new Vue({ diff --git a/app/assets/javascripts/repository/queries/getPermissions.query.graphql b/app/assets/javascripts/repository/queries/getPermissions.query.graphql new file mode 100644 index 00000000000..092fa44e2d0 --- /dev/null +++ b/app/assets/javascripts/repository/queries/getPermissions.query.graphql @@ -0,0 +1,9 @@ +query getPermissions($projectPath: ID!) { + project(fullPath: $projectPath) { + userPermissions { + pushCode + forkProject + createMergeRequestIn + } + } +} diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index a3575462de0..bb1cdcb1b31 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -154,4 +154,36 @@ module TreeHelper "logs-path" => logs_path } end + + def breadcrumb_data_attributes + attrs = { + can_collaborate: can_collaborate_with_project?(@project).to_s, + new_blob_path: project_new_blob_path(@project, @id), + new_branch_path: new_project_branch_path(@project), + new_tag_path: new_project_tag_path(@project), + can_edit_tree: can_edit_tree?.to_s + } + + if can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project) + continue_param = { + to: project_new_blob_path(@project, @id), + notice: edit_in_new_fork_notice, + notice_now: edit_in_new_fork_notice_now + } + + attrs.merge!( + fork_new_blob_path: project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_param), + fork_new_directory_path: project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_param.merge({ + to: request.fullpath, + notice: _("%{edit_in_new_fork_notice} Try to create a new directory again.") % { edit_in_new_fork_notice: edit_in_new_fork_notice } + })), + fork_upload_blob_path: project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_param.merge({ + to: request.fullpath, + notice: _("%{edit_in_new_fork_notice} Try to upload a file again.") % { edit_in_new_fork_notice: edit_in_new_fork_notice } + })) + ) + end + + attrs + end end diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml index 6763513f9ae..95fdad125a7 100644 --- a/app/views/projects/_files.html.haml +++ b/app/views/projects/_files.html.haml @@ -20,6 +20,9 @@ - if vue_file_list_enabled? #js-tree-list{ data: { project_path: @project.full_path, project_short_path: @project.path, ref: ref, full_name: @project.name_with_namespace } } + - if can_edit_tree? + = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post + = render 'projects/blob/new_dir' - if @tree.readme = render "projects/tree/readme", readme: @tree.readme - else diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 1d0bc588c9c..41cd044a5b0 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -11,7 +11,7 @@ - addtotree_toggle_attributes = { title: _("You can only add files when you are on a branch"), data: { container: 'body' }, class: 'disabled has-tooltip' } - if vue_file_list_enabled? - #js-repo-breadcrumb + #js-repo-breadcrumb{ data: breadcrumb_data_attributes } - else %ul.breadcrumb.repo-breadcrumb %li.breadcrumb-item diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b1fe304e46f..8cd1c27ec56 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -138,9 +138,15 @@ msgstr[1] "" msgid "%{edit_in_new_fork_notice} Try to cherry-pick this commit again." msgstr "" +msgid "%{edit_in_new_fork_notice} Try to create a new directory again." +msgstr "" + msgid "%{edit_in_new_fork_notice} Try to revert this commit again." msgstr "" +msgid "%{edit_in_new_fork_notice} Try to upload a file again." +msgstr "" + msgid "%{filePath} deleted" msgstr "" @@ -707,6 +713,9 @@ msgstr "" msgid "Add to review" msgstr "" +msgid "Add to tree" +msgstr "" + msgid "Add user(s) to the group:" msgstr "" diff --git a/spec/frontend/repository/components/breadcrumbs_spec.js b/spec/frontend/repository/components/breadcrumbs_spec.js index 068fa317a87..707eae34793 100644 --- a/spec/frontend/repository/components/breadcrumbs_spec.js +++ b/spec/frontend/repository/components/breadcrumbs_spec.js @@ -1,12 +1,14 @@ import { shallowMount, RouterLinkStub } from '@vue/test-utils'; +import { GlDropdown } from '@gitlab/ui'; import Breadcrumbs from '~/repository/components/breadcrumbs.vue'; let vm; -function factory(currentPath) { +function factory(currentPath, extraProps = {}) { vm = shallowMount(Breadcrumbs, { propsData: { currentPath, + ...extraProps, }, stubs: { RouterLink: RouterLinkStub, @@ -41,4 +43,20 @@ describe('Repository breadcrumbs component', () => { .attributes('aria-current'), ).toEqual('page'); }); + + it('does not render add to tree dropdown when permissions are false', () => { + factory('/', { canCollaborate: false }); + + vm.setData({ userPermissions: { forkProject: false, createMergeRequestIn: false } }); + + expect(vm.find(GlDropdown).exists()).toBe(false); + }); + + it('renders add to tree dropdown when permissions are true', () => { + factory('/', { canCollaborate: true }); + + vm.setData({ userPermissions: { forkProject: true, createMergeRequestIn: true } }); + + expect(vm.find(GlDropdown).exists()).toBe(true); + }); }); -- cgit v1.2.1 From a5ca1e488d350d48a42f757a765835dc1bf4663d Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 12 Jul 2019 16:52:55 -0300 Subject: Update current limitations for automatic background verification --- .../geo/disaster_recovery/background_verification.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/administration/geo/disaster_recovery/background_verification.md b/doc/administration/geo/disaster_recovery/background_verification.md index 8eee9427b56..27866b7536e 100644 --- a/doc/administration/geo/disaster_recovery/background_verification.md +++ b/doc/administration/geo/disaster_recovery/background_verification.md @@ -171,14 +171,21 @@ If the **primary** and **secondary** nodes have a checksum verification mismatch ## Current limitations -Until [issue #5064][ee-5064] is completed, background verification doesn't cover -CI job artifacts and traces, LFS objects, or user uploads in file storage. -Verify their integrity manually by following [these instructions][foreground-verification] -on both nodes, and comparing the output between them. +Automatic background verification doesn't cover attachments, LFS objects, +job artifacts, and user uploads in file storage. You can keep track of the +progress to include them in [ee-1430]. For now, you can verify their integrity +manually by following [these instructions][foreground-verification] on both +nodes, and comparing the output between them. + +In GitLab EE 12.1, Geo calculates checksums for attachments, LFS objects and +archived traces on secondary nodes after the transfer, compares it with the +stored checksums, and rejects transfers if mismatched. Please note that Geo +currently does not support an automatic way to verify these data if they have +been synced before GitLab EE 12.1. Data in object storage is **not verified**, as the object store is responsible for ensuring the integrity of the data. [reset-verification]: background_verification.md#reset-verification-for-projects-where-verification-has-failed [foreground-verification]: ../../raketasks/check.md -[ee-5064]: https://gitlab.com/gitlab-org/gitlab-ee/issues/5064 +[ee-1430]: https://gitlab.com/groups/gitlab-org/-/epics/1430 -- cgit v1.2.1 From 9b4b4e74130839062932083c214589c67ccfea51 Mon Sep 17 00:00:00 2001 From: Sanad Liaquat Date: Mon, 15 Jul 2019 22:32:43 +0500 Subject: Backport ee to ce --- qa/qa/page/project/sub_menus/common.rb | 6 +++++- qa/qa/resource/merge_request.rb | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/qa/qa/page/project/sub_menus/common.rb b/qa/qa/page/project/sub_menus/common.rb index c94e1e85256..3c9e8085748 100644 --- a/qa/qa/page/project/sub_menus/common.rb +++ b/qa/qa/page/project/sub_menus/common.rb @@ -12,7 +12,11 @@ module QA end def within_submenu - within('.fly-out-list') do + if has_css?('.fly-out-list') + within('.fly-out-list') do + yield + end + else yield end end diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb index 45cb317e0eb..7969de726e4 100644 --- a/qa/qa/resource/merge_request.rb +++ b/qa/qa/resource/merge_request.rb @@ -9,6 +9,7 @@ module QA :description, :source_branch, :target_branch, + :target_new_branch, :assignee, :milestone, :labels, @@ -27,6 +28,7 @@ module QA Repository::ProjectPush.fabricate! do |resource| resource.project = project resource.branch_name = 'master' + resource.new_branch = @target_new_branch resource.remote_branch = target_branch end end @@ -52,6 +54,7 @@ module QA @labels = [] @file_name = "added_file.txt" @file_content = "File Added" + @target_new_branch = true end def fabricate! -- cgit v1.2.1 From 119be7ffd12d72b0871357330145efbacef4d4e6 Mon Sep 17 00:00:00 2001 From: ddavison Date: Mon, 15 Jul 2019 15:54:05 -0700 Subject: Refactor more common Page Objects to use data tags Common usages within GitLab QA include login page, navbars and layouts --- app/views/devise/shared/_signup_box.html.haml | 14 ++++++------ app/views/doorkeeper/authorizations/new.html.haml | 2 +- qa/qa/page/main/oauth.rb | 4 ++-- qa/qa/page/main/sign_up.rb | 26 +++++++++++------------ 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 034273558bb..074edf645ba 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -7,26 +7,26 @@ = render "devise/shared/error_messages", resource: resource .name.form-group = f.label :name, _('Full name'), class: 'label-bold' - = f.text_field :name, class: "form-control top qa-new-user-name js-block-emoji js-validate-length", :data => { :max_length => max_name_length, :max_length_message => s_("SignUp|Name is too long (maximum is %{max_length} characters).") % { max_length: max_name_length } }, required: true, title: _("This field is required.") + = f.text_field :name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_name_length, :max_length_message => s_("SignUp|Name is too long (maximum is %{max_length} characters).") % { max_length: max_name_length }, :qa_selector => 'new_user_name_field' }, required: true, title: _("This field is required.") .username.form-group = f.label :username, class: 'label-bold' - = f.text_field :username, class: "form-control middle qa-new-user-username js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length } }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.") + = f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.") %p.validation-error.gl-field-error-ignore.field-validation.hide= _('Username is already taken.') %p.validation-success.gl-field-error-ignore.field-validation.hide= _('Username is available.') %p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking username availability...') .form-group = f.label :email, class: 'label-bold' - = f.email_field :email, class: "form-control middle qa-new-user-email", required: true, title: _("Please provide a valid email address.") + = f.email_field :email, class: "form-control middle", data: { qa_selector: 'new_user_email_field' }, required: true, title: _("Please provide a valid email address.") .form-group = f.label :email_confirmation, class: 'label-bold' - = f.email_field :email_confirmation, class: "form-control middle qa-new-user-email-confirmation", required: true, title: _("Please retype the email address.") + = f.email_field :email_confirmation, class: "form-control middle", data: { qa_selector: 'new_user_email_confirmation_field' }, required: true, title: _("Please retype the email address.") .form-group.append-bottom-20#password-strength = f.label :password, class: 'label-bold' - = f.password_field :password, class: "form-control bottom qa-new-user-password", required: true, pattern: ".{#{@minimum_password_length},}", title: _("Minimum length is %{minimum_password_length} characters.") % { minimum_password_length: @minimum_password_length } + = f.password_field :password, class: "form-control bottom", data: { qa_selector: 'new_user_password_field' }, required: true, pattern: ".{#{@minimum_password_length},}", title: _("Minimum length is %{minimum_password_length} characters.") % { minimum_password_length: @minimum_password_length } %p.gl-field-hint.text-secondary= _('Minimum length is %{minimum_password_length} characters') % { minimum_password_length: @minimum_password_length } - if Gitlab::CurrentSettings.current_application_settings.enforce_terms? .form-group - = check_box_tag :terms_opt_in, '1', false, required: true, class: 'qa-new-user-accept-terms' + = check_box_tag :terms_opt_in, '1', false, required: true, data: { qa_selector: 'new_user_accept_terms_checkbox' } = label_tag :terms_opt_in do - terms_link = link_to s_("I accept the|Terms of Service and Privacy Policy"), terms_path, target: "_blank" - accept_terms_label = _("I accept the %{terms_link}") % { terms_link: terms_link } @@ -36,4 +36,4 @@ - if show_recaptcha_sign_up? = recaptcha_tags .submit-container - = f.submit _("Register"), class: "btn-register btn qa-new-user-register-button" + = f.submit _("Register"), class: "btn-register btn", data: { qa_selector: 'new_user_register_button' } diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml index dae9a7acf6b..5d57337a568 100644 --- a/app/views/doorkeeper/authorizations/new.html.haml +++ b/app/views/doorkeeper/authorizations/new.html.haml @@ -46,4 +46,4 @@ = hidden_field_tag :response_type, @pre_auth.response_type = hidden_field_tag :scope, @pre_auth.scope = hidden_field_tag :nonce, @pre_auth.nonce - = submit_tag _("Authorize"), class: "btn btn-success prepend-left-10" + = submit_tag _("Authorize"), class: "btn btn-success prepend-left-10", data: { qa_selector: 'authorization_button' } diff --git a/qa/qa/page/main/oauth.rb b/qa/qa/page/main/oauth.rb index 5f6ddb9a114..2b1a9ab2b6a 100644 --- a/qa/qa/page/main/oauth.rb +++ b/qa/qa/page/main/oauth.rb @@ -5,7 +5,7 @@ module QA module Main class OAuth < Page::Base view 'app/views/doorkeeper/authorizations/new.html.haml' do - element :authorization_button, 'submit_tag _("Authorize")' # rubocop:disable QA/ElementWithPattern + element :authorization_button end def needs_authorization? @@ -13,7 +13,7 @@ module QA end def authorize! - click_button 'Authorize' + click_element :authorization_button end end end diff --git a/qa/qa/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb index 46a105003d0..c47d2ce9c74 100644 --- a/qa/qa/page/main/sign_up.rb +++ b/qa/qa/page/main/sign_up.rb @@ -5,28 +5,28 @@ module QA module Main class SignUp < Page::Base view 'app/views/devise/shared/_signup_box.html.haml' do - element :new_user_name - element :new_user_username - element :new_user_email - element :new_user_email_confirmation - element :new_user_password + element :new_user_name_field + element :new_user_username_field + element :new_user_email_field + element :new_user_email_confirmation_field + element :new_user_password_field element :new_user_register_button - element :new_user_accept_terms + element :new_user_accept_terms_checkbox end def sign_up!(user) - fill_element :new_user_name, user.name - fill_element :new_user_username, user.username - fill_element :new_user_email, user.email - fill_element :new_user_email_confirmation, user.email - fill_element :new_user_password, user.password + fill_element :new_user_name_field, user.name + fill_element :new_user_username_field, user.username + fill_element :new_user_email_field, user.email + fill_element :new_user_email_confirmation_field, user.email + fill_element :new_user_password_field, user.password - check_element :new_user_accept_terms if has_element?(:new_user_accept_terms) + check_element :new_user_accept_terms_checkbox if has_element?(:new_user_accept_terms_checkbox) signed_in = retry_until do click_element :new_user_register_button - Page::Main::Menu.act { has_personal_area? } + Page::Main::Menu.perform(&:has_personal_area?) end raise "Failed to register and sign in" unless signed_in -- cgit v1.2.1 From 2a442ae7a89d5c6a683903043a171d40aed2dcb0 Mon Sep 17 00:00:00 2001 From: Woko Date: Tue, 16 Jul 2019 03:25:15 +0000 Subject: fix `register runner with services` --- doc/ci/docker/using_docker_images.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 90565efe196..f9dcd6417e0 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -35,8 +35,8 @@ sudo gitlab-runner register \ --description "docker-ruby-2.1" \ --executor "docker" \ --docker-image ruby:2.1 \ - --docker-postgres latest \ - --docker-mysql latest + --docker-services postgres:latest \ + --docker-services mysql:latest ``` The registered runner will use the `ruby:2.1` Docker image and will run two -- cgit v1.2.1 From adece6f7f947aae14b60226cdb8c835d5a3fa684 Mon Sep 17 00:00:00 2001 From: Heinrich Lee Yu Date: Tue, 16 Jul 2019 14:51:56 +0800 Subject: Change time tracking units feature release version --- doc/workflow/time_tracking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/workflow/time_tracking.md b/doc/workflow/time_tracking.md index 4286a3625a2..b55c6b2e3df 100644 --- a/doc/workflow/time_tracking.md +++ b/doc/workflow/time_tracking.md @@ -75,7 +75,7 @@ Default conversion rates are 1mo = 4w, 1w = 5d and 1d = 8h. ### Limit displayed units to hours -> Introduced in GitLab 12.0. +> Introduced in GitLab 12.1. The display of time units can be limited to hours through the option in **Admin Area > Settings > Preferences** under 'Localization'. -- cgit v1.2.1 From dbc1544840a582ee509df017690ae3ff2666bc1b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 16 Jul 2019 11:02:37 +0100 Subject: Fixed create merge request button not enabling Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/64454 --- .../javascripts/create_merge_request_dropdown.js | 2 +- .../frontend/create_merge_request_dropdown_spec.js | 34 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js index 052168bb21c..dce9c1a5410 100644 --- a/app/assets/javascripts/create_merge_request_dropdown.js +++ b/app/assets/javascripts/create_merge_request_dropdown.js @@ -182,7 +182,7 @@ export default class CreateMergeRequestDropdown { } enable() { - if (!canCreateConfidentialMergeRequest()) return; + if (isConfidentialIssue() && !canCreateConfidentialMergeRequest()) return; this.createMergeRequestButton.classList.remove('disabled'); this.createMergeRequestButton.removeAttribute('disabled'); diff --git a/spec/frontend/create_merge_request_dropdown_spec.js b/spec/frontend/create_merge_request_dropdown_spec.js index 6e41fdabdce..dcc6fa96d18 100644 --- a/spec/frontend/create_merge_request_dropdown_spec.js +++ b/spec/frontend/create_merge_request_dropdown_spec.js @@ -1,6 +1,7 @@ import axios from '~/lib/utils/axios_utils'; import MockAdapter from 'axios-mock-adapter'; import CreateMergeRequestDropdown from '~/create_merge_request_dropdown'; +import confidentialState from '~/confidential_merge_request/state'; import { TEST_HOST } from './helpers/test_constants'; describe('CreateMergeRequestDropdown', () => { @@ -66,4 +67,37 @@ describe('CreateMergeRequestDropdown', () => { ); }); }); + + describe('enable', () => { + beforeEach(() => { + dropdown.createMergeRequestButton.classList.add('disabled'); + }); + + afterEach(() => { + confidentialState.selectedProject = {}; + }); + + it('enables button when not confidential issue', () => { + dropdown.enable(); + + expect(dropdown.createMergeRequestButton.classList).not.toContain('disabled'); + }); + + it('enables when can create confidential issue', () => { + document.querySelector('.js-create-mr').setAttribute('data-is-confidential', 'true'); + confidentialState.selectedProject = { name: 'test' }; + + dropdown.enable(); + + expect(dropdown.createMergeRequestButton.classList).not.toContain('disabled'); + }); + + it('does not enable when can not create confidential issue', () => { + document.querySelector('.js-create-mr').setAttribute('data-is-confidential', 'true'); + + dropdown.enable(); + + expect(dropdown.createMergeRequestButton.classList).toContain('disabled'); + }); + }); }); -- cgit v1.2.1 From 1dde18f39424f6eaebe1777d0bfa35c5805178cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C5=82gorzata=20Ksionek?= Date: Tue, 16 Jul 2019 11:38:19 +0200 Subject: Add code review remarks Make specs more readable --- app/models/cycle_analytics/base_methods.rb | 27 ------------------- app/models/cycle_analytics/group_level.rb | 2 +- app/models/cycle_analytics/level_base.rb | 27 +++++++++++++++++++ app/models/cycle_analytics/project_level.rb | 2 +- lib/gitlab/cycle_analytics/permissions.rb | 2 +- spec/lib/gitlab/cycle_analytics/code_stage_spec.rb | 30 ++++++++++------------ .../cycle_analytics/group_stage_summary_spec.rb | 1 + .../lib/gitlab/cycle_analytics/issue_stage_spec.rb | 22 ++++++++-------- spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb | 30 ++++++++++------------ .../gitlab/cycle_analytics/review_stage_spec.rb | 17 ++++++------ .../gitlab/cycle_analytics/staging_stage_spec.rb | 16 ++++++------ spec/lib/gitlab/cycle_analytics/usage_data_spec.rb | 2 +- 12 files changed, 87 insertions(+), 91 deletions(-) delete mode 100644 app/models/cycle_analytics/base_methods.rb create mode 100644 app/models/cycle_analytics/level_base.rb diff --git a/app/models/cycle_analytics/base_methods.rb b/app/models/cycle_analytics/base_methods.rb deleted file mode 100644 index 1c2a0854769..00000000000 --- a/app/models/cycle_analytics/base_methods.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module CycleAnalytics - module BaseMethods - STAGES = %i[issue plan code test review staging production].freeze - - def all_medians_by_stage - STAGES.each_with_object({}) do |stage_name, medians_per_stage| - medians_per_stage[stage_name] = self[stage_name].project_median - end - end - - def stats - @stats ||= STAGES.map do |stage_name| - self[stage_name].as_json - end - end - - def no_stats? - stats.all? { |hash| hash[:value].nil? } - end - - def [](stage_name) - Gitlab::CycleAnalytics::Stage[stage_name].new(options: options) - end - end -end diff --git a/app/models/cycle_analytics/group_level.rb b/app/models/cycle_analytics/group_level.rb index 58bd2eb0d9a..35efd8b8809 100644 --- a/app/models/cycle_analytics/group_level.rb +++ b/app/models/cycle_analytics/group_level.rb @@ -2,7 +2,7 @@ module CycleAnalytics class GroupLevel - include BaseMethods + include LevelBase attr_reader :options def initialize(options:) diff --git a/app/models/cycle_analytics/level_base.rb b/app/models/cycle_analytics/level_base.rb new file mode 100644 index 00000000000..543349ebf8f --- /dev/null +++ b/app/models/cycle_analytics/level_base.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module CycleAnalytics + module LevelBase + STAGES = %i[issue plan code test review staging production].freeze + + def all_medians_by_stage + STAGES.each_with_object({}) do |stage_name, medians_per_stage| + medians_per_stage[stage_name] = self[stage_name].project_median + end + end + + def stats + @stats ||= STAGES.map do |stage_name| + self[stage_name].as_json + end + end + + def no_stats? + stats.all? { |hash| hash[:value].nil? } + end + + def [](stage_name) + Gitlab::CycleAnalytics::Stage[stage_name].new(options: options) + end + end +end diff --git a/app/models/cycle_analytics/project_level.rb b/app/models/cycle_analytics/project_level.rb index abe1be11eae..4aa426c58a1 100644 --- a/app/models/cycle_analytics/project_level.rb +++ b/app/models/cycle_analytics/project_level.rb @@ -2,7 +2,7 @@ module CycleAnalytics class ProjectLevel - include BaseMethods + include LevelBase attr_reader :project, :options def initialize(project, options:) diff --git a/lib/gitlab/cycle_analytics/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb index 0da041f8950..55214e6b896 100644 --- a/lib/gitlab/cycle_analytics/permissions.rb +++ b/lib/gitlab/cycle_analytics/permissions.rb @@ -23,7 +23,7 @@ module Gitlab end def get - ::CycleAnalytics::BaseMethods::STAGES.each do |stage| + ::CycleAnalytics::LevelBase::STAGES.each do |stage| @stage_permission_hash[stage] = authorized_stage?(stage) end diff --git a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb index 68f73ba3c93..933f3c7896e 100644 --- a/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/code_stage_spec.rb @@ -34,11 +34,11 @@ describe Gitlab::CycleAnalytics::CodeStage do end describe '#events' do - it 'exposes merge requests that closes issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) + it 'exposes merge requests that closes issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) end end @@ -74,11 +74,11 @@ describe Gitlab::CycleAnalytics::CodeStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title) end end @@ -101,18 +101,16 @@ describe Gitlab::CycleAnalytics::CodeStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(4) - expect(result.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title, mr_3_1.title, mr_3_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(4) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title, mr_3_1.title, mr_3_2.title) end it 'exposes merge requests that close issues with full path for subgroup' do - result = stage.events - - expect(result.count).to eq(4) - expect(result.find { |event| event[:title] == mr_3_1.title }[:url]).to include("#{subgroup.full_path}") + expect(subject.count).to eq(4) + expect(subject.find { |event| event[:title] == mr_3_1.title }[:url]).to include("#{subgroup.full_path}") end end end diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb index 8e552b23283..7e79c3f939c 100644 --- a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb @@ -7,6 +7,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do let(:project_2) { create(:project, :repository, namespace: group) } let(:from) { 1.day.ago } let(:user) { create(:user, :admin) } + subject { described_class.new(group, from: Time.now, current_user: user).data } describe "#new_issues" do diff --git a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb index 64ac9df52b2..ffd0b84cb57 100644 --- a/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/issue_stage_spec.rb @@ -63,11 +63,11 @@ describe Gitlab::CycleAnalytics::IssueStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title) end end @@ -85,18 +85,16 @@ describe Gitlab::CycleAnalytics::IssueStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(4) - expect(result.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(4) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title) end it 'exposes merge requests that close issues with full path for subgroup' do - result = stage.events - - expect(result.count).to eq(4) - expect(result.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}") + expect(subject.count).to eq(4) + expect(subject.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}") end end end diff --git a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb index 55de6192af1..3cd1320ca9c 100644 --- a/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/plan_stage_spec.rb @@ -29,11 +29,11 @@ describe Gitlab::CycleAnalytics::PlanStage do end describe '#events' do - it 'exposes issues with metrics' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title) + it 'exposes issues with metrics' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_1.title, issue_2.title) end end @@ -65,11 +65,11 @@ describe Gitlab::CycleAnalytics::PlanStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title) end end @@ -88,18 +88,16 @@ describe Gitlab::CycleAnalytics::PlanStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(4) - expect(result.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(4) + expect(subject.map { |event| event[:title] }).to contain_exactly(issue_2_1.title, issue_2_2.title, issue_3_1.title, issue_3_2.title) end it 'exposes merge requests that close issues with full path for subgroup' do - result = stage.events - - expect(result.count).to eq(4) - expect(result.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}") + expect(subject.count).to eq(4) + expect(subject.find { |event| event[:title] == issue_3_1.title }[:url]).to include("#{subgroup.full_path}") end end end diff --git a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb index 9c7cb5811d0..6d14973c711 100644 --- a/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/review_stage_spec.rb @@ -35,13 +35,14 @@ describe Gitlab::CycleAnalytics::ReviewStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_1.title, mr_2.title) end end + context 'when group is given' do let(:user) { create(:user) } let(:group) { create(:group) } @@ -77,11 +78,11 @@ describe Gitlab::CycleAnalytics::ReviewStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:title] }).to contain_exactly(mr_2_1.title, mr_2_2.title) end end end diff --git a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb index 3e2d748396f..9ca12cc448c 100644 --- a/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/staging_stage_spec.rb @@ -39,11 +39,11 @@ describe Gitlab::CycleAnalytics::StagingStage do end describe '#events' do - it 'exposes builds connected to merge request' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) + it 'exposes builds connected to merge request' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) end end @@ -84,11 +84,11 @@ describe Gitlab::CycleAnalytics::StagingStage do end describe '#events' do - it 'exposes merge requests that close issues' do - result = stage.events + subject { stage.events } - expect(result.count).to eq(2) - expect(result.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) + it 'exposes merge requests that close issues' do + expect(subject.count).to eq(2) + expect(subject.map { |event| event[:name] }).to contain_exactly(build_1.name, build_2.name) end end end diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb index 4ef33ff6e2b..ad61bdeace7 100644 --- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb @@ -34,7 +34,7 @@ describe Gitlab::CycleAnalytics::UsageData do expect(result).to have_key(:avg_cycle_analytics) - CycleAnalytics::BaseMethods::STAGES.each do |stage| + CycleAnalytics::LevelBase::STAGES.each do |stage| expect(result[:avg_cycle_analytics]).to have_key(stage) stage_values = result[:avg_cycle_analytics][stage] -- cgit v1.2.1 From e6c61c89527eb1a8f0f8affff5a76c81af656d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C5=82gorzata=20Ksionek?= Date: Tue, 16 Jul 2019 12:28:08 +0200 Subject: Add timecop remarks --- .../cycle_analytics/group_stage_summary_spec.rb | 84 ++++++++++++++-------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb index 7e79c3f939c..6505fc714c4 100644 --- a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb @@ -11,56 +11,80 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do subject { described_class.new(group, from: Time.now, current_user: user).data } describe "#new_issues" do - it "finds the number of issues created after the 'from date'" do - Timecop.freeze(5.days.ago) { create(:issue, project: project) } - Timecop.freeze(5.days.ago) { create(:issue, project: project_2) } - Timecop.freeze(5.days.from_now) { create(:issue, project: project) } - Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + context 'with from date' do + before do + Timecop.freeze(5.days.ago) { create(:issue, project: project) } + Timecop.freeze(5.days.ago) { create(:issue, project: project_2) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + end - expect(subject.first[:value]).to eq(2) + it "finds the number of issues created after it" do + expect(subject.first[:value]).to eq(2) + end end - it "doesn't find issues from other projects" do - Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group))) } - Timecop.freeze(5.days.from_now) { create(:issue, project: project) } - Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + context 'with other projects' do + before do + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group))) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + end - expect(subject.first[:value]).to eq(2) + it "doesn't find issues from them" do + expect(subject.first[:value]).to eq(2) + end end - it "finds issues from subgroups" do - Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group, parent: group))) } - Timecop.freeze(5.days.from_now) { create(:issue, project: project) } - Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + context 'with subgroups' do + before do + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group, parent: group))) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project) } + Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } + end - expect(subject.first[:value]).to eq(3) + it "finds issues from them" do + expect(subject.first[:value]).to eq(3) + end end end describe "#deploys" do - it "finds the number of deploys made created after the 'from date'" do - Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project) } - Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project) } - Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project_2) } - Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project_2) } + context 'with from date' do + before do + Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project) } + Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project) } + Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project_2) } + Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project_2) } + end - expect(subject.second[:value]).to eq(2) + it "finds the number of deploys made created after it" do + expect(subject.second[:value]).to eq(2) + end end - it "doesn't find deploys from other projects" do - Timecop.freeze(5.days.from_now) do - create(:deployment, :success, project: create(:project, :repository, namespace: create(:group))) + context 'with other projects' do + before do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: create(:project, :repository, namespace: create(:group))) + end end - expect(subject.second[:value]).to eq(0) + it "doesn't find deploys from them" do + expect(subject.second[:value]).to eq(0) + end end - it "finds deploys from subgroups" do - Timecop.freeze(5.days.from_now) do - create(:deployment, :success, project: create(:project, :repository, namespace: create(:group, parent: group))) + context 'with subgroups' do + before do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: create(:project, :repository, namespace: create(:group, parent: group))) + end end - expect(subject.second[:value]).to eq(1) + it "finds deploys from them" do + expect(subject.second[:value]).to eq(1) + end end end end -- cgit v1.2.1 From d5080eb5ffb0ffdb8133eee3039a4429c87a107d Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Tue, 16 Jul 2019 14:07:42 +0200 Subject: Revert "Workaround for Rails 5, where LIMIT..." This reverts commit 6823e7defb45dfd86d5258b40d6f82482d1ef451. Originally implemented in https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21839, but an error was reported in https://gitlab.com/gitlab-org/gitlab-ce/issues/51729 resulting in a workaround introduced in https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21879. Now Rails 5.2 is used, this workaround no longer seems needed, so this reverts it. --- lib/gitlab/database/subquery.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/gitlab/database/subquery.rb b/lib/gitlab/database/subquery.rb index 10971d2b274..2a6f39c6a27 100644 --- a/lib/gitlab/database/subquery.rb +++ b/lib/gitlab/database/subquery.rb @@ -6,11 +6,7 @@ module Gitlab class << self def self_join(relation) t = relation.arel_table - # Work around a bug in Rails 5, where LIMIT causes trouble - # See https://gitlab.com/gitlab-org/gitlab-ce/issues/51729 - r = relation.limit(nil).arel - r.take(relation.limit_value) if relation.limit_value - t2 = r.as('t2') + t2 = relation.arel.as('t2') relation.unscoped.joins(t.join(t2).on(t[:id].eq(t2[:id])).join_sources.first) end -- cgit v1.2.1 From f219b744336364ff316cbddb1b097539ebec27bc Mon Sep 17 00:00:00 2001 From: Donald Cook Date: Tue, 16 Jul 2019 12:44:15 +0000 Subject: CE backport: Removed non-decimal event values Snowplow only accepts decimals for value This MR removes sending of value implicitly with the value attribute --- app/views/devise/shared/_tabs_normal.html.haml | 2 +- app/views/layouts/_search.html.haml | 2 +- app/views/layouts/header/_default.html.haml | 2 +- app/views/layouts/header/_new_dropdown.haml | 2 +- app/views/layouts/nav/_dashboard.html.haml | 4 ++-- app/views/projects/_new_project_fields.html.haml | 4 ++-- app/views/projects/issues/import_csv/_modal.html.haml | 2 +- app/views/projects/new.html.haml | 6 +++--- app/views/projects/project_templates/_built_in_templates.html.haml | 4 ++-- app/views/shared/_visibility_radios.html.haml | 2 +- doc/development/fe_guide/event_tracking.md | 2 +- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml index 4cd03be572f..ab8c22532fd 100644 --- a/app/views/devise/shared/_tabs_normal.html.haml +++ b/app/views/devise/shared/_tabs_normal.html.haml @@ -3,4 +3,4 @@ %a.nav-link.qa-sign-in-tab.active{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in - if allow_signup? %li.nav-item{ role: 'presentation' } - %a.nav-link.qa-register-tab{ href: '#register-pane', data: { track_label: 'sign_in_register', track_property: 'sign_in', track_event: 'click_button', track_value: 'register', toggle: 'tab' }, role: 'tab' } Register + %a.nav-link.qa-register-tab{ href: '#register-pane', data: { track_label: 'sign_in_register', track_property: '', track_event: 'click_button', track_value: '', toggle: 'tab' }, role: 'tab' } Register diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index a5f57f5893c..c62dce880c0 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -2,7 +2,7 @@ - group_data_attrs = { group_path: j(@group.path), name: j(@group.name), issues_path: issues_group_path(@group), mr_path: merge_requests_group_path(@group) } - if @project && @project.persisted? - project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: project_issues_path(@project), mr_path: project_merge_requests_path(@project), issues_disabled: !@project.issues_enabled? } -.search.search-form{ data: { track_label: "navbar_search", track_event: "activate_form_input" } } +.search.search-form{ data: { track_label: "navbar_search", track_event: "activate_form_input", track_value: "" } } = form_tag search_path, method: :get, class: 'form-inline' do |f| .search-input-container .search-input-wrap diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index fc2dea25c77..89f99472270 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -64,7 +64,7 @@ .dropdown-menu.dropdown-menu-right = render 'layouts/header/help_dropdown' - if header_link?(:user_dropdown) - %li.nav-item.header-user.dropdown{ data: { track_label: "profile_dropdown", track_event: "click_dropdown", qa_selector: 'user_menu' } } + %li.nav-item.header-user.dropdown{ data: { track_label: "profile_dropdown", track_event: "click_dropdown", track_value: "", qa_selector: 'user_menu' } } = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do = image_tag avatar_icon_for_user(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar" = sprite_icon('angle-down', css_class: 'caret-down') diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml index 1d7a501e5c2..e28efb09be5 100644 --- a/app/views/layouts/header/_new_dropdown.haml +++ b/app/views/layouts/header/_new_dropdown.haml @@ -1,4 +1,4 @@ -%li.header-new.dropdown{ data: { track_label: "new_dropdown", track_event: "click_dropdown" } } +%li.header-new.dropdown{ data: { track_label: "new_dropdown", track_event: "click_dropdown", track_value: "" } } = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", id: "js-onboarding-new-project-link", title: _("New..."), ref: 'tooltip', aria: { label: _("New...") }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do = sprite_icon('plus-square', size: 16) = sprite_icon('angle-down', css_class: 'caret-down') diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 54028dc8554..cbe713b7468 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -2,7 +2,7 @@ -# https://gitlab.com/gitlab-org/gitlab-ce/issues/49713 for more information. %ul.list-unstyled.navbar-sub-nav - if dashboard_nav_link?(:projects) - = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown", data: { track_label: "projects_dropdown", track_event: "click_dropdown" } }) do + = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown", data: { track_label: "projects_dropdown", track_event: "click_dropdown", track_value: "" } }) do %button.btn{ type: 'button', data: { toggle: "dropdown" } } = _('Projects') = sprite_icon('angle-down', css_class: 'caret-down') @@ -10,7 +10,7 @@ = render "layouts/nav/projects_dropdown/show" - if dashboard_nav_link?(:groups) - = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { id: 'nav-groups-dropdown', class: "home dropdown header-groups qa-groups-dropdown", data: { track_label: "groups_dropdown", track_event: "click_dropdown" } }) do + = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { id: 'nav-groups-dropdown', class: "home dropdown header-groups qa-groups-dropdown", data: { track_label: "groups_dropdown", track_event: "click_dropdown", track_value: "" } }) do %button.btn{ type: 'button', data: { toggle: "dropdown" } } = _('Groups') = sprite_icon('angle-down', css_class: 'caret-down') diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml index 7541737f79c..5d88be0925e 100644 --- a/app/views/projects/_new_project_fields.html.haml +++ b/app/views/projects/_new_project_fields.html.haml @@ -54,7 +54,7 @@ .form-group.row.initialize-with-readme-setting %div{ :class => "col-sm-12" } .form-check - = check_box_tag 'project[initialize_with_readme]', '1', false, class: 'form-check-input qa-initialize-with-readme-checkbox', data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "init_with_readme" } + = check_box_tag 'project[initialize_with_readme]', '1', false, class: 'form-check-input qa-initialize-with-readme-checkbox', data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "init_with_readme", track_value: "" } = label_tag 'project[initialize_with_readme]', class: 'form-check-label' do .option-title %strong= s_('ProjectsNew|Initialize repository with a README') @@ -62,4 +62,4 @@ = s_('ProjectsNew|Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.') = f.submit _('Create project'), class: "btn btn-success project-submit", data: { track_label: "#{track_label}", track_event: "click_button", track_property: "create_project", track_value: "" } -= link_to _('Cancel'), dashboard_projects_path, class: 'btn btn-cancel', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "cancel" } += link_to _('Cancel'), dashboard_projects_path, class: 'btn btn-cancel', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "cancel", track_value: "" } diff --git a/app/views/projects/issues/import_csv/_modal.html.haml b/app/views/projects/issues/import_csv/_modal.html.haml index 86bc54786ad..fe4a4236896 100644 --- a/app/views/projects/issues/import_csv/_modal.html.haml +++ b/app/views/projects/issues/import_csv/_modal.html.haml @@ -20,5 +20,5 @@ = _('It must have a header row and at least two columns: the first column is the issue title and the second column is the issue description. The separator is automatically detected.') = _('The maximum file size allowed is %{size}.') % { size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes) } .modal-footer - %button{ type: 'submit', class: 'btn btn-success', title: _('Import issues'), data: { track_label: "export_issues_csv", track_event: "click_button"} } + %button{ type: 'submit', class: 'btn btn-success', title: _('Import issues'), data: { track_label: "export_issues_csv", track_event: "click_button", track_value: ""} } = _('Import issues') diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 6eb7a124a5d..fabe636b05c 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -32,15 +32,15 @@ .col-lg-9.js-toggle-container %ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' } %li.nav-item{ role: 'presentation' } - %a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab', track_label: 'blank_project', track_event: "click_tab" }, role: 'tab' } + %a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab', track_label: 'blank_project', track_event: "click_tab", track_value: "" }, role: 'tab' } %span.d-none.d-sm-block= s_('ProjectsNew|Blank project') %span.d-block.d-sm-none= s_('ProjectsNew|Blank') %li.nav-item{ role: 'presentation' } - %a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab', track_label: 'create_from_template', track_event: "click_tab" }, role: 'tab' } + %a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab', track_label: 'create_from_template', track_event: "click_tab", track_value: "" }, role: 'tab' } %span.d-none.d-sm-block.qa-project-create-from-template-tab= s_('ProjectsNew|Create from template') %span.d-block.d-sm-none= s_('ProjectsNew|Template') %li.nav-item{ role: 'presentation' } - %a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab', track_label: 'import_project', track_event: "click_tab" }, role: 'tab' } + %a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab', track_label: 'import_project', track_event: "click_tab", track_value: "" }, role: 'tab' } %span.d-none.d-sm-block= s_('ProjectsNew|Import project') %span.d-block.d-sm-none= s_('ProjectsNew|Import') = render_if_exists 'projects/new_ci_cd_only_project_tab', active_tab: active_tab diff --git a/app/views/projects/project_templates/_built_in_templates.html.haml b/app/views/projects/project_templates/_built_in_templates.html.haml index 6159f1c3542..d1c09e83fd3 100644 --- a/app/views/projects/project_templates/_built_in_templates.html.haml +++ b/app/views/projects/project_templates/_built_in_templates.html.haml @@ -9,9 +9,9 @@ .text-muted = template.description .controls.d-flex.align-items-center - %a.btn.btn-default.append-right-10{ href: template.preview, rel: 'noopener noreferrer', target: '_blank', data: { track_label: "create_from_template", track_property: "template_preview", track_event: "click_button", track_value: template.name } } + %a.btn.btn-default.append-right-10{ href: template.preview, rel: 'noopener noreferrer', target: '_blank', data: { track_label: "template_preview", track_property: template.name, track_event: "click_button", track_value: "" } } = _("Preview") %label.btn.btn-success.template-button.choose-template.append-bottom-0{ for: template.name } - %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name, data: { track_label: "create_from_template", track_property: "template_use", track_event: "click_button" } } + %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name, data: { track_label: "template_use", track_property: template.name, track_event: "click_button", track_value: "" } } %span = _("Use template") diff --git a/app/views/shared/_visibility_radios.html.haml b/app/views/shared/_visibility_radios.html.haml index 342fdb20d41..82ffdc9cd13 100644 --- a/app/views/shared/_visibility_radios.html.haml +++ b/app/views/shared/_visibility_radios.html.haml @@ -4,7 +4,7 @@ - next if disallowed || restricted .form-check - = form.radio_button model_method, level, checked: (selected_level == level), class: 'form-check-input', data: { track_label: "blank_project", track_event: "activate_form_input", track_property: "#{model_method}", track_value: "#{level}" } + = form.radio_button model_method, level, checked: (selected_level == level), class: 'form-check-input', data: { track_label: "blank_project", track_event: "activate_form_input", track_property: "#{model_method}_#{level}", track_value: "" } = form.label "#{model_method}_#{level}", class: 'form-check-label' do = visibility_level_icon(level) .option-title diff --git a/doc/development/fe_guide/event_tracking.md b/doc/development/fe_guide/event_tracking.md index 6ab3fa4acf3..716f6ad7f92 100644 --- a/doc/development/fe_guide/event_tracking.md +++ b/doc/development/fe_guide/event_tracking.md @@ -47,7 +47,7 @@ There's a more convenient solution to this problem. When working with HAML templ Below is an example of `data-track-*` attributes assigned to a button in HAML: ```ruby -%button.btn{ data: { track_label: "create_from_template", track_property: "template_preview", track_event: "click_button", track_value: "my-template" } } +%button.btn{ data: { track_label: "template_preview", track_property: "my-template", track_event: "click_button", track_value: "" } } ``` By calling `bindTrackableContainer('.my-container')`, click handlers get bound to all elements located in `.my-container` provided that they have the necessary `data-track-*` attributes assigned to them. -- cgit v1.2.1 From 96454b0fb632e3129876456f3f190ada8caf144e Mon Sep 17 00:00:00 2001 From: Tetiana Chupryna Date: Tue, 16 Jul 2019 13:06:09 +0000 Subject: Docs Dependencies API --- doc/api/README.md | 1 + doc/api/dependencies.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 doc/api/dependencies.md diff --git a/doc/api/README.md b/doc/api/README.md index 9d90677e2bb..8e60d1c61df 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -29,6 +29,7 @@ The following API resources are available in the project context: | [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` | | [Container Registry](container_registry.md) | `/projects/:id/registry/repositories` | | [Custom attributes](custom_attributes.md) | `/projects/:id/custom_attributes` (also available for groups and users) | +| [Dependencies](dependencies.md) **[ULTIMATE]** | `/projects/:id/dependencies` | [Deploy keys](deploy_keys.md) | `/projects/:id/deploy_keys` (also available standalone) | | [Deployments](deployments.md) | `/projects/:id/deployments` | | [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) | diff --git a/doc/api/dependencies.md b/doc/api/dependencies.md new file mode 100644 index 00000000000..ed5ebdade19 --- /dev/null +++ b/doc/api/dependencies.md @@ -0,0 +1,50 @@ +# Dependencies API **(ULTIMATE)** + +CAUTION: **Caution:** +This API is in an alpha stage and considered unstable. +The response payload may be subject to change or breakage +across GitLab releases. + +Every call to this endpoint requires authentication. To perform this call, user should be authorized to read +[Project Security Dashboard](../user/application_security/security_dashboard/index.md#project-security-dashboard). + +## List project dependencies + +Get a list of project dependencies. This API partially mirroring +[Dependency List](../user/application_security/dependency_scanning/index.md#dependency-list) feature. +This list can be generated only for [languages and package managers](../user/application_security/dependency_scanning/index.md#supported-languages-and-package-managers) +supported by Gemnasium. + +``` +GET /projects/:id/dependencies +GET /projects/:id/vulnerabilities?package_manger=maven +GET /projects/:id/vulnerabilities?package_manger=yarn,bundler +``` + +| Attribute | Type | Required | Description | +| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). | +| `package_manager` | string array | no | Returns dependencies belonging to specified package manager. Valid values: `bundler`, `composer`, `maven`, `npm`, `pip` or `yarn`. | + +```bash +curl --header "PRIVATE-TOKEN: " https://gitlab.example.com/api/v4/projects/4/dependencies +``` + +Example response: + +```json +[ + { + "name": "rails", + "version": "5.0.1", + "package_manager": "bundler", + "dependency_file_path": "Gemfile.lock" + }, + { + "name": "hanami", + "version": "1.3.1", + "package_manager": "bundler", + "dependency_file_path": "Gemfile.lock" + } +] +``` -- cgit v1.2.1 From 556d213cbc8b2ce44fd4d7fafde0a28804d3ae29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Tue, 16 Jul 2019 13:12:37 +0000 Subject: Refactored WebIdeCommitsCount class We're adding more redis base counters to the web ide and other classes. We're refactoring this class in other to use the logic in other places. --- lib/api/commits.rb | 2 +- lib/gitlab/usage_data.rb | 2 +- lib/gitlab/usage_data_counters/redis_counter.rb | 19 ++++++++ .../usage_data_counters/web_ide_commits_counter.rb | 13 ++++++ lib/gitlab/web_ide_commits_counter.rb | 17 ------- .../usage_data_counters/redis_counter_spec.rb | 54 ++++++++++++++++++++++ spec/lib/gitlab/web_ide_commits_counter_spec.rb | 19 -------- spec/requests/api/commits_spec.rb | 2 +- 8 files changed, 89 insertions(+), 39 deletions(-) create mode 100644 lib/gitlab/usage_data_counters/redis_counter.rb create mode 100644 lib/gitlab/usage_data_counters/web_ide_commits_counter.rb delete mode 100644 lib/gitlab/web_ide_commits_counter.rb create mode 100644 spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb delete mode 100644 spec/lib/gitlab/web_ide_commits_counter_spec.rb diff --git a/lib/api/commits.rb b/lib/api/commits.rb index eebded87ebc..c414ad75d9d 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -126,7 +126,7 @@ module API if result[:status] == :success commit_detail = user_project.repository.commit(result[:result]) - Gitlab::WebIdeCommitsCounter.increment if find_user_from_warden + Gitlab::UsageDataCounters::WebIdeCommitsCounter.increment if find_user_from_warden present commit_detail, with: Entities::CommitDetail, stats: params[:stats] else diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 0180fe7fa71..055e01a9399 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -130,7 +130,7 @@ module Gitlab def usage_counters { - web_ide_commits: Gitlab::WebIdeCommitsCounter.total_count + web_ide_commits: Gitlab::UsageDataCounters::WebIdeCommitsCounter.total_count } end diff --git a/lib/gitlab/usage_data_counters/redis_counter.rb b/lib/gitlab/usage_data_counters/redis_counter.rb new file mode 100644 index 00000000000..123b8e1bef1 --- /dev/null +++ b/lib/gitlab/usage_data_counters/redis_counter.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Gitlab + module UsageDataCounters + module RedisCounter + def increment + Gitlab::Redis::SharedState.with { |redis| redis.incr(redis_counter_key) } + end + + def total_count + Gitlab::Redis::SharedState.with { |redis| redis.get(redis_counter_key).to_i } + end + + def redis_counter_key + raise NotImplementedError + end + end + end +end diff --git a/lib/gitlab/usage_data_counters/web_ide_commits_counter.rb b/lib/gitlab/usage_data_counters/web_ide_commits_counter.rb new file mode 100644 index 00000000000..62236fa07a3 --- /dev/null +++ b/lib/gitlab/usage_data_counters/web_ide_commits_counter.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Gitlab + module UsageDataCounters + class WebIdeCommitsCounter + extend RedisCounter + + def self.redis_counter_key + 'WEB_IDE_COMMITS_COUNT' + end + end + end +end diff --git a/lib/gitlab/web_ide_commits_counter.rb b/lib/gitlab/web_ide_commits_counter.rb deleted file mode 100644 index 1cd9b5295b9..00000000000 --- a/lib/gitlab/web_ide_commits_counter.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module WebIdeCommitsCounter - WEB_IDE_COMMITS_KEY = "WEB_IDE_COMMITS_COUNT".freeze - - class << self - def increment - Gitlab::Redis::SharedState.with { |redis| redis.incr(WEB_IDE_COMMITS_KEY) } - end - - def total_count - Gitlab::Redis::SharedState.with { |redis| redis.get(WEB_IDE_COMMITS_KEY).to_i } - end - end - end -end diff --git a/spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb new file mode 100644 index 00000000000..38b4c22e186 --- /dev/null +++ b/spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::UsageDataCounters::RedisCounter, :clean_gitlab_redis_shared_state do + context 'when redis_key is not defined' do + subject do + Class.new.extend(described_class) + end + + describe '.increment' do + it 'raises a NotImplementedError exception' do + expect { subject.increment}.to raise_error(NotImplementedError) + end + end + + describe '.total_count' do + it 'raises a NotImplementedError exception' do + expect { subject.total_count}.to raise_error(NotImplementedError) + end + end + end + + context 'when redis_key is defined' do + subject do + counter_module = described_class + + Class.new do + extend counter_module + + def self.redis_counter_key + 'foo_redis_key' + end + end + end + + describe '.increment' do + it 'increments the web ide commits counter by 1' do + expect do + subject.increment + end.to change { subject.total_count }.from(0).to(1) + end + end + + describe '.total_count' do + it 'returns the total amount of web ide commits' do + subject.increment + subject.increment + + expect(subject.total_count).to eq(2) + end + end + end +end diff --git a/spec/lib/gitlab/web_ide_commits_counter_spec.rb b/spec/lib/gitlab/web_ide_commits_counter_spec.rb deleted file mode 100644 index c51889a1c63..00000000000 --- a/spec/lib/gitlab/web_ide_commits_counter_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::WebIdeCommitsCounter, :clean_gitlab_redis_shared_state do - describe '.increment' do - it 'increments the web ide commits counter by 1' do - expect do - described_class.increment - end.to change { described_class.total_count }.from(0).to(1) - end - end - - describe '.total_count' do - it 'returns the total amount of web ide commits' do - expect(described_class.total_count).to eq(0) - end - end -end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 3df5d9412f8..204e378f7be 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -281,7 +281,7 @@ describe API::Commits do end it 'does not increment the usage counters using access token authentication' do - expect(::Gitlab::WebIdeCommitsCounter).not_to receive(:increment) + expect(::Gitlab::UsageDataCounters::WebIdeCommitsCounter).not_to receive(:increment) post api(url, user), params: valid_c_params end -- cgit v1.2.1 From 0f9a5087fcdb6cdbe1f7cef24c5fbb361755a2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhamed=20Huseinba=C5=A1i=C4=87?= Date: Tue, 16 Jul 2019 13:52:04 +0000 Subject: Note in the docs about possibility of disabling Auto DevOps at group-level for self-managed instances --- doc/topics/autodevops/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 6dfe42f68cf..f9ad952aaad 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -222,7 +222,7 @@ full use of Auto DevOps are available. If this is your fist time, we recommend y [quick start guide](quick_start_guide.md). GitLab.com users can enable/disable Auto DevOps at the project-level only. Self-managed users -can enable/disable Auto DevOps at either the project-level or instance-level. +can enable/disable Auto DevOps at the project-level, group-level or instance-level. ### Enabling/disabling Auto DevOps at the instance-level (Administrators only) -- cgit v1.2.1 From 1ebef4aae55adb5e17a76b92aea74467e49130bf Mon Sep 17 00:00:00 2001 From: Patrick Derichs Date: Fri, 5 Jul 2019 12:50:20 +0200 Subject: Add result to MoveService#execute_multiple It adds a hash response which includes the count, success state and the moved issues itself so the caller has additional information about the result of the process. --- app/controllers/boards/issues_controller.rb | 7 ++-- app/services/boards/issues/move_service.rb | 39 +++++++++++++++++++---- spec/controllers/boards/issues_controller_spec.rb | 13 ++++++-- spec/services/boards/issues/move_service_spec.rb | 4 +-- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb index 353a9806fd1..90528f75ffd 100644 --- a/app/controllers/boards/issues_controller.rb +++ b/app/controllers/boards/issues_controller.rb @@ -58,11 +58,8 @@ module Boards service = Boards::Issues::MoveService.new(board_parent, current_user, move_params(true)) issues = Issue.find(params[:ids]) - if service.execute_multiple(issues) - head :ok - else - head :unprocessable_entity - end + + render json: service.execute_multiple(issues) end def update diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb index 755d723b9a0..00ce27db7c8 100644 --- a/app/services/boards/issues/move_service.rb +++ b/app/services/boards/issues/move_service.rb @@ -11,26 +11,51 @@ module Boards end def execute_multiple(issues) - return false if issues.empty? + return execute_multiple_empty_result if issues.empty? + handled_issues = [] last_inserted_issue_id = nil - issues.map do |issue| + count = issues.each.inject(0) do |moved_count, issue| issue_modification_params = issue_params(issue) - next if issue_modification_params.empty? + next moved_count if issue_modification_params.empty? if last_inserted_issue_id - issue_modification_params[:move_between_ids] = move_between_ids({ move_after_id: nil, move_before_id: last_inserted_issue_id }) + issue_modification_params[:move_between_ids] = move_below(last_inserted_issue_id) end last_inserted_issue_id = issue.id - move_single_issue(issue, issue_modification_params) - end.all? + handled_issue = move_single_issue(issue, issue_modification_params) + handled_issues << present_issue_entity(handled_issue) if handled_issue + handled_issue && handled_issue.valid? ? moved_count + 1 : moved_count + end + + { + count: count, + success: count == issues.size, + issues: handled_issues + } end private + def present_issue_entity(issue) + ::API::Entities::Issue.represent(issue) + end + + def execute_multiple_empty_result + @execute_multiple_empty_result ||= { + count: 0, + success: false, + issues: [] + } + end + + def move_below(id) + move_between_ids({ move_after_id: nil, move_before_id: id }) + end + def move_single_issue(issue, issue_modification_params) - return false unless can?(current_user, :update_issue, issue) + return unless can?(current_user, :update_issue, issue) update(issue, issue_modification_params) end diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index 246d6f9e0f9..f8a25d814ed 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -160,7 +160,7 @@ describe Boards::IssuesController do end end - describe 'PUT move_multiple' do + describe 'PUT bulk_move' do let(:todo) { create(:group_label, group: group, name: 'Todo') } let(:development) { create(:group_label, group: group, name: 'Development') } let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user } @@ -200,12 +200,21 @@ describe Boards::IssuesController do put :bulk_move, params: move_issues_params expect(response).to have_gitlab_http_status(expected_status) + if expected_status == 200 + expect(json_response).to include( + 'count' => move_issues_params[:ids].size, + 'success' => true + ) + + expect(json_response['issues'].pluck('id')).to include(*move_issues_params[:ids]) + end + list_issues user: requesting_user, board: board, list: list2 expect(response).to have_gitlab_http_status(200) expect(response).to match_response_schema('entities/issue_boards') - responded_issues = json_response['issues'] + responded_issues = JSON.parse(response.body)['issues'] expect(responded_issues.length).to eq expected_issue_count ids_in_order = responded_issues.pluck('id') diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb index 1bfb5602df2..cf84ec8fd4c 100644 --- a/spec/services/boards/issues/move_service_spec.rb +++ b/spec/services/boards/issues/move_service_spec.rb @@ -68,8 +68,8 @@ describe Boards::Issues::MoveService do project.add_developer(user) end - it 'returns false if list of issues is empty' do - expect(described_class.new(group, user, params).execute_multiple([])).to eq(false) + it 'returns the expected result if list of issues is empty' do + expect(described_class.new(group, user, params).execute_multiple([])).to eq({ count: 0, success: false, issues: [] }) end context 'moving multiple issues' do -- cgit v1.2.1 From 52bdcdff20127b09b5f088138b899c9ad6a1048b Mon Sep 17 00:00:00 2001 From: Patrick Derichs Date: Tue, 16 Jul 2019 14:55:59 +0200 Subject: Use match_array instead of include --- spec/controllers/boards/issues_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index f8a25d814ed..2f0a8f93bd5 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -206,7 +206,7 @@ describe Boards::IssuesController do 'success' => true ) - expect(json_response['issues'].pluck('id')).to include(*move_issues_params[:ids]) + expect(json_response['issues'].pluck('id')).to match_array(move_issues_params[:ids]) end list_issues user: requesting_user, board: board, list: list2 -- cgit v1.2.1 From ca089d91f6313b32a6381aef36583e83869f4017 Mon Sep 17 00:00:00 2001 From: Patrick Derichs Date: Tue, 16 Jul 2019 16:17:37 +0200 Subject: Modified spec so a second call to json_response is avoided --- spec/controllers/boards/issues_controller_spec.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index 2f0a8f93bd5..0db58fbefc1 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -196,7 +196,7 @@ describe Boards::IssuesController do sign_in(signed_in_user) end - it 'moves issues as expected' do + it 'responds as expected' do put :bulk_move, params: move_issues_params expect(response).to have_gitlab_http_status(expected_status) @@ -208,13 +208,18 @@ describe Boards::IssuesController do expect(json_response['issues'].pluck('id')).to match_array(move_issues_params[:ids]) end + end + + it 'moves issues as expected' do + put :bulk_move, params: move_issues_params + expect(response).to have_gitlab_http_status(expected_status) list_issues user: requesting_user, board: board, list: list2 expect(response).to have_gitlab_http_status(200) expect(response).to match_response_schema('entities/issue_boards') - responded_issues = JSON.parse(response.body)['issues'] + responded_issues = json_response['issues'] expect(responded_issues.length).to eq expected_issue_count ids_in_order = responded_issues.pluck('id') -- cgit v1.2.1 From 81f1028801c81369e10c251d422592f959cf35a8 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Tue, 16 Jul 2019 09:20:53 -0500 Subject: Default manual_sorting feature flag to on --- app/controllers/groups_controller.rb | 2 +- app/controllers/projects/issues_controller.rb | 2 +- app/views/shared/issuable/_sort_dropdown.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index dbddee47997..c2517ddc10a 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -8,7 +8,7 @@ class GroupsController < Groups::ApplicationController include RecordUserLastActivity before_action do - push_frontend_feature_flag(:manual_sorting) + push_frontend_feature_flag(:manual_sorting, default_enabled: true) end respond_to :html diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 228de8bc6f3..db7ca7ef0d7 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -11,7 +11,7 @@ class Projects::IssuesController < Projects::ApplicationController include RecordUserLastActivity before_action do - push_frontend_feature_flag(:manual_sorting) + push_frontend_feature_flag(:manual_sorting, default_enabled: true) end def issue_except_actions diff --git a/app/views/shared/issuable/_sort_dropdown.html.haml b/app/views/shared/issuable/_sort_dropdown.html.haml index 403e001bfe8..df0523595f5 100644 --- a/app/views/shared/issuable/_sort_dropdown.html.haml +++ b/app/views/shared/issuable/_sort_dropdown.html.haml @@ -1,7 +1,7 @@ - sort_value = @sort - sort_title = issuable_sort_option_title(sort_value) - viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues' -- manual_sorting = viewing_issues && controller.controller_name != 'dashboard' && Feature.enabled?(:manual_sorting) +- manual_sorting = viewing_issues && controller.controller_name != 'dashboard' && Feature.enabled?(:manual_sorting, default_enabled: true) .dropdown.inline.prepend-left-10.issue-sort-dropdown .btn-group{ role: 'group' } -- cgit v1.2.1 From 1f34c37de05480724b8e0915384d9bae96a67db9 Mon Sep 17 00:00:00 2001 From: Greg Myers Date: Tue, 16 Jul 2019 14:35:50 +0000 Subject: Fix rake db:migrate:status typo in docs --- doc/administration/raketasks/maintenance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md index b3aa09abd1c..8d0b5b42515 100644 --- a/doc/administration/raketasks/maintenance.md +++ b/doc/administration/raketasks/maintenance.md @@ -257,7 +257,7 @@ sudo gitlab-rake gitlab:exclusive_lease:clear[project_housekeeping:4] To check the status of migrations, you can use the following rake task: ```bash -sudo gitlab-rake db:migrations:status +sudo gitlab-rake db:migrate:status ``` This will output a table with a `Status` of `up` or `down` for -- cgit v1.2.1 From d589a4e8e1e54eb3810cc2c29649f34a632fd3e5 Mon Sep 17 00:00:00 2001 From: drew cimino Date: Tue, 16 Jul 2019 11:43:08 -0400 Subject: Build instead of create --- spec/lib/gitlab/ci/pipeline/seed/build_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index e0bff638233..46ea0d7554b 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -233,7 +233,7 @@ describe Gitlab::Ci::Pipeline::Seed::Build do using RSpec::Parameterized let(:pipeline) do - create(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source) + build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source) end context 'matches' do -- cgit v1.2.1 From 41b77136eec455c13357566ff959382a50bc4043 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 16 Jul 2019 16:03:29 +0000 Subject: Revert "Merge branch '61145-fix-button-dimensions' into 'master'" This reverts merge request !28545 --- app/assets/stylesheets/framework/animations.scss | 2 +- app/assets/stylesheets/framework/buttons.scss | 128 +++++++++------------ app/assets/stylesheets/framework/dropdowns.scss | 17 ++- app/assets/stylesheets/framework/variables.scss | 4 - app/assets/stylesheets/pages/commits.scss | 4 +- app/assets/stylesheets/pages/issuable.scss | 4 + app/assets/stylesheets/pages/issues.scss | 1 + app/assets/stylesheets/pages/note_form.scss | 1 + app/assets/stylesheets/pages/projects.scss | 3 +- app/assets/stylesheets/pages/tree.scss | 2 +- app/helpers/dropdowns_helper.rb | 2 +- .../projects/issues/import_csv/_button.html.haml | 2 +- app/views/shared/issuable/_feed_buttons.html.haml | 8 +- .../unreleased/61145-fix-button-dimensions.yml | 5 - spec/support/features/rss_shared_examples.rb | 4 +- 15 files changed, 89 insertions(+), 98 deletions(-) delete mode 100644 changelogs/unreleased/61145-fix-button-dimensions.yml diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 6bc5632365f..6f5a2e561af 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -104,7 +104,7 @@ } .btn { - @include transition(border-color); + @include transition(background-color, border-color, color, box-shadow); } .dropdown-menu-toggle, diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index e0b6da31261..767832e242c 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -24,11 +24,12 @@ border-radius: $border-radius-default; font-size: $gl-font-size; font-weight: $gl-font-weight-normal; - padding: $gl-bordered-btn-vert-padding $gl-bordered-btn-horz-padding; + padding: $gl-vert-padding $gl-btn-padding; &:focus, &:active { background-color: $btn-active-gray; + box-shadow: $gl-btn-active-background; } } @@ -49,89 +50,77 @@ color: $text; } - &:not(:disabled):not(.disabled) { - &:hover { - box-shadow: inset 0 0 0 1px $hover-border, 0 2px 2px 0 $gl-btn-hover-shadow-light; - } + &:hover, + &:focus { + background-color: $hover-background; + border-color: $hover-border; + color: $hover-text; - &:focus { - box-shadow: inset 0 0 0 1px $hover-border, 0 0 4px 1px $blue-300; + > .icon { + color: $hover-text; } + } - &:hover, - &:focus { - background-color: $hover-background; - border-color: $hover-border; - color: $hover-text; + &:focus { + box-shadow: 0 0 4px 1px $blue-300; + } - > .icon { - color: $hover-text; - } - } + &:active { + background-color: $active-background; + border-color: $active-border; + box-shadow: inset 0 2px 4px 0 rgba($black, 0.2); + color: $active-text; - &:active, - &:active:focus { - background-color: $active-background; - border-color: $active-border; - box-shadow: inset 0 0 0 1px $hover-border, inset 0 2px 4px 0 rgba($black, 0.2); + > .icon { color: $active-text; + } - > .icon { - color: $active-text; - } + &:focus { + box-shadow: inset 0 2px 4px 0 rgba($black, 0.2); } } } -@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color, $hover-shadow-color: $gl-btn-hover-shadow-dark) { +@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) { background-color: $light; border-color: $border-light; color: $color; - &:not(:disabled):not(.disabled) { - &:hover { - box-shadow: inset 0 0 0 1px $border-normal, 0 2px 2px 0 $hover-shadow-color; - } - - &:focus { - box-shadow: inset 0 0 0 1px $border-normal, 0 0 4px 1px $blue-300; - } + &:hover, + &:focus { + background-color: $normal; + border-color: $border-normal; + color: $color; + } - &:hover, - &:focus { - background-color: $normal; - border-color: $border-normal; - color: $color; - } + &:active, + &.active { + box-shadow: $gl-btn-active-background; - &:active, - &.active { - box-shadow: inset 0 2px 4px 0 $gl-btn-hover-shadow-dark; - background-color: $dark; - border-color: $border-dark; - color: $color; - } + background-color: $dark; + border-color: $border-dark; + color: $color; } } @mixin btn-green { - @include btn-color($green-500, $green-600, $green-500, $green-700, $green-600, $green-800, $white-light); + @include btn-color($green-500, $green-600, $green-600, $green-700, $green-700, $green-800, $white-light); } @mixin btn-blue { - @include btn-color($blue-500, $blue-600, $blue-500, $blue-700, $blue-600, $blue-800, $white-light); + @include btn-color($blue-500, $blue-600, $blue-600, $blue-700, $blue-700, $blue-800, $white-light); } @mixin btn-orange { - @include btn-color($orange-500, $orange-600, $orange-500, $orange-700, $orange-600, $orange-800, $white-light); + @include btn-color($orange-500, $orange-600, $orange-600, $orange-700, $orange-700, $orange-800, $white-light); } @mixin btn-red { - @include btn-color($red-500, $red-600, $red-500, $red-700, $red-600, $red-800, $white-light); + @include btn-color($red-500, $red-600, $red-600, $red-700, $red-700, $red-800, $white-light); } @mixin btn-white { - @include btn-color($white-light, $gray-400, $gray-200, $gray-400, $gl-gray-200, $gray-500, $gl-text-color, $gl-btn-hover-shadow-light); + @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-gray-dark, $gl-text-color); } @mixin btn-with-margin { @@ -160,20 +149,21 @@ color: $gl-text-color; white-space: nowrap; - line-height: $gl-btn-line-height; &:focus:active { outline: 0; } - &.btn-xs { - font-size: $gl-btn-xs-font-size; - line-height: $gl-btn-xs-line-height; + &.btn-sm { + padding: 4px 10px; + font-size: $gl-btn-small-font-size; + line-height: $gl-btn-small-line-height; } - &.btn-sm, &.btn-xs { - padding: 3px $gl-bordered-btn-vert-padding; + padding: 2px $gl-btn-padding; + font-size: $gl-btn-xs-font-size; + line-height: $gl-btn-xs-line-height; } &.btn-success, @@ -249,7 +239,7 @@ &.dropdown-toggle { .fa-caret-down { - margin: 0; + margin-left: 3px; } } @@ -282,7 +272,10 @@ } svg { - @include btn-svg; + height: 15px; + width: 15px; + position: relative; + top: 2px; } svg, @@ -337,12 +330,6 @@ &.btn-grouped { @include btn-with-margin; } - - .btn { - border-radius: $border-radius-default; - font-size: $gl-font-size; - line-height: $gl-btn-line-height; - } } .btn-clipboard { @@ -500,25 +487,18 @@ &:active, &:focus { color: $gl-text-color-secondary; - border: 1px solid $border-gray-normal-dashed; background-color: $white-normal; } } -.btn-svg { - padding: $gl-bordered-btn-vert-padding; - - svg { - @include btn-svg; - display: block; - } +.btn-svg svg { + @include btn-svg; } // All disabled buttons, regardless of color, type, etc %disabled { background-color: $gray-light !important; border-color: $gray-200 !important; - box-shadow: none; color: $gl-text-color-disabled !important; opacity: 1 !important; cursor: default !important; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 05afcecca05..29f63e9578d 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -8,6 +8,12 @@ } } +@mixin chevron-active { + .fa-chevron-down { + color: $gray-darkest; + } +} + @mixin set-visible { transform: translateY(0); display: block; @@ -43,6 +49,7 @@ .dropdown-toggle, .dropdown-menu-toggle { + @include chevron-active; border-color: $gray-darkest; } @@ -58,12 +65,12 @@ .dropdown-toggle, .confidential-merge-request-fork-group .dropdown-toggle { - padding: $gl-bordered-btn-vert-padding $gl-bordered-btn-horz-padding; + padding: 6px 8px 6px 10px; background-color: $white-light; color: $gl-text-color; font-size: 14px; - line-height: $gl-btn-line-height; text-align: left; + border: 1px solid $border-color; border-radius: $border-radius-base; white-space: nowrap; @@ -96,6 +103,10 @@ padding-right: 25px; } + .fa { + color: $gray-darkest; + } + .fa-chevron-down { font-size: $dropdown-chevron-size; position: relative; @@ -104,10 +115,12 @@ } &:hover { + @include chevron-active; border-color: $gray-darkest; } &:focus:active { + @include chevron-active; border-color: $dropdown-toggle-active-border-color; outline: 0; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 047a9799c3f..c108f45622f 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -405,8 +405,6 @@ $tanuki-yellow: #fca326; */ $green-500-focus: rgba($green-500, 0.4); $gl-btn-active-background: rgba(0, 0, 0, 0.16); -$gl-btn-hover-shadow-dark: rgba($black, 0.2); -$gl-btn-hover-shadow-light: rgba($black, 0.1); $gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background; /* @@ -483,8 +481,6 @@ $gl-btn-padding: 10px; $gl-btn-line-height: 16px; $gl-btn-vert-padding: 8px; $gl-btn-horz-padding: 12px; -$gl-bordered-btn-vert-padding: $gl-btn-vert-padding - 1px; -$gl-bordered-btn-horz-padding: $gl-btn-horz-padding - 1px; $gl-btn-small-font-size: 13px; $gl-btn-small-line-height: 18px; $gl-btn-xs-font-size: 13px; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 3d11aa58871..0b0a4e50146 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -214,10 +214,10 @@ .label, .btn { - padding: $gl-bordered-btn-vert-padding $gl-bordered-btn-horz-padding; + padding: $gl-vert-padding $gl-btn-padding; border: 1px $border-color solid; font-size: $gl-font-size; - line-height: $gl-btn-line-height; + line-height: $line-height-base; border-radius: 0; display: flex; align-items: center; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 66ea70e79da..6a0127eb51c 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -929,6 +929,10 @@ margin: 0; } } + + .dropdown-toggle > .icon { + margin: 0 3px; + } } .right-sidebar-collapsed { diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index e51ca44476c..8359a60ec9f 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -267,6 +267,7 @@ ul.related-merge-requests > li { .fa-caret-down { pointer-events: none; color: inherit; + margin-left: 0; } } } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 1d57a4a4784..c6bac33e888 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -417,6 +417,7 @@ table { i { color: $white-light; + padding-right: 2px; margin-top: 2px; } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 09a576c11cb..e453f4a2264 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -429,7 +429,7 @@ padding: 0; background: transparent; border: 0; - line-height: 2; + line-height: 34px; margin: 0; > li + li::before { @@ -792,6 +792,7 @@ .btn { margin-top: $gl-padding; + padding: $gl-btn-vert-padding $gl-btn-padding; line-height: $gl-btn-line-height; .icon { diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 5c732ab0d1f..5664f46484e 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -90,7 +90,7 @@ .add-to-tree { vertical-align: top; - padding: $gl-bordered-btn-vert-padding; + padding: 8px; svg { top: 0; diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index fd94f07cc2c..64c5fae7d96 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -46,7 +46,7 @@ module DropdownsHelper def dropdown_toggle(toggle_text, data_attr, options = {}) default_label = data_attr[:default_label] - content_tag(:button, disabled: options[:disabled], class: "dropdown-menu-toggle btn #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do + content_tag(:button, disabled: options[:disabled], class: "dropdown-menu-toggle #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}") output << icon('chevron-down') output.html_safe diff --git a/app/views/projects/issues/import_csv/_button.html.haml b/app/views/projects/issues/import_csv/_button.html.haml index 8442a53ed61..acc2c50294f 100644 --- a/app/views/projects/issues/import_csv/_button.html.haml +++ b/app/views/projects/issues/import_csv/_button.html.haml @@ -1,6 +1,6 @@ - type = local_assigns.fetch(:type, :icon) -%button.csv-import-button.btn.btn-svg{ title: _('Import CSV'), class: ('has-tooltip' if type == :icon), +%button.csv-import-button.btn{ title: _('Import CSV'), class: ('has-tooltip' if type == :icon), data: { toggle: 'modal', target: '.issues-import-modal' } } - if type == :icon = sprite_icon('upload') diff --git a/app/views/shared/issuable/_feed_buttons.html.haml b/app/views/shared/issuable/_feed_buttons.html.haml index c9506a3295c..83f60fa6fe2 100644 --- a/app/views/shared/issuable/_feed_buttons.html.haml +++ b/app/views/shared/issuable/_feed_buttons.html.haml @@ -1,4 +1,4 @@ -= link_to safe_params.merge(rss_url_options), class: 'btn btn-svg has-tooltip js-rss-button', data: { container: 'body' }, title: _('Subscribe to RSS feed') do - = sprite_icon('rss') -= link_to safe_params.merge(calendar_url_options), class: 'btn btn-svg has-tooltip', data: { container: 'body' }, title: _('Subscribe to calendar') do - = sprite_icon('calendar') += link_to safe_params.merge(rss_url_options), class: 'btn has-tooltip', data: { container: 'body' }, title: _('Subscribe to RSS feed') do + = icon('rss') += link_to safe_params.merge(calendar_url_options), class: 'btn has-tooltip', data: { container: 'body' }, title: _('Subscribe to calendar') do + = custom_icon('icon_calendar') diff --git a/changelogs/unreleased/61145-fix-button-dimensions.yml b/changelogs/unreleased/61145-fix-button-dimensions.yml deleted file mode 100644 index 8f209ceaa8e..00000000000 --- a/changelogs/unreleased/61145-fix-button-dimensions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Updating button dimensions according to design spec -merge_request: 28545 -author: -type: fixed diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb index 02d310a9afa..0de92aedba5 100644 --- a/spec/support/features/rss_shared_examples.rb +++ b/spec/support/features/rss_shared_examples.rb @@ -6,7 +6,7 @@ end shared_examples "it has an RSS button with current_user's feed token" do it "shows the RSS button with current_user's feed token" do - expect(page).to have_css("a:has(.fa-rss)[href*='feed_token=#{user.feed_token}'], .js-rss-button[href*='feed_token=#{user.feed_token}']") + expect(page).to have_css("a:has(.fa-rss)[href*='feed_token=#{user.feed_token}']") end end @@ -18,6 +18,6 @@ end shared_examples "it has an RSS button without a feed token" do it "shows the RSS button without a feed token" do - expect(page).to have_css("a:has(.fa-rss):not([href*='feed_token']), .js-rss-button:not([href*='feed_token'])") + expect(page).to have_css("a:has(.fa-rss):not([href*='feed_token'])") end end -- cgit v1.2.1 From bd31c4be0d7cfcb0c2cc887a66c313c592ce8e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Cunha?= Date: Tue, 16 Jul 2019 16:11:10 +0000 Subject: Enable GitLabb runner to be uninstalled from cluster - Set as uninstallable app - Update docs - Adjust specs --- app/models/clusters/applications/runner.rb | 15 +++++----- app/models/clusters/concerns/application_core.rb | 10 +++++++ app/models/clusters/concerns/application_status.rb | 24 +++++++++------- .../check_uninstall_progress_service.rb | 1 + ...66-kubernetes-applications-uninstall-runner.yml | 5 ++++ doc/user/clusters/applications.md | 3 +- spec/models/clusters/applications/runner_spec.rb | 33 +++++++++++++++++++++- .../check_uninstall_progress_service_spec.rb | 8 +++++- 8 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 changelogs/unreleased/60666-kubernetes-applications-uninstall-runner.yml diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index f0256ff4d41..6ae8c3bd7f3 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -29,13 +29,6 @@ module Clusters content_values.to_yaml end - # Need to investigate if pipelines run by this runner will stop upon the - # executor pod stopping - # I.e.run a pipeline, and uninstall runner while pipeline is running - def allowed_to_uninstall? - false - end - def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name: name, @@ -47,6 +40,14 @@ module Clusters ) end + def prepare_uninstall + runner&.update!(active: false) + end + + def post_uninstall + runner.destroy! + end + private def ensure_runner diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb index 4514498b84b..803a65726d3 100644 --- a/app/models/clusters/concerns/application_core.rb +++ b/app/models/clusters/concerns/application_core.rb @@ -46,6 +46,16 @@ module Clusters command.version = version end end + + def prepare_uninstall + # Override if your application needs any action before + # being uninstalled by Helm + end + + def post_uninstall + # Override if your application needs any action after + # being uninstalled by Helm + end end end end diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb index 54a3dda6d75..342d766f723 100644 --- a/app/models/clusters/concerns/application_status.rb +++ b/app/models/clusters/concerns/application_status.rb @@ -59,29 +59,33 @@ module Clusters transition [:scheduled] => :uninstalling end - before_transition any => [:scheduled] do |app_status, _| - app_status.status_reason = nil + before_transition any => [:scheduled] do |application, _| + application.status_reason = nil end - before_transition any => [:errored] do |app_status, transition| + before_transition any => [:errored] do |application, transition| status_reason = transition.args.first - app_status.status_reason = status_reason if status_reason + application.status_reason = status_reason if status_reason end - before_transition any => [:updating] do |app_status, _| - app_status.status_reason = nil + before_transition any => [:updating] do |application, _| + application.status_reason = nil end - before_transition any => [:update_errored, :uninstall_errored] do |app_status, transition| + before_transition any => [:update_errored, :uninstall_errored] do |application, transition| status_reason = transition.args.first - app_status.status_reason = status_reason if status_reason + application.status_reason = status_reason if status_reason end - before_transition any => [:installed, :updated] do |app_status, _| + before_transition any => [:installed, :updated] do |application, _| # When installing any application we are also performing an update # of tiller (see Gitlab::Kubernetes::Helm::ClientCommand) so # therefore we need to reflect that in the database. - app_status.cluster.application_helm.update!(version: Gitlab::Kubernetes::Helm::HELM_VERSION) + application.cluster.application_helm.update!(version: Gitlab::Kubernetes::Helm::HELM_VERSION) + end + + after_transition any => [:uninstalling], :use_transactions => false do |application, _| + application.prepare_uninstall end end end diff --git a/app/services/clusters/applications/check_uninstall_progress_service.rb b/app/services/clusters/applications/check_uninstall_progress_service.rb index 8786d295d6a..e51d84ef052 100644 --- a/app/services/clusters/applications/check_uninstall_progress_service.rb +++ b/app/services/clusters/applications/check_uninstall_progress_service.rb @@ -23,6 +23,7 @@ module Clusters private def on_success + app.post_uninstall app.destroy! rescue StandardError => e app.make_errored!(_('Application uninstalled but failed to destroy: %{error_message}') % { error_message: e.message }) diff --git a/changelogs/unreleased/60666-kubernetes-applications-uninstall-runner.yml b/changelogs/unreleased/60666-kubernetes-applications-uninstall-runner.yml new file mode 100644 index 00000000000..3632c8eec20 --- /dev/null +++ b/changelogs/unreleased/60666-kubernetes-applications-uninstall-runner.yml @@ -0,0 +1,5 @@ +--- +title: Allow GitLab Runner to be uninstalled from the UI +merge_request: 30176 +author: +type: added diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index 2246ea8ed5a..67c21bd7639 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -251,6 +251,7 @@ The applications below can be uninstalled. | Application | GitLab version | Notes | | ----------- | -------------- | ----- | +| GitLab Runner | 12.2+ | Any running pipelines will be canceled. | | Ingress | 12.1+ | The associated load balancer and IP will be deleted and cannot be restored. Furthermore, it can only be uninstalled if JupyterHub is not installed. | | JupyterHub | 12.1+ | All data not committed to GitLab will be deleted and cannot be restored. | | Prometheus | 11.11+ | All data will be deleted and cannot be restored. | @@ -278,7 +279,7 @@ Error: remote error: tls: bad certificate To avoid installation errors: - Before starting the installation of applications, make sure that time is synchronized - between your GitLab server and your Kubernetes cluster. +between your GitLab server and your Kubernetes cluster. - Ensure certificates are not out of sync. When installing applications, GitLab expects a new cluster with no previous installation of Helm. You can confirm that the certificates match via `kubectl`: diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index 4f0cd0efe9c..4abe45a2152 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -18,7 +18,7 @@ describe Clusters::Applications::Runner do subject { gitlab_runner.can_uninstall? } - it { is_expected.to be_falsey } + it { is_expected.to be_truthy } end describe '#install_command' do @@ -156,4 +156,35 @@ describe Clusters::Applications::Runner do end end end + + describe '#prepare_uninstall' do + it 'pauses associated runner' do + active_runner = create(:ci_runner, contacted_at: 1.second.ago) + + expect(active_runner.status).to eq(:online) + + application_runner = create(:clusters_applications_runner, :scheduled, runner: active_runner) + application_runner.prepare_uninstall + + expect(active_runner.status).to eq(:paused) + end + end + + describe '#make_uninstalling!' do + subject { create(:clusters_applications_runner, :scheduled, runner: ci_runner) } + + it 'calls prepare_uninstall' do + expect_any_instance_of(described_class).to receive(:prepare_uninstall).and_call_original + + subject.make_uninstalling! + end + end + + describe '#post_uninstall' do + it 'destroys its runner' do + application_runner = create(:clusters_applications_runner, :scheduled, runner: ci_runner) + + expect { application_runner.post_uninstall }.to change { Ci::Runner.count }.by(-1) + end + end end diff --git a/spec/services/clusters/applications/check_uninstall_progress_service_spec.rb b/spec/services/clusters/applications/check_uninstall_progress_service_spec.rb index 9ab83d913f5..a948b442441 100644 --- a/spec/services/clusters/applications/check_uninstall_progress_service_spec.rb +++ b/spec/services/clusters/applications/check_uninstall_progress_service_spec.rb @@ -41,7 +41,7 @@ describe Clusters::Applications::CheckUninstallProgressService do end end - context 'when application is installing' do + context 'when application is uninstalling' do RESCHEDULE_PHASES.each { |phase| it_behaves_like 'a not yet terminated installation', phase } context 'when installation POD succeeded' do @@ -56,6 +56,12 @@ describe Clusters::Applications::CheckUninstallProgressService do service.execute end + it 'runs application post_uninstall' do + expect(application).to receive(:post_uninstall).and_call_original + + service.execute + end + it 'destroys the application' do expect(worker_class).not_to receive(:perform_in) -- cgit v1.2.1 From 18535eb411887ac8283cc24b8ed1e384d3aabcc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C5=82gorzata=20Ksionek?= Date: Tue, 16 Jul 2019 17:17:21 +0200 Subject: Add code review remarks Change small things for better readability --- app/models/cycle_analytics/group_level.rb | 11 +++--- app/serializers/group_analytics_stage_entity.rb | 2 +- lib/gitlab/cycle_analytics/summary/group/issue.rb | 2 +- .../cycle_analytics/group_stage_summary_spec.rb | 46 +++++++++++----------- spec/models/cycle_analytics/group_level_spec.rb | 4 +- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/app/models/cycle_analytics/group_level.rb b/app/models/cycle_analytics/group_level.rb index 35efd8b8809..508cde0ca00 100644 --- a/app/models/cycle_analytics/group_level.rb +++ b/app/models/cycle_analytics/group_level.rb @@ -3,19 +3,20 @@ module CycleAnalytics class GroupLevel include LevelBase - attr_reader :options + attr_reader :options, :group - def initialize(options:) - @options = options + def initialize(group:, options:) + @group = group + @options = options.merge(group: group) end def summary - @summary ||= ::Gitlab::CycleAnalytics::GroupStageSummary.new(options[:group], + @summary ||= ::Gitlab::CycleAnalytics::GroupStageSummary.new(group, from: options[:from], current_user: options[:current_user]).data end - def permissions(user: nil) + def permissions(*) STAGES.each_with_object({}) do |stage, obj| obj[stage] = true end diff --git a/app/serializers/group_analytics_stage_entity.rb b/app/serializers/group_analytics_stage_entity.rb index 019a3086f68..81be20e7dd8 100644 --- a/app/serializers/group_analytics_stage_entity.rb +++ b/app/serializers/group_analytics_stage_entity.rb @@ -9,7 +9,7 @@ class GroupAnalyticsStageEntity < Grape::Entity expose :description expose :group_median, as: :value do |stage| - # median returns a BatchLoader instance which we first have to unwrap by using to_f + # group_median returns a BatchLoader instance which we first have to unwrap by using to_f # we use to_f to make sure results below 1 are presented to the end-user stage.group_median.to_f.nonzero? ? distance_of_time_in_words(stage.group_median) : nil end diff --git a/lib/gitlab/cycle_analytics/summary/group/issue.rb b/lib/gitlab/cycle_analytics/summary/group/issue.rb index a5188056cb7..70073e6d843 100644 --- a/lib/gitlab/cycle_analytics/summary/group/issue.rb +++ b/lib/gitlab/cycle_analytics/summary/group/issue.rb @@ -16,7 +16,7 @@ module Gitlab end def value - @value ||= IssuesFinder.new(@current_user, group_id: @group.id, include_subgroups: true).execute.created_after(@from).count + @value ||= IssuesFinder.new(@current_user, group_id: @group.id, include_subgroups: true, created_after: @from).execute.count end end end diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb index 6505fc714c4..eea4f33ccb8 100644 --- a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb @@ -22,6 +22,16 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do it "finds the number of issues created after it" do expect(subject.first[:value]).to eq(2) end + + context 'with subgroups' do + before do + Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group, parent: group))) } + end + + it "finds issues from them" do + expect(subject.first[:value]).to eq(3) + end + end end context 'with other projects' do @@ -35,18 +45,6 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do expect(subject.first[:value]).to eq(2) end end - - context 'with subgroups' do - before do - Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project, namespace: create(:group, parent: group))) } - Timecop.freeze(5.days.from_now) { create(:issue, project: project) } - Timecop.freeze(5.days.from_now) { create(:issue, project: project_2) } - end - - it "finds issues from them" do - expect(subject.first[:value]).to eq(3) - end - end end describe "#deploys" do @@ -61,29 +59,29 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do it "finds the number of deploys made created after it" do expect(subject.second[:value]).to eq(2) end - end - context 'with other projects' do - before do - Timecop.freeze(5.days.from_now) do - create(:deployment, :success, project: create(:project, :repository, namespace: create(:group))) + context 'with subgroups' do + before do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: create(:project, :repository, namespace: create(:group, parent: group))) + end end - end - it "doesn't find deploys from them" do - expect(subject.second[:value]).to eq(0) + it "finds deploys from them" do + expect(subject.second[:value]).to eq(3) + end end end - context 'with subgroups' do + context 'with other projects' do before do Timecop.freeze(5.days.from_now) do - create(:deployment, :success, project: create(:project, :repository, namespace: create(:group, parent: group))) + create(:deployment, :success, project: create(:project, :repository, namespace: create(:group))) end end - it "finds deploys from them" do - expect(subject.second[:value]).to eq(1) + it "doesn't find deploys from them" do + expect(subject.second[:value]).to eq(0) end end end diff --git a/spec/models/cycle_analytics/group_level_spec.rb b/spec/models/cycle_analytics/group_level_spec.rb index 70c370bc39d..154c1b9c0f8 100644 --- a/spec/models/cycle_analytics/group_level_spec.rb +++ b/spec/models/cycle_analytics/group_level_spec.rb @@ -12,10 +12,10 @@ describe CycleAnalytics::GroupLevel do let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") } let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) } - subject { described_class.new(options: { from: from_date, group: group, current_user: user }) } + subject { described_class.new(group: group, options: { from: from_date, current_user: user }) } describe '#permissions' do - it 'returns permissions' do + it 'returns true for all stages' do expect(subject.permissions.values.uniq).to eq([true]) end end -- cgit v1.2.1 From 196dfd1243d5b5cf9786ff5019049104f7c22c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C5=82gorzata=20Ksionek?= Date: Tue, 16 Jul 2019 18:03:13 +0200 Subject: Remove changelog entry --- changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml diff --git a/changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml b/changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml deleted file mode 100644 index 49f558a6c9c..00000000000 --- a/changelogs/unreleased/adjust-cycle-analytics-to-group-level.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adjust cycle analytics to group level -merge_request: 30391 -author: -type: added -- cgit v1.2.1 From 2549d2f08ab1e1c8c1436896e1db15655a112c62 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 16 Jul 2019 16:51:31 +0000 Subject: Do not fail danger if suggestion commit msg > 72 We have another rule that requires squash enabled when suggestions are applied. No need to fail the ones that are more than 72 characters long since they will be squashed anyway. --- danger/commit_messages/Dangerfile | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/danger/commit_messages/Dangerfile b/danger/commit_messages/Dangerfile index ec494635f02..0c675cc4c9c 100644 --- a/danger/commit_messages/Dangerfile +++ b/danger/commit_messages/Dangerfile @@ -88,6 +88,19 @@ def lint_commit(commit) # rubocop:disable Metrics/AbcSize # We ignore revert commits as they are well structured by Git already return false if commit.message.start_with?('Revert "') + # Fail if a suggestion commit is used and squash is not enabled + if commit.message.start_with?('Apply suggestion to') + if gitlab.mr_json['squash'] + return false + else + fail_commit( + commit, + 'If you are applying suggestions, enable squash in the merge request and re-run the failed job' + ) + return true + end + end + failures = false subject, separator, details = commit.message.split("\n", 3) @@ -114,16 +127,6 @@ def lint_commit(commit) # rubocop:disable Metrics/AbcSize ) end - # Fail if a suggestion commit is used and squash is not enabled - if commit.message.start_with?('Apply suggestion to') && !gitlab.mr_json['squash'] - fail_commit( - commit, - 'If you are applying suggestions, squash needs to be enabled in the merge request' - ) - - failures = true - end - unless subject_starts_with_capital?(subject) fail_commit(commit, 'The commit subject must start with a capital letter') failures = true -- cgit v1.2.1 From f6888b9b608deb2f36a2198466cd279e1a6f2520 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 16 Jul 2019 19:54:14 +0300 Subject: Document all the available DAST variables --- doc/user/application_security/dast/index.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md index a0a917c5ebd..4b98dd73d76 100644 --- a/doc/user/application_security/dast/index.md +++ b/doc/user/application_security/dast/index.md @@ -162,6 +162,28 @@ As the DAST job belongs to a separate `dast` stage that runs after all [default stages](../../../ci/yaml/README.md#stages), don't forget to add `stage: dast` when you override the template job definition. +## Available variables + +DAST can be [configured](#customizing-the-dast-settings) using environment variables. +Since it's a wrapper around the ZAP scanning scripts +([baseline](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan) +or [full](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) scan), it +accepts all arguments those scripts recognize (the arguments are the same). +The choice of the scan type depends on the `DAST_FULL_SCAN_ENABLED` environment +variable value. + +| Environment variable | Required | Description | +|-----------------------------| ----------|--------------------------------------------------------------------------------| +| `DAST_WEBSITE` | yes | The URL of the website to scan. | +| `DAST_AUTH_URL` | no | The authentication URL of the website to scan. | +| `DAST_USERNAME` | no | The username to authenticate to in the website. | +| `DAST_PASSWORD` | no | The password to authenticate to in the website. | +| `DAST_USERNAME_FIELD` | no | The name of username field at the sign-in HTML form. | +| `DAST_PASSWORD_FIELD` | no | The name of password field at the sign-in HTML form. | +| `DAST_AUTH_EXCLUDE_URLS` | no | The URLs to skip during the authenticated scan; comma-separated, no spaces in between. | +| `DAST_TARGET_AVAILABILITY_TIMEOUT` | no | Time limit in seconds to wait for target availability. Scan is attempted nevertheless if it runs out. Integer. Defaults to `60`. | +| `DAST_FULL_SCAN_ENABLED` | no | Switches the tool to execute [ZAP Full Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Full-Scan) instead of [ZAP Baseline Scan](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan). Boolean. `true`, `True`, or `1` are considered as true value, otherwise false. Defaults to `false`. | + ## Security Dashboard The Security Dashboard is a good place to get an overview of all the security -- cgit v1.2.1 From f5dd35617925b427a7006777c19eabc4e9be82cb Mon Sep 17 00:00:00 2001 From: Sarah Yasonik Date: Tue, 16 Jul 2019 17:07:47 +0000 Subject: Update weight/priority definitions in prometheus dashboard docs --- doc/user/project/integrations/prometheus.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index eb223bf06bc..72f12972596 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -145,7 +145,6 @@ To configure a custom dashboard: ```yaml dashboard: 'Dashboard Title' - priority: 2 panel_groups: - group: 'Group Title' panels: @@ -178,7 +177,6 @@ The following tables outline the details of expected properties. | Property | Type | Required | Description | | ------ | ------ | ------ | ------ | | `dashboard` | string | yes | Heading for the dashboard. Only one dashboard should be defined per file. | -| `priority` | number | no, default to definition order | Order to appear in dashboard dropdown. Lower number means higher priority, which will be higher in the dropdown. Numbers do not need to be consecutive. | | `panel_groups` | array | yes | The panel groups which should be on the dashboard. | **Panel group (`panel_groups`) properties:** @@ -186,7 +184,7 @@ The following tables outline the details of expected properties. | Property | Type | Required | Description | | ------ | ------ | ------ | ------ | | `group` | string | required | Heading for the panel group. | -| `priority` | number | optional, defaults to order in file | Order to appear on the dashboard. Lower number means higher priority, which will be higher on the page. Numbers do not need to be consecutive. | +| `priority` | number | optional, defaults to order in file | Order to appear on the dashboard. Higher number means higher priority, which will be higher on the page. Numbers do not need to be consecutive. | | `panels` | array | required | The panels which should be in the panel group. | **Panel (`panels`) properties:** -- cgit v1.2.1 From 7a796c76c2357411e6f3a564cda92031550cb989 Mon Sep 17 00:00:00 2001 From: Jarek Ostrowski Date: Tue, 16 Jul 2019 17:31:12 +0000 Subject: Align merge request icons and text Align icons left Align text left Adjust padding to be consistent Make icons same 24 size Add changelog mr number Add right margin to widget status container Add left margin inside widget icon container Prepend left padding for error message Only give button left margin, some have buttons Prettify Adjust test summary icons Prettify Fix empty line before lint Remove author Switch to use gl-padding Use utility classes instead Prettify Change loading icon size to md Update summary_row.vue --- .../reports/components/issue_status_icon.vue | 2 +- .../javascripts/reports/components/report_item.vue | 9 +++++--- .../reports/components/report_section.vue | 4 ++-- .../javascripts/reports/components/summary_row.vue | 14 +++++++---- .../components/mr_widget_icon.vue | 2 +- .../components/mr_widget_pipeline.vue | 10 ++++---- .../components/mr_widget_status_icon.vue | 4 ++-- .../components/states/commits_header.vue | 2 +- .../components/states/nothing_to_merge.vue | 4 +++- app/assets/stylesheets/framework/icons.scss | 3 --- app/assets/stylesheets/pages/merge_requests.scss | 27 ++++++++++++++-------- app/assets/stylesheets/pages/pipelines.scss | 4 ++-- app/assets/stylesheets/pages/reports.scss | 9 +++----- changelogs/unreleased/61613-spacing-mr-widgets.yml | 5 ++++ 14 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 changelogs/unreleased/61613-spacing-mr-widgets.yml diff --git a/app/assets/javascripts/reports/components/issue_status_icon.vue b/app/assets/javascripts/reports/components/issue_status_icon.vue index 04fba43b2f3..386653b9444 100644 --- a/app/assets/javascripts/reports/components/issue_status_icon.vue +++ b/app/assets/javascripts/reports/components/issue_status_icon.vue @@ -16,7 +16,7 @@ export default { statusIconSize: { type: Number, required: false, - default: 32, + default: 24, }, }, computed: { diff --git a/app/assets/javascripts/reports/components/report_item.vue b/app/assets/javascripts/reports/components/report_item.vue index 2be9c37b00a..d477fafd3f5 100644 --- a/app/assets/javascripts/reports/components/report_item.vue +++ b/app/assets/javascripts/reports/components/report_item.vue @@ -27,7 +27,7 @@ export default { statusIconSize: { type: Number, required: false, - default: 32, + default: 24, }, isNew: { type: Boolean, @@ -43,12 +43,15 @@ export default { };