Restore granular token traversal in GraphQL type authorization

What does this MR do and why?

Follow-up to the type-level gPAT authorization work in !238311 (merged) and !241225 (merged), which moved granular personal access token authorization in GraphQL from a per-field extension to the object/type level. That move dropped the traversal behavior the field extension provided: a granular token could no longer reach a child resource through its parent boundary (for example group { groupMembers }) without also holding the parent's read permission, even though the equivalent REST endpoint allows it.

This MR restores traversal at the type level:

  • Gitlab::Graphql::Authz::TraversalSelection inspects the fields selected on the object being authorized. When every selected field defers to an authorized, non-leaf child type, the object is reached only for traversal.
  • In that case the granular check verifies boundary visibility (read_boundary) instead of the object's own permission; the child types enforce their own permissions.
  • Reading the object's own data (scalar fields) still requires its permission. Leaf children (all-scalar types, for example RepositoryLanguageType) also still require the parent permission, because an empty collection would otherwise bypass authorization.
  • Abstract (interface/union) return types are resolved to their concrete implementers (for example MemberInterface).

It also removes the now-unused traversal: GraphQL directive argument and its helper plumbing, and regenerates the introspection schema.

Related to #601728 (closed)

Closes #605833 (closed)

How to set up and validate locally

  1. Enable the granular_personal_access_tokens feature flag.

  2. Create a fine-grained token scoped to a group with only read_member (no read_group).

  3. Run:

    curl --request POST \
      --header "Authorization: Bearer <TOKEN>" \
      --header "Content-Type: application/json" \
      --data '{"query":"query { group(fullPath: \"<GROUP>\") { groupMembers { nodes { id } } } }"}' \
      http://127.0.0.1:3000/api/graphql

    The members resolve even though the token does not hold read_group, matching GET /api/v4/groups/:id/members.

MR acceptance checklist

This MR meets the MR acceptance checklist.

Edited by Alex Buijs

Merge request reports

Loading
Loading