Remove membership requirement from granular token authorization

What does this MR do and why?

Fine-grained personal access tokens are scoped to a group or project (a "boundary") plus a list of permissions. GitLab additionally required the token's owner to be a member of that group or project — but membership isn't how GitLab grants access, so users were denied things they're allowed to do.

Example: any signed-in user can create an issue in a public project without being a member, but a token scoped to that project with Issue: Create got a 403 naming the very permission it held. An old api-scope token worked, so moving to fine-grained tokens lost functionality. The same applied to internal projects (non-members got no permissions at all), members of a subproject reading its parent group, and organization owners acting on projects in their organization.

This removes the membership check from the permission rule. The policy now answers only "what does this token permit?". Whether the user is allowed is decided by the endpoint's own authorization, which already runs after this check on every request.

Token creation still requires membership to pick a boundary; that half is tracked in the issue.

References

Screenshots or screen recordings

Not applicable — no UI changes.

How to set up and validate locally

  1. Create a public project your user is not a member of.

  2. Create a fine-grained personal access token with "all memberships" access and the Issue: Create permission.

  3. Post an issue to that project:

    curl --request POST --header "PRIVATE-TOKEN: <token>" \
      "http://gdk.test:3000/api/v4/projects/<project_id>/issues?title=test"
  4. Before: 403 Access denied: ... [Issue: Create]. After: the issue is created.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Specs run locally, all green: boundary_policy, authorize_granular_scopes_service, middleware/go, git_access (granular block), and api/group_packages.

Merge request reports

Loading
Loading