- migrations/0010_deletion_requests: speichert nur den Token-Hash, nie das Token selbst - archive/internal/deletionapproval: RequestDeletion (einmaliges Klartext-Token), ConfirmAndExecute (FOR UPDATE-Lock, andere Person als requester, zeitkonstanter Tokenvergleich via subtle.ConstantTimeCompare, ruft danach genau einmal deletionworkflow.Destroy (RET-03) auf) - 4 Tests real bestanden, inkl. echtem Nebenlaeufigkeits-Test (zwei echte Goroutinen, genau 1 Erfolg + 1 ErrAlreadyExecuted, genau ein destruction_log-Eintrag) - Migration real auf dms_tenant_test angewendet Pruefungen siehe archive/docs/CMP-06-PRUEFPROTOKOLL.md
49 lines
2.7 KiB
Markdown
49 lines
2.7 KiB
Markdown
# 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.
|