Skip to content

SCRUM-200 — gateway_dev: admin-auth upstream, users route, viewer read gate

Plan ref: GWD-1 (docs/11-admin-plane-plan.md).

What changed

Change Where
Upstream phpadmin → adminauth (env GATEWAY_DEV_UPSTREAM_ADMINAUTH_URL) services/gateway_dev/internal/router/dev.go, config, harnesses, .env.example, README
/api/admin/config/ gate live_ops → viewer (Config re-checks writes per route). POST …/channels/live/releases stays admin dev.go
New routes /api/admin/users and /api/admin/users/ → adminauth, admin minimum dev.go
Compose: GATEWAY_DEV_UPSTREAM_ADMINAUTH_URL: ${ADMIN_AUTH_URL:-http://admin-auth:8080} deploy/compose.yaml, deploy/.env.example
Docs: techspec route table; adminui router comment (UI config routes stay live_ops until SCRUM-252/253) docs/05-gateway-techspec.md, services/adminui/src/router/index.ts

Why both users patterns: with only the subtree, Go 1.27's ServeMux answers /api/admin/users with a 307 to /api/admin/users/ before auth runs, so an anonymous caller would learn the route exists instead of getting a 401.

How to verify

1. Unit tests

cd services/gateway_dev
go vet ./... && go test -race -count=1 ./...

Expected: all packages ok. The new table test is internal/authn/middleware_test.go: TestRoutePolicyUnderRealMiddleware — the real route table through the real middleware, 16 rows:

Request viewer live_ops admin
GET /api/admin/config/namespaces 200 → config
PUT /api/admin/config/namespaces/x/draft 200 → config (Config itself will 403)
POST /api/admin/config/channels/live/releases 403 403 200 → config
POST /api/admin/config/channels/staging/releases 200 → config
GET /api/admin/users 403 403 200 → adminauth
POST /api/admin/users 200 → adminauth
PATCH /api/admin/users/{id} 403 200 → adminauth
DELETE /api/admin/users/invites/42 200 → adminauth
POST /admin-auth/login (no token) 200 → adminauth
GET /api/admin/dashboard/overview, GET /api/admin/session/players 200

The test client refuses to follow redirects, so a 307 fails the row.

Mutation check (done during review): deleting the exact /api/admin/users route made 4 rows fail with status = 307; lowering the users subtree to RoleLiveOps failed the PATCH … as live_ops row. Both reverted.

2. Smoke test (real binaries, echo upstreams)

cd services/gateway_dev
bash testdata/smoke/smoke.sh

Every line prints -> <actual> [expect <expected>]; they should match. The ones this ticket changed:

--- 13. the config prefix needs viewer ---
    GET /api/admin/config/namespaces (viewer)  -> 200 - upstream=config
--- 14b. staff account management is admin-only ... ---
    GET /api/admin/users (viewer)   -> 403 insufficient_role
    GET /api/admin/users (live_ops) -> 403 insufficient_role
    GET /api/admin/users (admin)    -> 200 - upstream=adminauth   [NOT 307]
    POST /api/admin/users (admin)   -> 200 - upstream=adminauth   [NOT 307]
    PATCH /api/admin/users/some-id (admin) -> 200 - upstream=adminauth
--- extra ---
    GET /admin-auth/login -> 200 - upstream=adminauth

No line should start with FINDING.

3. By hand (optional)

Follow services/gateway_dev/testdata/manual/TESTING.md — the role table there now includes the users row and the viewer config read.

Results at time of writing

  • go vet, go test -race: pass (gateway_dev, all packages).
  • smoke.sh: all checks as expected, no FINDING.
  • adminui: vue-tsc, eslint, prettier on the router file, guard.spec.ts (11 tests): pass.
  • docker compose config: not run (no Docker on the dev box); YAML parses and the variable renders as written.

Deploy note

The host's stand-in auth container is named php-admin-auth. Before redeploying gateway_dev there, set ADMIN_AUTH_URL=http://php-admin-auth:8080 in deploy/.env, or /admin-auth/* returns 502 until admin-auth is in compose (SCRUM-240).