Add granular token authorization to User & collaboration mutations

What does this MR do and why?

Adds authorize_granular_token directives, permission definitions, and authorization tests for the User & collaboration features group of GraphQL mutations (40 mutations), as part of clearing config/authz/graphql/authorization_todo.txt.

Implementation follows the GraphQL implementation guide.

All 40 group mutations are covered (no skips); 40 lines removed from the authorization todo file. Generated token documentation regenerated via gitlab:permissions:graphql:compile_docs.

Mutations and permissions

Mutation class Directive
app/graphql/mutations/achievements/award.rb authorize_granular_token permissions: :award_achievement, boundary_argument: :achievement_id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/achievements/create.rb authorize_granular_token permissions: :create_achievement, boundary_argument: :namespace_id, boundary_type: :group
app/graphql/mutations/achievements/delete.rb authorize_granular_token permissions: :delete_achievement, boundary_argument: :achievement_id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/achievements/delete_user_achievement.rb authorize_granular_token permissions: :delete_user_achievement, boundary_argument: :user_achievement_id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/achievements/revoke.rb authorize_granular_token permissions: :revoke_achievement, boundary_argument: :user_achievement_id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/achievements/update.rb authorize_granular_token permissions: :update_achievement, boundary_argument: :achievement_id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/achievements/update_user_achievement.rb authorize_granular_token permissions: :update_user_achievement, boundary: :user, boundary_type: :user
app/graphql/mutations/achievements/update_user_achievement_priorities.rb authorize_granular_token permissions: :update_user_achievement, boundary: :user, boundary_type: :user
app/graphql/mutations/award_emojis/add.rb authorize_granular_token permissions: :create_award_emoji, boundaries: [
app/graphql/mutations/award_emojis/remove.rb authorize_granular_token permissions: :delete_award_emoji, boundaries: [
app/graphql/mutations/award_emojis/toggle.rb authorize_granular_token permissions: [:create_award_emoji, :delete_award_emoji], boundaries: [
app/graphql/mutations/custom_emoji/create.rb authorize_granular_token permissions: :create_custom_emoji, boundary_argument: :group_path, boundary_type: :group
app/graphql/mutations/custom_emoji/destroy.rb authorize_granular_token permissions: :delete_custom_emoji, boundary_argument: :id, boundary: :group, boundary_type: :group
app/graphql/mutations/customer_relations/contacts/create.rb authorize_granular_token permissions: :create_crm_contact, boundary_argument: :group_id, boundary_type: :group
app/graphql/mutations/customer_relations/contacts/update.rb authorize_granular_token permissions: :update_crm_contact, boundary_argument: :id, boundary: :group, boundary_type: :group
app/graphql/mutations/customer_relations/organizations/create.rb authorize_granular_token permissions: :create_crm_organization, boundary_argument: :group_id, boundary_type: :group
app/graphql/mutations/customer_relations/organizations/update.rb authorize_granular_token permissions: :update_crm_organization, boundary_argument: :id, boundary: :group, boundary_type: :group
app/graphql/mutations/import/source_users/cancel_reassignment.rb authorize_granular_token permissions: :update_placeholder_reassignment, boundary_argument: :id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/import/source_users/keep_all_as_placeholder.rb authorize_granular_token permissions: :update_placeholder_reassignment, boundary_argument: :namespace_id, boundary_type: :group
app/graphql/mutations/import/source_users/keep_as_placeholder.rb authorize_granular_token permissions: :update_placeholder_reassignment, boundary_argument: :id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/import/source_users/reassign.rb authorize_granular_token permissions: :update_placeholder_reassignment, boundary_argument: :id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/import/source_users/resend_notification.rb authorize_granular_token permissions: :update_placeholder_reassignment, boundary_argument: :id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/import/source_users/retry_failed_reassignment.rb authorize_granular_token permissions: :update_placeholder_reassignment, boundary_argument: :id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/import/source_users/undo_keep_as_placeholder.rb authorize_granular_token permissions: :update_placeholder_reassignment, boundary_argument: :id, boundary: :namespace, boundary_type: :group
app/graphql/mutations/timelogs/create.rb authorize_granular_token permissions: :create_timelog, boundary_argument: :issuable_id, boundary: :resource_parent, boundary_type: :project
app/graphql/mutations/timelogs/delete.rb authorize_granular_token permissions: :delete_timelog, boundary_argument: :id, boundary: :project, boundary_type: :project
app/graphql/mutations/user_callouts/create.rb authorize_granular_token permissions: :create_callout, boundary: :user, boundary_type: :user
app/graphql/mutations/users/custom_attributes/delete.rb authorize_granular_token permissions: :delete_custom_attribute, boundary: :instance, boundary_type: :instance
app/graphql/mutations/users/custom_attributes/set.rb authorize_granular_token permissions: :update_custom_attribute, boundary: :instance, boundary_type: :instance
app/graphql/mutations/users/group_callouts/create.rb authorize_granular_token permissions: :create_callout, boundary_argument: :group_id, boundary_type: :group
app/graphql/mutations/users/saved_replies/create.rb authorize_granular_token permissions: :create_saved_reply, boundary: :user, boundary_type: :user
app/graphql/mutations/users/saved_replies/destroy.rb authorize_granular_token permissions: :delete_saved_reply, boundary: :user, boundary_type: :user
app/graphql/mutations/users/saved_replies/update.rb authorize_granular_token permissions: :update_saved_reply, boundary: :user, boundary_type: :user
app/graphql/mutations/users/set_namespace_commit_email.rb authorize_granular_token permissions: :update_commit_email, boundary: :user, boundary_type: :user
ee/app/graphql/mutations/groups/saved_replies/create.rb authorize_granular_token permissions: :create_saved_reply, boundary_argument: :group_id, boundary_type: :group
ee/app/graphql/mutations/groups/saved_replies/destroy.rb authorize_granular_token permissions: :delete_saved_reply, boundary_argument: :id, boundary: :group, boundary_type: :group
ee/app/graphql/mutations/groups/saved_replies/update.rb authorize_granular_token permissions: :update_saved_reply, boundary_argument: :id, boundary: :group, boundary_type: :group
ee/app/graphql/mutations/projects/saved_replies/create.rb authorize_granular_token permissions: :create_saved_reply, boundary_argument: :project_id, boundary_type: :project
ee/app/graphql/mutations/projects/saved_replies/destroy.rb authorize_granular_token permissions: :delete_saved_reply, boundary_argument: :id, boundary: :project, boundary_type: :project
ee/app/graphql/mutations/projects/saved_replies/update.rb authorize_granular_token permissions: :update_saved_reply, boundary_argument: :id, boundary: :project, boundary_type: :project

How was this validated?

  • bundle exec rake gitlab:permissions:validate passes
  • bundle exec rake gitlab:permissions:graphql:compile_docs regenerated and committed
  • Authorization specs added per boundary type (shared example authorizing granular token permissions for GraphQL); specs run in CI

Note: MR opened by the orchestrating session on behalf of the implementation agent; description assembled from the branch diff.

🤖 Generated with Claude Code

Merge request reports

Loading