- migrations/0008_legal_hold_destruction: legal_holds (historisiert, Partial-Unique-Index gegen doppelte aktive Sperre), destruction_log (append-only, per Postgres-Trigger gegen UPDATE/DELETE geschuetzt) - archive/internal/deletionworkflow: SetLegalHold (Begruendungspflicht), ReleaseLegalHold (Aufheben selbst protokolliert, keine Loeschung der Zeile), ReleaseExpired (Freigabeprozess active->expired, keine Sofortloeschung, Sperre wird respektiert), Destroy (verlangt vorherigen expired-Status, prueft Sperre erneut, transaktional mit Protokolleintrag) - 6 Tests, alle Pflichtpruefungen real bestanden (Sperre widersteht Loeschversuch, Protokoll real unveraenderlich per Trigger, Aufheben real protokolliert) - Migration real auf dms_tenant_test angewendet Pruefungen siehe archive/docs/RET-03-PRUEFPROTOKOLL.md
65 lines
3.5 KiB
Markdown
65 lines
3.5 KiB
Markdown
# RET-03 – Prüfprotokoll: Löschworkflow & Aufbewahrungssperre (Legal Hold)
|
||
|
||
Voraussetzung RET-01, RET-02 – beide bereits Fertig.
|
||
|
||
## Umsetzung
|
||
|
||
- `archive/migrations/0008_legal_hold_destruction.up/down.sql`:
|
||
- `legal_holds` – historisiert (Sperre wird NIE gelöscht, nur
|
||
`released_at`/`released_by` gesetzt), Partial-Unique-Index erlaubt
|
||
höchstens eine AKTIVE Sperre je Objekt.
|
||
- `destruction_log` – append-only, real per Postgres-TRIGGER gegen
|
||
UPDATE/DELETE geschützt (`destruction_log_immutable()`,
|
||
`RAISE EXCEPTION`) — DB-Ebene, nicht nur Anwendungscode.
|
||
- `archive/internal/deletionworkflow`:
|
||
- `SetLegalHold` — Begründung PFLICHT (`ErrReasonRequired`),
|
||
`ErrAlreadyOnHold` bei bereits aktiver Sperre (DB-Unique-Index als
|
||
zusätzliche Absicherung gegen Wettlaufsituationen).
|
||
- `ReleaseLegalHold` — hebt auf, OHNE die Zeile zu löschen (das
|
||
Aufheben selbst bleibt dauerhaft nachvollziehbar).
|
||
- `ReleaseExpired` — Freigabeprozess: `active` → `expired`, NIEMALS
|
||
direkt `deleted`. Objekte mit aktiver Sperre werden übersprungen,
|
||
unabhängig vom Fristablauf.
|
||
- `Destroy` — verlangt vorherigen Status `expired`
|
||
(`ErrNotReleased` sonst), prüft die Sperre ZUSÄTZLICH direkt vor der
|
||
Vernichtung erneut (Verteidigung in der Tiefe gegen eine
|
||
zwischenzeitlich gesetzte Sperre), schreibt Statuswechsel und
|
||
Protokolleintrag in EINER Transaktion.
|
||
|
||
## Prüfungen
|
||
|
||
| # | Prüfung | Ergebnis |
|
||
|---|---|---|
|
||
| 1 | Objekt mit aktiver Sperre widersteht einem direkten Löschversuch | **bestanden** – `TestDestroy_ObjectWithActiveHoldResistsDeletion`: Objekt bereits im Status `expired` (simuliert vorherige Freigabe), Sperre danach gesetzt, `Destroy` liefert `ErrOnLegalHold`, Status bleibt unverändert |
|
||
| 2 | Protokolleintrag nach Vernichtung ist nachträglich nicht änderbar (DB-Constraint oder Trigger) | **bestanden** – `TestDestructionLog_IsImmutable`: nach echter `Destroy`-Ausführung direkter `UPDATE`/`DELETE` per rohem SQL (umgeht die Go-API vollständig) — beide scheitern real am Postgres-Trigger, nicht nur weil das Paket keine Änderungsfunktion anbietet |
|
||
| 3 | Aufheben einer Sperre ist selbst protokolliert | **bestanden** – `TestReleaseLegalHold_IsItselfLogged`: nach `ReleaseLegalHold` sind `released_at`/`released_by` real gesetzt, die ursprüngliche Sperr-Zeile bleibt bestehen (kein DELETE) |
|
||
|
||
**Zusätzliche Tests (Akzeptanzkriterium 1, nicht in den drei
|
||
Pflichtprüfungen, aber je AC mindestens ein Test):**
|
||
- `TestReleaseExpired_NoImmediateDeletionAndHoldIsRespected` — fälliges
|
||
Objekt wird `expired` (NICHT `deleted`), gesperrtes fälliges Objekt
|
||
bleibt `active`, nicht fälliges Objekt bleibt `active`.
|
||
- `TestSetLegalHold_RequiresReason`, `TestDestroy_RequiresPriorRelease`.
|
||
|
||
## Build/Test-Ergebnis (192.168.1.131)
|
||
|
||
```
|
||
go build ./... -> clean
|
||
go vet ./... -> clean
|
||
golangci-lint run ./... -> 0 issues
|
||
go test ./... -p 1 -> alle Archive-Pakete bestanden (inkl. deletionworkflow, 6 Tests)
|
||
```
|
||
|
||
Migration `0008_legal_hold_destruction` real auf `dms_tenant_test`
|
||
angewendet (Test-DB, in der auch RET-06-API läuft) — sauber, ohne
|
||
Fehler.
|
||
|
||
## Gesamtergebnis
|
||
|
||
**Bestanden.** Alle drei Akzeptanzkriterien und alle drei
|
||
Pflichtprüfungen real erfüllt. Damit ist das RET-Epic (RET-01 bis
|
||
RET-10) vollständig abgeschlossen: Objektmodell, Fristenengine,
|
||
Modul-Adapter (Interface + Dienst), Konfigurationsoberfläche (Backend +
|
||
Frontend + CORS), Benachrichtigungen, WORM-Speicher und jetzt
|
||
Löschworkflow mit Legal Hold.
|