Commit 0aa318a1 authored by Etienne Baqué's avatar Etienne Baqué

Merge branch 'group-links-create-service-prevent-sharing-groups' into 'master'

Check Group Hierarchy Share Settings in GroupLinks CreateService

See merge request gitlab-org/gitlab!63107
parents 098ee9d6 aa55411b
...@@ -11,8 +11,8 @@ class Groups::GroupLinksController < Groups::ApplicationController ...@@ -11,8 +11,8 @@ class Groups::GroupLinksController < Groups::ApplicationController
if shared_with_group if shared_with_group
result = Groups::GroupLinks::CreateService result = Groups::GroupLinks::CreateService
.new(shared_with_group, current_user, group_link_create_params) .new(group, shared_with_group, current_user, group_link_create_params)
.execute(group) .execute
return render_404 if result[:http_status] == 404 return render_404 if result[:http_status] == 404
......
...@@ -442,6 +442,12 @@ class Group < Namespace ...@@ -442,6 +442,12 @@ class Group < Namespace
end end
end end
def self_and_descendants_ids
strong_memoize(:self_and_descendants_ids) do
self_and_descendants.pluck(:id)
end
end
def direct_members def direct_members
GroupMember.active_without_invites_and_requests GroupMember.active_without_invites_and_requests
.non_minimal_access .non_minimal_access
......
...@@ -3,27 +3,51 @@ ...@@ -3,27 +3,51 @@
module Groups module Groups
module GroupLinks module GroupLinks
class CreateService < Groups::BaseService class CreateService < Groups::BaseService
def execute(shared_group) def initialize(shared_group, shared_with_group, user, params)
unless group && shared_group && @shared_group = shared_group
super(shared_with_group, user, params)
end
def execute
unless shared_with_group && shared_group &&
can?(current_user, :admin_group_member, shared_group) && can?(current_user, :admin_group_member, shared_group) &&
can?(current_user, :read_group, group) can?(current_user, :read_group, shared_with_group) &&
sharing_allowed?
return error('Not Found', 404) return error('Not Found', 404)
end end
link = GroupGroupLink.new( link = GroupGroupLink.new(
shared_group: shared_group, shared_group: shared_group,
shared_with_group: group, shared_with_group: shared_with_group,
group_access: params[:shared_group_access], group_access: params[:shared_group_access],
expires_at: params[:expires_at] expires_at: params[:expires_at]
) )
if link.save if link.save
group.refresh_members_authorized_projects(direct_members_only: true) shared_with_group.refresh_members_authorized_projects(direct_members_only: true)
success(link: link) success(link: link)
else else
error(link.errors.full_messages.to_sentence, 409) error(link.errors.full_messages.to_sentence, 409)
end end
end end
private
attr_reader :shared_group
alias_method :shared_with_group, :group
def sharing_allowed?
sharing_outside_hierarchy_allowed? || within_hierarchy?
end
def sharing_outside_hierarchy_allowed?
!shared_group.root_ancestor.namespace_settings.prevent_sharing_groups_outside_hierarchy
end
def within_hierarchy?
shared_group.root_ancestor.self_and_descendants_ids.include?(shared_with_group.id)
end
end end
end end
end end
...@@ -372,7 +372,7 @@ module API ...@@ -372,7 +372,7 @@ module API
expires_at: params[:expires_at] expires_at: params[:expires_at]
} }
result = ::Groups::GroupLinks::CreateService.new(shared_with_group, current_user, group_link_create_params).execute(shared_group) result = ::Groups::GroupLinks::CreateService.new(shared_group, shared_with_group, current_user, group_link_create_params).execute
shared_group.preload_shared_group_links shared_group.preload_shared_group_links
if result[:status] == :success if result[:status] == :success
......
...@@ -11,8 +11,8 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -11,8 +11,8 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
let_it_be(:group) { create(:group, :private, parent: group_parent) } let_it_be(:group) { create(:group, :private, parent: group_parent) }
let_it_be(:group_child) { create(:group, :private, parent: group) } let_it_be(:group_child) { create(:group, :private, parent: group) }
let_it_be(:shared_group_parent) { create(:group, :private) } let_it_be(:shared_group_parent, refind: true) { create(:group, :private) }
let_it_be(:shared_group) { create(:group, :private, parent: shared_group_parent) } let_it_be(:shared_group, refind: true) { create(:group, :private, parent: shared_group_parent) }
let_it_be(:shared_group_child) { create(:group, :private, parent: shared_group) } let_it_be(:shared_group_child) { create(:group, :private, parent: shared_group) }
let_it_be(:project_parent) { create(:project, group: shared_group_parent) } let_it_be(:project_parent) { create(:project, group: shared_group_parent) }
...@@ -28,7 +28,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -28,7 +28,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
let(:user) { group_user } let(:user) { group_user }
subject { described_class.new(group, user, opts) } subject { described_class.new(shared_group, group, user, opts) }
before do before do
group.add_guest(group_user) group.add_guest(group_user)
...@@ -36,11 +36,11 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -36,11 +36,11 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
end end
it 'adds group to another group' do it 'adds group to another group' do
expect { subject.execute(shared_group) }.to change { group.shared_group_links.count }.from(0).to(1) expect { subject.execute }.to change { group.shared_group_links.count }.from(0).to(1)
end end
it 'returns false if shared group is blank' do it 'returns false if shared group is blank' do
expect { subject.execute(nil) }.not_to change { group.shared_group_links.count } expect { described_class.new(nil, group, user, opts) }.not_to change { group.shared_group_links.count }
end end
context 'user does not have access to group' do context 'user does not have access to group' do
...@@ -51,7 +51,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -51,7 +51,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
end end
it 'returns error' do it 'returns error' do
result = subject.execute(shared_group) result = subject.execute
expect(result[:status]).to eq(:error) expect(result[:status]).to eq(:error)
expect(result[:http_status]).to eq(404) expect(result[:http_status]).to eq(404)
...@@ -67,7 +67,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -67,7 +67,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
end end
it 'returns error' do it 'returns error' do
result = subject.execute(shared_group) result = subject.execute
expect(result[:status]).to eq(:error) expect(result[:status]).to eq(:error)
expect(result[:http_status]).to eq(404) expect(result[:http_status]).to eq(404)
...@@ -85,7 +85,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -85,7 +85,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
it 'is executed only for the direct members of the group' do it 'is executed only for the direct members of the group' do
expect(UserProjectAccessChangedService).to receive(:new).with(contain_exactly(group_user.id)).and_call_original expect(UserProjectAccessChangedService).to receive(:new).with(contain_exactly(group_user.id)).and_call_original
subject.execute(shared_group) subject.execute
end end
end end
...@@ -94,7 +94,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -94,7 +94,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
let(:user) { group_user } let(:user) { group_user }
it 'create proper authorizations' do it 'create proper authorizations' do
subject.execute(shared_group) subject.execute
expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey
expect(Ability.allowed?(user, :read_project, project)).to be_truthy expect(Ability.allowed?(user, :read_project, project)).to be_truthy
...@@ -106,7 +106,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -106,7 +106,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
let(:user) { parent_group_user } let(:user) { parent_group_user }
it 'create proper authorizations' do it 'create proper authorizations' do
subject.execute(shared_group) subject.execute
expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey
expect(Ability.allowed?(user, :read_project, project)).to be_falsey expect(Ability.allowed?(user, :read_project, project)).to be_falsey
...@@ -118,7 +118,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -118,7 +118,7 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
let(:user) { child_group_user } let(:user) { child_group_user }
it 'create proper authorizations' do it 'create proper authorizations' do
subject.execute(shared_group) subject.execute
expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey
expect(Ability.allowed?(user, :read_project, project)).to be_falsey expect(Ability.allowed?(user, :read_project, project)).to be_falsey
...@@ -127,4 +127,28 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do ...@@ -127,4 +127,28 @@ RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
end end
end end
end end
context 'sharing outside the hierarchy is disabled' do
before do
shared_group_parent.namespace_settings.update!(prevent_sharing_groups_outside_hierarchy: true)
end
it 'prevents sharing with a group outside the hierarchy' do
result = subject.execute
expect(group.reload.shared_group_links.count).to eq(0)
expect(result[:status]).to eq(:error)
expect(result[:http_status]).to eq(404)
end
it 'allows sharing with a group within the hierarchy' do
sibling_group = create(:group, :private, parent: shared_group_parent)
sibling_group.add_guest(group_user)
result = described_class.new(shared_group, sibling_group, user, opts).execute
expect(sibling_group.reload.shared_group_links.count).to eq(1)
expect(result[:status]).to eq(:success)
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment