Skip to content

request-validation: 403 auth-deny for Hub (missing-permission probe token) #392

@esraagamal6

Description

@esraagamal6

Phase B of the Camunda Hub negative-suite auth coverage (camunda/camunda-hub#25264). Phase A (401 auth-absent) landed in #391. This issue covers 403 unauthorized.

Hub authorization model (assessed from camunda-hub restapi)

The Public API v2 uses @PreAuthorize("hasAuthority(PERMISSION_X) && hasAccessToOrganization(...)") on each controller:

  • CRUD authority per op — read / create / update / delete (PublicApiToken.PERMISSION_*), mechanically derivable from the HTTP method (GET/search→read, POST-create→create, PATCH→update, DELETE→delete).
  • Org access — token organizationId must match the resource's (org-wide; no per-resource roles).
  • Returns 403, not 404, on authz failure.

Key simplification: the SpEL is hasAuthority(...) && hasAccessToOrganization(...) — authority is checked first and short-circuits, so a token lacking the op's permission yields 403 without any resource/org lookup. → Hub 403 tests can use dummy keys (no fixtures needed to make resources exist, unlike OCA's auth-deny which needed real resources to avoid 404).

Why the existing authDeny (OCA) doesn't fit

request-validation/src/analysis/authDeny.ts is OCA-specific: a hardcoded SLICE of get-by-key reads using a Basic-auth zero-grant probe (denyProbeHeaders()) + real fixtures. Hub is Bearer/JWT + CRUD-authority, dummy keys OK.

Proposed implementation

  1. Per-op permission mapping from method (config-driven or convention): GET/*/search→read, POST(non-search create)→create, PATCH→update, DELETE→delete.
  2. A reduced-permission Bearer probe token — analogous to denyProbeHeaders() but Bearer: a token missing the op's required permission (e.g. a Keycloak web-modeler/client scoped to fewer web-modeler-public-api permissions, or a read-only token used against create/update/delete). Mint via the Keycloak setup already in docker/docker-compose.hub.yml (the c8-client M2M client + audience mapper).
  3. Generalize authDeny to a config-driven Hub variant (method→permission, dummy params, Bearer probe) emitting one 403 scenario per op, distinct from OCA's Basic zero-grant slice. Likely gate via a per-config authDenyMode mirroring the authAbsentMode added in request-validation: 401 negatives for uniformly-secured APIs (Hub) — missing + invalid token #391.
  4. Emitter: denyProbeHeaders() currently renders a Basic header (OCA). Needs a Bearer-probe variant for Hub (or parameterize the probe-header fn per config).
  5. Emit into the existing rbac profile (or a hub-appropriate profile); run against a secured Hub.

Open questions

  • Token minting: simplest is a second Keycloak client/scope with reduced permissions; confirm the SM Identity → permission mapping (create:*/read:*/… → PublicApiToken authorities).
  • Wrong-org 403 is largely a SaaS concern (SM is single-org) — scope local coverage to the missing-permission flavor.

Acceptance

  • Per-op 403 scenarios for Hub v2 (missing required CRUD permission → 403).
  • Bearer reduced-permission probe token wired (fixture/Keycloak).
  • OCA auth-deny unchanged.
  • Runs green against a secured local Hub.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions