Add granular token authorization to Incident management & alerting mutations
What does this MR do?
Adds granular Personal Access Token (gPAT) authorization to all 27 GraphQL mutations in the Incident management & alerting group (Group 7 of the GraphQL mutation authorization effort).
Each mutation gets an authorize_granular_token directive, backed by raw and
assignable permission definitions and a request-spec authorization test
(it_behaves_like 'authorizing granular token permissions for GraphQL'). The
27 corresponding mutation: lines are removed from
config/authz/graphql/authorization_todo.txt.
All 27 resources are project-scoped, so every directive uses a :project
boundary — resolved either directly from a project_path full-path argument,
or from a GlobalID argument via the resolved record's project.
Mutation → permission → boundary
| Mutation | Permission (raw) | Boundary |
|---|---|---|
AlertSetAssignees |
update_alert |
project (project_path) |
UpdateAlertStatus |
update_alert |
project (project_path) |
AlertTodoCreate |
create_todo (reused) |
project (project_path) |
CreateAlertIssue |
create_issue (reused) + update_alert |
project (project_path) |
HttpIntegrationCreate |
create_http_integration |
project (project_path) |
HttpIntegrationUpdate |
update_http_integration |
project (id → project) |
HttpIntegrationResetToken |
update_http_integration |
project (id → project) |
HttpIntegrationDestroy |
delete_http_integration |
project (id → project) |
PrometheusIntegrationCreate |
create_http_integration (inherited) |
project (project_path) |
PrometheusIntegrationUpdate |
update_http_integration (inherited) |
project (id → project) |
PrometheusIntegrationResetToken |
update_http_integration (inherited) |
project (id → project) |
TimelineEventCreate |
create_timeline_event |
project (incident_id → project) |
TimelineEventPromoteFromNote |
create_timeline_event |
project (note_id → project) |
TimelineEventUpdate |
update_timeline_event |
project (id → project) |
TimelineEventDestroy |
delete_timeline_event |
project (id → project) |
TimelineEventTagCreate |
create_timeline_event_tag |
project (project_path) |
EscalationPolicyCreate |
create_escalation_policy |
project (project_path) |
EscalationPolicyUpdate |
update_escalation_policy |
project (id → project) |
EscalationPolicyDestroy |
delete_escalation_policy |
project (id → project) |
IssuableResourceLinkCreate |
create_issuable_resource_link |
project (id [Issue] → project) |
IssuableResourceLinkDestroy |
delete_issuable_resource_link |
project (id [link] → project) |
OncallRotationCreate |
create_oncall_rotation |
project (project_path) |
OncallRotationUpdate |
update_oncall_rotation |
project (id → project) |
OncallRotationDestroy |
delete_oncall_rotation |
project (project_path) |
OncallScheduleCreate |
create_oncall_schedule |
project (project_path) |
OncallScheduleUpdate |
update_oncall_schedule |
project (project_path) |
OncallScheduleDestroy |
delete_oncall_schedule |
project (project_path) |
Permissions: created vs reused
Reused (no new permission):
create_todo(existingcreate_todoassignable) forAlertTodoCreatecreate_issue(existingcreate_work_itemassignable) forCreateAlertIssue, which also requires the newupdate_alert(see judgment calls)create_http_integration/update_http_integrationfor the three Prometheus integration mutations (see judgment call below)
New raw permissions (19) under config/authz/permissions/:
update_alert; create/update/delete_escalation_policy;
create/update/delete_http_integration;
create/update/delete_oncall_schedule;
create/update/delete_oncall_rotation;
create/update/delete_timeline_event; create_timeline_event_tag;
create/delete_issuable_resource_link.
New assignable permissions (15) — all under the existing monitoring
category, matching the pre-existing per-resource-per-action pattern there
(e.g. alert_metric_image, escalation_policy/read). There were essentially
no existing incident-management assignable permissions to reuse, so these
capabilities had to be introduced; the escalation-policy ones extend the
existing monitoring/escalation_policy resource that already had read.
Nested resources are folded into their parent's per-verb assignable bundles rather than getting standalone bundles. Raw permission names and mutation directives are unchanged — only assignable membership moves:
create/update/delete_oncall_rotationare bundled into the matchingcreate/update/delete_oncall_scheduleassignables (rotations are nested under schedules).create_timeline_event_tagis bundled into thecreate_timeline_eventassignable (tags are adjuncts of timeline events).
Judgment calls
-
CreateAlertIssuerequires bothcreate_issueandupdate_alert. The mutation inheritsauthorize :update_alert_management_alertfromMutations::AlertManagement::Base, andAlertManagement::CreateAlertIssueServicelinks the new issue to the alert and posts a system note on it. RBAC already gates the mutation on both abilities, so requiringcreate_issuealone would have been narrower than the role check. Permission arrays are AND semantics, and both assignables (Work Item: Create,Alert: Update) are grantable at the project boundary. -
Prometheus integrations reuse HTTP integration permissions. The three
PrometheusIntegration*mutations subclass theHttpIntegration*mutations and operate on the sameAlertManagement::HttpIntegrationrecords (PrometheusIntegrationCreatecreates a prometheus-type HTTP integration; update/reset-token resolve the project's HTTP integration). They inherit the parent's directive, so reusingcreate/update_http_integrationis both the honest domain mapping and required by the framework: enforcement reads the first directive, and a subclass's own directive cannot override an inherited one. This also avoids adding user-facing permissions for a deprecated feature. -
IssuableResourceLink#projectassociation.IssuableResourceLinkDestroyonly receives the link's GlobalID, and the model had no single method returning a Project (onlybelongs_to :issue). Ahas_one :project, through: :issueassociation was added so the boundary resolves and the gPAT boundary preloader can batch-load it viareflect_on_association. Safe here becauseissuable_resource_links,issues, andprojectsare allgitlab_main_org, so the join does not cross database schemas. -
Oncall rotations vs schedules keep distinct raw permissions even though they share the
admin_incident_management_oncall_scheduleability, but the rotation permissions are folded into the schedule assignable bundles since rotations are nested under schedules.
Skipped
None. All 27 mutations are authorized.
Validation
bundle exec rake gitlab:permissions:validatepasses (permission definitions, assignable permissions, REST, and GraphQL all valid — every declared permission/boundary has its authorization test).bundle exec rake gitlab:permissions:graphql:compile_docsregenerateddoc/auth/tokens/fine_grained_access_tokens_graphql.md(committed).- Request specs run in CI (not locally).
See the GraphQL implementation guide.
Changelog: added