Commit 561233ab authored by Vasilii Iakliushin's avatar Vasilii Iakliushin

Add "shared_visible_only" option to project's groups API

Contributes to https://gitlab.com/gitlab-org/gitlab/-/issues/28902

* Filter out shared groups user does not have access to

Changelog: added
parent 29582602
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
# current_user - which user is requesting groups # current_user - which user is requesting groups
# params: # params:
# with_shared: boolean (optional) # with_shared: boolean (optional)
# shared_visible_only: boolean (optional)
# shared_min_access_level: integer (optional) # shared_min_access_level: integer (optional)
# skip_groups: array of integers (optional) # skip_groups: array of integers (optional)
# #
...@@ -37,25 +38,35 @@ module Projects ...@@ -37,25 +38,35 @@ module Projects
Ability.allowed?(current_user, :read_project, project) Ability.allowed?(current_user, :read_project, project)
end end
# rubocop: disable CodeReuse/ActiveRecord
def all_groups def all_groups
groups = [] groups = []
groups << project.group.self_and_ancestors if project.group groups += [project.group.self_and_ancestors] if project.group
groups += with_shared_groups if params[:with_shared]
return [Group.none] if groups.compact.empty?
if params[:with_shared] groups
shared_groups = project.invited_groups end
if params[:shared_min_access_level] def with_shared_groups
shared_groups = shared_groups.where( shared_groups = project.invited_groups
'project_group_links.group_access >= ?', params[:shared_min_access_level] shared_groups = apply_min_access_level(shared_groups)
)
end
groups << shared_groups if params[:shared_visible_only]
[
shared_groups.public_to_user(current_user),
shared_groups.for_authorized_group_members(current_user&.id)
]
else
[shared_groups]
end end
end
groups << Group.none if groups.compact.empty? # rubocop: disable CodeReuse/ActiveRecord
groups def apply_min_access_level(groups)
return groups unless params[:shared_min_access_level]
groups.where('project_group_links.group_access >= ?', params[:shared_min_access_level])
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
---
title: Add shared_visible_only option to project's groups API
merge_request: 61118
author:
type: added
...@@ -1072,6 +1072,7 @@ GET /projects/:id/groups ...@@ -1072,6 +1072,7 @@ GET /projects/:id/groups
| `skip_groups` | array of integers | **{dotted-circle}** No | Skip the group IDs passed. | | `skip_groups` | array of integers | **{dotted-circle}** No | Skip the group IDs passed. |
| `with_shared` | boolean | **{dotted-circle}** No | Include projects shared with this group. Default is `false`. | | `with_shared` | boolean | **{dotted-circle}** No | Include projects shared with this group. Default is `false`. |
| `shared_min_access_level` | integer | **{dotted-circle}** No | Limit to shared groups with at least this [access level](members.md#valid-access-levels). | | `shared_min_access_level` | integer | **{dotted-circle}** No | Limit to shared groups with at least this [access level](members.md#valid-access-levels). |
| `shared_visible_only` | boolean | **{dotted-circle}** No | Limit to shared groups user has access to. |
```json ```json
[ [
......
...@@ -618,6 +618,8 @@ module API ...@@ -618,6 +618,8 @@ module API
optional :skip_groups, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of group ids to exclude from list' optional :skip_groups, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of group ids to exclude from list'
optional :with_shared, type: Boolean, default: false, optional :with_shared, type: Boolean, default: false,
desc: 'Include shared groups' desc: 'Include shared groups'
optional :shared_visible_only, type: Boolean, default: false,
desc: 'Limit to shared groups user has access to'
optional :shared_min_access_level, type: Integer, values: Gitlab::Access.all_values, optional :shared_min_access_level, type: Integer, values: Gitlab::Access.all_values,
desc: 'Limit returned shared groups by minimum access level to the project' desc: 'Limit returned shared groups by minimum access level to the project'
use :pagination use :pagination
......
...@@ -8,7 +8,7 @@ RSpec.describe Projects::GroupsFinder do ...@@ -8,7 +8,7 @@ RSpec.describe Projects::GroupsFinder do
let_it_be(:root_group) { create(:group, :public) } let_it_be(:root_group) { create(:group, :public) }
let_it_be(:project_group) { create(:group, :public, parent: root_group) } let_it_be(:project_group) { create(:group, :public, parent: root_group) }
let_it_be(:shared_group_with_dev_access) { create(:group, :private, parent: root_group) } let_it_be(:shared_group_with_dev_access) { create(:group, :private, parent: root_group) }
let_it_be(:shared_group_with_reporter_access) { create(:group, :private) } let_it_be(:shared_group_with_reporter_access) { create(:group, :public) }
let_it_be(:public_project) { create(:project, :public, group: project_group) } let_it_be(:public_project) { create(:project, :public, group: project_group) }
let_it_be(:private_project) { create(:project, :private, group: project_group) } let_it_be(:private_project) { create(:project, :private, group: project_group) }
...@@ -53,6 +53,24 @@ RSpec.describe Projects::GroupsFinder do ...@@ -53,6 +53,24 @@ RSpec.describe Projects::GroupsFinder do
is_expected.to match_array([project_group, root_group, shared_group_with_dev_access]) is_expected.to match_array([project_group, root_group, shared_group_with_dev_access])
end end
end end
context 'when shared_visible_only is on' do
let(:params) { super().merge(shared_visible_only: true) }
it 'returns ancestor and public shared groups' do
is_expected.to match_array([project_group, root_group, shared_group_with_reporter_access])
end
context 'when user has access to the private shared group' do
before do
shared_group_with_dev_access.add_guest(current_user)
end
it 'returns ancestor and shared groups user has access to' do
is_expected.to match_array([project_group, root_group, shared_group_with_reporter_access, shared_group_with_dev_access])
end
end
end
end end
context 'when skip group option is on' do context 'when skip group option is on' do
...@@ -74,6 +92,19 @@ RSpec.describe Projects::GroupsFinder do ...@@ -74,6 +92,19 @@ RSpec.describe Projects::GroupsFinder do
it 'returns ancestor groups for this project' do it 'returns ancestor groups for this project' do
is_expected.to match_array([project_group, root_group]) is_expected.to match_array([project_group, root_group])
end end
context 'when visible shared groups are requested' do
let(:params) do
{
with_shared: true,
shared_visible_only: true
}
end
it 'returns ancestor groups and public shared groups for this project' do
is_expected.to match_array([project_group, root_group, shared_group_with_reporter_access])
end
end
end end
end end
end end
......
...@@ -1683,7 +1683,7 @@ RSpec.describe API::Projects do ...@@ -1683,7 +1683,7 @@ RSpec.describe API::Projects do
let_it_be(:root_group) { create(:group, :public, name: 'root group') } let_it_be(:root_group) { create(:group, :public, name: 'root group') }
let_it_be(:project_group) { create(:group, :public, parent: root_group, name: 'project group') } let_it_be(:project_group) { create(:group, :public, parent: root_group, name: 'project group') }
let_it_be(:shared_group_with_dev_access) { create(:group, :private, parent: root_group, name: 'shared group') } let_it_be(:shared_group_with_dev_access) { create(:group, :private, parent: root_group, name: 'shared group') }
let_it_be(:shared_group_with_reporter_access) { create(:group, :private) } let_it_be(:shared_group_with_reporter_access) { create(:group, :public) }
let_it_be(:private_project) { create(:project, :private, group: project_group) } let_it_be(:private_project) { create(:project, :private, group: project_group) }
let_it_be(:public_project) { create(:project, :public, group: project_group) } let_it_be(:public_project) { create(:project, :public, group: project_group) }
...@@ -1765,6 +1765,14 @@ RSpec.describe API::Projects do ...@@ -1765,6 +1765,14 @@ RSpec.describe API::Projects do
end end
end end
context 'when shared_visible_only is on' do
let(:params) { super().merge(shared_visible_only: true) }
it_behaves_like 'successful groups response' do
let(:expected_groups) { [root_group, project_group, shared_group_with_reporter_access] }
end
end
context 'when search by shared group name' do context 'when search by shared group name' do
let(:params) { super().merge(search: 'shared') } let(:params) { super().merge(search: 'shared') }
......
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