# 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. --- ## 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**: Eine globale Rolle auf Root-Mandant-Ebene. Wird in `_initSysAdminRole()` beim Bootstrap angelegt -- keine Template-Kopie. - **SystemUser / EventUser**: Technische System-Accounts, nicht an echte Benutzer gebunden. --- ## 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) ```python 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 ```python 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 ```python 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 ```python 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 ```python 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`, `delete` als Access Levels - **`view`**: Boolean, steuert Grundsichtbarkeit - **Mapping**: `TABLE_NAMESPACE` in `interfaceRbac.py` ordnet Modellnamen logischen Namespaces zu - **`buildDataObjectKey()`**: Baut Keys wie `data.{namespace}.{ModelName}` oder `data.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 1. **Rollen laden**: Mandate-Rollen via `UserMandate` → `UserMandateRole`; Feature-Rollen via `FeatureAccess` → `FeatureAccessRole` (`getRulesForUserBulk()` in `rbac.py`) 2. **AccessRules laden** fuer alle gefundenen Rollen 3. **Gruppierung** nach Priority-Stufe 4. **Hoechste Prioritaet gewinnt** bei DATA-Permissions 5. **View wird OR-verknuepft** ueber alle Rollen der hoechsten Prioritaetsstufe 6. **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 Filter - `m`: `WHERE mandateId = :mandateId` - `o`: `WHERE _createdBy = :userId` - `n`: `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