Files
florian.wildeandClaude Opus 5.5 c06c051637 feat(rotation): nach Abbruch erneut rotieren (#033)
Bisher war vorgaengerId eindeutig: nach einer abgebrochenen Rotation
passte kein zweiter Nachfolger in die Kette, und der Schlüssel ließ sich
nur über Neuanlage, Zuweisen und Widerruf ersetzen.

Jetzt ist die Relation 1:n. Ein partieller Index erlaubt je Vorgänger
nur einen Nachfolger, der weder widerrufen ist noch gerade widerrufen
wird. Maßgeblich ist der jüngste. Erst wenn der Abbruch abgeschlossen
ist, beginnt die Rotation wieder bei Schritt 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:09:42 +02:00

3.5 KiB
Raw Permalink Blame History

#032 Nacharbeiten aus Phase 4/5

Status: erledigt Meilenstein: v1.0.0 Labels: test, chore Geschätzt: 2 h Tatsächlich: 0,75 h

Ziel

Kleinere offene Punkte aus den Berichten der Phasen 4 und 5 erledigen.

Aufgaben

  • Massenwiderruf im Browser testen (E2E mit frischer Bestätigung über den Mock-OIDC)
  • Laufende Rotation abbrechen können (Nachfolger widerrufen, Vorgänger wieder aktiv)
  • Protokoll-IDs ohne Zweierschritte (Spalten-Default und Trigger ziehen beide nextval)
  • „Zum Dry-Run“ aus der Zuweisungsmatrix direkt zur Dry-Run-Karte springen (Anker)

Akzeptanzkriterien

  • Alle Suiten grün

Abhängigkeiten

#023, #024, #025, #027

Notizen / Entscheidungen

  • Massenwiderruf: Der neue E2E-Test (widerruf.spec.ts) fand einen echten Fehler: Die Seite /schluessel/widerruf filterte ?id= mit /^[a-z0-9]{1,64}$/. Vom Worker erzeugte Schlüssel tragen eine UUID mit Bindestrichen – jeder erzeugte Schlüssel fiel still heraus („Keine Schlüssel gewählt“). Jetzt idsAusSuchparameter in src/lib/schluessel.ts (mit Test). Außerdem nannte der Hinweis nach verweigerter Bestätigung den falschen Knopf („Widerruf vorbereiten“ statt „2 Schlüssel widerrufen“). datenbank() und oidcBestaetigen() liegen jetzt in tests/e2e/hilfen.ts.
  • Rotation abbrechen: kein neues Feld. Abbrechen = Widerruf des Nachfolgers über den Ablauf aus #024 (Dry-Run, er kann schon ausgerollt sein); ein Nachfolger im Widerruf bzw. widerrufen ist die abgebrochene Rotation (rotationAbgebrochen). vorgaengerId bleibt für die Historie, der Abbruch steht als schluessel.rotation.abgebrochen im Protokoll – in derselben Transaktion wie das Vormerken (widerrufEinleiten nimmt dafür optional zusaetzlich an). Der alte Schlüssel bleibt unverändert; bei gleichem Alias zeigt die config nach dem Anwenden wieder auf die alte Datei (nur verbleibende Zuweisungen landen im Block).
  • Abbrechen geht nur, solange der alte Schlüssel noch nicht entfernt wird (Schritt 5) – sonst wären am Ende beide halb entfernt. Auf dem Nachfolger ersetzt „Rotation abbrechen“ den normalen „Widerrufen“-Knopf, damit der Abbruch immer im Protokoll steht.
  • widerrufAbschliessen setzt ROTIERT nur noch bei einem aktiven Nachfolger, der nicht selbst im Widerruf ist; die Seite schreibt „Ersetzt durch …“ nur bei ROTIERT.
  • Kein zweiter Nachfolger: vorgaengerId ist eindeutig (1:1-Relation). Nach einem Abbruch lässt sich derselbe Schlüssel deshalb nicht erneut über sshkv rotieren; Seite und Fehlermeldung sagen das (neuen Schlüssel anlegen, zuweisen, alten widerrufen). Eine erneute Rotation bräuchte eine 1:n-Relation mit eindeutigem Teilindex „ein nicht widerrufener Nachfolger je Vorgänger“. → Behoben in #033 (partieller Index statt 1:1).
  • Protokoll-IDs: Migration 20261012173000_protokoll_id_lueckenlos setzt den Spalten-Default auf den Platzhalter 0 (Prisma: @default(0), create ohne ID geht weiter); die ID zieht nur noch der Trigger unter der Advisory-Sperre aus der weiterhin zugeordneten Sequenz. Bestehende Zeilen bleiben unverändert (die ID steckt in der Hash-Eingabe), alte Lücken bleiben also stehen. Eine mitgegebene ID überschreibt der Trigger weiterhin.
  • Anker: Die Dry-Run-Karte trägt id="dry-run" (Karte nimmt eine optionale id) und scroll-mt-20 wegen der festen Kopfleiste der schmalen Ansicht; „Zum Dry-Run“ zeigt auf /schluessel/<id>#dry-run.