Allow token authentication for design management images

What does this MR do and why?

Design management images (/-/design_management/designs/:id/(:sha/)raw_image and …/resized_image/:size) are currently only accessible to session-authenticated (browser) requests. API clients can read all design metadata — including the image URLs — through GraphQL (DesignManagement::Design.image / imageV432x230), but then have no way to actually fetch those images on non-public projects:

  • the routes are not part of the REST or GraphQL API (the REST Design entity's image_url points back at this same web route), and
  • web requests only accept PAT/OAuth tokens for an allowlist of request formats — RSS/ICS, /api, repository archives, release downloads (Gitlab::Auth::AuthFinders#valid_web_access_format?) — which design images are not part of.

So a token that can read everything about a design cannot download it: clients (mobile apps, integrations, exporters) get a sign-in redirect instead of the image.

This MR adds a :design sessionless authentication format, valid only on design management image paths, and authenticates it in Projects::DesignManagement::DesignsController show actions — the same mechanism (and motivation) previously used for repository archives (#28978 (closed)) and release downloads.

Notes for reviewers:

  • Authorization is unchanged. authorize_read_design! (can?(current_user, :read_design, design)) still runs on every request; this only adds an authentication transport for a read-only endpoint.
  • Tokens must carry the api or read_api scope (RequestAuthenticator#find_user_from_any_authentication_method passes scopes: [:api, :read_api], as for the other web-access formats). Insufficient scopes keep behaving as unauthenticated.
  • Project/group access tokens (bot users) are intentionally not added to RequestAuthenticator#can_sign_in_bot?, matching release downloads.

How to set up and validate locally

  1. On a private project, add a design to an issue (issue → Designs → upload), and create a personal access token with the read_api scope.

  2. Get the design's image URL via GraphQL:

    { project(fullPath: "<path>") { issue(iid: "<iid>") { designCollection { designs(first: 1) { nodes { image } } } } } }
  3. Without this change: curl -i -H "PRIVATE-TOKEN: <token>" "<image url>" redirects to /users/sign_in.

  4. With this change: the same request returns the image (200, Content-Type: image/…). A token with an insufficient scope (for example read_user) still gets the sign-in redirect, and a token whose user cannot read the design still gets a 404.

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Merge request reports

Loading