18 KiB
18 KiB
Multi-Tenant Gateway - Implementation Progress Tracking
Konzept: mandate_implementation_gateway.md
Letzte Aktualisierung: 2026-01-18
Gesamtfortschritt
| Phase | Status | Fortschritt |
|---|---|---|
| Phase 1: Foundation | ✅ Abgeschlossen | 7/7 |
| Phase 2: RBAC Core | ✅ Abgeschlossen | 4/4 |
| Phase 3: Auth & Context | ✅ Abgeschlossen | 6/6 |
| Phase 4: Routes Migration | ✅ Abgeschlossen | 11/11 |
| Phase 5: Interfaces Migration | ✅ Abgeschlossen | 5/5 |
| Phase 6: Features Migration | ✅ Abgeschlossen | 7/7 |
| Phase 7: Cleanup | ✅ Abgeschlossen | 3/3 |
| Phase 8: Neue Features | ✅ Abgeschlossen | 5/5 |
Alle Phasen abgeschlossen!
Phase 1: Foundation (Datenmodelle & DB) ✅
Ziel: Neue Datenstrukturen ohne Breaking Changes am bestehenden Code.
| Schritt | Datei | Aktion | Status |
|---|---|---|---|
| 1.1 | datamodelFeatures.py |
NEU: Feature, FeatureInstance |
✅ |
| 1.2 | datamodelMembership.py |
NEU: UserMandate, FeatureAccess, UserMandateRole, FeatureAccessRole |
✅ |
| 1.3 | datamodelInvitation.py |
NEU: Invitation |
✅ |
| 1.4 | datamodelRbac.py |
ADD: Role.mandateId, Role.featureInstanceId, Role.featureCode, AccessRule.roleId |
✅ |
| 1.5 | datamodelUam.py |
ADD: User.isSysAdmin, Mandate ohne language |
✅ |
| 1.6 | datamodelSecurity.py |
Token ohne mandateId |
✅ |
| 1.7 | interfaceBootstrap.py |
roleId-basiert, erstellt UserMandate + UserMandateRole | ✅ |
Notizen:
- Bootstrap erstellt jetzt Root-Mandate, System-Rollen (sysadmin, admin, user) und verknüpft Initial-User via Junction Tables
isSysAdminFeld hat Validator für None → False Konvertierung- App startet erfolgreich mit neuer Struktur
Phase 2: RBAC Core (Kern-Logik) ✅
Ziel: Neues RBAC-System parallel zum alten lauffähig.
| Schritt | Datei | Aktion | Status |
|---|---|---|---|
| 2.1 | security/rbac.py |
Neue getRulesForUserBulk() mit Junction Tables |
✅ |
| 2.2 | interfaceRbac.py |
buildRbacWhereClause() mit explizitem mandateId |
✅ |
| 2.3 | interfaceDbAppObjects.py |
Neue Membership-Methoden (parallel zu alten) | ✅ |
| 2.4 | interfaceFeatures.py |
NEU: Feature-Instanz-Management | ✅ |
Notizen:
getRulesForUserBulk()implementiert mit optimierten SQL JOINs (3 Queries statt N+1)- Membership-Methoden hinzugefügt:
getUserMandate,createUserMandate,deleteUserMandate,getRoleIdsForUserMandate, etc. - Feature-Methoden hinzugefügt:
getFeatureAccess,createFeatureAccess,getRoleIdsForFeatureAccess interfaceFeatures.pyerstellt mit Template-Kopierung und Synchronisation
Phase 3: Auth & Context (Authentifizierung) ✅
Ziel: Request-Context-System einführen.
| Schritt | Datei | Aktion | Status |
|---|---|---|---|
| 3.1 | auth/authentication.py |
RequestContext + getRequestContext() + requireSysAdmin() |
✅ |
| 3.2 | routeSecurityLocal.py |
Login mit neuem Token (ohne mandateId) |
✅ |
| 3.3 | routeSecurityMsft.py |
OAuth anpassen (Token ohne mandateId) |
✅ |
| 3.4 | routeSecurityGoogle.py |
OAuth anpassen (Token ohne mandateId) |
✅ |
| 3.5 | datamodelSecurity.py |
Token ohne mandateId |
✅ (Phase 1) |
| 3.6 | auth/tokenRefreshService.py |
Bereits korrekt (OAuth-Token, nicht JWT) | ✅ |
Notizen:
RequestContextKlasse implementiert: User + mandateId + featureInstanceId + roleIdsgetRequestContext()FastAPI Dependency: LiestX-Mandate-IdundX-Instance-IdHeaderrequireSysAdmin()FastAPI Dependency: PrüftisSysAdminFlag mit Audit-Logging- JWT-Token enthalten KEIN
mandateIdmehr - Mandant-Kontext kommt per Request-Header - Token-Validierung in
_getUserBase()prüft nur nochuserId, nicht mehrmandateId - Audit-Logging für System-Funktionen (Login/Logout) verwendet
mandateId="system"(kein User-Kontext)
Phase 4: Routes Migration ✅
Ziel: Alle Routes auf neues Context-System migrieren.
4.A - Admin Routes ✅
| Schritt | Datei | Status |
|---|---|---|
| 4.A.1 | routeSecurityAdmin.py |
✅ |
| 4.A.2 | routeAdminRbacRoles.py |
✅ |
| 4.A.3 | routeDataUsers.py |
✅ |
| 4.A.4 | routeDataMandates.py |
✅ |
| 4.A.5 | routeRbac.py |
✅ |
Notizen Phase 4.A:
- Alle Admin-Routes verwenden jetzt
requireSysAdmin()Dependency roleLabels-basierte Prüfungen durchisSysAdminersetztcurrentUser.mandateIddurchRequestContextersetzt- User-Filterung via
UserMandateJunction Table - Audit-Logging verwendet
context.mandateIdoder "system"
4.B - Feature Routes ✅
| Schritt | Datei | Status |
|---|---|---|
| 4.B.1 | routeFeatureTrustee.py (← routeDataTrustee.py) |
✅ |
| 4.B.2 | routeFeatureChatbot.py (← routeChatbot.py) |
✅ |
| 4.B.3 | routeFeatureRealEstate.py (← routeRealEstate.py) |
✅ |
| 4.B.4 | routeFeatureNeutralization.py (← routeDataNeutralization.py) |
✅ |
| 4.B.5 | routeFeatureAutomation.py (← routeAdminAutomationEvents.py) |
✅ |
| 4.B.6 | routeFeatureChatDynamic.py (← routeChatPlayground.py) |
✅ |
Notizen Phase 4.B:
- Alle Feature-Routes umbenannt mit
routeFeature...Prefix getCurrentUserdurchgetRequestContextersetztcurrentUser.mandateIddurchcontext.mandateIdersetztcurrentUser.roleLabelsdurchcontext.roleIdsersetztrequireSysAdmin()für Sysadmin-Endpoints (z.B. Automation Events)- Alte Route-Dateien gelöscht, Imports in
app.pyaktualisiert
Phase 5: Interfaces Migration ✅
Ziel: Alle Interfaces auf Context-System migrieren.
| Schritt | Datei | Status |
|---|---|---|
| 5.1 | interfaceDbTrusteeObjects.py |
✅ |
| 5.2 | interfaceDbChatObjects.py |
✅ |
| 5.3 | interfaceDbRealEstateObjects.py |
✅ |
| 5.4 | interfaceDbComponentObjects.py |
✅ |
| 5.5 | interfaceVoiceObjects.py |
✅ |
Notizen Phase 5:
getInterface(currentUser)→getInterface(currentUser, mandateId=...)setUserContext(currentUser)→setUserContext(currentUser, mandateId=...)self.mandateId = currentUser.mandateId→self.mandateId = mandateId(aus Parameter)- SysAdmins können ohne
mandateIdarbeiten (cross-mandate Operationen) - Singleton-Cache-Keys verwenden jetzt
userId_mandateIdstattuserId_user.mandateId - Feature-Routes aktualisiert:
getInterface(context.user, mandateId=context.mandateId)
Phase 6: Features Migration ✅
Ziel: Feature-Module auf Context-System migrieren.
| Schritt | Datei | Status |
|---|---|---|
| 6.1 | features/chatbot/mainChatbot.py |
✅ |
| 6.2 | features/realEstate/mainRealEstate.py |
✅ |
| 6.3 | features/dynamicOptions/mainDynamicOptions.py |
✅ |
| 6.4 | features/neutralizePlayground/mainNeutralizePlayground.py |
✅ |
| 6.5 | workflows/workflowManager.py |
✅ |
| 6.6 | workflows/methods/methodBase.py |
✅ (keine Änderung nötig) |
| 6.7 | services/serviceNeutralization/mainServiceNeutralization.py |
✅ (verwendet interfaceDbApp.mandateId) |
Notizen Phase 6:
ServicesKlasse aktualisiert:mandateIdals expliziter ParametergetInterface(user, workflow, mandateId)für alle Services- Feature-Funktionen aktualisiert:
mandateIdals Parameter stattcurrentUser.mandateId - Route-Aufrufe aktualisiert:
str(context.mandateId)wird übergeben workflowManager.py: Verwendetself.services.mandateIdfür neue WorkflowsmainDynamicOptions.py: Verwendetservices.mandateIdfür Benutzer-Lookup
Phase 7: Cleanup & Breaking Changes ✅
Ziel: Alte Felder entfernen (erst wenn alles migriert ist!)
| Schritt | Datei | Aktion | Status |
|---|---|---|---|
| 7.1 | datamodelUam.py |
ENTFERNE User.mandateId, User.roleLabels |
✅ |
| 7.2 | interfaceDbAppObjects.py |
Alte Methoden entfernen | ✅ |
| 7.3 | interfaceBootstrap.py |
Alte roleLabels-Logik entfernen | ✅ |
Änderungen Phase 7:
datamodelUam.py:User.mandateIdundUser.roleLabelsFelder entferntinterfaceDbAppObjects.py:createUser():roleLabelsParameter entfernt, nur nochisSysAdminupdateUser():mandateIdHandling entferntdeleteRole(): Prüft jetztUserMandateRolestattuser.roleLabelsgetAccessRules(): NeuerroleIdParameter hinzugefügtcountRoleAssignments(): Neue Methode für Role-Zählung viaUserMandateRole
interfaceBootstrap.py:mandateIdundroleLabelsaus Admin/Event User Erstellung entferntsecurity/rbac.py:user.roleLabelsFallback entferntrouteDataUsers.py: VerwendetcreateUserMandate()für Role-AssignmentrouteSecurityLocal.py:roleLabelsaus User-Registrierung entferntrouteRbac.py: Verwendetcontext.roleIdsstattuser.roleLabelsrouteAdminRbacRoles.py: Komplett aufUserMandateRoleumgestellt mit Hilfsfunktionen
Phase 8: Neue Features ✅
Ziel: Neue Funktionalität hinzufügen.
| Schritt | Datei | Status |
|---|---|---|
| 8.1 | routeFeatures.py (NEU) |
✅ |
| 8.2 | routeInvitations.py (NEU) |
✅ |
| 8.3 | routeRbacExport.py (NEU) |
✅ |
| 8.4 | routeGdpr.py (NEU) |
✅ |
| 8.5 | app.py (Routes registriert) |
✅ |
Notizen Phase 8:
routeFeatures.py: Feature/FeatureInstance CRUD, Template-Rollen-Sync,/api/features/myEndpointrouteInvitations.py: Token-basierte Einladungen, öffentliche Validierung, Accept-FlowrouteRbacExport.py: Global und Mandate-scoped Export/Import von RBAC-RegelnrouteGdpr.py: DSGVO Art. 15 (Datenexport), Art. 17 (Löschrecht), Art. 20 (Datenportabilität)- Alle neuen Routes in
app.pyregistriert
Zusätzliche Änderungen (Nicht in Phasen)
| Datei | Änderung | Status |
|---|---|---|
connectorDbPostgre.py |
Entfernt Warning bei leerem userId (Bootstrap) | ✅ |
routeSecurityAdmin.py |
DB-Funktionen generisch gemacht (dynamische DB-Discovery) | ✅ |
env_*.env |
Konsolidierte DB-Konfiguration (DB_HOST, DB_USER, etc.) | ✅ |
interfaceDb*Objects.py |
Hardcoded DB-Namen (poweron_app, poweron_chat, etc.) | ✅ |
rootAccess.py |
Bootstrap-Aufruf wenn kein Initial-User | ✅ |
Bekannte Probleme / TODOs
- DB-Indizes für Junction Tables erstellen (Performance) →
sql/001_multi_tenant_indexes_triggers_fk.sql - IMMUTABLE Triggers für Role/AccessRule auf DB-Ebene →
sql/001_multi_tenant_indexes_triggers_fk.sql - Foreign Key Constraints für CASCADE DELETE →
sql/001_multi_tenant_indexes_triggers_fk.sql - Add/Remove User from Mandate Endpoints →
routeDataMandates.py - Combined Register + Accept Invitation Endpoint →
routeInvitations.py
Changelog
2026-01-17 (Phase 8 - Erweiterungen)
- Fehlende Endpoints nachgeliefert:
routeDataMandates.py: User-Management innerhalb MandatenGET /api/mandates/{mandateId}/users- Benutzer im Mandant auflistenPOST /api/mandates/{mandateId}/users- Benutzer zu Mandant hinzufügenDELETE /api/mandates/{mandateId}/users/{targetUserId}- Benutzer aus Mandant entfernenPUT /api/mandates/{mandateId}/users/{targetUserId}/roles- Benutzer-Rollen aktualisieren- SysAdmin Self-Eskalation Prevention: SysAdmin kann sich nicht selbst hinzufügen
- Orphan Prevention: Letzter Admin kann nicht entfernt werden
routeInvitations.py: Combined Register + AcceptPOST /api/invitations/register-and-accept- Registrierung + Einladung in einem Schritt (öffentlich)
- DB Migration Script erstellt:
sql/001_multi_tenant_indexes_triggers_fk.sql- Indexes für alle Junction Tables (UserMandate, UserMandateRole, FeatureAccess, etc.)
- IMMUTABLE Triggers für Role und AccessRule (mandateId/featureInstanceId/roleId)
- Foreign Key Constraints mit CASCADE DELETE
2026-01-17 (Phase 8)
- Phase 8 abgeschlossen:
routeFeatures.pyNEU: Feature-Definitionen (SysAdmin), FeatureInstance CRUD (Mandate-Admin)GET /api/features/- Liste aller FeaturesGET /api/features/{featureCode}- Feature-DetailsPOST /api/features/- Feature erstellen (SysAdmin)GET /api/features/instances- Feature-Instanzen im MandantPOST /api/features/instances- Feature-Instanz erstellenDELETE /api/features/instances/{id}- Feature-Instanz löschenPOST /api/features/instances/{id}/sync-roles- Rollen synchronisierenGET /api/features/my- Eigene Feature-Instanzen (kein Mandant-Kontext nötig)GET /api/features/templates/roles- Template-Rollen (SysAdmin)POST /api/features/templates/roles- Template-Rolle erstellen (SysAdmin)
routeInvitations.pyNEU: Token-basierte Einladungen für Self-Service OnboardingPOST /api/invitations/- Einladung erstellen (Mandate-Admin)GET /api/invitations/- Einladungen auflistenDELETE /api/invitations/{id}- Einladung widerrufenGET /api/invitations/validate/{token}- Einladung validieren (öffentlich)POST /api/invitations/accept/{token}- Einladung annehmen (authentifiziert)
routeRbacExport.pyNEU: RBAC Export/ImportGET /api/rbac/export/global- Globale Templates exportieren (SysAdmin)POST /api/rbac/import/global- Globale Templates importieren (SysAdmin)GET /api/rbac/export/mandate- Mandant-RBAC exportieren (Mandate-Admin)POST /api/rbac/import/mandate- Mandant-RBAC importieren (Mandate-Admin)
routeGdpr.pyNEU: DSGVO-Compliance EndpointsGET /api/user/me/data-export- Datenexport (Art. 15)GET /api/user/me/data-portability- Datenportabilität JSON-LD (Art. 20)DELETE /api/user/me- Account löschen (Art. 17)GET /api/user/me/consent-info- Datenschutz-Informationen
app.py: Alle neuen Routes registriert
2026-01-18
- Phase 6 abgeschlossen:
services/__init__.py:ServicesKlasse akzeptiertmandateIdParametergetInterface(user, workflow, mandateId)übergibtmandateIdan alle Interface-AufrufemainChatbot.py:chatProcess(user, mandateId, ...)-mandateIdfür Workflow-ErstellungmainRealEstate.py:processNaturalLanguageCommand(user, mandateId, ...)+ alle CREATE-OperationenmainDynamicOptions.py:services.mandateIdstattcurrentUser.mandateIdmainNeutralizePlayground.py:NeutralizationPlayground(user, mandateId)KonstruktorworkflowManager.py:self.services.mandateIdfür neue Workflows- Route-Aufrufe aktualisiert:
str(context.mandateId)wird übergeben methodBase.py: Keine Änderung nötig (verwendet Services)mainServiceNeutralization.py: Keine Änderung nötig (verwendetinterfaceDbApp.mandateId)
- Phase 5 abgeschlossen:
interfaceDbTrusteeObjects.py:getInterface(user, mandateId)+setUserContext(user, mandateId)interfaceDbChatObjects.py:getInterface(user, mandateId)+setUserContext(user, mandateId)interfaceDbRealEstateObjects.py:getInterface(user, mandateId)+setUserContext(user, mandateId)interfaceDbComponentObjects.py:getInterface(user, mandateId)+setUserContext(user, mandateId)interfaceVoiceObjects.py:getVoiceInterface(user, mandateId)+setUserContext(user, mandateId)- Feature-Routes aktualisiert:
getInterface(context.user, mandateId=context.mandateId)
- Phase 4.B abgeschlossen:
routeFeatureTrustee.py: Alle Endpoints aufgetRequestContext()umgestelltrouteFeatureChatbot.py: Streaming + RBAC mitcontext.userrouteFeatureRealEstate.py:context.mandateIdfür Projekt-/Parzellen-FilterungrouteFeatureNeutralization.py: Config + Processing mitcontext.userrouteFeatureAutomation.py:requireSysAdmin()für alle EndpointsrouteFeatureChatDynamic.py: Workflow-Start/Stop mitcontext.user- Alte Route-Dateien gelöscht (6 Dateien)
app.pyImports aktualisiert
- Phase 5 - GREENFIELD Cleanup:
- ALLE Fallbacks auf
currentUser.mandateIdentfernt (GREENFIELD = keine Backwards Compatibility) interfaceDbAppObjects.py:self.mandateIdkommt nur aus Parameter, nicht aus UserinterfaceDbTrusteeObjects.py: Fallback-Logik entferntinterfaceDbChatObjects.py:self.mandateIdstattself.currentUser.mandateIdinterfaceDbRealEstateObjects.py: Fallback-Logik entferntinterfaceDbComponentObjects.py:self.mandateIdfür alle Record-ErstellungeninterfaceVoiceObjects.py: Fallback-Logik entferntinterfaceRbac.py: Keine Fallbacks mehr,mandateIdist explizit erforderlich- Modul-Header aktualisiert (keine "Backwards Compatibility" Referenzen mehr)
- ALLE Fallbacks auf
2026-01-17
- Phase 3 abgeschlossen:
RequestContextKlasse inauth/authentication.pyimplementiertgetRequestContext()FastAPI Dependency mitX-Mandate-IdundX-Instance-IdHeader-HandlingrequireSysAdmin()FastAPI Dependency für System-Level-Operationen- JWT-Token enthalten kein
mandateIdmehr (Local, Microsoft, Google) - Token-Validierung prüft nur noch
userId auth/__init__.pyexportiert neue Funktionen
- Phase 4.A abgeschlossen:
routeSecurityAdmin.py: Alle Endpoints aufrequireSysAdmin()umgestelltrouteAdminRbacRoles.py:requireSysAdmin()+getRootInterface()routeDataUsers.py:getRequestContext()für Mandate-Scope, UserMandate-FilterungrouteDataMandates.py:requireSysAdmin()(Mandates sind System-Ressourcen)routeRbac.py: Permissions mitgetRequestContext(), Rules/Roles mitrequireSysAdmin()
2026-01-16
- Phase 1 abgeschlossen
- Startup-Fehler behoben (isSysAdmin Validator, Bootstrap-Aufruf)
- DB-Konfiguration konsolidiert
- DB-Admin-Funktionen generisch gemacht
- Phase 2 abgeschlossen:
getRulesForUserBulk()insecurity/rbac.pyimplementiert (optimierte Bulk-Queries)- Membership-Methoden in
interfaceDbAppObjects.pyhinzugefügt interfaceFeatures.pyNEU erstellt (Feature-Instanz-Management mit Template-Kopierung)