diff --git a/features/INDEX.md b/features/INDEX.md index 90326f5..a53ceec 100644 --- a/features/INDEX.md +++ b/features/INDEX.md @@ -23,11 +23,13 @@ | PROJ-9 | Synapse-Integration (Health Check, User Provisioning, Raum-Sync) | Deployed | [PROJ-9-synapse-integration-health-check.md](PROJ-9-synapse-integration-health-check.md) | 2026-09-11 | | PROJ-10 | Deployment (Docker Compose + Synapse Installer) | Deployed | [PROJ-10-deployment.md](PROJ-10-deployment.md) | 2026-09-11 | | PROJ-11 | Chat (Matrix Client-Server API) | Planned | [PROJ-11-chat.md](PROJ-11-chat.md) | 2026-09-11 | +| PROJ-12 | Automatische Löschfrist für ausgetretene Mitglieder (DSGVO) | Planned | [PROJ-12-mitglieder-loeschfrist.md](PROJ-12-mitglieder-loeschfrist.md) | 2026-09-11 | Noch nicht gestartet (siehe [docs/PRD.md](../docs/PRD.md) Core Features): dedizierte Tenant-Isolation-Tests. P2 (Medien-Anhänge, Push) bewusst aus PROJ-11 ausgeklammert, siehe dessen Non-Goals. Nachträge 2026-09-11 (commit bc4d749): Passwort-Reset → [[PROJ-1]], Mitglied hart löschen → [[PROJ-3]]. +Nachtrag 2026-09-11 (commit ffe2d45): API-Zugriff bei suspended/left sofort entziehen (nicht nur Matrix) → [[PROJ-3]]. -## Next Available ID: PROJ-12 +## Next Available ID: PROJ-13 diff --git a/features/PROJ-12-mitglieder-loeschfrist.md b/features/PROJ-12-mitglieder-loeschfrist.md new file mode 100644 index 0000000..f6530ef --- /dev/null +++ b/features/PROJ-12-mitglieder-loeschfrist.md @@ -0,0 +1,41 @@ +# PROJ-12: Automatische Löschfrist für ausgetretene Mitglieder (DSGVO) + +**Status:** Planned +**Created:** 2026-09-11 + +## Summary +Mitglieder mit Status `left` (Austritt) werden aktuell unbegrenzt aufbewahrt — [[PROJ-3]] sperrt nur den API-/Matrix-Zugriff, löscht aber keine Daten. Das verletzt den DSGVO-Grundsatz der Speicherbegrenzung (Art. 5 Abs. 1 lit. e, Art. 17): sobald kein Zweck mehr besteht (keine Mitgliedschaft, keine gesetzliche Aufbewahrungspflicht für reine Mitgliederdaten ohne Beitrags-/Buchhaltungsbezug — siehe PRD Non-Goals), müssen die Daten gelöscht werden. Dieses Feature führt eine vereinskonfigurierbare Aufbewahrungsfrist ein, nach der `left`-Mitglieder automatisch über den bestehenden `DELETE /api/members/:memberId`-Pfad ([[PROJ-3]]) hart gelöscht werden. + +## Dependencies +- Requires: [[PROJ-3]] (Mitgliederverwaltung) — nutzt den bestehenden harten Lösch-Pfad (Matrix-Erase, Cascade-Deletes, Audit-Log) 1:1, nur automatisch statt manuell ausgelöst +- Requires: [[PROJ-2]] (Verein anlegen) — Frist wird pro Organisation konfiguriert + +## User Stories +1. Als Vereinsadmin will ich pro Verein festlegen, nach wie vielen Tagen ausgetretene Mitglieder automatisch gelöscht werden, damit ich die Satzung/Aufbewahrungspraxis meines Vereins abbilden kann. +2. Als Vereinsadmin will ich einen sinnvollen Standardwert vorfinden, falls ich die Einstellung nicht bewusst ändere, damit DSGVO-Konformität nicht von mir vergessen werden kann. +3. Als System will ich täglich prüfen, welche `left`-Mitglieder die Frist ihrer Organisation überschritten haben, und sie automatisch löschen. +4. Als Vereinsadmin will ich im Audit-Log sehen, dass ein Mitglied automatisch (nicht manuell) gelöscht wurde, um den Vorgang nachvollziehen zu können. + +## Acceptance Criteria +- [ ] `organizations`-Tabelle bekommt Feld `memberRetentionDays` (integer, not null, Default 90) +- [ ] Neues UI-Feld in den Organisationseinstellungen zum Ändern der Frist (nur Org-Admin) +- [ ] Täglicher Hintergrund-Job (analog `startReconciliationWorker`-Pattern in `sync-worker.ts`) prüft `members` mit `status = 'left'`, deren `updatedAt` (Zeitpunkt des Status-Wechsels) älter ist als `organizations.memberRetentionDays` +- [ ] Betroffene Mitglieder werden über denselben Pfad wie manuelles Löschen entfernt (Matrix-Erase, Cascade-Deletes für Team-Zuordnungen/Rollen/Eltern-Verknüpfungen) +- [ ] Audit-Log-Eintrag `action: "member.auto_deleted"` mit `actorUserId: null` (System-Aktion, kein menschlicher Akteur), Metadata enthält die angewandte Frist +- [ ] Keine Benachrichtigung an Admin vor der Löschung (bewusst still im Hintergrund) +- [ ] Reaktivierung vor Fristablauf (Status zurück auf `active`) bricht die geplante Löschung ab — Job muss den aktuellen Status zum Ausführungszeitpunkt prüfen, nicht nur einmalig einplanen + +## Edge Cases +- `members.updatedAt` existiert aktuell nicht im Schema (nur `createdAt`) — muss ergänzt werden, sonst kein verlässlicher Zeitpunkt für "seit wann `left`" +- Mitglied wird mehrfach `left`→`active`→`left` — Frist zählt ab dem letzten Wechsel zu `left`, nicht kumulativ +- Sehr kurze Frist (z.B. 0 oder negative Werte) — Validierung auf Minimum (z.B. ≥ 1 Tag) im Backend +- Job läuft während laufender manueller Löschung desselben Mitglieds — `DELETE`-Route ist idempotent genug (member existiert nicht mehr → 404), Job sollte 404 vom eigenen internen Aufruf abfangen statt zu crashen +- Synapse nicht erreichbar während Auto-Löschung — gleiches Verhalten wie beim manuellen Löschen: Matrix-Erase-Fehler wird geloggt, DB-Löschung läuft trotzdem durch (siehe bestehendes `try/catch` in [[PROJ-3]]) + +## Non-Goals (für dieses Feature) +- Self-Service-Löschantrag durch das Mitglied selbst (Art. 17-Betroffenenrecht als eigener Flow) — separates Feature +- Aufbewahrung von Beitrags-/Buchhaltungsdaten (10-Jahres-Frist nach §147 AO) — laut PRD Non-Goal (kein Buchhaltungsmodul), betrifft dieses Feature nicht +- Anonymisierung statt Löschung als Alternative — MVP macht hartes Löschen, kein Soft-Anonymisieren + +## Notes +Direkt aus DSGVO-Rückfrage im Chat entstanden, kein formaler Requirements-Workshop nötig — Scope ist klar umrissen durch bestehenden Lösch-Pfad aus [[PROJ-3]].