Skip to content

fix(queue): require authentication and ADMIN role on queue admin endpoints - #18

Merged
ameeribro4-sudo merged 2 commits into
OpenPeerX:mainfrom
ZionApprove:fix/issue-11-queue-admin-auth
Aug 20, 2026
Merged

fix(queue): require authentication and ADMIN role on queue admin endpoints#18
ameeribro4-sudo merged 2 commits into
OpenPeerX:mainfrom
ZionApprove:fix/issue-11-queue-admin-auth

Conversation

@ZionApprove

@ZionApprove ZionApprove commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #11

Queue administration and control endpoints were reachable by any anonymous HTTP client: both QueueController and QueueAdminController shipped with their guard lines commented out, referencing guard symbols that do not exist in the codebase. This PR wires the real auth stack onto both controllers — JwtAuthGuard for authentication, plus the identity-domain RbacGuard with @Roles(UserRole.ADMIN) for authorization — so destructive operations (pause/empty/drain, DLQ recover/clear, job retry/remove, manual triggers, test endpoints) are admin-only, while read-only metrics and health remain available to any authenticated user.

The single most important design decision: RbacGuard instead of AdminGuard. AdminGuard compares user.role against the lowercase string literal 'admin', but the auth service mints tokens with enum-valued roles ('ADMIN', 'SUPER_ADMIN'), so AdminGuard would reject every real admin. RbacGuard accepts UserRole.ADMIN/UserRole.SUPER_ADMIN, handles both role and roles claims, and is the house pattern already used by InstitutionalController.

Why

Before this change, POST api/admin/queue/control/:queueName/pause, DELETE api/admin/queue/control/:queueName, POST api/admin/queue/dlq/:queueName/:jobId/recover, DELETE api/admin/queue/dlq/:queueName, job retry/remove, and the api/queue/test/* endpoints returned 200 to unauthenticated callers. The commented-out imports pointed at ../auth/guards/roles.guard and ../auth/decorators/roles.decorator, which do not exist — so "uncomment the guards" would not compile. The fix must wire the real primitives: JwtAuthGuard (src/auth/guards/jwt-auth.guard.ts) + RbacGuard (src/identity/roles/guards/rbac.guard.ts) with @Roles (src/identity/roles/decorators/roles.decorator.ts).

The access model is deliberate and mirrors the issue's proposed design: read-only observability (GET api/queue/metrics*, GET api/queue/health) stays authenticated-but-not-admin so operators can monitor without escalation; every mutating or admin-surface route requires ADMIN. QueueModule now imports AuthModule (re-exports the configured JwtModule/JwtService needed by JwtAuthGuard) and RolesModule (provides RoleService + RbacGuard), the same wiring InstitutionalModule already uses — no new module cycle (verified with madge).

What was built

File What it contains
src/queue/queue-admin.controller.ts Class-level @UseGuards(JwtAuthGuard, RbacGuard) + @Roles(UserRole.ADMIN) on every api/admin/queue/* route; removed the commented-out placeholder guards; added @ApiUnauthorizedResponse/@ApiForbiddenResponse Swagger metadata.
src/queue/queue.controller.ts Class-level @UseGuards(JwtAuthGuard) (all api/queue/* routes authenticated); per-route @UseGuards(RbacGuard) + @Roles(UserRole.ADMIN) on jobs retry/remove, pause/resume/empty, manual triggers, and test/*; read routes (metrics*, health) untouched except for the auth requirement. Removed the dead commented-out imports and trailing scaffold comment.
src/queue/queue.module.ts Imports AuthModule + RolesModule so the guards' dependencies (JwtService, RoleService) resolve in QueueModule's injector.
test/queue-auth.e2e-spec.ts New e2e spec: real controllers + real guards, queue services mocked, access tokens minted via JwtService. Covers the 401/403/200 matrix across read and destructive routes, non-admin on admin routes, SUPER_ADMIN passthrough, and invalid/refresh-type token rejection. 36 tests.

Integration changes outside src/queue/

  • README.md — added a "Queue Management API" subsection under API Documentation documenting the auth requirement per endpoint group.

Acceptance criteria coverage

Contract

  • An unauthenticated request to any api/admin/queue/* route returns 401 (test/queue-auth.e2e-spec.tsit.each over 9 admin routes, plus job routes).
  • An authenticated non-admin request to any api/admin/queue/* route returns 403 (test/queue-auth.e2e-spec.ts — non-admin token on the same 9-route matrix).
  • An authenticated non-admin request to api/queue/jobs/* mutating routes returns 403 (test/queue-auth.e2e-spec.tsPOST .../retry and DELETE .../jobs/:jobId with user token).
  • An authenticated admin request to api/admin/queue/* succeeds (test/queue-auth.e2e-spec.ts — admin token: pause 200, empty 200).

Service

  • Read-only api/queue/metrics and api/queue/health remain reachable with a valid access token (test/queue-auth.e2e-spec.ts — user token on both returns 200; no token returns 401).
  • The change compiles with npm run build and does not introduce a new module cycle (madge: 3 pre-existing cycles before and after, none involving queue).

Tests

  • A new e2e spec covers the 401/403/200 matrix for read and destructive routes (test/queue-auth.e2e-spec.ts, 36/36 passing).
  • Existing specs still pass via npm run test (450/502 passing, 41/70 suites — byte-identical to the base branch; the 52 failures are pre-existing ts-jest resolution errors on absolute src/... imports, reproduced on base).

Documentation

  • The admin-role requirement is documented in the Swagger metadata (@ApiBearerAuth was already present; added @ApiUnauthorizedResponse/@ApiForbiddenResponse on both controllers and per mutating route) and in the README API section (new "Queue Management API" table).

Deliberately deferred

None — the full issue scope is implemented. SchedulerController and HorizontalScalingController also lack guards but are outside this issue's stated files-in-scope and acceptance criteria; guarding them is a natural follow-up if the maintainers want the same treatment.

Test plan

  • npm run build — succeeds
  • npx jest --config ./test/jest-e2e.json --runInBand test/queue-auth.e2e-spec.ts — 36/36 passing (all new tests for this feature)
  • npm run test — 450/502 passing (52 pre-existing failures, identical on base branch)
  • npx eslint on changed files — 0 new issues vs base (base: 32 on queue files; now: 30 on queue files; the 20 warnings in the new e2e spec are the same no-unsafe-argument class present in existing e2e specs)
  • npx madge --circular --extensions ts src/ — no new circular dependencies (3 pre-existing, unchanged)

Env vars / Notes

No new environment variables or config keys. No persisted data changes, so no migration. Runtime behavior change: any client that previously called queue admin/control endpoints anonymously will now receive 401/403 — this is the intended security fix.

Wire real auth onto the queue controllers: QueueController and
QueueAdminController shipped with commented-out placeholder guards
that referenced non-existent symbols, leaving every queue control
and admin route reachable anonymously.

- api/admin/queue/*: JwtAuthGuard + RbacGuard + @roles(ADMIN)
- api/queue reads (metrics, health): JwtAuthGuard
- api/queue mutations (jobs, pause/resume/empty, triggers, tests):
  RbacGuard + @roles(ADMIN)

Uses the identity-domain RbacGuard rather than AdminGuard because the
latter compares user.role against the lowercase literal 'admin' while
the auth service mints enum-valued roles ('ADMIN'), so real admins
would be rejected. QueueModule now imports AuthModule and RolesModule
to provide JwtService and RoleService, mirroring InstitutionalModule.

Adds test/queue-auth.e2e-spec.ts covering the 401/403/200 matrix for
read and destructive routes with minted access tokens.

@ameeribro4-sudo ameeribro4-sudo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ameeribro4-sudo
ameeribro4-sudo merged commit 42c119a into OpenPeerX:main Aug 20, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Queue admin endpoints are unauthenticated: pause, empty, and DLQ replay are exposed anonymously

2 participants