Modelo de Autorización en Camunda 8¶
Resumen: Camunda 8 implementa un modelo de autorización granular con ~20 tipos de recursos, ~40 tipos de permisos (clasificados como read o write), y 6 tipos de owners (USER, CLIENT, ROLE, GROUP, MAPPING_RULE, TENANT). El flujo de autorización pasa por SecurityContext → ResourceAccessController → ResourceAccessChecks, donde los IDs autorizados se inyectan como filtros directamente en las queries. La configuración es declarativa via YAML.
Estructura del modelo¶
Resource types (~20)¶
Camunda 8 define aproximadamente 20 tipos de recursos protegibles. Cada recurso representa una entidad del sistema sobre la cual se pueden otorgar o denegar permisos:
| Categoría | Resource types |
|---|---|
| Workflow | PROCESS_DEFINITION, DECISION_DEFINITION, FORM |
| Runtime | PROCESS_INSTANCE, DECISION_INSTANCE |
| Operational | JOB, INCIDENT, BATCH_OPERATION |
| Data | VARIABLE, FLOWNODE_INSTANCE |
| Infrastructure | DEPLOYMENT, MESSAGE |
| Identity | USER, GROUP, ROLE, CLIENT, MAPPING_RULE, TENANT, AUTHORIZATION |
| System | SYSTEM (configuración global) |
Permission types (~40)¶
Cada resource type tiene un conjunto de permisos aplicables. Los permisos están clasificados como read (lectura/consulta) o write (modificación/acción):
Permisos read (ejemplos):
- READ — ver la definición o instancia del recurso.
- READ_PROCESS_INSTANCE — ver instancias de un proceso específico.
- READ_USER_TASK — ver user tasks asignadas.
Permisos write (ejemplos):
- CREATE — crear nuevas instancias o recursos.
- CREATE_PROCESS_INSTANCE — iniciar una nueva instancia de proceso.
- UPDATE — modificar un recurso existente.
- DELETE — eliminar un recurso.
- UPDATE_PROCESS_INSTANCE — modificar variables, resolver incidents.
- DELETE_PROCESS_INSTANCE — cancelar una instancia.
La clasificación read/write es relevante porque permite que los checks de autorización se optimicen: las operaciones de solo lectura pueden resolverse más rápidamente y con menor overhead.
Owner types¶
Los permisos se asignan a owners — entidades que poseen autorizaciones:
USER¶
- Representa un usuario humano autenticado.
- Identificado por
username(para basic auth) osubjectclaim (para OIDC). - Puede tener permisos directos o heredados via roles/grupos.
CLIENT¶
- Representa una aplicación o servicio (machine-to-machine).
- Identificado por
clientIddel token OAuth2. - Típicamente usado por job workers, conectores, y servicios internos.
ROLE¶
- Agrupación lógica de permisos.
- Un usuario o cliente puede tener múltiples roles.
- Roles predefinidos:
admin,operator,viewer,taskuser.
GROUP¶
- Agrupación de usuarios.
- Los grupos heredan los permisos asignados al grupo.
- Útil para organizar permisos por equipo o departamento.
MAPPING_RULE¶
- Regla que mapea claims de un token OIDC a roles, grupos, o tenants.
- Permite configuración declarativa: "si el claim
department=engineering, asignar roldeveloper". - Ver concepts/authentication-flow para detalles del mapeo.
TENANT¶
- Entidad de aislamiento en arquitecturas multi-tenant.
- Los permisos de un tenant acotan el scope de acceso a recursos de ese tenant.
- Ver concepts/multi-tenancy para detalles.
Authorization scopes¶
Los permisos se aplican con un scope que determina a qué instancias específicas del recurso aplica:
ID scope (specific)¶
El permiso aplica a un recurso identificado por un ID específico:
- resourceType: PROCESS_DEFINITION
permission: CREATE_PROCESS_INSTANCE
scope:
type: ID
value: "order-process" # solo este proceso
PROPERTY scope (attribute)¶
El permiso aplica a recursos que coincidan con un atributo específico:
- resourceType: PROCESS_DEFINITION
permission: READ
scope:
type: PROPERTY
attribute: "tenantId"
value: "tenant-a" # solo procesos del tenant A
ANY scope (wildcard *)¶
El permiso aplica a todas las instancias del resource type:
Resolución de scopes¶
Cuando se verifica un permiso, el sistema evalúa los scopes en orden de especificidad:
- ID match: si hay un permiso con scope ID que coincida exactamente → aplica.
- PROPERTY match: si hay un permiso con scope PROPERTY que coincida con el atributo → aplica.
- ANY match: si hay un permiso con scope ANY → aplica.
- No match: acceso denegado.
Flujo de autorización (check flow)¶
Pipeline de verificación¶
flowchart TD
A[Request HTTP/gRPC] --> B[SecurityContext<br/>extrae identidad del request]
B --> C[ResourceAccessController<br/>orquesta el check]
C --> D[ResourceAccessChecks<br/>evalúa permisos concretos]
D -->|AUTHORIZED| E[proceder]
D -->|UNAUTHORIZED| F[403 Forbidden]
SecurityContext¶
El SecurityContext es un objeto que encapsula la identidad del request actual:
- Fuente: extraído del token de autenticación (JWT para OIDC, header para basic auth).
- Contenido: username/clientId, roles, grupos, tenants, claims adicionales.
- Thread-local: se almacena en un
ThreadLocalpara ser accesible en cualquier punto del stack sin pasarlo explícitamente. - Inmutable: una vez creado para un request, no se modifica.
ResourceAccessController¶
Es el orquestador central de los checks de autorización:
- Recibe la solicitud de check: "¿puede
user-XhacerREADsobrePROCESS_DEFINITION:order-process?" - Resuelve los permisos efectivos del usuario:
- Permisos directos del usuario.
- Permisos heredados de sus roles.
- Permisos heredados de sus grupos.
- Permisos derivados de mapping rules.
- Evalúa los scopes contra el recurso solicitado.
- Retorna AUTHORIZED o UNAUTHORIZED.
ResourceAccessChecks¶
Implementaciones específicas por contexto:
- REST API checks: verifican acceso a endpoints REST.
- gRPC checks: verifican acceso a servicios gRPC del broker.
- Search checks: verifican acceso a queries de Elasticsearch/OpenSearch.
- Job checks: verifican que un worker puede activar/completar jobs de un proceso.
IDs autorizados como filtros en queries¶
Una optimización clave del sistema de autorización es cómo se integra con las consultas de datos:
Problema¶
En un sistema con miles de process definitions, verificar permisos uno por uno para cada resultado de una query sería prohibitivamente lento:
// Enfoque ingenuo (lento)
results = query("SELECT * FROM process_definitions")
filtered = results.filter(pd -> authorizationCheck(user, READ, pd))
Solución: inyección de filtros¶
En lugar de filtrar post-query, el sistema inyecta los IDs autorizados directamente como filtros en la query:
// Enfoque optimizado
authorizedIds = getAuthorizedResourceIds(user, PROCESS_DEFINITION, READ)
results = query("SELECT * FROM process_definitions WHERE id IN (:authorizedIds)")
Este enfoque:
- Resuelve los IDs autorizados una vez al inicio de la query.
- Inyecta los IDs como un filtro (cláusula
INen SQL,termsfilter en Elasticsearch). - La base de datos aplica el filtro como parte de su ejecución normal de la query.
- Solo retorna resultados autorizados — no hay post-filtrado.
Casos especiales¶
- ANY scope: si el usuario tiene permiso con scope ANY, no se agrega filtro (acceso total).
- Sin permisos: si el usuario no tiene permisos, la query retorna vacío sin ejecutarse.
- Muchos IDs: si la lista de IDs autorizados es muy larga, se usan técnicas de batching o subqueries.
Configuración declarativa en YAML¶
Las autorizaciones se configuran declarativamente, típicamente en archivos YAML que se cargan al inicio:
authorizations:
- ownerType: ROLE
ownerKey: "operator"
resourceType: PROCESS_INSTANCE
permissions:
- type: READ
scope: ANY
- type: UPDATE_PROCESS_INSTANCE
scope: ANY
- type: DELETE_PROCESS_INSTANCE
scope: ANY
- ownerType: ROLE
ownerKey: "viewer"
resourceType: PROCESS_DEFINITION
permissions:
- type: READ
scope: ANY
- ownerType: USER
ownerKey: "admin-user"
resourceType: AUTHORIZATION
permissions:
- type: READ
scope: ANY
- type: UPDATE
scope: ANY
Ventajas del enfoque declarativo¶
- Versionable: la configuración de permisos puede estar en control de versiones (Git).
- Auditable: cambios de permisos se rastrean como cualquier cambio de configuración.
- Reproducible: un entorno se puede recrear con la misma configuración.
- Testeable: la configuración se puede validar en CI/CD.
Consideraciones para un MVP¶
- Esencial: algún modelo de autorización es necesario para producción, pero puede empezar simple.
- MVP mínimo: un modelo con solo USER y ROLE, sin scopes (equivalente a ANY en todo), es suficiente para empezar. Agregar scopes ID y PROPERTY cuando haya necesidad de control granular.
- Recomendado: la inyección de IDs autorizados como filtros en queries es una optimización importante que previene problemas de rendimiento. Implementar desde el inicio.
- Simplificable: los ~20 resource types pueden reducirse a los esenciales: PROCESS_DEFINITION, PROCESS_INSTANCE, JOB, DECISION_DEFINITION.
- Simplificable: MAPPING_RULE puede diferirse hasta que se implemente OIDC; con basic auth, asignar roles directamente a usuarios es suficiente.
- Simplificable: la configuración YAML puede simplificarse a un formato más simple o incluso a una tabla en la base de datos.