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::PromoteToEpicrequirescreate_epicon the target group, which comes from thegroup_pathargument when given, otherwise it falls back to the issue's project's group at resolve time.additional_scopes:cannot express that fallback.Mutations::EpicTree::Reorderrequiresupdate_epicon the groups of the moving object and its new parent. Those groups are GIDs nested inside themovedinput 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
PromoteToEpicunder Update/Project only; the additional group requirement (create_work_itemon 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)