fix(queue): require authentication and ADMIN role on queue admin endpoints - #18
Merged
ameeribro4-sudo merged 2 commits intoAug 20, 2026
Merged
Conversation
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.
Closed
9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #11
Queue administration and control endpoints were reachable by any anonymous HTTP client: both
QueueControllerandQueueAdminControllershipped 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 —JwtAuthGuardfor authentication, plus the identity-domainRbacGuardwith@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:
RbacGuardinstead ofAdminGuard.AdminGuardcomparesuser.roleagainst the lowercase string literal'admin', but the auth service mints tokens with enum-valued roles ('ADMIN','SUPER_ADMIN'), soAdminGuardwould reject every real admin.RbacGuardacceptsUserRole.ADMIN/UserRole.SUPER_ADMIN, handles bothroleandrolesclaims, and is the house pattern already used byInstitutionalController.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 theapi/queue/test/*endpoints returned 200 to unauthenticated callers. The commented-out imports pointed at../auth/guards/roles.guardand../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 requiresADMIN.QueueModulenow importsAuthModule(re-exports the configuredJwtModule/JwtServiceneeded byJwtAuthGuard) andRolesModule(providesRoleService+RbacGuard), the same wiringInstitutionalModulealready uses — no new module cycle (verified with madge).What was built
src/queue/queue-admin.controller.ts@UseGuards(JwtAuthGuard, RbacGuard)+@Roles(UserRole.ADMIN)on everyapi/admin/queue/*route; removed the commented-out placeholder guards; added@ApiUnauthorizedResponse/@ApiForbiddenResponseSwagger metadata.src/queue/queue.controller.ts@UseGuards(JwtAuthGuard)(allapi/queue/*routes authenticated); per-route@UseGuards(RbacGuard)+@Roles(UserRole.ADMIN)on jobs retry/remove, pause/resume/empty, manual triggers, andtest/*; read routes (metrics*,health) untouched except for the auth requirement. Removed the dead commented-out imports and trailing scaffold comment.src/queue/queue.module.tsAuthModule+RolesModuleso the guards' dependencies (JwtService,RoleService) resolve inQueueModule's injector.test/queue-auth.e2e-spec.tsJwtService. Covers the 401/403/200 matrix across read and destructive routes, non-admin on admin routes,SUPER_ADMINpassthrough, 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
api/admin/queue/*route returns401(test/queue-auth.e2e-spec.ts—it.eachover 9 admin routes, plus job routes).api/admin/queue/*route returns403(test/queue-auth.e2e-spec.ts— non-admin token on the same 9-route matrix).api/queue/jobs/*mutating routes returns403(test/queue-auth.e2e-spec.ts—POST .../retryandDELETE .../jobs/:jobIdwith user token).api/admin/queue/*succeeds (test/queue-auth.e2e-spec.ts— admin token: pause 200, empty 200).Service
api/queue/metricsandapi/queue/healthremain reachable with a valid access token (test/queue-auth.e2e-spec.ts— user token on both returns 200; no token returns 401).npm run buildand does not introduce a new module cycle (madge: 3 pre-existing cycles before and after, none involvingqueue).Tests
test/queue-auth.e2e-spec.ts, 36/36 passing).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 absolutesrc/...imports, reproduced on base).Documentation
@ApiBearerAuthwas already present; added@ApiUnauthorizedResponse/@ApiForbiddenResponseon 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.
SchedulerControllerandHorizontalScalingControlleralso 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— succeedsnpx 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 eslinton 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 sameno-unsafe-argumentclass 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.