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

3.5 KiB
Raw Blame History

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: activeexpired, 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.