Skip to content

fix(job-queue): limit job endpoints to the job's creator and administrators - #38383

Draft
AP2300 wants to merge 2 commits into
mainfrom
job-queue-owner-scoped-access
Draft

AP2300 wants to merge 2 commits into
mainfrom
job-queue-owner-scoped-access

Conversation

@AP2300

@AP2300 AP2300 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The job endpoints (/api/v1/jobs/** and /api/v1/content/_import/**) now answer for the user who created a job, or for a CMS Administrator. Job listings are narrowed to the caller's own jobs, and job responses no longer include the parameters the server sets for uploaded files.

Changes

  • Job API (JobQueueManagerAPI): new getJob(jobId, User), cancelJob(jobId, User) and getJobs(JobListView, queueName, User, page, pageSize).
    • The job's creator is the job's userId parameter. A CMS Administrator is user.isAdmin(), not a portlet check.
    • For any other user the job is reported as not found (DoesNotExistException, 404), the same as an id that does not exist.
    • The user-less methods stay for internal callers (maintenance, folder bulk delete). Their Javadoc points request-serving code to the user-aware ones.
  • Listings (PostgresJobQueue): an optional owner condition, parameters @> jsonb_build_object('userId', ?).
    • It is bound as a parameter and served by the existing GIN index on job.parameters.
    • Paging and totals count the same jobs.
    • A new JobListView enum holds each listing's states, so the per-state methods now delegate to one view-based method. Their SQL and ordering are unchanged.
  • REST: JobQueueResource, ContentImportResource and SSEMonitorUtil pass the requesting user. The helpers no longer offer user-less read, cancel or list methods.
  • Responses (JobResponseUtil.forResponse): the following are left out of job parameters. The processors still receive them.
    • tempFileId and requestFingerPrint.
    • Bulk upload's requestFingerprint, and the tempFileId of each stagedFiles entry. stagedFiles itself stays, with each file's name and size.
  • Generic create: POST /api/v1/jobs/{queueName} and /{queueName}/upload answer 400 when the request supplies any of those parameters. The server still sets them for an uploaded file.
  • Smaller fixes:
    • The listing fallback after a read error builds a valid empty page. JobPaginatedResult requires total, page and pageSize, so the previous fallback threw.
    • A date-range listing refuses an owner instead of ignoring it.
  • OpenAPI: list, read, cancel, monitor and create descriptions updated; openapi.yaml regenerated.

Tests

  • JobQueueHelperIntegrationTest:
    • creator, other user, administrator and no user, for read, cancel, monitor and listings, including an administrator cancelling another user's job;
    • a job with no recorded creator is available to CMS Administrators only;
    • a userId in a create request is replaced by the requesting user's id;
    • the staged-file parameters left out of responses, including bulk upload's;
    • 400 on create, for each of the three parameter spellings;
    • a resource-level test across every /api/v1/jobs endpoint and all 8 listings.
    • The class now initializes the integration environment itself. Run on its own, it failed with relation "cms_role" does not exist.
  • PostgresJobQueueIntegrationTest: owner-narrowed listings for every JobListView, per queue and across queues, with paging and totals.
  • ContentImportResourceIntegrationTest: another back-end user gets 404 on read and cancel, and does not see the job in any of the 7 listings; the monitor opens for the requesting user. The administrator reads the job without the staged-file parameters.
  • Postman: JobQueueResourceAPITests and ContentImportResource assertions now expect tempFileId and requestFingerPrint to be absent.
  • Local runs: 88 integration tests pass across the three classes above and two existing job-queue classes: the generic-endpoint queue checks and FolderBulkDeleteCancellationIT. The new tests were confirmed failing before the change. A local image built from this branch was also checked endpoint by endpoint with a creator, another back-end user and an administrator.

All test classes are already registered in Junit5Suite1; no suite change.

Risk

  • REST contract change (rollback category M-3):
    • Non-administrators stop seeing other users' jobs, and listing totals shrink to match.
    • Job responses lose the parameters listed above.
    • Creating a job through the generic endpoint with those parameters now answers 400.
    • No frontend code reads them. Content Drive's import toast and folder-delete status read the user's own jobs only.
    • Rolling back restores the previous behaviour. There is no schema, data or index change.
  • Jobs created without a userId parameter are available to CMS Administrators only. Every current producer sets it.

Fixes dotCMS/private-issues#872

🤖 Generated with Claude Code

…rators

The job endpoints under /api/v1/jobs and /api/v1/content/_import now
answer for the user who created a job, or for a CMS Administrator:

- Reading, cancelling and monitoring a job by id answer 404 for anyone
  else, the same as for an id that does not exist.
- Job listings hold only the caller's own jobs for non-administrators,
  narrowed in SQL so paging and totals stay correct.
- Job responses leave out the parameters the server sets for uploaded
  files (tempFileId, requestFingerPrint, and bulk upload's
  requestFingerprint and stagedFiles[].tempFileId); the processors
  still receive them.
- The generic create endpoint answers 400 when a request supplies
  those parameters itself.

JobQueueManagerAPI gains getJob, cancelJob and getJobs variants that
take the requesting User. A new JobListView enum holds the job states
of each listing, and the existing listing methods now delegate to the
view-based one. The user-less methods stay for internal callers
(maintenance, folder bulk delete).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the Area : Backend PR changes Java/Maven backend code label Oct 30, 2026
- An administrator can cancel another user's job.
- A job created without a userId parameter is available to CMS
  Administrators only; canAccess now refuses it outright for everyone
  else instead of comparing against a missing value.
- A userId parameter in a create request is replaced by the requesting
  user's id.
- requestFingerprint (bulk upload's spelling) is rejected on create,
  like the other two spellings.
- Re-indent the monitor endpoint's 500 response annotation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area : Backend PR changes Java/Maven backend code

1 participant