Return project settings to fine-grained tokens

What does this MR do and why?

A fine-grained token with update_project can run ProjectSettingsUpdate, but it gets no settings back, because ProjectSetting declares no authorize_granular_token directive. On master the mutation saves the settings and then answers Cannot return null for non-nullable field ProjectSettingsUpdatePayload.projectSettings. DuoContextExclusionSettings declares none either, so Project.duoContextExclusionSettings is null for a token with read_project.

Both types hold settings of one project, so both now declare parent_authorizes: :read_project, the form added by !266839 (merged), and leave authorization_todo.txt. That matches REST, where GET /projects/:id asks a fine-grained token for read_project and returns project settings. ProjectSetting is still returned only in the payload of ProjectSettingsUpdate, which checks update_project.

The checks for the user do not change, so a token reads no more than its user can. The project field of ProjectSetting keeps read_project, so update_project alone gets project: null on a private project.

References

  • Part of #638945, the Code Review GraphQL types for fine-grained tokens.
  • Related to #593878, the GraphQL gaps for fine-grained tokens.

I found this while working on gitlab-mcp-server.

Screenshots or screen recordings

No UI change.

How to set up and validate locally

  1. Where ProjectSettingsUpdate is available (GitLab.com, or an instance with an active GitLab Duo add-on), create a fine-grained token with Project: Update for a project you maintain.
  2. Run mutation { projectSettingsUpdate(input: { fullPath: "<project>", duoFeaturesEnabled: true }) { projectSettings { duoFeaturesEnabled } errors } } with it.
  3. On master the setting is saved and the response is the error above; on this branch projectSettings returns the settings.
  4. With a token that has Project: Read, run query { project(fullPath: "<project>") { duoContextExclusionSettings { exclusionRules } } }: null on master, the rules on this branch.

I ran these steps as new examples of the two request specs: they fail on master's types and pass on this branch.

Checks I ran (Ruby 3.3.11)
  • mutations/projects/project_settings_update_spec.rb: the token with update_project gets the settings, the exclusion rules among them, and the project field stays null for it. The payload has its own examples because the skipped child type shared example expects no GraphQL error when the token lacks the parent's permission, and a refused mutation returns one.
  • project/duo_context_exclusion_settings_spec.rb: the skipped child type shared example with read_project on Project.duoContextExclusionSettings.
  • With master's types and todo list, the two request specs give 18 examples, 3 failures, all three new or changed.
  • On this branch, the two request specs, the two type specs and the ProjectSettingsUpdate mutation spec: 43 examples, 0 failures. The two request specs and gitlab:permissions:validate also pass on a local merge with current master.
  • gitlab:permissions:validate, RuboCop and the commit linter pass; the three compile_docs tasks change nothing of this MR. 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.

Edited by José M. Requena Plens

Merge request reports

Loading
Loading