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: 1. **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 2. **Data integrity issue**: In [some cases](https://gitlab.com/gitlab-org/gitlab/-/work_items/504386), users who were never members of a namespace are shown as the source of membership 3. **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 1. Create a user `test` and add them to the `test` group 2. Ensure the `test/banking` subgroup inherits users from the `test` parent group 3. Add the `test/banking` group to `deploy/test/banking/banking` (another top-level group's project) via group invite 4. Navigate to the `deploy/test/banking/banking` project members page 5. 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 ![Screenshot_2024-10-02_at_11.27.21_AM](/uploads/8959ab6dcd79da2cad0dfd16877bca3d/Screenshot_2024-10-02_at_11.27.21_AM.png) 1. Create a top-level group "Group 1" (with path `group-1`) 2. Create a subgroup within "Group 1" (`group-1/subgroup`) 3. Create a project within "Group 1" (`group-1/target-project`) 4. Invite a user (`example-user`) to "Group 1" with the **Guest** role 5. Invite `example-user` to `group-1/subgroup` with the **Developer** role 6. Invite `group-1/subgroup` to `group-1/target-project` with Developer permissions 7. View the `group-1/target-project > Manage > Members` page and check the "Source" for `example-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](https://gitlab.com/gitlab-org/gitlab/-/work_items/504386) 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?
issue