15 KiB
RBAC-System
Ueberblick
Das Role-Based Access Control baut auf vier Stufen auf: System, Mandant, Feature und Feature-Instanz. Berechtigungen werden in Access Rules pro Rollenlabel und Kontext (DATA, UI, RESOURCE) gebunden. Auswertung erfolgt zentral ueber interfaceRbac.py; Zielbild: moeglichst filternd in SQL statt vollstaendiger Tabellen-Scans in Python. Nutzer koennen mehrere Rollenlabels gleichzeitig tragen; die effektive Berechtigung entsteht aus Oeffnungslogik (Union) ueber alle Rollen.
Platform-Governance-Autoritaet (zwei orthogonale Flags)
Neben den Mandanten-scoped Rollen kennt das System zwei plattformweite User-Flags auf der User-Tabelle. Sie sind einzeln vergebbar und decken zwei unabhaengige Autoritaets-Achsen ab:
| Flag | Zweck | RBAC-Bypass | FastAPI-Dependency |
|---|---|---|---|
isSysAdmin |
Infrastruktur-Operator (Logs, Tokens, DB-Health, i18n-Master, global-scoped Files/Sources) | ja | requireSysAdmin |
isPlatformAdmin |
Cross-Mandate-Governance (User-/Mandate-/RBAC-/Feature-Registry-Verwaltung ueber alle Mandanten) | nein | requirePlatformAdmin |
Wichtig:
isSysAdminwirkt als harter RBAC-Engine-Bypass inrbac.py:getUserPermissions. Reserviert fuer Infrastruktur-Operationen, die unabhaengig vom RBAC-Schema funktionieren muessen.isPlatformAdmingibt keinen impliziten Daten-Zugriff. Er erlaubt nur den Zugriff auf Admin-Routen, die explizitrequirePlatformAdmindeklarieren (z.B.routeDataMandates,routeAdminRbacRules,routeBilling-Cross-Mandate,routeAdminUserAccessOverview).- Die historische
sysadmin-Rolle im Root-Mandant wurde eliminiert. Eine einmalige idempotente Migration ininterfaceBootstrap._migrateAndDropSysAdminRole()befoerdert ehemalige Rolleninhaber zuisPlatformAdmin=Trueund entfernt Rolle, AccessRules undUserMandateRole-Eintraege.
Beispiel-Profile:
- "Operations-Engineer" →
isSysAdmin=true,isPlatformAdmin=false(kann Logs und DB pruefen, aber keine User/Mandate verwalten). - "Customer-Success-Admin" →
isSysAdmin=false,isPlatformAdmin=true(kann Mandanten deblockieren, User-Access-Overview sehen, aber nicht in Server-Logs schauen). - "Plattform-Super-Admin" → beide auf
true.
Details + Migrations-Plan: wiki/c-work/4-done/2026-04-sysadmin-authority-split.md.
4-Stufen-Hierarchie
System (PowerOn Platform)
|
+-- System Template Rollen (isSystemRole=true, mandateId=null, featureCode=null)
| Labels: "admin", "user", "viewer"
| -> kopiert bei Mandant-Erstellung via copySystemRolesToMandate()
|
+-- Feature Template Rollen (isSystemRole=false, mandateId=null, featureCode=X)
| Labels: "workspace-admin", "workspace-user", "workspace-viewer",
| "graphicalEditor-admin", "graphicalEditor-user", ...
| -> kopiert bei Feature-Instanz-Erstellung via _copyTemplateRoles()
|
+-- Mandant (Tenant)
+-- Mandate-Rollen (Instanzen der System Templates)
| mandateId=X, featureInstanceId=null, featureCode=null
| "admin", "user", "viewer"
| Zuordnung: UserMandateRole <- UserMandate <- User
|
+-- Feature-Instanz A (z.B. "AI Workspace Produktion")
| +-- Feature-Instanz-Rollen (Instanzen der Feature Templates)
| | mandateId=X, featureInstanceId=Y, featureCode="workspace"
| | "workspace-admin", "workspace-user", "workspace-viewer"
| | Zuordnung: FeatureAccessRole <- FeatureAccess <- User
| +-- Daten (Workflows, Files, Chats, ...)
|
+-- Feature-Instanz B (z.B. "Graphical Editor Dev")
+-- Feature-Instanz-Rollen
+-- Daten (Workflows, Runs, Tasks, ...)
Sonderfaelle
- SysAdmin / PlatformAdmin: Plattformweite Autoritaet wird ueber User-Flags geregelt (
isSysAdmin,isPlatformAdmin), nicht ueber Rollen. Siehe Abschnitt "Platform-Governance-Autoritaet". - SystemUser / EventUser: Technische System-Accounts, nicht an echte Benutzer gebunden. Beide bekommen
isSysAdmin=true; nuradminbekommt zusaetzlichisPlatformAdmin=true.
Zwei getrennte Template-Rollen-Systeme
Templates sind Blaupausen. Sie definieren welche Rollen und Berechtigungen ein neuer Mandant oder eine neue Feature-Instanz erhaelt. Die beiden Template-Systeme sind vollstaendig getrennt -- kein Kreuz-Zuweisen.
System Template Rollen (fuer Mandanten)
| Feld | Wert |
|---|---|
isSystemRole |
true |
mandateId |
null |
featureInstanceId |
null |
featureCode |
null |
roleLabel |
"admin", "user", "viewer" |
| Definiert in | interfaceBootstrap.py → initRoles() (Zeilen ~473-540) |
| Kopier-Mechanismus | copySystemRolesToMandate() in interfaceBootstrap.py |
| Ausgeloest bei | Mandant-Erstellung (createMandate(), _provisionMandateForUser()) |
| Bootstrap-Sync | _ensureAllMandatesHaveSystemRoles() beim Startup |
Feature Template Rollen (fuer Feature-Instanzen)
| Feld | Wert |
|---|---|
isSystemRole |
false |
mandateId |
null |
featureInstanceId |
null |
featureCode |
z.B. "workspace", "automation2", "trustee" |
roleLabel |
z.B. "workspace-admin", "workspace-user" |
| Definiert in | TEMPLATE_ROLES in jedem Feature-Main (z.B. mainWorkspace.py Zeilen ~83-135) |
| Sync in DB | _syncTemplateRolesToDb() bei registerFeature() |
| Kopier-Mechanismus | _copyTemplateRoles() in interfaceFeatures.py (Zeilen ~209-304) |
| Ausgeloest bei | Feature-Instanz-Erstellung (createFeatureInstance(copyTemplateRoles=True)) |
TEMPLATE_ROLES Format (Beispiel Workspace)
TEMPLATE_ROLES = [
{
"roleLabel": "workspace-admin",
"description": {"en": "Workspace Administrator", "de": "Workspace-Administrator"},
"accessRules": [
{"context": "UI", "item": "ui.feature.workspace", "view": True},
{"context": "RESOURCE", "item": "resource.feature.workspace", "view": True},
{"context": "DATA", "item": None, "read": "a", "create": "a", "update": "a", "delete": "a", "view": True}
]
},
{
"roleLabel": "workspace-user",
"description": {"en": "Workspace User", "de": "Workspace-Benutzer"},
"accessRules": [
{"context": "UI", "item": "ui.feature.workspace", "view": True},
{"context": "RESOURCE", "item": "resource.feature.workspace", "view": True},
{"context": "DATA", "item": None, "read": "m", "create": "m", "update": "m", "delete": "m", "view": True}
]
}
]
Kopier-Invariante
Templates werden nur bei Erstellung kopiert. Spaetere Aenderungen an Templates werden nicht automatisch an bestehende Mandanten oder Feature-Instanzen propagiert. Fuer nachtraeglichen Sync existiert syncRolesFromTemplate() in interfaceFeatures.py -- aber dies muss explizit aufgerufen werden.
Datenmodell
Role
class Role(PowerOnModel):
id: str # UUID, auto-generiert
mandateId: Optional[str] # null = Global/Template
featureInstanceId: Optional[str] # null = nicht Instanz-spezifisch
featureCode: Optional[str] # z.B. "workspace", "graphicalEditor"
roleLabel: str # z.B. "workspace-admin"
description: TextMultilingual # {"en": "...", "de": "..."}
isSystemRole: bool # true nur fuer System Template Rollen
Immutable nach Erstellung: mandateId, featureInstanceId, featureCode (erzwungen in datamodelRbac.py).
AccessRule
class AccessRule(PowerOnModel):
id: str
roleId: str # FK -> Role
context: AccessRuleContext # DATA | UI | RESOURCE
item: Optional[str] # null = generic, oder spezifischer Key
view: bool
read: Optional[AccessLevel] # nur bei DATA relevant
create: Optional[AccessLevel]
update: Optional[AccessLevel]
delete: Optional[AccessLevel]
AccessLevel
class AccessLevel(str, Enum):
NONE = "n" # Kein Zugriff
OWN = "o" # Nur eigene Datensaetze (userId-Match)
MANDATE = "m" # Alle im gleichen Mandanten
ALL = "a" # Alle (mandantenuebergreifend, nur SysAdmin)
Membership-Modelle
class UserMandate(PowerOnModel):
userId: str
mandateId: str
enabled: bool
class UserMandateRole(PowerOnModel):
userMandateId: str # FK -> UserMandate
roleId: str # FK -> Role (Mandate-Rolle)
class FeatureAccess(PowerOnModel):
userId: str
featureInstanceId: str
enabled: bool
class FeatureAccessRole(PowerOnModel):
featureAccessId: str # FK -> FeatureAccess
roleId: str # FK -> Role (Feature-Instanz-Rolle)
ER-Diagramm (Vereinfacht)
User
|
+-- UserMandate (1:N)
| |
| +-- UserMandateRole (1:N) ---> Role (mandateId=X)
| |
| +-- AccessRule (1:N)
|
+-- FeatureAccess (1:N)
|
+-- FeatureAccessRole (1:N) ---> Role (featureInstanceId=Y)
|
+-- AccessRule (1:N)
Access-Rule-Kontexte
DATA
Steuert CRUD-Berechtigungen auf Datenbank-Ebene.
- Item-Format: Auto-generiert aus Tabellenname + Namespace (z.B.
data.uam.UserInDB,data.feature.trustee.TrusteePosition) - Prueft:
read,create,update,deleteals Access Levels view: Boolean, steuert Grundsichtbarkeit- Mapping:
TABLE_NAMESPACEininterfaceRbac.pyordnet Modellnamen logischen Namespaces zu buildDataObjectKey(): Baut Keys wiedata.{namespace}.{ModelName}oderdata.feature.{featureCode}.{ModelName}
UI
Steuert Sichtbarkeit und Aktivierung von Oberflaechenelementen.
- Item-Format: Kaskadierende Strings (z.B.
playground.voice.settings,ui.feature.workspace.editor) - Prueft: Nur
view(Boolean) - Verwendung: Navigation, Seitenleiste, Buttons, Tabs
RESOURCE
Steuert Zugriff auf Systemressourcen.
- Item-Format: Kaskadierende Strings (z.B.
ai.model.anthropic,resource.feature.workspace.execute) - Prueft: Primaer
view(Boolean) - Verwendung: KI-Modelle, Aktionen, Features
Resolution-Algorithmus
Priority-System
| Rollen-Typ | Scope | Priority |
|---|---|---|
| Global/Template | mandateId=null, featureInstanceId=null |
1 (niedrigste) |
| Mandate-Rolle | mandateId=X, featureInstanceId=null |
2 |
| Feature-Instanz-Rolle | mandateId=X, featureInstanceId=Y |
3 (hoechste) |
Resolution-Ablauf
- Rollen laden: Mandate-Rollen via
UserMandate→UserMandateRole; Feature-Rollen viaFeatureAccess→FeatureAccessRole(getRulesForUserBulk()inrbac.py) - AccessRules laden fuer alle gefundenen Rollen
- Gruppierung nach Priority-Stufe
- Hoechste Prioritaet gewinnt bei DATA-Permissions
- View wird OR-verknuepft ueber alle Rollen der hoechsten Prioritaetsstufe
- Item-Spezifitaet innerhalb einer Stufe: exact > prefix > generic (laengster passender Praefix gewinnt)
DATA: Oeffnungsrechte (CUD vs. Read)
| Read-Level | Maximales CUD |
|---|---|
n (NONE) |
Kein CUD moeglich |
o (OWN) |
CUD hoechstens o oder n |
m (MANDATE) |
CUD hoechstens m, o oder n |
a (ALL) |
Alle Stufen fuer CUD zulaessig |
Mehrere Rollen (Union-Logik)
Ueber Rollen hinweg gilt Union (OR): wenn eine Rolle nach interner Aufloesung view: true liefert, gilt das Element als sichtbar/erlaubt. Fuer DATA werden die freizuegigsten Stufen ueber Rollen kombiniert.
SQL-Integration
buildRbacWhereClause() in interfaceRbac.py uebersetzt die aufgeloesten Access Levels in SQL-WHERE-Bedingungen:
a: Kein Filterm:WHERE mandateId = :mandateIdo:WHERE _createdBy = :userIdn:WHERE 1=0(kein Zugriff)
RBAC-gefilterte Abfragen laufen ueber getRecordsetWithRBAC().
Bootstrap-Ablauf
Beim Gateway-Start (initBootstrap() in interfaceBootstrap.py):
1. initRootMandate() -- Root-Mandant sicherstellen
2. _deduplicateRoles() -- Duplikate bereinigen, isSystemRole-Flags fixen
3. initRoles() -- System Template Rollen (admin/user/viewer) anlegen
4. initRbacRules() -- Default AccessRules auf Template-Rollen
5. _ensureAllMandatesHaveSystemRoles()
-- copySystemRolesToMandate() fuer jeden Mandanten
6. _initSysAdminRole() -- SysAdmin-Rolle auf Root-Mandant
7. _ensureUiContextRules() -- UI-Kontext-Regeln pruefen
8. Admin/Event-User sichern
9. assignInitialUserMemberships()
-- UserMandate + UserMandateRole fuer Admin/SysAdmin
Danach bei Feature-Registrierung (registerAllFeaturesInCatalog() → registerFeature()):
10. _syncTemplateRolesToDb() -- Feature Template Rollen upserten
-- AccessRules fuer Templates synchronisieren
Bei Laufzeit:
- createMandate() -- copySystemRolesToMandate() fuer neuen Mandanten
- createFeatureInstance() -- _copyTemplateRoles() fuer neue Feature-Instanz
Systemfelder (DATA)
Felder id und Namen mit fuehrendem _ (z.B. _createdBy, _createdAt) sind fuer Anwendungs-CUD geschuetzt. Der Connector erzwingt das unabhaengig von Access Rules.
Frontend-Optionen fuer Rollen
frontend_options an Feldern kann statische Listen oder String-Referenzen (z.B. "user.role") nutzen. Dynamische Optionen ueber /api/options/{optionsName}. Typ-Hilfen: gateway/modules/shared/frontendTypes.py.
Schluessel-Dateien
| Thema | Pfad |
|---|---|
| RBAC-Auswertung, SQL-Integration | gateway/modules/interfaces/interfaceRbac.py |
| AccessRule/Permission-Modelle | gateway/modules/datamodels/datamodelRbac.py |
| Membership-Modelle | gateway/modules/datamodels/datamodelMembership.py |
| Bootstrap (System Templates, Copy) | gateway/modules/interfaces/interfaceBootstrap.py |
| Feature-Template-Copy | gateway/modules/interfaces/interfaceFeatures.py |
| Feature-Registrierung | gateway/modules/system/registry.py |
| RBAC-Rule-Resolution | gateway/modules/security/rbac.py |
| TEMPLATE_ROLES (Beispiel) | gateway/modules/features/workspace/mainWorkspace.py |
Regeln / Invarianten
- Mandanten-Rollen und Feature-Instanz-Rollen strikt trennen (keine Kreuz-Zuweisung der Label-Typen)
- Templates werden nur bei Erstellung kopiert -- keine automatische Propagierung
- Spezifisch vor generisch innerhalb einer Rolle; Union ueber Rollen
- UI/RESOURCE: Sichtbar nur wenn
view: true; nicht berechtigte UI-Eintraege werden gar nicht geliefert - DATA: CUD darf nicht ueber dem jeweiligen Read-Level liegen
- Performance-Ziel: DATA-Zugriffe mit RBAC-Filter in SQL