Return suggested reviewers, blockers and diffs to fine-grained tokens
What does this MR do and why?
A fine-grained personal access token that can read a merge request gets no data for three of its EE fields, because their types declare no authorize_granular_token directive. Each type is returned only by its field on MergeRequest, which authorizes the token with read_merge_request, and each now declares a directive and leaves authorization_todo.txt:
| Type | Returned only by | Directive |
|---|---|---|
SuggestedReviewersType |
MergeRequest.suggestedReviewers |
parent_authorizes: :read_merge_request |
BlockingMergeRequests |
MergeRequest.blockingMergeRequests |
read_merge_request_dependency |
MergeRequestDiff |
MergeRequest.mergeRequestDiffs |
parent_authorizes: :read_merge_request |
BlockingMergeRequests asks for read_merge_request_dependency on the merge request's project, as the REST merge request dependency routes do. It is in Merge Request: Read with read_merge_request, as read_merge_request_diff and read_merge_request_reviewer are, so a token with that group reads the three fields and the skips widen nothing.
The checks for the user stay as they are. Each merge request in visibleMergeRequests is a MergeRequest checked against the token again, which leaves out a blocker in a project the token does not cover; totalCount still counts it, as GitLab already shows users how many blockers they cannot read.
SuggestedReviewersType and MergeRequestDiff use the parent_authorizes: form added by !266839 (merged).
References
- Part of #638945, the Code Review GraphQL types for fine-grained tokens.
- Related to #593878, the GraphQL gaps for fine-grained tokens.
MergeRequestRiskAssessmentandApprovalRuleBlockageReasonalready skip withparent_authorizes.
I found this while working on gitlab-mcp-server.
Screenshots or screen recordings
No UI change.
How to set up and validate locally
-
In a private project with a merge request, create a fine-grained token with Project: Read and Merge Request: Read for that project, and run:
query { project(fullPath: "<project>") { mergeRequest(iid: "<iid>") { blockingMergeRequests { totalCount } mergeRequestDiffs { nodes { createdAt } } } } } -
On master
blockingMergeRequestsisnullandmergeRequestDiffs.nodesis empty; on this branch both return data.
The new examples check each field, a token without read_merge_request_dependency, and a blocker in a project the token does not cover.
Checks I ran (Ruby 3.3.11)
- Step 1 as a request spec,
suggestedReviewersincluded: master returnssuggestedReviewers: null,blockingMergeRequests: nullandmergeRequestDiffs: { nodes: [] }with no error, and this branch returns the three. - With a second blocker in another project the user can read, the token gets
totalCount: 2,hiddenCount: 0and only the blocker in its own project invisibleMergeRequests. - With master's types and todo list, the new examples give 7 examples, 4 failures: every example that expects data fails.
- Putting back any one type file from master fails only the examples that read that type.
- With the skip
BlockingMergeRequestshad before, the new examples give 7 examples, 1 failure: the one withoutread_merge_request_dependency. - On this branch, the changed spec, the three type specs and the blocking merge requests mutation spec: 27 examples, 0 failures, 1 pending (a shared example that needs a top-level group).
gitlab:permissions:validate, RuboCop and the commit linter pass;gitlab:permissions:graphql:compile_docschanges nothing beyond the row it adds forBlockingMergeRequests, which is committed. Left to the pipeline: Ruby 3.4.10 and the FOSS run.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.