Skip to content

Users with membership through group invitations (indirect membership) ignored as eligible approvers in security approval rules with Custom roles

Summary

A GitLab Ultimate customer reported this issue[Internal ticket].

Custom roles do not work with security policy approval rules when users have indirect membership through group invitations. Security policies that require approval from custom roles only recognize users who are directly assigned to projects with custom roles, but fail to recognize users who receive custom roles through group invitations (indirect membership). This affects all forms of group sharing, including local group invitations and SAML group syncing.

Steps to reproduce

  1. Set up group invitation with custom roles:
    • Create a custom role (e.g., based on Developer role with additional permissions)
    • Create a GitLab group and add users to it with the custom role
    • Create a project in a different namespace
    • Invite the group to the project with the custom role (Settings > Members > Invite group)
  2. Create a security policy with custom role requirement:
    • Create a merge request approval policy in Security > Policies

    • Configure the policy to require approval from the custom role only (not the base role)

    • Example policy:

      type: approval_policy
      name: Custom Role Approval Required
      rules:
        - type: any_merge_request
      actions:
        - type: require_approval
          approvals_required: 1
          role_approvers:
            - custom_role_name
  3. Test approval behaviour:
    • Create a merge request in the project
    • Observe that users with the custom role through group invitation cannot approve
    • Verify that the same users appear in the project members list with the correct custom role

Alternative reproduction with SAML group syncing:

  • Follow the same steps but use SAML group syncing instead of manual group invitations
  • Configure SAML SSO and group links that assign users to the custom role
  • The same issue occurs with SAML-synced groups

Example Project

This affects any project where:

  • Groups with custom roles are invited to projects (indirect membership)
  • Security policies require approval from custom roles

This includes scenarios with:

  • Local group invitations with custom roles
  • SAML group syncing with custom roles
  • Any other form of group sharing where custom roles are assigned

What is the current bug behavior?

  • Users who receive custom roles through group invitations (indirect membership) are not recognized as eligible approvers for security policy approval rules that specify custom roles
  • The security policy approval widget shows these users as ineligible even though they have the required custom role
  • Base roles (Developer, Maintainer, etc.) work correctly for indirect membership through group invitations
  • This affects all forms of group sharing: local group invitations, SAML group syncing, etc.

What is the expected correct behavior?

  • Users with custom roles assigned through group invitations should be recognized as eligible approvers for security policy approval rules
  • Custom roles should work consistently whether assigned through direct project membership or indirect membership via group invitations
  • The behavior should match how base roles currently work with indirect membership
  • This should work for all forms of group sharing (local groups, SAML syncing, etc.)

Relevant logs and/or screenshots

Sample screenshots in ticket.

Working scenario (base role): When the security policy is configured to require approval from "developer" role, users with custom roles based on Developer can approve because the base role is recognized through indirect membership.

Broken scenario (custom role only): When the security policy requires approval only from the custom role, the same users cannot approve despite having the custom role through group invitation.

Evidence from project members page: Users appear correctly in the project members list with their custom roles, confirming the group invitation and role assignment is working.

Output of checks

This issue affects all GitLab instances (SaaS and Self-Managed) using custom roles with group invitations.

Results of GitLab environment info

Expand for output related to GitLab environment info
GitLab Self Managed - 18.4.1 

Results of GitLab application Check

Expand for output related to the GitLab application check

Possible fixes

Root Cause Analysis: The issue is in ee/app/models/ee/project_team.rb in the members_with_access_level_or_custom_roles method. When checking for custom roles, it only queries direct project members:

users.joins(:members).where(members: { member_role_id: member_role_ids })

This doesn't account for users who have indirect access through group membership with custom roles.

Potential Solutions:

  1. Enhance project authorization propagation: Modify the project authorization system to include custom role information for indirect members, similar to how base roles are handled.
  2. Update approval rule evaluation logic: Modify ApprovalRuleLike#scan_result_policy_read_role_approvers to check both direct project members and group members with custom roles.
  3. Extend the query logic: Update members_with_access_level_or_custom_roles to include a union query that checks group members with custom roles for users who have indirect project access.

Files that likely need changes:

  • ee/app/models/ee/project_team.rb
  • ee/app/models/concerns/approval_rule_like.rb
  • Potentially the project authorization refresh logic

Edited by 🤖 GitLab Bot 🤖