Make resolver-level granular token scope checks visible to docs and validation tooling

The fine-grained (granular) personal access token framework for GraphQL enforces token permissions through authorize_granular_token directives declared on types and mutations. Three pieces of tooling read only those declared directives: the docs generator that produces doc/auth/tokens/fine_grained_access_tokens_graphql.md (rake task gitlab:permissions:graphql:compile_docs), the validation task gitlab:permissions:validate, and the spec permission scanner that tracks authorization test coverage. Some mutations cannot express a required boundary as a directive because the boundary is not derivable from a top-level mutation argument, so they call Authz::Tokens::AuthorizeGranularScopesService manually in the resolver instead. That manual path is invisible to all three tools.

Current examples

  • Mutations::Issues::PromoteToEpic requires create_epic on the target group, which comes from the group_path argument when given, otherwise it falls back to the issue's project's group at resolve time. additional_scopes: cannot express that fallback.
  • Mutations::EpicTree::Reorder requires update_epic on the groups of the moving object and its new parent. Those groups are GIDs nested inside the moved input object, and the boundary extractor only reads top-level arguments.
  • ProjectSubscriptionCreate (added in !245447 (merged)) uses the same manual pattern.

Consequences

  • The generated reference docs miss these requirements. For example, the docs list PromoteToEpic under Update/Project only; the additional group requirement (create_work_item on the target group) is invisible. A customer who builds a token from the docs gets denied at runtime.
  • Spec coverage accounting drifts. The additional_scope_permissions: option of the authorization shared example registers a test credit for the permission with the spec scanner, but no directive registers a matching endpoint, so the credit is not tied to any declaration.

Proposal

One option under consideration: let a directive declare a scope with a permission and boundary type but no boundary argument, marked as resolved in the resolver. This would let the docs generator, validation task, and scanner see the requirement while runtime enforcement stays in the resolver.

Raised by a maintainer during review of !245441 (merged): !245441 (comment 3768432918)

Edited by 🤖 GitLab Bot 🤖