Access control
Pulp Engine supports tenant, folder and template grants in file, PostgreSQL and SQL Server storage. Grants add permissions; there are no deny rules. Named users, OIDC users and database API keys have stable principals. A shared editor session’s actor is an audit label and cannot acquire a named user’s grants or ownership.
Roles and actions
| Role | Permissions |
|---|---|
viewer |
template:read, render:run for published labels |
author / editor |
template:read, template:write, render:preview, asset:read, asset:write, ai:use, folder:write |
publisher |
Author plus label:promote, template:restore, render:run, render:draft |
scheduler |
template:read, render:run, schedule:write-own |
auditor |
template:read, audit:read, usage:read, schedule:read |
admin |
All 22 actions |
none |
No base grant; explicit grants and ownership still apply |
Legacy render |
render:run, render:draft |
Legacy preview |
render:preview |
The full action catalogue is:
| Area | Actions |
|---|---|
| Templates | template:read, template:write, template:delete, template:restore |
| Publication | label:promote |
| Rendering | render:run, render:draft, render:preview |
| Assets and AI | asset:read, asset:write, ai:use |
| Folders | folder:write, folder:delete |
| Schedules | schedule:read, schedule:write, schedule:write-own |
| Governance | audit:read, audit:purge, usage:read, user:admin, role:admin, ops:admin |
Asset, AI, audit, usage, user, role and operations actions, and schedule:read,
require tenant grants. Assets have no per-resource ACL in this release. Putting
one of these actions in a folder or template binding does not grant it globally.
Inheritance and ownership
A user’s base role applies throughout their tenant. Explicit bindings may target
the tenant (resourceId: "*"), a folder ID or a template key. Folder grants include
descendants: /fin matches /fin/invoices, and does not match /finance.
Tenant-only actions are ignored below tenant scope.
RBAC_OWNER_ROLE=author implicitly allows a named user to edit and preview their
own template, including a root template. Only template, rendering and publication
actions can come from ownership. Ownership never grants global assets or AI, or
folder administration. Set RBAC_OWNER_ROLE=none to disable implicit ownership.
Other supported owner roles are viewer and publisher.
Unreadable templates return 404. An action denied on a readable template returns
403. List totals and pagination include only readable templates. Render batches
check every item before doing any render work. A viewer needs a published label;
HEAD, pinned versions and @latest require render:draft. An omitted label can
use the configured default live label.
Schedulers see and manage their own schedules. Auditors can read every schedule;
schedule:write grants management of all schedules. Schedule create, update and
trigger also require render:run on the template. Scheduled executions do not
re-check the creator’s grants at fire time in this release.
Purging a template, recreating a deleted key, or deleting a folder removes its bindings. Moving a template checks both its source and destination permissions. Folder grants follow the current folder hierarchy.
Administration and discovery
GET /auth/me requires authentication and returns the principal, legacy scope,
base role, effective bindings, tenant actions and owner role. It omits tenantId.
Use expand=permissions on template detail or list reads for effective actions.
The admin console has a Roles page for custom roles. The editor’s folder menu and
template details offer Manage access to users with role:admin. Inherited
grants show their source and must be removed at that source.
/admin/roles supports GET and POST; /admin/roles/:name supports PUT and DELETE.
/admin/role-bindings supports GET and POST; /admin/role-bindings/:id supports
DELETE. Binding lists accept principal/resource filters and includeInherited.
All these operations require role:admin. Built-in roles are immutable. A custom
role in use cannot be deleted. Successful mutations write rbac_mutation audits.
Database changes and their audit insert commit in one transaction. File mode is
single-process: atomic file replacement plus synchronous audit append rolls back
ordinary audit failures; it cannot provide a transaction across two files after
a process or disk failure. Resource cleanup revokes grants before deleting files.
TypeScript: client.auth.me(), client.admin.roles, client.admin.roleBindings.
Python: client.auth.me(), client.admin.roles, client.admin.role_bindings.
The APIs accept built-in user roles plus none; generated clients that assumed
only admin | editor must regenerate their types.
Credentials, OIDC and cache
Existing unmodified API-key scopes retain the previous route matrix. The new
role engine applies when a key has an explicit different role, a binding or a
static restrictTo restriction. API_KEYS_JSON can restrict a scope to folder
paths or template keys, for example:
[{"name":"invoices","key":"replace-me","scope":"render","tenantId":"default","restrictTo":{"folders":["/Finance"],"templates":["public-summary"]}}]
Restricted static keys cannot mint editor or console sessions. Named-user and OIDC sessions use the user’s personal signing key. Existing mixed-mode named sessions signed with a shared key must sign in again to regain named grants. Moving a user to another tenant invalidates their previous tenant session.
OIDC_ROLE_MAPPINGS_JSON (or _FILE) maps exact group names to roles and optional
folder paths. Stored group bindings also apply. Unknown folders are logged and
grant nothing. Current groups are persisted on every login. OIDC_ROLE_SYNC=true
also updates the base role from the legacy admin/editor group rules on each
login. Its default is false for compatibility; hardened OIDC deployments warn
until synchronization is enabled. With synchronization disabled, a previous
base admin role can survive removal from the admin group.
Role/binding writes invalidate the local resolver cache. Other replicas converge
within RBAC_CACHE_TTL_MS (default 10000; range 0–300000). Zero disables caching.
User groups and credentials have their own registry refresh behavior. The editor
refreshes permissions on focus and every 10 seconds; the API always enforces them.
The editor falls back to legacy scope controls only when /auth/me returns 404.