From 8fc050a28207a6f9ed320843e170bda77d40aed1 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Thu, 23 Apr 2026 17:21:34 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20TESTING.md=20mit=20Check-Listen=20f?= =?UTF-8?q?=C3=BCr=20Security=20+=20Email-Log-System?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strukturierter Test-Katalog für manuelle Abnahmetests vor einem Release. Security-System (10 Abschnitte): - Login + Rate-Limiting - Passwort-Reset-Flow (Mitarbeiter + Portal) - Rate-Limiting Passwort-Reset - Berechtigungen (RBAC) - Portal-Isolation (DSGVO-kritisch) - Session-Invalidation - Audit-Log - DSGVO-Features (Export, Löschanfragen, Einwilligungen) - Verschlüsselte Credentials - DSGVO-Einwilligung sperrt Tabs für Mitarbeiter Email-Log-System (5 Abschnitte): - Email-Log-Seite (UI, Filter, Suche, Pagination) - Alle 6 Kontexte durchspielen: consent-link, authorization-request, customer-email, birthday-greeting, birthday-greeting-auto, password-reset - Fehlgeschlagener Versand wird geloggt (Test mit falschem SMTP-Passwort) - Details-Modal mit SMTP-Details - Automatisches Logging (Kontext, triggeredBy) Außerdem: Neue Kontexte in EmailLogs.tsx CONTEXT_LABELS ergänzt (birthday-greeting, birthday-greeting-auto, password-reset). Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/todo.md | 8 +- docs/TESTING.md | 172 ++++++++++++++++++++++ frontend/src/pages/settings/EmailLogs.tsx | 3 + 3 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 docs/TESTING.md diff --git a/backend/todo.md b/backend/todo.md index 3a665999..95ec7d9b 100644 --- a/backend/todo.md +++ b/backend/todo.md @@ -4,11 +4,9 @@ ## 🔜 Offen -### Email Log & System testen -- Senden testen -- Empfangen testen - -### Security System testen +### Manuelle Tests (vor Release durchklicken) +Checklisten für Security + Email-Log-System stehen in **[docs/TESTING.md](../docs/TESTING.md)**. +Einmal komplett durchlaufen vor v1.0.0-Release. ### 🚀 SaaS-Ausbau: Instance-per-Customer + Admin-Portal + GoCardless diff --git a/docs/TESTING.md b/docs/TESTING.md new file mode 100644 index 00000000..7715fe9b --- /dev/null +++ b/docs/TESTING.md @@ -0,0 +1,172 @@ +# Manueller Test-Katalog (v1.0.0) + +Checklisten für manuelle Abnahmetests vor einem Release. Durchläuft die kritischen +Features Schritt für Schritt. Geschätzte Dauer für einen kompletten Durchlauf: ~60 Minuten. + +--- + +## 🔐 Security-System + +### 1. Login & Rate-Limiting + +- [ ] **Mitarbeiter-Login** mit korrekten Credentials → Erfolgreich +- [ ] **Mitarbeiter-Login** mit falschem Passwort → Fehlermeldung "Ungültige Anmeldedaten" +- [ ] **Portal-Login** mit Kunden-E-Mail + Passwort → Erfolgreich ins Portal +- [ ] **Rate-Limit Login**: 10× falsch nacheinander versuchen → Nach 10. Versuch: "Zu viele + Login-Versuche. Bitte in 15 Minuten erneut versuchen." +- [ ] **Rate-Limit zählt erfolgreiche Logins nicht**: 5× falsch, dann 1× korrekt, dann wieder + 5× falsch → immer noch erlaubt (weil erfolgreiche nicht zählen) + +### 2. Passwort-Reset-Flow + +- [ ] Auf Login-Seite: Link **"Passwort vergessen?"** sichtbar +- [ ] Klick öffnet `/password-reset/request` +- [ ] **Unbekannte E-Mail** eingeben → Trotzdem "E-Mail gesendet"-Bestätigung + (User-Enumeration-Schutz: Backend verrät nicht, ob Email existiert) +- [ ] **Bekannte Mitarbeiter-E-Mail** eingeben, Typ "Mitarbeiter" wählen → Reset-Mail geht raus +- [ ] Reset-Link aus Mail öffnen → Formular "Neues Passwort" +- [ ] **Passwörter stimmen nicht überein** → Fehlermeldung +- [ ] **Passwort < 6 Zeichen** → Fehlermeldung +- [ ] Gültiges Passwort setzen → "Passwort geändert", Redirect zu /login +- [ ] **Neuer Login** mit dem neuen Passwort → Funktioniert +- [ ] **Alter Session** (falls vorher eingeloggt) → Wurde gekickt, muss neu einloggen +- [ ] **Reset-Link ein zweites Mal nutzen** → Fehlermeldung "Ungültiger oder bereits verwendeter Link" +- [ ] **Reset-Link nach 2h+** nutzen → Fehlermeldung "Der Link ist abgelaufen" +- [ ] Gleicher Flow für **Portal-Kunden** (Typ "Kunde" wählen) + +### 3. Rate-Limiting Passwort-Reset + +- [ ] 5× Reset-Anfrage für dieselbe E-Mail senden → OK +- [ ] 6. Anfrage innerhalb einer Stunde → "Zu viele Passwort-Reset-Anfragen" + +### 4. Berechtigungen (RBAC) + +- [ ] **Admin**-User: kann Benutzer, Rollen, Einstellungen verwalten +- [ ] **Mitarbeiter**-User: kann Kunden/Verträge bearbeiten, **keine** User-Verwaltung +- [ ] **Mitarbeiter (Lesen)**: alles sichtbar, aber Buttons "Bearbeiten/Löschen" fehlen +- [ ] **Portal-Kunde**: sieht **nur eigene** Verträge + Daten, nicht die anderer Kunden + +### 5. Portal-Isolation (wichtig für DSGVO) + +- [ ] Als Portal-Kunde A einloggen +- [ ] In der URL manuell `/customers/999` eintippen (anderer Kunde) → Zugriff verweigert +- [ ] `/contracts/999` (fremder Vertrag) → Zugriff verweigert +- [ ] API-Call via Browser-Devtools `GET /api/customers/999` → 403 Forbidden +- [ ] Nur mit **Vollmacht** (RepresentativeAuthorization) kann Kunde A die Daten von B sehen + +### 6. Session-Invalidation + +- [ ] Eingeloggt als Mitarbeiter, in 2 Browser-Tabs +- [ ] Admin ändert Rolle des Mitarbeiters (User-Verwaltung) +- [ ] Nächster Request im Tab → wird zum Login redirectet (tokenInvalidatedAt greift) + +### 7. Audit-Log + +- [ ] Einstellungen → Audit-Protokoll öffnen +- [ ] Kunde anlegen → Eintrag mit Typ CREATE erscheint +- [ ] Kunden-Feld ändern (z.B. Geburtsort) → UPDATE-Eintrag mit Vorher/Nachher-Details +- [ ] Kunde löschen → DELETE-Eintrag +- [ ] DSGVO-Export herunterladen → EXPORT-Eintrag +- [ ] Filter nach Benutzer funktioniert +- [ ] Filter nach Aktion funktioniert +- [ ] Details-Modal zeigt Vorher/Nachher-Werte + +### 8. DSGVO-Features + +- [ ] Kunde einlegen → DSGVO-Tab +- [ ] Datenexport ausführen → JSON-Datei mit allen Daten des Kunden +- [ ] Löschanfrage erstellen → erscheint im DSGVO-Dashboard (Admin) +- [ ] Anonymisierung ausführen → Kundendaten werden anonymisiert, aktive Verträge bleiben +- [ ] Einwilligungen (alle 4 Typen) können pro Kunde gesetzt/widerrufen werden +- [ ] PDF-Upload als Alternative zu Online-Einwilligungen → Haken werden auf GRANTED gesetzt +- [ ] PDF löschen → Haken werden auf WITHDRAWN gesetzt + +### 9. Verschlüsselte Credentials + +- [ ] Ein Portal-Passwort (z.B. eines Anbieter-Zugangs) speichern +- [ ] In der DB (z.B. via Prisma Studio oder DB-GUI) nachschauen: + `portalPasswordEncrypted` darf **nicht im Klartext** sichtbar sein +- [ ] Portal-Passwort in der UI anzeigen → wird korrekt entschlüsselt + +### 10. DSGVO-Einwilligung Mitarbeiter + +- [ ] Als Mitarbeiter Kunde öffnen OHNE Einwilligung → Tabs Zähler/Verträge/Bankkarten/Ausweise/Email gesperrt +- [ ] Nach Einwilligung (alle 4 Haken oder PDF) → Tabs wieder zugänglich + +--- + +## ✉ Email-Log-System + +### 1. Email-Log-Seite öffnen + +- [ ] Einstellungen → **Email-Protokoll** öffnen +- [ ] Statistik-Cards werden angezeigt: Gesamt, Erfolgreich, Fehlgeschlagen, Letzte 24h +- [ ] Log-Liste zeigt vergangene Versendungen +- [ ] Filter: **Erfolgreich/Fehlgeschlagen** +- [ ] Filter: **Kontext** (Datenschutz-Link, Vollmacht-Anfrage, Kunden-E-Mail, Geburtstagsgruß, Passwort-Reset) +- [ ] Suche nach Empfänger-E-Mail oder Betreff +- [ ] Pagination (Seite 1, 2, 3 …) +- [ ] Klick auf Eintrag → Details-Modal mit SMTP-Info, Message-ID, ggf. Fehlermeldung + +### 2. Erfolgreicher Versand loggen – alle Kontexte durchspielen + +Für jeden Kontext: Aktion im CRM durchführen → danach im Email-Log prüfen, +ob der Eintrag erstellt wurde. + +#### Datenschutz-Link +- [ ] Kunde öffnen → Einwilligungen/Datenschutz-Tab → "Link per Email senden" +- [ ] Log-Eintrag mit Kontext "Datenschutz-Link", Empfänger = Kunden-E-Mail, Status ✓ + +#### Vollmacht-Anfrage +- [ ] Kunde A mit Vertreter B → Vollmachten-Tab → "Anfrage per Email senden" +- [ ] Log-Eintrag mit Kontext "Vollmacht-Anfrage" + +#### Kunden-E-Mail (via Email-Client) +- [ ] Kunde öffnen → Email-Tab → Mail verfassen und senden +- [ ] Log-Eintrag mit Kontext "Kunden-E-Mail" + +#### Geburtstagsgruß (manuell) +- [ ] Kunde mit Geburtsdatum → Cake-Button → "Gruß jetzt senden" → Email +- [ ] Log-Eintrag mit Kontext "Geburtstagsgruß (manuell)" + +#### Geburtstagsgruß (automatisch) +- [ ] Test-Kunde anlegen mit Geburtsdatum = heute +- [ ] Auto-Geburtstagsgruß aktivieren (Cake-Button → Checkbox + Kanal "Email") +- [ ] Server neu starten (Cron macht Catch-up nach 30s) +- [ ] Nach ~1 Min: Log-Eintrag mit Kontext "Geburtstagsgruß (automatisch)" + +#### Passwort-Reset +- [ ] Logout → "Passwort vergessen?" → eigene Admin-E-Mail eingeben +- [ ] Log-Eintrag mit Kontext "Passwort-Reset" + +### 3. Fehlgeschlagener Versand loggen + +- [ ] Temporär SMTP-Passwort ungültig machen (Einstellungen → Provider bearbeiten) +- [ ] Beliebige E-Mail-Aktion auslösen (z.B. Datenschutz-Link senden) +- [ ] Log-Eintrag mit Status ✗ und Fehlermeldung ("SMTP-Authentifizierung fehlgeschlagen") +- [ ] Im Browser: Toast-Benachrichtigung mit Fehler erscheint +- [ ] Passwort wieder korrigieren + +### 4. Details-Modal + +- [ ] Klick auf erfolgreichen Eintrag: Zeigt Absender, Empfänger, Betreff, SMTP-Server/Port/Verschlüsselung, Message-ID, ggf. SMTP-Server-Antwort +- [ ] Klick auf fehlgeschlagenen Eintrag: Zusätzlich klare Fehlermeldung +- [ ] Kunden-Link im Modal: bei customerId → klickbar zum Kunden + +### 5. Automatisches Logging + +- [ ] SMTP-Server, Port, Verschlüsselung werden bei jedem Versand geloggt +- [ ] Kontext wird korrekt mitgegeben (nicht "unknown") +- [ ] triggeredBy zeigt die auslösende User-E-Mail (nicht "cron" bei manuellen Aktionen) +- [ ] Bei automatischen Aktionen (Cron): triggeredBy = "cron" + +--- + +## Wie benutzen? + +1. Diese Datei öffnen +2. Von oben nach unten durchklicken, Häkchen setzen (oder im Editor durch `- [x]`) +3. Gefundene Bugs in GitHub Issues oder direkt als Korrektur-Commit einbauen + +Bei frisch geladener Datenbank (z.B. Dev-System): vorher 2-3 Test-Kunden mit vollständigen +Stammdaten + mindestens 1 Email-Provider-Konfiguration anlegen. diff --git a/frontend/src/pages/settings/EmailLogs.tsx b/frontend/src/pages/settings/EmailLogs.tsx index 4abed1c2..46f653e4 100644 --- a/frontend/src/pages/settings/EmailLogs.tsx +++ b/frontend/src/pages/settings/EmailLogs.tsx @@ -13,6 +13,9 @@ const CONTEXT_LABELS: Record = { 'consent-link': 'Datenschutz-Link', 'authorization-request': 'Vollmacht-Anfrage', 'customer-email': 'Kunden-E-Mail', + 'birthday-greeting': 'Geburtstagsgruß (manuell)', + 'birthday-greeting-auto': 'Geburtstagsgruß (automatisch)', + 'password-reset': 'Passwort-Reset', }; export default function EmailLogs() {