ADR 0010 — IAM RBAC: policies, rollen en security-first enforcement¶
Status: Accepted (Phase 1–3A, UI redesign, 3B–3D partial)
Date: 2026-06-30
Related: Authentication, Security, ADR 0009, API Keys & Products
Context¶
MeshFlows identity gebruikt al een scope-gebaseerd RBAC-model:
- Permissions — string scopes (
meshflows:workflow:run:demo, wildcardsmeshflows:workflow:run:*) - Groups — bundels van permissions (in de UI “Roles” genoemd)
- Users — lid van groepen + optionele directe grants
De dashboard Access-pagina bood een vereenvoudigde vinkjes-UI boven dit model. Dat leidde tot problemen:
- Flatten-bug —
GETretourneerde effectieve rechten (direct ∪ groep), maarPUTschreef die set terug als directe rechten → groepsrechten werden gedupliceerd; afvinken van geërfde rechten had geen effect. - Beperkte UI — platform-, IAM- en design-rechten ontbraken in de vinkjes; groepsrechten waren niet beheerbaar via UI.
- Enforcement-inconsistentie — gateway
check_scopesdeed exacte string-match; orchestrator/dashboard gebruikten wildcard-matching (scope_allowed). - Stale JWT scopes —
/auth/melas scopes uit het token, niet uit de database → ingetrokken rechten bleven geldig tot token-expiry.
Let op: Gateway workflow policies (YAML: required_headers, oauth_inbound, rate_limit_product, transform) zijn geen IAM-rechten. Die blijven runtime/edge concerns (zie ADR 0009).
Decision¶
1. Terminologie¶
| Term | Implementatie | UI-label |
|---|---|---|
| Permission / policy | Rij in permissions, scope-string |
Permission |
| Role | Rij in groups + group_permissions |
Role (groep) |
| Direct grant | user_permissions |
Extra rechten (override) |
| Effective | direct ∪ from_groups |
Effectief |
2. API-contract (identity)¶
GET /users/{username}/permissions retourneert een object:
{
"direct": ["meshflows:runtime:secrets:read"],
"from_groups": ["meshflows:workflow:run:*"],
"effective": ["meshflows:runtime:secrets:read", "meshflows:workflow:run:*"]
}
PUT /users/{username}/permissions schrijft alleen direct (vervangt user_permissions); response = zelfde structuur.
GET /groups/{group}/permissions en PUT /groups/{group}/permissions beheren rol-rechten.
POST /permissions blijft beschikbaar om nieuwe scopes te registreren (catalogus).
3. Enforcement — één wildcard-semantiek¶
Alle services gebruiken scope_allowed(granted, required):
meshflows:*→ alles- exacte match of prefix-wildcard (
meshflows:workflow:run:*matchtmeshflows:workflow:run:demo)
Toepassing:
- Identity (
_require_admin, API-key scope intersection) - Gateway (
check_scopes) - Dashboard / orchestrator / scheduler (bestaand)
4. Live scopes bij /auth/me¶
Na JWT-validatie worden scopes en groups opnieuw uit de database geladen. Intrekking van rechten is direct zichtbaar in dashboard en identity-admin (gateway JWT blijft cached tot expiry — acceptabel met korte TTL; optioneel later: gateway introspectie).
5. UI — master-detail IAM (Phase 2–3D)¶
Fase 1 (afgerond): direct vs inherited vinkjes, flatten-bug opgelost.
Huidige UI (Access-pagina):
- Tabs: Users | Groups | Policy catalog | Audit log
- Master-detail met filterbare lijsten (geen dropdowns voor users/groepen)
- Policy-toggles met kleuraccenten (platform, workflow, secrets, break-glass)
- Groepslidmaatschap via klikbare chips
- Per-workflow run/retrigger/schedule met workflow-filter
- Break-glass (
meshflows:*) vraagt bevestiging - 2FA optioneel — status in user-detail; admin reset bij verloren device (geen platform-enforce)
Implementatie: engine/services/dashboard/app/static/app.iam-ui.js
6. Security-baseline¶
meshflows:iam:managevereist voor IAM-mutaties (niet alleenplatform:admin).meshflows:*alleen op break-glass accounts; MFA aanbevolen maar niet verplicht platform-breed.- API keys: effectieve scopes = intersection key-scopes ∩ user effective, met wildcard-semantiek.
- Geen deny-ACL in Phase 1 (allow-only).
Consequences¶
Positief¶
- Groepsbeleid blijft bron van waarheid; geen stille duplicatie naar
user_permissions. - Wildcard-rechten werken consistent op gateway en API keys.
- Snellere intrekking via live
/auth/me. - Duidelijk pad naar uitbreidbare permission-catalogus.
Negatief / trade-offs¶
GET /users/.../permissionsbreaking change (waslist[str], wordt object) — alleen dashboard/automation callers; documenteren in wiki.- Gateway JWT blijft statisch tot expiry; productie:
IDENTITY_JWT_EXP_MINUTESlaag houden ofGATEWAY_JWT_SCOPE_MODE=live. - Scheduler en orchestrator importeren
meshflows_rbac(Docker build contextengine/).
Implementation checklist (Phase 1)¶
- [x] ADR 0010
- [x] Structured permissions API +
GET /groups/{name}/permissions - [x]
_current_userlive scopes from DB - [x] Gateway
check_scopes+rbac.py - [x] API key validation wildcard intersection
- [x] Dashboard Access UI: direct vs inherited, role editor
- [x] Tests
Implementation checklist (Phase 2)¶
- [x] Shared
engine/meshflows_rbacpackage (identity, gateway, dashboard) - [x] IAM audit log table +
GET /audit/iam - [x] Permission catalog API + dashboard UI
- [x] Docker build context from
engine/for RBAC services - [x] Gateway JWT live introspection (
GATEWAY_JWT_SCOPE_MODE=live, cache via/auth/me)
Implementation checklist (Phase 3)¶
- [x] Gateway live JWT scopes (3A)
- [x] Scheduler + orchestrator →
meshflows_rbac(3B) - [x] Role templates wiki (19-iam-role-templates.md) (3C)
- [x] IAM UX: master-detail UI, audit action filters, break-glass confirm, admin MFA reset (3D partial)
- [x] Access-review API + CLI (
engine/scripts/iam-access-review.py) - [x] Auto-register per-workflow scopes (
POST /permissions/sync-workflows, orchestrator on reload) - [ ] Break-glass periodic review automation (cron / CI integration left to operators)
See also¶
engine/meshflows_rbac/— sharedscope_allowed,PERMISSION_LABELSengine/services/identity/app/rbac.py— re-exportengine/services/identity/app/main.py— IAM tables en endpointsengine/services/dashboard/app/static/app.iam-ui.js— Access UI