Ein Schlüssel soll nach Projektende mit einem Klick von allen Hosts verschwinden – aber nie als „widerrufen“ gelten, solange er irgendwo noch liegt. Deshalb ein Zwischenzustand (widerrufEingeleitetAm): Verwendungen werden zum Entfernen vorgemerkt, der bestehende Dry-Run mit Hash-Sperre zeigt und schreibt die Änderungen, und erst wenn keine Verwendung mehr offen ist, setzt der Abschluss WIDERRUFEN (bzw. ROTIERT für #023). Scheitert ein Host, bleibt der Schlüssel im Zwischenzustand und die Seite nennt die offenen Ziele. Zielkonten lassen sich optional mit entfernen, aber erst nach den Schlüsseln und nie, wenn ein anderer aktiver Schlüssel sie noch nutzt. Massenwiderruf (Admin, frische Bestätigung) nutzt einen gemeinsamen Dry-Run und protokolliert je Schlüssel. Abgelaufene Schlüssel erscheinen im Filter „abgelaufen“ und bieten den Widerruf an – entfernt wird nie automatisch. Migration 20261012130000_widerruf_eingeleitet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
3.2 KiB
Markdown
50 lines
3.2 KiB
Markdown
# #024 Widerruf und Massenwiderruf
|
||
**Status:** erledigt
|
||
**Meilenstein:** v0.4.0
|
||
**Labels:** backend, ui, sicherheit
|
||
**Geschätzt:** 3 h **Tatsächlich:** 0 h 25 min
|
||
|
||
## Ziel
|
||
Ein-Klick-Widerruf inkl. Benutzer/sudoers; Massenwiderruf mit Re-Auth.
|
||
|
||
## Aufgaben
|
||
- [x] „Widerrufen“ auf der Schlüsselseite mit Dialog, der jede Entfernung auflistet
|
||
- [x] Public- und Private-Verwendungen → ENTFERNEN, Dry-Run, anwenden, erst dann `WIDERRUFEN`
|
||
- [x] Zielkonten optional entfernen (nur `vonSshkvAngelegt`, nie ein noch genutztes)
|
||
- [x] Massenwiderruf auf der Schlüsselliste, ein gemeinsamer Dry-Run, Protokoll je Schlüssel
|
||
- [x] Abgelaufene Schlüssel erkennbar (Filter) und Widerruf anbieten – ohne automatisches Entfernen
|
||
|
||
## Akzeptanzkriterien
|
||
- [x] DB-Tests: Zustände, Rechte, Zielkonten, Protokoll ohne Geheimnisse (`tests/widerruf.test.ts`)
|
||
- [x] Integrationstest gegen zwei Hosts inkl. „Host nicht erreichbar“ (`tests/integration/widerruf.test.ts`)
|
||
- [x] E2E im Browser (`tests/e2e/widerruf.spec.ts`), Screenshot `widerruf-desktop.png`
|
||
|
||
## Abhängigkeiten
|
||
#018
|
||
|
||
## Notizen / Entscheidungen
|
||
- **Zwischenzustand statt Sofort-Status:** neues Feld `Schluessel.widerrufEingeleitetAm` (Migration
|
||
`20261012130000_widerruf_eingeleitet`). Der Status bleibt AKTIV/ABGELAUFEN, bis keine Verwendung mehr
|
||
offen ist; erst `widerrufAbschliessen` setzt `WIDERRUFEN` (bzw. `ROTIERT`, wenn ein aktiver Nachfolger
|
||
existiert, #023). Schlüsselseite und Liste zeigen „Widerruf unvollständig“ und die offenen Ziele.
|
||
- **Reihenfolge:** vormerken → Dry-Run/Anwenden (bestehende Hash-Sperre aus #016/#017) → optional
|
||
Zielkonten (`ZIELKONTO_ENTFERNEN`, erst wenn die Schlüssel überall weg sind) → abschließen.
|
||
Ein Ziel, das im Dry-Run scheitert (nicht erreichbar), wird nicht angewendet; seine Verwendung bleibt
|
||
ENTFERNEN, ein neuer Dry-Run versucht es erneut. Der Abschluss läuft nach erfolgreichem Anwenden
|
||
automatisch und meldet, was noch fehlt.
|
||
- **Nichts zu schreiben:** Steht der Schlüssel schon nirgends mehr, bietet das Dry-Run-Panel
|
||
„Zustand übernehmen“ an (sonst bliebe der Widerruf hängen).
|
||
- **Archivierte Hosts:** sshkv verbindet sich dort nicht mehr – deren Verwendungen gelten sofort als
|
||
entfernt; der Dialog sagt das, das Protokoll nennt die Hosts.
|
||
- **Zielkonten:** Ein Zielkonto kennt seinen Schlüssel nicht; Kandidat ist ein von sshkv angelegtes Konto
|
||
auf (Host, Benutzer) einer Zuweisung der widerrufenen Schlüssel. Nutzt ein anderer, nicht im Widerruf
|
||
befindlicher Schlüssel das Konto noch, bleibt es (Dialog nennt den Schlüssel).
|
||
- **Rechte:** nur Public Keys → `publicKeyAusrollen`; Private Keys → `privateKeyAusrollen` (Admin);
|
||
mehrere Schlüssel → `massenwiderruf`, Zielkonten → `zielkontenVerwalten` (jeweils Admin mit frischer
|
||
Bestätigung). Der Deployment-Key ist nicht widerrufbar. Ein Schlüssel im Widerruf lässt sich nicht
|
||
mehr zuweisen.
|
||
- **Ablauf:** Status-Filter „abgelaufen“ umfasst aktive Schlüssel mit vergangenem Ablaufdatum; die
|
||
Schlüsselseite bietet dann „Jetzt widerrufen“. Entfernt wird nie automatisch.
|
||
- Massenwiderruf als GET-Formular `/schluessel/widerruf?id=…` – die Auswahl der Zielkonten reist als
|
||
`?zielkonten=` mit und übersteht Neuladen und erneute Bestätigung.
|