From be20a8545e98f7ee4e60a8e988e7cb226ef3f3b9 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Sat, 1 Aug 2026 07:28:11 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20Abschlussbericht=20Phase=200+1=20und=20?= =?UTF-8?q?vollst=C3=A4ndiger=20Sanierungsplan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Kompletter Statusbericht mit allen 5 Gates - Datenbankrollen-Architektur dokumentiert - RLS-Architektur dokumentiert - Verifizierte Sicherheitsnachweise - Durchgeführte Code-Änderungen und Migrationen - Offene Risiken - Vollständiger Sanierungsplan Phase 2-10 - Gesamtschätzung: 120-210h verbleibend - Empfohlene Reihenfolge --- docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md | 476 +++++++++++++++++++++++++ 1 file changed, 476 insertions(+) create mode 100644 docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md diff --git a/docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md b/docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md new file mode 100644 index 0000000..93fc0ce --- /dev/null +++ b/docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md @@ -0,0 +1,476 @@ +# LeoCRM — Abschlussbericht Phase 0 + Phase 1 und vollständiger Sanierungsplan + +**Datum:** 2026-08-01 +**Git-Commit:** 733fa1c (main) +**Alembic-Head:** 0090 +**Produktion:** https://crm.media-on.de — healthy + +--- + +## 1. Aktueller Stand + +### 1.1 Abgenommene Gates + +| Gate | Beschreibung | Status | +|------|-------------|--------| +| Gate 1 | Reproduzierbares Coolify-Deployment | ✅ Bestanden | +| Gate 2 | Neuinstallation auf leerer Datenbank | ✅ Bestanden | +| Gate 3 | Vollständiger Restore-Test | ✅ Bestanden | +| Gate 4 | Passwort-Reset end-to-end | ✅ Bestanden | +| Gate 5 | Worker und Eventhandler | ✅ Bestanden | + +### 1.2 Produktionsstand + +| Komponente | Wert | +|-----------|------| +| Git-Commit | 733fa1c | +| Docker-Image | stvabl4vaqru7jclx4ittzr3:733fa1c | +| API-Container | stvabl4vaqru7jclx4ittzr3-201530032526 — healthy | +| Worker-Container | leocrm-worker — healthy | +| Alembic-Head | 0090 | +| Tabellen | 124 | +| RLS-Tabellen | 108 (alle Tenant-Tabellen) | +| RLS-Policies | 112 | +| Legacy app.tenant_id Policies | 0 | +| DB-Rollen | 5 (crm_platform_admin, crm_migration, crm_auth, crm_api, crm_worker) | +| crm_api | NOSUPERUSER, NOBYPASSRLS — API-Laufzeit | +| crm_auth | NOSUPERUSER, NOBYPASSRLS — Login/Authentifizierung | +| crm_worker | NOSUPERUSER, NOBYPASSRLS — Worker-Laufzeit | +| crm_migration | NOSUPERUSER, BYPASSRLS — Migrationen und DDL | +| ~~crm_runtime~~ | Gelöscht | + +### 1.3 Datenbankrollen-Architektur + +``` +┌─────────────────────────────────────────────────────────────┐ +│ PostgreSQL (crm_db) │ +├─────────────────────────────────────────────────────────────┤ +│ crm_user (POSTGRES_USER, SUPERUSER) │ +│ └── Nur für Bootstrap und DB-Initialisierung │ +│ │ +│ crm_migration (NOSUPERUSER, BYPASSRLS, Tabellenowner) │ +│ ├── Alembic-Migrationen (0001–0090) │ +│ ├── Plugin-Migrationen (DDL) │ +│ └── Datenmigrationen (tenantübergreifend) │ +│ │ +│ crm_auth (NOSUPERUSER, NOBYPASSRLS) │ +│ ├── Login/Logout │ +│ ├── Tenant-Auflösung │ +│ ├── User/Tenant-Membership │ +│ └── Password-Reset-Token │ +│ │ +│ crm_api (NOSUPERUSER, NOBYPASSRLS, kein Owner) │ +│ ├── Normale API-Abfragen (SELECT, INSERT, UPDATE, DELETE) │ +│ ├── Audit-Log (über separate Session mit Tenant-Kontext) │ +│ └── Keine DDL-Rechte │ +│ │ +│ crm_worker (NOSUPERUSER, NOBYPASSRLS, kein Owner) │ +│ ├── ARQ-Background-Jobs │ +│ ├── Outbox-Processing (per-Tenant mit RLS-Kontext) │ +│ ├── Cron-Jobs (scheduler_tick, tasks_due_reminder) │ +│ └── Event-Handler für aktive Plugins │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 1.4 RLS-Architektur + +- **Fail-closed:** Kein Tenant-Kontext = kein Zugriff auf Tenant-Daten +- **Policy:** `USING/WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid)` +- **FORCE ROW LEVEL SECURITY** auf allen 108 Tenant-Tabellen +- **Scoped to:** `crm_api, crm_worker` (nicht PUBLIC) +- **21 globale Tabellen** ohne RLS: users, tenants, sessions, plugins, etc. +- **0 legacy Policies** mit `app.tenant_id` (alle durch `app.current_tenant_id` ersetzt) + +### 1.5 Verifizierte Sicherheitsnachweise + +| Test | Ergebnis | +|------|----------| +| RLS ohne Tenant-Kontext | 0 rows (fail-closed) ✅ | +| RLS mit Tenant A | Nur Tenant-A-Daten ✅ | +| RLS mit Tenant B | Nur Tenant-B-Daten ✅ | +| Cross-Tenant INSERT | Blockiert (RLS violation) ✅ | +| Cross-Tenant UPDATE | 0 rows affected ✅ | +| Cross-Tenant DELETE | 0 rows affected ✅ | +| WITH CHECK (tenant_id ändern) | Blockiert ✅ | +| DDL durch crm_api | Blockiert (permission denied) ✅ | +| Login über crm_auth | 200 OK ✅ | +| Passwort-Reset end-to-end | Email zugestellt, Token einmalig, Session widerrufen ✅ | +| Leere DB-Installation | 124 Tabellen, 0090, keine manuellen Eingriffe ✅ | +| Restore + Upgrade | 0086 → 0090, Datenintegrität erhalten ✅ | + +### 1.6 Durchgeführte Code-Änderungen (Phase 0 + Phase 1) + +| Commit | Beschreibung | +|--------|-------------| +| v-phase0-baseline | Git-Baseline bei 11d6faa | +| 4a5c905 | P0-Fix: Plugin-Migrationen über Migrations-Engine | +| 1029613 | Migration 0085: crm_runtime DROP ROLE Fix | +| 48ddd78 | Mail Plugin Migration 0009 Fix | +| 569476b | prestart.sh: DB-Rollen-Passwörter setzen | +| b5191f0 | Migration 0089: sessions.updated_at | +| 010ef44 | 40 Migrationen idempotent gemacht (IF NOT EXISTS) | +| 89b775b | Migration 0090: Legacy policies fix + seed_admin.py rewrite | +| cea21ff | Gate 5: Worker event handlers + per-tenant outbox | +| 94847ea | PluginModel.active Fix (worker crash) | +| 733fa1c | Gate 3: Restore-Test Doku | + +### 1.7 Migrationen + +| Migration | Beschreibung | +|-----------|-------------| +| 0085 | RLS-Restore: Rollen, Policies, Grants, FORCE RLS auf 108 Tabellen | +| 0086 | Globaltabellen-Korrektur: FORCE RLS entfernt von 5 globalen Tabellen | +| 0087 | password_reset_tokens: created_at, updated_at | +| 0088 | Auth RLS policies: password_reset_tokens, audit_log für crm_auth | +| 0089 | sessions: updated_at Spalte | +| 0090 | Legacy app.tenant_id policies auf _old Tabellen fixen | + +### 1.8 Offene Risiken + +| # | Risiko | Bewertung | +|---|--------|-----------| +| 1 | Coolify-API-Token im Chat verwendet | Mittel — Token widerrufen und neu erstellen | +| 2 | Test-DB-Passwort (TestDbPass2026) | Niedrig — nur in Testumgebung verwendet | +| 3 | Worker-Env-Variablen manuell gesetzt | Mittel — bei Coolify-Rebuild verloren, muss in Coolify .env dokumentiert werden | +| 4 | pg_restore --no-acl überspringt Grants | Niedrig — Restore-Prozedur muss Grants neu anwenden | +| 5 | DMS-Dateien nicht im Restore-Test | Niedrig — Storage-Volume separat sichern | +| 6 | Bootstrap über crm_user (SUPERUSER) | Niedrig — akzeptiert für Gate 2, später auf crm_migration umstellen | + +--- + +## 2. Vollständiger Sanierungsplan — Verbleibende Phasen + +### Phase 2 — Datenintegrität + +**Ziel:** Konsistente Fremdschlüssel, keine verwaisten Datensätze, saubere Sequenzen. + +**Aufgaben:** +1. Fremdschlüssel-Constraints prüfen und fehlende ergänzen +2. Verwaiste Datensätze identifizieren und bereinigen +3. Sequenzen synchronisieren (sync mit MAX(id)) +4. ON DELETE CASCADE prüfen und dokumentieren +5. Datenbank-Integritäts-Test-Suite erstellen +6. Migration für fehlende FK-Constraints erstellen + +**Abnahmekriterien:** +- Alle FK-Constraints vorhanden und gültig +- Keine verwaisten Datensätze +- Alle Sequenzen synchron +- Integritäts-Tests grün + +**Aufwand:** 8–16 Stunden + +--- + +### Phase 3 — Plugin-Lifecycle + +**Ziel:** Saubere Plugin-Aktivierung, Deaktivierung und Migration ohne Race-Conditions. + +**Aufgaben:** +1. Plugin-Aktivierung: Prüfen ob bereits aktiv, idempotent machen +2. Plugin-Deaktivierung: Event-Handler deregistrieren, Cron-Jobs entfernen +3. Plugin-Migration: Versionierung und Rollback +4. Tenant-Plugin-Aktivierung: Per-Tenant mit Tenant-Kontext +5. Plugin-Abhängigkeiten: Load-Order respektieren +6. Plugin-Router: Nur in API registrieren, nicht im Worker +7. Plugin-Event-Handler: Nur für aktive Plugins registrieren +8. Test: Plugin aktivieren → deaktivieren → reaktivieren + +**Abnahmekriterien:** +- Plugin-Aktivierung ist idempotent +- Plugin-Deaktivierung deregistriert Event-Handler +- Plugin-Migrationen haben Versionierung +- Tenant-Plugin-Aktivierung funktioniert mit RLS +- Keine Race-Conditions bei paralleler Aktivierung + +**Aufwand:** 6–10 Stunden + +--- + +### Phase 4 — Sichere KI-Delegation + +**Ziel:** KI-Agenten können sicher und kontrolliert Aufgaben ausführen. + +**Aufgaben:** +1. Delegation-Contract definieren (Input, Output, Permissions) +2. KI-Agent-Permissions: Tenant-scoped, keine Cross-Tenant +3. KI-Agent-Session: Separate Session mit Tenant-Kontext +4. KI-Agent-Limits: Max executions, timeout, rate-limit +5. KI-Agent-Audit: Alle Aktionen protokollieren +6. KI-Agent-Rollback: Fehlerhafte Aktionen zurückrollen +7. KI-Agent-Approval: Menschliche Freigabe für kritische Aktionen +8. Test: KI-Agent erstellt Kontakt → aktualisiert → löscht (nur im eigenen Tenant) + +**Abnahmekriterien:** +- KI-Agent kann nur im zugewiesenen Tenant arbeiten +- KI-Agent-Aktionen sind auditiert +- KI-Agent-Timeout und Rate-Limit funktionieren +- KI-Agent kann keine Cross-Tenant-Daten lesen/schreiben +- Kritische Aktionen erfordern Freigabe + +**Aufwand:** 12–24 Stunden + +--- + +### Phase 5 — Transactional Outbox + +**Ziel:** Zuverlässige Event-Zustellung ohne Events zu verlieren. + +**Aufgaben:** +1. Outbox-Claim: Per-Tenant mit Tenant-Kontext (bereits implementiert in Gate 5) +2. Outbox-Event-Consumer: Erwartete Consumer pro Event registrieren +3. Outbox-Dead-Letter: Events nach max_attempts in DLQ +4. Outbox-Monitoring: Backlog-Metriken, Failed-Jobs-Alert +5. Outbox-Retry: Exponentieller Backoff (bereits implementiert) +6. Outbox-Idempotency: consumer_inbox Check (bereits implementiert) +7. Outbox-Delivery-Guarantee: At-least-once, consumer must be idempotent +8. Test: Event erzeugen → Worker verarbeitet → Consumer ausführen → Idempotency prüfen + +**Abnahmekriterien:** +- Events gehen nicht verloren (auch bei Worker-Crash) +- Events werden mindestens einmal zugestellt +- Consumer sind idempotent +- Dead-Letter-Queue funktioniert +- Backlog-Monitoring funktioniert + +**Aufwand:** 14–24 Stunden + +--- + +### Phase 6 — Workspaces + +**Ziel:** Mehrere unabhängige Workspaces pro Benutzer, pro Browser-Tab. + +**Aufgaben:** +1. Workspace-Model: UUID, Name, Owner, Tenant, Config +2. Workspace-Widget-Config: Eigene UUID, Position, Größe, Konfiguration +3. Workspace-Store: Zentraler React/Zustand-Store +4. Workspace-Switcher: Sofortiger Wechsel ohne Page-Reload +5. sessionStorage als Persistenz (nicht mehrere unabhängige Hook-Zustände) +6. Sidebar reagiert sofort auf Workspace-Wechsel +7. Leerer Workspace zeigt keine Module +8. Direkte Links auf berechtigte Fachobjekte funktionieren +9. Mehrfach-Widgets: Gleicher widget_key kann mehrfach vorkommen +10. Workspace-Manager: Kann nur eigenen Workspace konfigurieren +11. Cross-Tenant-Zuweisungen unmöglich +12. Ausgeblendetes Modul erscheint nicht in Navigation + +**Abnahmekriterien:** +1. Einkauf und Verkauf stellen dasselbe Kontakte-Modul unterschiedlich dar +2. Kalender unterscheiden sich pro Workspace +3. Workspacekonfiguration macht keine unberechtigten Daten sichtbar +4. Zwei Browser-Tabs können unterschiedliche Workspaces verwenden +5. Derselbe Widget-Typ kann mehrfach vorkommen +6. Workspace-Manager kann nur seinen Workspace konfigurieren +7. Workspace-Manager kann keine Rechte ändern +8. Cross-Tenant-Zuweisungen sind unmöglich +9. Ein ausgeblendetes Modul erscheint nicht in der Navigation +10. Direkte berechtigte Objektlinks bleiben erreichbar + +**Aufwand:** 30–50 Stunden + +--- + +### Phase 7 — DMS und Attachments + +**Ziel:** Konsistenter Storage- und Berechtigungspfad für alle Dateiabläufe. + +**Aufgaben:** +1. Attachment-Upload streamend implementieren (kein vollständiges await file.read()) +2. Download über Storage-Streaming +3. Alte Attachments nach files + entity_attachments migrieren +4. Deduplikation nur tenantlokal +5. Physische Datei nur löschen wenn keine Referenzen existieren +6. Technische Felder (storage_path, Hashwerte) nicht an Clients ausgeben +7. Entity-Typen konsistent registrieren +8. Größenlimit, MIME-Prüfung und Hashing zentralisieren +9. Lokales Storage und S3 identisch behandeln +10. Keine Cross-Tenant-Dateireferenzen +11. Optional: Malware-Scan + +**Abnahmekriterien:** +- Große Dateien verursachen keine mehrfache RAM-Belegung +- Lokaler und S3-Storage funktionieren +- Bestehende Attachments bleiben erhalten +- Tenantfremde Dateien können nicht referenziert werden +- Aktive Dateien werden nicht versehentlich physisch gelöscht + +**Aufwand:** 12–20 Stunden + +--- + +### Phase 8 — Verbleibende Sicherheits- und Betriebsfehler + +**HTML:** +1. Alle Mail-, Signatur- und HTML-Pfade serverseitig mit derselben Sanitization behandeln + +**Gäste:** +2. Tenant-Slug verpflichtend oder eindeutige Tenant-Auswahl +3. Gleiche E-Mail in mehreren Tenants darf Login nicht zum Absturz bringen +4. Sofortiger Session-Widerruf +5. Einladungstoken nur gehasht, einmalig, mit Ablaufzeit und Widerruf + +**Webhooks:** +6. SSRF-Schutz beibehalten +7. DNS-Ziel beim tatsächlichen Connect erneut prüfen +8. Redirects begrenzen oder deaktivieren +9. Secrets verschlüsselt speichern, nur einmal bei Erstellung anzeigen +10. Interne und private Netze blockieren +11. Retry und Fehlerstatus implementieren + +**Healthchecks:** +12. Trennen: /health/live, /health/ready, /metrics +13. Readiness muss bei nicht verfügbaren Abhängigkeiten HTTP 503 liefern + +**Build:** +14. Entfernen: `npm ci || npm install` → Verwenden: `RUN npm ci` +15. Python-Abhängigkeiten exakt pinnen oder über Lockdatei verwalten + +**Report-Worker:** +16. Keine direkten Cross-Plugin-Imports +17. DMS nur über Contract oder Core-Service +18. PDF-Erstellung nur im Worker +19. Synchronen API-Reportpfad entfernen oder stark begrenzen +20. Read-only-Dateisystem, CPU- und RAM-Limits, kein allgemeiner Netzwerkzugriff + +**Aufwand:** 10–18 Stunden + +--- + +### Phase 9 — CI und verbindliche Quality Gates + +**Ziel:** Jeder Merge muss folgende Gates bestehen: + +| # | Gate | +|---|------| +| 1 | Python Compile | +| 2 | Ruff | +| 3 | Python Typecheck | +| 4 | Vollständige Testcollection | +| 5 | Pytest | +| 6 | Frontend Typecheck | +| 7 | Vitest | +| 8 | Frontend Production Build | +| 9 | Cross-Plugin-Importprüfung | +| 10 | SQL-Injection-Prüfung | +| 11 | Jinja-Sandbox-Test | +| 12 | RLS-Variablenprüfung | +| 13 | RLS-Abdeckungsprüfung | +| 14 | Cross-Tenant-Integrationstest | +| 15 | Test mit echter crm_api-Rolle | +| 16 | Login-Test mit crm_auth | +| 17 | Alembic auf leerer Datenbank | +| 18 | Upgrade von vorherigem Release | +| 19 | Container Smoke Test | +| 20 | API- und Worker-Healthcheck | +| 21 | Dependency Scan | +| 22 | Prüfung auf unerlaubte Bootstrap-RLS-Policies | +| 23 | Prüfung der Tabellenowner | +| 24 | Prüfung auf genau einen Alembic-Head | + +Kein Gate darf über `|| true`, `allow_failure` oder `continue-on-error` ignoriert werden. + +**Aufwand:** 16–28 Stunden + +--- + +### Phase 10 — Backup, Restore, Monitoring und Pilotfreigabe + +**Backup:** +1. PostgreSQL, DMS/Object Storage, Secrets, Verschlüsselungsschlüssel, Anwendungsversion, Alembic-Stand + +**Restore:** +2. PostgreSQL wiederherstellen → DMS wiederherstellen → Secrets → alembic current → alembic upgrade head → App/Worker starten → Login testen → Datensatzanzahlen vergleichen → RLS testen → Dateien stichprobenartig öffnen → Outbox/Worker testen → Workspace prüfen + +**Monitoring:** +3. Externes Monitoring für: API Liveness, API Readiness, Worker Heartbeat, Redis, PostgreSQL, Outbox-Rückstau, Failed Jobs, Fehlerrate, Antwortzeit, DB-Pool-Auslastung, Storage-Erreichbarkeit + +**Pilotfreigabe:** +4. Erst freigeben wenn: + - alle P0- und P1-Tests grün + - Cross-Tenant-Tests mit echter Runtime-Rolle grün + - Backup und Restore praktisch getestet + - KI-Delegation auditiert funktioniert + - mindestens ein kompletter Geschäftsablauf getestet + - keine offenen kritischen Findings + - App, Worker und Migrationen getrennte Rollen verwenden + - RLS auf allen Fachtabellen aktiv und erzwungen + +**Aufwand:** 12–20 Stunden + +--- + +## 3. Gesamtschätzung + +### Reine Codeänderungen + +| Phase | Beschreibung | Aufwand | +|------|-------------|---------| +| 0+1 | Ausgangsbasis, Login, DB-Rollen, RLS | ✅ Abgeschlossen | +| 2 | Datenintegrität | 8–16 h | +| 3 | Plugin-Lifecycle | 6–10 h | +| 4 | Sichere KI-Delegation | 12–24 h | +| 5 | Transactional Outbox | 14–24 h | +| 6 | Workspaces | 30–50 h | +| 7 | DMS und Attachments | 12–20 h | +| 8 | Sicherheitsreste und Build | 10–18 h | +| 9 | CI und Quality Gates | 16–28 h | +| 10 | Backup, Restore, Monitoring | 12–20 h | +| **Gesamt** | **Verbleibend** | **120–210 h** | + +### Einschließlich Migrationen, Tests und Deployment + +| Bereich | Aufwand | +|----------|---------| +| Verbleibende Codeänderungen | 120–210 h | +| Tests, Fehlerkorrekturen, Deployment | +30–50 h | +| **Gesamt verbleibend** | **150–260 h** | + +### Pilotfähiger technischer Kern (ohne vollständige Workspaces) + +| Bereich | Aufwand | +|----------|---------| +| Datenintegrität | 8–16 h | +| Plugin-Lifecycle | 6–10 h | +| Sichere KI-Delegation | 12–24 h | +| Outbox | 14–24 h | +| DMS und Attachments | 12–20 h | +| Sicherheitsreste und Build | 10–18 h | +| CI | 16–28 h | +| Backup, Restore, Monitoring | 12–20 h | +| **Gesamt (ohne Workspaces)** | **90–160 h** | + +### Vollständige Workspaces zusätzlich + +| Bereich | Aufwand | +|----------|---------| +| Workspaces | 30–50 h | +| **Gesamt einschließlich Workspaces** | **120–210 h** | + +--- + +## 4. Empfohlene Reihenfolge + +1. **Phase 2** (Datenintegrität) — Fundament für alle weiteren Phasen +2. **Phase 3** (Plugin-Lifecycle) — Saubere Basis für Plugin-Funktionen +3. **Phase 5** (Outbox) — Bereits teilweise implementiert, fertigstellen +4. **Phase 4** (KI-Delegation) — Baut auf Outbox auf +5. **Phase 7** (DMS) — Unabhängig, parallel möglich +6. **Phase 8** (Sicherheitsreste) — Unabhängig, parallel möglich +7. **Phase 9** (CI) — Nach allen Code-Phasen, vor Pilot +8. **Phase 6** (Workspaces) — Größter Aufwand, nach Kern-Stabilität +9. **Phase 10** (Backup, Monitoring, Pilot) — Als Abschluss + +--- + +## 5. Nächste Schritte + +1. **Freigabe Phase 2** — Nach Abnahme dieses Berichts +2. **Coolify-API-Token widerrufen** — Token wurde im Chat verwendet +3. **Produktions-Passwörter rotieren** — Falls noch nicht geschehen +4. **Coolify .env dokumentieren** — WORKER_DATABASE_URL und MIGRATION_DATABASE_URL für Worker-Container +5. **Restore-Prozedur dokumentieren** — Grants müssen nach pg_restore neu angewendet werden + +--- + +*Dieser Bericht wurde am 2026-08-01 erstellt und entspricht dem Stand Commit 733fa1c auf main.*