Authorize fine-grained tokens to read achievements

What does this MR do and why?

A fine-grained personal access token gets no achievements or awards back: the lists come back empty. The Achievement, UserAchievement and UserAchievementCount GraphQL types have no authorize_granular_token directive, and read_achievement and read_user_achievement are only listed in definitions_todo.txt, so no permission a user can select grants them.

This MR:

  • defines both permissions and removes them from definitions_todo.txt;
  • adds the read_achievement assignable permission (Achievement: Read, group and user boundaries) holding both, as delete_achievement holds its two;
  • declares the user boundary on UserAchievement and UserAchievementCount, which belong to the recipient, and the group boundary on the Namespace.achievements field;
  • lets Achievement skip with parent_authorizes: a recipient can read it without access to its group, and a group boundary on the type would always take precedence over a user one;
  • removes the three types from authorization_todo.txt and regenerates doc/auth/tokens/fine_grained_access_tokens_graphql.md.

Policies do not change. A group's achievements need Group: Read and Achievement: Read on the group. A user profile (userAchievements, achievementCounts) needs User: Read and Achievement: Read on the user boundary, and the awards listed under a group's achievements need Achievement: Read on the user boundary too.

The choices to check are the new assignable permission and the parent_authorizes skip on Achievement.

References

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

Screenshots or screen recordings

Not applicable: no frontend change.

How to set up and validate locally

  1. Create a fine-grained token in the Rails console as in Step 7 of doc/development/permissions/granular_access/graphql_implementation_guide.md, for a member of a group with an awarded achievement, with the group as the boundary and permissions: [:read_group, :read_achievement].

  2. Query the group's achievements with it:

    curl --request POST --header "PRIVATE-TOKEN: <token>" --header "Content-Type: application/json" \
      --data '{"query": "{ group(fullPath: \"<group>\") { achievements { nodes { name userAchievements { nodes { id } } } } } }"}' \
      --url "http://gdk.test:3000/api/graphql"
  3. On master read_achievement cannot be granted and nodes is empty. On this branch the achievements are returned, their awards only when the token also holds read_achievement on the user boundary, and a token without read_achievement gets no achievements.

Specs I ran (Ruby 3.3.11)
  • On master's code the new group spec does not load: the shared example finds no assignable permission for read_achievement.
  • On the first commit's code, with group boundaries on the three types: 29 examples, 8 failures, among them a token with only the user boundary reading its own awards.
  • On this branch, the changed specs, every other achievement spec (types, resolvers, policies, mutations, the user achievements query with its N+1 example), the namespace, group and user type specs, and the authz and permission task specs: 899 examples, 3 pending, and 6 failures, all in awarded_achievements_controller_spec.rb, which needs the built frontend.
  • bundle exec rake gitlab:permissions:validate: all six checks pass. RuboCop on the changed Ruby files: no offenses. The commit linter: no findings.
  • Left to the pipeline: Ruby 3.4.10, the FOSS-only jobs and awarded_achievements_controller_spec.rb, which needs the built frontend.

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