Check read_boundary instead of membership for token boundaries

What does this MR do and why?

Fine-grained personal access tokens are scoped to one group or project. Two decisions were being made with a membership check: whether you may scope a token to a resource, and whether you may be told that resource exists at all.

Membership isn't how GitLab grants access, so you couldn't scope a token to a public or internal project you weren't a member of — even though you can act on it. The only way round it was the broadest "all memberships" scope, which applies the permission to every group and project you belong to. Fixing the authorization half alone would have pushed people toward exactly that.

Both decisions now check a read_boundary permission on the group or project, declared in the role definitions rather than in the policy files. Alongside that, Authz::Boundary no longer answers any authorization questions — it's back to being a plain description of a boundary.

Users who reach a resource without being a member — through a public or internal project, a parent group they see via a subproject, or organization ownership — can now scope tokens to it. Resources you can't see still return 404 rather than revealing they exist.

Stacked on !256275 (merged) and targets its branch; it will retarget to master once that merges.

References

Screenshots or screen recordings

Not applicable — no UI changes. The token creation form still lists only your memberships, so this is reachable through the REST and GraphQL APIs for now.

How to set up and validate locally

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

  2. Scope a fine-grained token to it:

    curl --request POST --header "PRIVATE-TOKEN: <token>" \
      --header "Content-Type: application/json" \
      --data '{"name":"t","expires_at":"2026-12-01","granular_scopes":[{"access":"selected_memberships","permissions":["read_job"],"project_ids":[<project_id>]}]}' \
      "http://gdk.test:3000/api/v4/user/personal_access_tokens"
  3. Before: 404. After: the token is created and scoped to that project.

  4. Repeat with a private project you can't see — still 404.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Specs run locally: 3874 examples, 0 failures, across the boundary model and policy, the authorization service, role definitions, project and group policies, the REST and GraphQL token-creation specs, and group packages. rake gitlab:permissions:validate passes.

Merge request reports

Loading
Loading