Files
nexarch/archive/docs/RET-03-PRUEFPROTOKOLL.md
T
sysops e23f514850 RET-03: löschworkflow-und-aufbewahrungssperre-legal-hold
- 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
2026-08-30 14:27:21 +02:00

65 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.