feat(widerruf): Widerruf und Massenwiderruf mit Dry-Run (#024)
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>
This commit is contained in:
+33
-30
@@ -274,44 +274,47 @@ model HostSchluessel {
|
||||
// ---------------------------------------------------------------- Schluessel
|
||||
|
||||
model Schluessel {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
/// Zweck, z. B. „Backup srv-backup01 → srv-pve01“.
|
||||
name String
|
||||
name String
|
||||
/// Fuer Dateinamen und Kommentare: backup-srv-backup01-srv-pve01.
|
||||
slug String @unique
|
||||
typ SchluesselTyp
|
||||
kategorie SchluesselKategorie
|
||||
status SchluesselStatus @default(AKTIV)
|
||||
slug String @unique
|
||||
typ SchluesselTyp
|
||||
kategorie SchluesselKategorie
|
||||
status SchluesselStatus @default(AKTIV)
|
||||
/// OpenSSH-Zeile ohne Optionen: "ssh-ed25519 AAAA… kommentar".
|
||||
publicKey String
|
||||
fingerprint String @unique
|
||||
kommentar String
|
||||
publicKey String
|
||||
fingerprint String @unique
|
||||
kommentar String
|
||||
/// Envelope-Chiffrat des Private Keys. Liest ausschliesslich src/worker/geheimnisse.ts.
|
||||
privatVerschluesselt Bytes?
|
||||
privatVerschluesselt Bytes?
|
||||
/// Data-Key, verschluesselt mit dem Master-Key.
|
||||
dekVerschluesselt Bytes?
|
||||
masterKeyVersion Int?
|
||||
privatGeloeschtAm DateTime?
|
||||
passphraseGeschuetzt Boolean @default(false)
|
||||
dekVerschluesselt Bytes?
|
||||
masterKeyVersion Int?
|
||||
privatGeloeschtAm DateTime?
|
||||
passphraseGeschuetzt Boolean @default(false)
|
||||
/// Genau ein Deployment-Key ist Instanz-Standard (partieller Index).
|
||||
instanzStandard Boolean @default(false)
|
||||
personId String?
|
||||
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
||||
vorlageId String?
|
||||
vorlage Vorlage? @relation(fields: [vorlageId], references: [id], onDelete: SetNull)
|
||||
tags String[]
|
||||
ablaufAm DateTime?
|
||||
zuletztVerwendetAm DateTime?
|
||||
instanzStandard Boolean @default(false)
|
||||
personId String?
|
||||
person Person? @relation(fields: [personId], references: [id], onDelete: SetNull)
|
||||
vorlageId String?
|
||||
vorlage Vorlage? @relation(fields: [vorlageId], references: [id], onDelete: SetNull)
|
||||
tags String[]
|
||||
ablaufAm DateTime?
|
||||
zuletztVerwendetAm DateTime?
|
||||
/// Widerruf eingeleitet (#024): Verwendungen stehen auf ENTFERNEN, der Status wechselt erst
|
||||
/// auf WIDERRUFEN/ROTIERT, wenn alle Entfernungen erfolgreich waren.
|
||||
widerrufEingeleitetAm DateTime?
|
||||
/// Rotationskette: der Schluessel, den dieser abgeloest hat.
|
||||
vorgaengerId String? @unique
|
||||
vorgaenger Schluessel? @relation("Rotation", fields: [vorgaengerId], references: [id], onDelete: SetNull)
|
||||
nachfolger Schluessel? @relation("Rotation")
|
||||
erstelltVonId String?
|
||||
erstelltVon Benutzer? @relation("SchluesselErsteller", fields: [erstelltVonId], references: [id], onDelete: SetNull)
|
||||
vorgaengerId String? @unique
|
||||
vorgaenger Schluessel? @relation("Rotation", fields: [vorgaengerId], references: [id], onDelete: SetNull)
|
||||
nachfolger Schluessel? @relation("Rotation")
|
||||
erstelltVonId String?
|
||||
erstelltVon Benutzer? @relation("SchluesselErsteller", fields: [erstelltVonId], references: [id], onDelete: SetNull)
|
||||
/// true, wenn eine Person den Key selbst hinterlegt hat (Self-Service).
|
||||
selbstHinterlegt Boolean @default(false)
|
||||
angelegtAm DateTime @default(now())
|
||||
geaendertAm DateTime @updatedAt
|
||||
selbstHinterlegt Boolean @default(false)
|
||||
angelegtAm DateTime @default(now())
|
||||
geaendertAm DateTime @updatedAt
|
||||
|
||||
publicVerwendungen PublicVerwendung[]
|
||||
privateVerwendungen PrivateVerwendung[]
|
||||
|
||||
Reference in New Issue
Block a user