wiki/implementation/Saas Multi Tenant Mandate/PROGRESS_TRACKING.md
2026-01-17 02:18:10 +01:00

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
  • isSysAdmin Feld 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.py erstellt 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:

  • RequestContext Klasse implementiert: User + mandateId + featureInstanceId + roleIds
  • getRequestContext() FastAPI Dependency: Liest X-Mandate-Id und X-Instance-Id Header
  • requireSysAdmin() FastAPI Dependency: Prüft isSysAdmin Flag mit Audit-Logging
  • JWT-Token enthalten KEIN mandateId mehr - Mandant-Kontext kommt per Request-Header
  • Token-Validierung in _getUserBase() prüft nur noch userId, nicht mehr mandateId
  • 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 durch isSysAdmin ersetzt
  • currentUser.mandateId durch RequestContext ersetzt
  • User-Filterung via UserMandate Junction Table
  • Audit-Logging verwendet context.mandateId oder "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
  • getCurrentUser durch getRequestContext ersetzt
  • currentUser.mandateId durch context.mandateId ersetzt
  • currentUser.roleLabels durch context.roleIds ersetzt
  • requireSysAdmin() für Sysadmin-Endpoints (z.B. Automation Events)
  • Alte Route-Dateien gelöscht, Imports in app.py aktualisiert

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.mandateIdself.mandateId = mandateId (aus Parameter)
  • SysAdmins können ohne mandateId arbeiten (cross-mandate Operationen)
  • Singleton-Cache-Keys verwenden jetzt userId_mandateId statt userId_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:

  • Services Klasse aktualisiert: mandateId als expliziter Parameter
  • getInterface(user, workflow, mandateId) für alle Services
  • Feature-Funktionen aktualisiert: mandateId als Parameter statt currentUser.mandateId
  • Route-Aufrufe aktualisiert: str(context.mandateId) wird übergeben
  • workflowManager.py: Verwendet self.services.mandateId für neue Workflows
  • mainDynamicOptions.py: Verwendet services.mandateId fü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.mandateId und User.roleLabels Felder entfernt
  • interfaceDbAppObjects.py:
    • createUser(): roleLabels Parameter entfernt, nur noch isSysAdmin
    • updateUser(): mandateId Handling entfernt
    • deleteRole(): Prüft jetzt UserMandateRole statt user.roleLabels
    • getAccessRules(): Neuer roleId Parameter hinzugefügt
    • countRoleAssignments(): Neue Methode für Role-Zählung via UserMandateRole
  • interfaceBootstrap.py: mandateId und roleLabels aus Admin/Event User Erstellung entfernt
  • security/rbac.py: user.roleLabels Fallback entfernt
  • routeDataUsers.py: Verwendet createUserMandate() für Role-Assignment
  • routeSecurityLocal.py: roleLabels aus User-Registrierung entfernt
  • routeRbac.py: Verwendet context.roleIds statt user.roleLabels
  • routeAdminRbacRoles.py: Komplett auf UserMandateRole umgestellt 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/my Endpoint
  • routeInvitations.py: Token-basierte Einladungen, öffentliche Validierung, Accept-Flow
  • routeRbacExport.py: Global und Mandate-scoped Export/Import von RBAC-Regeln
  • routeGdpr.py: DSGVO Art. 15 (Datenexport), Art. 17 (Löschrecht), Art. 20 (Datenportabilität)
  • Alle neuen Routes in app.py registriert

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 Mandaten
      • GET /api/mandates/{mandateId}/users - Benutzer im Mandant auflisten
      • POST /api/mandates/{mandateId}/users - Benutzer zu Mandant hinzufügen
      • DELETE /api/mandates/{mandateId}/users/{targetUserId} - Benutzer aus Mandant entfernen
      • PUT /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 + Accept
      • POST /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.py NEU: Feature-Definitionen (SysAdmin), FeatureInstance CRUD (Mandate-Admin)
      • GET /api/features/ - Liste aller Features
      • GET /api/features/{featureCode} - Feature-Details
      • POST /api/features/ - Feature erstellen (SysAdmin)
      • GET /api/features/instances - Feature-Instanzen im Mandant
      • POST /api/features/instances - Feature-Instanz erstellen
      • DELETE /api/features/instances/{id} - Feature-Instanz löschen
      • POST /api/features/instances/{id}/sync-roles - Rollen synchronisieren
      • GET /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.py NEU: Token-basierte Einladungen für Self-Service Onboarding
      • POST /api/invitations/ - Einladung erstellen (Mandate-Admin)
      • GET /api/invitations/ - Einladungen auflisten
      • DELETE /api/invitations/{id} - Einladung widerrufen
      • GET /api/invitations/validate/{token} - Einladung validieren (öffentlich)
      • POST /api/invitations/accept/{token} - Einladung annehmen (authentifiziert)
    • routeRbacExport.py NEU: RBAC Export/Import
      • GET /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.py NEU: DSGVO-Compliance Endpoints
      • GET /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: Services Klasse akzeptiert mandateId Parameter
    • getInterface(user, workflow, mandateId) übergibt mandateId an alle Interface-Aufrufe
    • mainChatbot.py: chatProcess(user, mandateId, ...) - mandateId für Workflow-Erstellung
    • mainRealEstate.py: processNaturalLanguageCommand(user, mandateId, ...) + alle CREATE-Operationen
    • mainDynamicOptions.py: services.mandateId statt currentUser.mandateId
    • mainNeutralizePlayground.py: NeutralizationPlayground(user, mandateId) Konstruktor
    • workflowManager.py: self.services.mandateId fü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 (verwendet interfaceDbApp.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 auf getRequestContext() umgestellt
    • routeFeatureChatbot.py: Streaming + RBAC mit context.user
    • routeFeatureRealEstate.py: context.mandateId für Projekt-/Parzellen-Filterung
    • routeFeatureNeutralization.py: Config + Processing mit context.user
    • routeFeatureAutomation.py: requireSysAdmin() für alle Endpoints
    • routeFeatureChatDynamic.py: Workflow-Start/Stop mit context.user
    • Alte Route-Dateien gelöscht (6 Dateien)
    • app.py Imports aktualisiert
  • Phase 5 - GREENFIELD Cleanup:
    • ALLE Fallbacks auf currentUser.mandateId entfernt (GREENFIELD = keine Backwards Compatibility)
    • interfaceDbAppObjects.py: self.mandateId kommt nur aus Parameter, nicht aus User
    • interfaceDbTrusteeObjects.py: Fallback-Logik entfernt
    • interfaceDbChatObjects.py: self.mandateId statt self.currentUser.mandateId
    • interfaceDbRealEstateObjects.py: Fallback-Logik entfernt
    • interfaceDbComponentObjects.py: self.mandateId für alle Record-Erstellungen
    • interfaceVoiceObjects.py: Fallback-Logik entfernt
    • interfaceRbac.py: Keine Fallbacks mehr, mandateId ist explizit erforderlich
    • Modul-Header aktualisiert (keine "Backwards Compatibility" Referenzen mehr)

2026-01-17

  • Phase 3 abgeschlossen:
    • RequestContext Klasse in auth/authentication.py implementiert
    • getRequestContext() FastAPI Dependency mit X-Mandate-Id und X-Instance-Id Header-Handling
    • requireSysAdmin() FastAPI Dependency für System-Level-Operationen
    • JWT-Token enthalten kein mandateId mehr (Local, Microsoft, Google)
    • Token-Validierung prüft nur noch userId
    • auth/__init__.py exportiert neue Funktionen
  • Phase 4.A abgeschlossen:
    • routeSecurityAdmin.py: Alle Endpoints auf requireSysAdmin() umgestellt
    • routeAdminRbacRoles.py: requireSysAdmin() + getRootInterface()
    • routeDataUsers.py: getRequestContext() für Mandate-Scope, UserMandate-Filterung
    • routeDataMandates.py: requireSysAdmin() (Mandates sind System-Ressourcen)
    • routeRbac.py: Permissions mit getRequestContext(), Rules/Roles mit requireSysAdmin()

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() in security/rbac.py implementiert (optimierte Bulk-Queries)
    • Membership-Methoden in interfaceDbAppObjects.py hinzugefügt
    • interfaceFeatures.py NEU erstellt (Feature-Instanz-Management mit Template-Kopierung)