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>
73 lines
3.9 KiB
TypeScript
73 lines
3.9 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
||
import { base32Dekodieren, totpCode, totpSchritt } from "../../src/lib/totp";
|
||
import { anmelden, oidcBestaetigen } from "./hilfen";
|
||
|
||
/**
|
||
* Lokale Konten (#035) im gebauten Stand: Admin legt ein Konto an, die Person meldet sich zum
|
||
* ersten Mal an (eigenes Passwort, TOTP), der Admin sperrt sie – die Sitzung endet sofort.
|
||
*/
|
||
test.setTimeout(120_000);
|
||
|
||
test("lokales Konto anlegen, erste Anmeldung, sperren", async ({ page, browser }) => {
|
||
const anmeldename = `zz-e2e-${Date.now().toString(36)}`;
|
||
const passwort = "ein-eigenes-e2e-passwort";
|
||
|
||
await anmelden(page);
|
||
await page.goto("/einstellungen");
|
||
await page.getByRole("link", { name: "Konten verwalten" }).click();
|
||
await expect(page.getByRole("heading", { name: "Benutzer", exact: true })).toBeVisible();
|
||
|
||
// Frische Bestaetigung immer selbst holen – ob ein frueherer Test sie schon gesetzt hat,
|
||
// haengt von der Reihenfolge ab. Dass das Recht sie verlangt, pruefen die Unit-Tests.
|
||
await page.goto("/bestaetigen?ziel=%2Feinstellungen%2Fbenutzer");
|
||
await oidcBestaetigen(page);
|
||
await expect(page).toHaveURL(/\/einstellungen\/benutzer$/);
|
||
await page.getByLabel("Anmeldename").fill(anmeldename);
|
||
await page.getByLabel("Name", { exact: true }).fill("E2E Lokal");
|
||
await page.getByLabel("Rolle", { exact: true }).selectOption("OPERATOR");
|
||
await page.getByRole("button", { name: "Konto anlegen" }).click();
|
||
const anzeige = page.getByRole("region", { name: `Einmal-Passwort für ${anmeldename}` });
|
||
const einmal = ((await anzeige.locator("code").textContent()) ?? "").trim();
|
||
expect(einmal).toMatch(/^[a-zA-Z2-9]{5}(-[a-zA-Z2-9]{5}){3}$/);
|
||
await page.screenshot({ path: "docs/anleitung/bilder/benutzer-desktop.png", fullPage: true });
|
||
|
||
// Zweiter Browser: die Person meldet sich zum ersten Mal an.
|
||
const kontext = await browser.newContext();
|
||
const person = await kontext.newPage();
|
||
await person.goto("/anmelden");
|
||
await person.getByText("Lokales Konto oder Notfallzugang", { exact: true }).click();
|
||
await person.getByLabel("Anmeldename").fill(anmeldename);
|
||
await person.getByLabel("Passwort").fill(einmal);
|
||
await person.getByRole("button", { name: "Mit Benutzername anmelden" }).click();
|
||
await expect(person).toHaveURL(/\/anmelden\/passwort/);
|
||
await person.getByLabel("Neues Passwort", { exact: true }).fill("zu-kurz");
|
||
await person.getByLabel("Neues Passwort wiederholen").fill("zu-kurz");
|
||
await person.getByRole("button", { name: "Passwort setzen" }).click();
|
||
await expect(person.getByText(/mindestens 12 Zeichen/).last()).toBeVisible();
|
||
await person.getByLabel("Neues Passwort", { exact: true }).fill(passwort);
|
||
await person.getByLabel("Neues Passwort wiederholen").fill(passwort);
|
||
await person.getByRole("button", { name: "Passwort setzen" }).click();
|
||
|
||
await expect(person).toHaveURL(/\/anmelden\/totp/);
|
||
const geheimnis = ((await person.locator("code").first().textContent()) ?? "").trim();
|
||
await person
|
||
.getByLabel("Code zur Bestätigung")
|
||
.fill(totpCode(base32Dekodieren(geheimnis), totpSchritt(Date.now())));
|
||
await person.getByRole("button", { name: /TOTP einrichten/ }).click();
|
||
await expect(person.getByRole("heading", { name: "Übersicht" })).toBeVisible();
|
||
await expect(person.getByText("Operator · über lokales Konto")).toBeVisible();
|
||
// Ein Operator verwaltet keine Benutzer.
|
||
await person.goto("/einstellungen/benutzer");
|
||
await expect(person).toHaveURL(/\/kein-zugriff/);
|
||
|
||
// Admin sperrt das Konto – die Sitzung der Person endet sofort.
|
||
await page.reload();
|
||
const zeile = page.getByRole("row").filter({ hasText: anmeldename });
|
||
await expect(zeile.getByText("Einrichtung offen")).toHaveCount(0);
|
||
await zeile.getByRole("button", { name: /Sperren/ }).click();
|
||
await expect(zeile.getByText("gesperrt", { exact: true })).toBeVisible();
|
||
await person.goto("/");
|
||
await expect(person).toHaveURL(/\/anmelden/);
|
||
await kontext.close();
|
||
});
|