Make Orbit: Read the one fine-grained token permission for GitLab Orbit

What does this MR do and why?

  • Adds one fine-grained token permission for GitLab Orbit: Orbit: Read (read_orbit), under the User tab.
  • It guards every Orbit REST route and POST /orbit/mcp. A fine-grained token without it gets a 403.
  • The token check is a gateway, same as Global Search's /search. Results follow the owner's access. Group and project selections don't limit it, and the token UI says so.
  • read_knowledge_graph and execute_orbit_mcp_tool stay defined, deprecated and hidden. They expand to raw permissions no Orbit route checks, so tokens that hold them get a 403 on Orbit. The tokens keep their other permissions and can still rotate, so no migration is needed.
  • Shows the settings resource as Orbit Setting. The permission name doesn't change.

Background and why we landed here: my comment on #992. Group-limited access moves to a follow-up with /groups/:id/orbit/* routes.

Token Orbit
Orbit: Read under User Everything the owner can read
Orbit: Read under Group and project, or All memberships 403
Old Knowledge Graph: Read or MCP tool: Execute 403, token still rotates
Legacy PAT with api or read_api Unchanged

References

Screenshots or screen recordings

orbit-read-permission-form

orbit-token-details

How to set up and validate locally

  1. Enable fine-grained tokens:

    Feature.enable(:granular_personal_access_tokens)
  2. Go to User Settings > Personal access tokens > Generate token > Fine-grained token.

  3. Open the User tab. Under GitLab Orbit, add Orbit with Read.

  4. Query two groups you can read:

    curl --header "PRIVATE-TOKEN: <token>" --header "Content-Type: application/json" \
      --request POST "https://gdk.test:3443/api/v4/orbit/query" \
      --data '{"query":{"query_type":"traversal","nodes":[{"id":"p","entity":"Project","filters":{"full_path":{"starts_with":"acme-corp/"}},"columns":["full_path"]}],"limit":3}}'
    # both groups: HTTP 200, row_count 3
  5. Call MCP with the same token:

    curl --header "Authorization: Bearer <token>" --header "Content-Type: application/json" \
      --request POST "https://gdk.test:3443/api/v4/orbit/mcp" \
      --data '{"jsonrpc":"2.0","method":"tools/list","id":"1","params":{}}'
    # HTTP 200
  6. Try a token without Orbit: Read, one with it only under Group and project access, and one with the old Knowledge Graph: Read:

    HTTP 403
    {"error":"insufficient_granular_scope", ... "[Orbit: Read]."}

Verified end to end on a GDK VM with the full Orbit stack. 13 checks cover user, group, all-memberships, legacy, old-name, and MCP tokens. One check rotates an old-name token.

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.

Related to gitlab-org/orbit/knowledge-graph#310 (closed)

Edited by Michael Angelo Rivera

Merge request reports

Loading
Loading