Incorrect membership source attribution when users have multiple inheritance paths
Summary
When users have membership through multiple inheritance paths (direct membership, subgroup inheritance, and/or group invites), GitLab incorrectly displays the membership source. The system should always display the source of the user's highest permission level, but currently shows incorrect or incomplete information about where that permission originates.
This manifests in several ways:
- Subgroup invite scenario: When a subgroup is invited to a project and users are inherited through that subgroup, the source shows the top-level parent group instead of the invited subgroup
- Data integrity issue: In some cases, users who were never members of a namespace are shown as the source of membership
- Complex inheritance with role escalation: When users have both direct group membership and subgroup membership with higher roles, and the subgroup is invited to a project, the source displays the lower-permission path instead of the highest-permission path
Expected Behavior
Core principle: The membership source should always reflect where the user's displayed highest permission comes from.
When a user has multiple possible sources of access to a project:
- Display the source that grants the highest role
- Use the correct membership type ("Shared" vs "Inherited" vs "Direct") based on that highest-permission source
- Show the most specific group/path in the inheritance chain
Membership type definitions:
- Shared: Access comes from a group being invited to the group or project (group sharing)
- Inherited: Access comes from being a member of a parent group/namespace in the hierarchy
- Direct: User was directly added to the project
Steps to Reproduce
Scenario A: Subgroup invite with simple inheritance
- Create a user
testand add them to thetestgroup - Ensure the
test/bankingsubgroup inherits users from thetestparent group - Add the
test/bankinggroup todeploy/test/banking/banking(another top-level group's project) via group invite - Navigate to the
deploy/test/banking/bankingproject members page - Observe the inheritance information for user
test
Current Behavior: User test displays Source: Inherited from test (top-level parent group)
Expected Behavior: User test should display Source: Invited group test/banking with membership type "Shared" (because the access comes from the subgroup being invited to the project)
Scenario B: Complex inheritance with role escalation
- Create a top-level group "Group 1" (with path
group-1) - Create a subgroup within "Group 1" (
group-1/subgroup) - Create a project within "Group 1" (
group-1/target-project) - Invite a user (
example-user) to "Group 1" with the Guest role - Invite
example-usertogroup-1/subgroupwith the Developer role - Invite
group-1/subgrouptogroup-1/target-projectwith Developer permissions - View the
group-1/target-project > Manage > Memberspage and check the "Source" forexample-user
Current Behavior: example-user shows Source: Inherited from Group 1 with Guest role
Expected Behavior: example-user should show Source: Invited group group-1/subgroup with Developer role and membership type "Shared" (because the highest permission comes from the subgroup being invited to the project, not the inherited Guest role from the parent)
Example Project: https://gitlab.com/membership-type-bug/target-project
Scenario C: Incorrect non-member attribution
Current Behavior: Users who aren't and have never been members of a namespace are shown as the source of membership
Expected Behavior: Only actual members in the inheritance chain should be shown as sources
Note: This scenario is intermittent and affects certain namespaces - full reproduction steps not yet identified.
Current vs Expected Comparison
| Scenario | Current Behavior | Expected Behavior | Expected Membership Type |
|---|---|---|---|
| User in parent group → subgroup invited to project | Shows "Inherited from parent-group" | Shows "Invited group subgroup" | Shared |
| User: Parent (Guest) + Subgroup (Developer) → subgroup invited to project | Shows "Inherited from parent-group" (Guest) | Shows "Invited group subgroup" (Developer) | Shared |
| Non-member shown as source | Shows incorrect user as source | Shows actual source in permission inheritance chain | [Depends on actual source] |
Impact
- Permission auditing is unreliable: Users cannot accurately determine who has access and why
- Security confusion: May lead to misunderstandings about actual access paths and permission levels
- Troubleshooting difficulty: Makes debugging permission issues significantly harder
- User trust: Violates principle of least surprise for access management
- Compliance risk: Incorrect source attribution could cause issues in security audits
Technical Investigation Needed
- How does the system currently determine which source to display when multiple paths exist?
- Is there logic to compare permission levels across different sources?
- Are there edge cases where the "highest permission source" might be ambiguous?
- What happens when permissions are equal from multiple sources?
