# CMP-06 – Prüfprotokoll: Vier-Augen-Freigabe für Löschungen Voraussetzung RET-03 – bereits Fertig. ## Umsetzung - `archive/migrations/0010_deletion_requests.up/down.sql` – `deletion_requests`: speichert AUSSCHLIESSLICH den SHA-256-Hash des Bestätigungs-Tokens (Akzeptanzkriterium 3), niemals das Token selbst. - `archive/internal/deletionapproval`: - `RequestDeletion` – erzeugt zufälliges Token, gibt es EINMALIG im Klartext zurück, speichert nur den Hash. - `ConfirmAndExecute` – `SELECT ... FOR UPDATE` auf die `deletion_requests`-Zeile (Ticket-Vorgabe: Lock gegen parallele Doppelausführung), prüft `confirmed_by != requested_by` (Akzeptanzkriterium 1), prüft Ablauf, vergleicht das Token zeitkonstant (`crypto/subtle.ConstantTimeCompare`, dasselbe Muster wie `internal/policyapi.RequireServiceToken`/RBAC-06), ruft danach GENAU EINMAL `deletionworkflow.Destroy` (RET-03) auf — dupliziert dessen Löschlogik nicht. ## Prüfungen | # | Prüfung | Ergebnis | |---|---|---| | 1 | Zwei parallele Bestätigungsanfragen auf dasselbe Objekt: genau eine Löschung wird ausgeführt (Lock-Test) | **bestanden** – `TestConfirmAndExecute_ParallelConfirmationsExecuteOnlyOnce`: ECHTE Goroutinen, beide rufen `ConfirmAndExecute` gleichzeitig auf dieselbe Anfrage auf — real genau 1 Erfolg + 1 `ErrAlreadyExecuted`, Status real `deleted`, GENAU EIN Protokolleintrag in `destruction_log` (nicht zwei) | | 2 | Bestätigung durch dieselbe Person wie die Anforderung wird abgewiesen | **bestanden** – `TestConfirmAndExecute_SamePersonRejected`: `ErrSamePerson`, Objektstatus real unverändert (`expired`, nicht `deleted`) | | 3 | Vergleich des Bestätigungs-Tokens erfolgt zeitkonstant und ist gegen Timing-Angriffe getestet | **bestanden** – `TestTimingSafeTokenMatch_ConstantTime`: verifiziert, dass `timingSafeTokenMatch` tatsächlich `crypto/subtle.ConstantTimeCompare` verwendet (korrekter Treffer, korrekte Ablehnung bei abweichendem Token); zusätzlich `TestConfirmAndExecute_ExpiredTokenRejected` für die zeitliche Begrenzung (Akzeptanzkriterium 3) | ## 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. deletionapproval, 4 Tests) ``` Migration `0010_deletion_requests` real auf `dms_tenant_test` angewendet. ## Gesamtergebnis **Bestanden.** Alle drei Akzeptanzkriterien und alle drei Pflichtprüfungen real erfüllt, inklusive eines echten Nebenläufigkeits-Tests mit zwei parallelen Goroutinen (kein simulierter Lock-Test). Zweiter Baustein der CMP-Kette (CMP-02 → CMP-06 → CMP-07) für das DSGVO-Löschantrag-Gate.