Commit fbbf7ef1 authored by Pavel Shutsin's avatar Pavel Shutsin

Merge branch 'group_members_all_api_fix' into 'master'

Shared groups fix for group/:id/members/all REST/GraphQL APIs

See merge request gitlab-org/gitlab!66778
parents 5b999de2 b684a009
# frozen_string_literal: true
class GroupMembersFinder < UnionFinder
RELATIONS = %i(direct inherited descendants).freeze
RELATIONS = %i(direct inherited descendants shared_from_groups).freeze
DEFAULT_RELATIONS = %i(direct inherited).freeze
INVALID_RELATION_TYPE_ERROR_MSG = "is not a valid relation type. Valid relation types are #{RELATIONS.join(', ')}."
RELATIONS_DESCRIPTIONS = {
direct: 'Members in the group itself',
inherited: "Members in the group's ancestor groups",
descendants: "Members in the group's subgroups"
descendants: "Members in the group's subgroups",
shared_from_groups: "Invited group's members"
}.freeze
include CreatedAtFilter
......@@ -28,11 +29,7 @@ class GroupMembersFinder < UnionFinder
end
def execute(include_relations: DEFAULT_RELATIONS)
return filter_members(group_members_list) if include_relations == [:direct]
groups = groups_by_relations(include_relations)
return GroupMember.none unless groups
members = all_group_members(groups).distinct_on_user_with_max_access_level
filter_members(members)
......@@ -45,22 +42,14 @@ class GroupMembersFinder < UnionFinder
def groups_by_relations(include_relations)
check_relation_arguments!(include_relations)
case include_relations.sort
when [:inherited]
group.ancestors
when [:descendants]
group.descendants
when [:direct, :inherited]
group.self_and_ancestors
when [:descendants, :direct]
group.self_and_descendants
when [:descendants, :inherited]
find_union([group.ancestors, group.descendants], Group)
when [:descendants, :direct, :inherited]
group.self_and_hierarchy
else
nil
end
related_groups = []
related_groups << Group.by_id(group.id) if include_relations&.include?(:direct)
related_groups << group.ancestors if include_relations&.include?(:inherited)
related_groups << group.descendants if include_relations&.include?(:descendants)
related_groups << group.shared_with_groups.public_or_visible_to_user(user) if include_relations&.include?(:shared_from_groups)
find_union(related_groups, Group)
end
def filter_members(members)
......
......@@ -18,7 +18,7 @@ class GroupMember < Member
default_scope { where(source_type: SOURCE_TYPE) } # rubocop:disable Cop/DefaultScope
scope :of_groups, ->(groups) { where(source_id: groups.select(:id)) }
scope :of_groups, ->(groups) { where(source_id: groups&.select(:id)) }
scope :of_ldap_type, -> { where(ldap: true) }
scope :count_users_by_group_id, -> { group(:source_id).count }
scope :with_user, -> (user) { where(user: user) }
......
......@@ -16555,6 +16555,7 @@ Group member relation.
| <a id="groupmemberrelationdescendants"></a>`DESCENDANTS` | Members in the group's subgroups. |
| <a id="groupmemberrelationdirect"></a>`DIRECT` | Members in the group itself. |
| <a id="groupmemberrelationinherited"></a>`INHERITED` | Members in the group's ancestor groups. |
| <a id="groupmemberrelationshared_from_groups"></a>`SHARED_FROM_GROUPS` | Invited group's members. |
### `GroupPermission`
......@@ -88,15 +88,20 @@ Example response:
]
```
## List all members of a group or project including inherited members
## List all members of a group or project including inherited and invited members
Gets a list of group or project members viewable by the authenticated user, including inherited members and permissions through ancestor groups.
Gets a list of group or project members viewable by the authenticated user, including inherited members, invited users, and permissions through ancestor groups.
If a user is a member of this group or project and also of one or more ancestor groups,
only its membership with the highest `access_level` is returned.
([Improved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56677) in GitLab 13.11.)
This represents the effective permission of the user.
Members from an invited group are returned if either:
- The invited group is public.
- The requester is also a member of the invited group.
This function takes pagination parameters `page` and `per_page` to restrict the list of users.
```plaintext
......@@ -202,11 +207,11 @@ Example response:
}
```
## Get a member of a group or project, including inherited members
## Get a member of a group or project, including inherited and invited members
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17744) in GitLab 12.4.
Gets a member of a group or project, including members inherited through ancestor groups. See the corresponding [endpoint to list all inherited members](#list-all-members-of-a-group-or-project-including-inherited-members) for details.
Gets a member of a group or project, including members inherited or invited through ancestor groups. See the corresponding [endpoint to list all inherited members](#list-all-members-of-a-group-or-project-including-inherited-and-invited-members) for details.
```plaintext
GET /groups/:id/members/all/:user_id
......
......@@ -50,7 +50,7 @@ module API
end
def find_all_members_for_group(group)
GroupMembersFinder.new(group).execute
GroupMembersFinder.new(group, current_user).execute(include_relations: [:inherited, :direct, :shared_from_groups])
end
def present_members(members)
......
This diff is collapsed.
......@@ -6,6 +6,6 @@ RSpec.describe Types::GroupMemberRelationEnum do
specify { expect(described_class.graphql_name).to eq('GroupMemberRelation') }
it 'exposes all the existing group member relation type values' do
expect(described_class.values.keys).to contain_exactly('DIRECT', 'INHERITED', 'DESCENDANTS')
expect(described_class.values.keys).to contain_exactly('DIRECT', 'INHERITED', 'DESCENDANTS', 'SHARED_FROM_GROUPS')
end
end
......@@ -56,12 +56,16 @@ RSpec.describe 'getting group members information' do
context 'member relations' do
let_it_be(:child_group) { create(:group, :public, parent: parent_group) }
let_it_be(:grandchild_group) { create(:group, :public, parent: child_group) }
let_it_be(:invited_group) { create(:group, :public) }
let_it_be(:child_user) { create(:user) }
let_it_be(:grandchild_user) { create(:user) }
let_it_be(:invited_user) { create(:user) }
let_it_be(:group_link) { create(:group_group_link, shared_group: child_group, shared_with_group: invited_group) }
before_all do
child_group.add_guest(child_user)
grandchild_group.add_guest(grandchild_user)
invited_group.add_guest(invited_user)
end
it 'returns direct members' do
......@@ -71,6 +75,13 @@ RSpec.describe 'getting group members information' do
expect_array_response(child_user)
end
it 'returns invited members plus inherited members' do
fetch_members(group: child_group, args: { relations: [:DIRECT, :INHERITED, :SHARED_FROM_GROUPS] })
expect(graphql_errors).to be_nil
expect_array_response(invited_user, user_1, user_2, child_user)
end
it 'returns direct and inherited members' do
fetch_members(group: child_group, args: { relations: [:DIRECT, :INHERITED] })
......
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