feat(deploymentkey): Rotation vor dem Umstellen abbrechen (#036)
Eine begonnene Rotation ließ sich nicht zurücknehmen; nahm ein Host den neuen Key dauerhaft nicht an, blieb nur das Übergehen. Abbrechen verwirft jetzt den neuen Key samt Private Key, ein Dry-Run nimmt ihn wieder von den Hosts, und eine neue Rotation kann sofort starten. Nebenbei: Aktionen der Benutzerverwaltung (#035) bekommen im Audit-Log eine eigene Gruppe, statt den Filter „Anmeldung“ zu verwässern; der E2E-Test für lokale Konten hängt nicht mehr von der Reihenfolge ab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -41,7 +41,7 @@ beforeEach(async () => {
|
||||
AKTEUR.id = (await prisma.benutzer.create({ data: { name: "Test-Admin", rolle: "ADMIN" } })).id;
|
||||
});
|
||||
|
||||
const schritt = (s: "erzeugen" | "pruefen" | "abschliessen") =>
|
||||
const schritt = (s: "erzeugen" | "pruefen" | "abbrechen" | "abschliessen") =>
|
||||
vorgangLaufen("ROTATION", { eingabe: { schritt: s }, angefordertVonId: AKTEUR.id });
|
||||
|
||||
async function trockenlaufUndAusrollen() {
|
||||
@@ -136,6 +136,35 @@ describe("Rotation des Deployment-Keys", () => {
|
||||
expect(await prisma.abweichung.count({ where: { hostId: a.id, behobenAm: null } })).toBe(0);
|
||||
});
|
||||
|
||||
it("bricht vor dem Umstellen ab und laesst danach eine neue Rotation zu (#036)", async () => {
|
||||
const a = await eingerichteterHost(zielA, "ziel-a");
|
||||
const alt = await prisma.schluessel.findFirstOrThrow({ where: { instanzStandard: true } });
|
||||
expect((await schritt("erzeugen")).status).toBe("ERLEDIGT");
|
||||
const neu = (await rotationsStand()).nachfolger!;
|
||||
await trockenlaufUndAusrollen();
|
||||
expect(await fingerprintsAuf(a)).toContain(neu.fingerprint);
|
||||
|
||||
expect((await schritt("abbrechen")).status).toBe("ERLEDIGT");
|
||||
const verworfen = await prisma.schluessel.findUniqueOrThrow({ where: { id: neu.id } });
|
||||
expect(verworfen).toMatchObject({ status: "WIDERRUFEN", privatVerschluesselt: null });
|
||||
expect(await rotationsStand()).toMatchObject({ phase: "bereit", abgebrochen: { id: neu.id } });
|
||||
// Ohne laufende Rotation gibt es nichts mehr abzubrechen.
|
||||
expect((await schritt("abbrechen")).status).toBe("FEHLGESCHLAGEN");
|
||||
|
||||
// Der Dry-Run nimmt den verworfenen Key wieder heraus; der alte arbeitet unveraendert.
|
||||
await trockenlaufUndAusrollen();
|
||||
const fingerprints = await fingerprintsAuf(a);
|
||||
expect(fingerprints).toContain(alt.fingerprint);
|
||||
expect(fingerprints).not.toContain(neu.fingerprint);
|
||||
expect(await anmeldungKlappt(a, alt.id)).toBe(true);
|
||||
|
||||
// Eine neue Rotation beginnt von vorn.
|
||||
expect((await schritt("erzeugen")).status).toBe("ERLEDIGT");
|
||||
const zweiter = (await rotationsStand()).nachfolger!;
|
||||
expect(zweiter.id).not.toBe(neu.id);
|
||||
expect(zweiter.vorgaengerId).toBe(alt.id);
|
||||
});
|
||||
|
||||
it("laesst Hosts mit eigenem Deployment-Key aus", async () => {
|
||||
const a = await eingerichteterHost(zielA, "ziel-a");
|
||||
const b = await eingerichteterHost(zielB, "ziel-b");
|
||||
|
||||
Reference in New Issue
Block a user