import { execFileSync } from "node:child_process"; import { expect, type Page } from "@playwright/test"; import "dotenv/config"; /** * Gemeinsame Schritte fuer Widerruf (#024) und Rotation (#023): anmelden, Hosts einrichten und * der Kern-Anwendungsfall aus kern-anwendungsfall.spec.ts als Ausgangslage. */ const ROOT_PASSWORT = process.env["SSHKV_TEST_ROOT_PASSWORT"] || "sshkv-test"; export const PORT = { a: (process.env["SSHKV_TEST_ZIEL_A"] ?? "127.0.0.1:2221").split(":")[1] ?? "2221", b: (process.env["SSHKV_TEST_ZIEL_B"] ?? "127.0.0.1:2222").split(":")[1] ?? "2222", }; export function aufZiel(ziel: "ziel-a" | "ziel-b", befehl: string): string { return execFileSync( "docker", ["compose", "-f", "docker-compose.test.yml", "exec", "-T", ziel, "sh", "-c", befehl], { encoding: "utf8" }, ); } export function zieleZuruecksetzen(): void { const reset = "userdel -r sshkv 2>/dev/null; userdel -r deploy 2>/dev/null; rm -f /etc/sudoers.d/sshkv*; rm -rf /root/.ssh; true"; aufZiel("ziel-a", reset); aufZiel("ziel-b", reset); } export async function anmelden(page: Page): Promise { await page.goto("/anmelden"); await page.getByRole("link", { name: /Mit Mock-OIDC anmelden/ }).click(); await page.locator('button[data-rolle="admin"]').click(); await page.getByRole("button", { name: "Anmelden", exact: true }).click(); await expect(page.getByRole("heading", { name: "Übersicht" })).toBeVisible(); } export async function hostEinrichten(page: Page, name: string, port: string): Promise { await page.goto("/hosts/neu"); await page.getByLabel("Name").fill(name); await page.getByLabel("Adresse").fill("127.0.0.1"); await page.getByLabel("Port").fill(port); await page.getByRole("button", { name: "Host anlegen" }).click(); await page.getByRole("button", { name: "Host-Key abrufen" }).click(); await page.getByRole("button", { name: "Fingerprint stimmt – vertrauen" }).click({ timeout: 30_000 }); await page.getByLabel("Passwort", { exact: true }).fill(ROOT_PASSWORT); await page.getByRole("button", { name: "Zugang einrichten" }).click(); await expect(page.getByRole("button", { name: "Verbindung prüfen" })).toBeVisible({ timeout: 60_000 }); } export async function archivieren(page: Page, name: string): Promise { await page.goto("/hosts"); await page.getByRole("link", { name }).first().click(); await page.getByRole("button", { name: "Host archivieren" }).click(); await page.getByRole("dialog").getByRole("button", { name: "Archivieren", exact: true }).click(); await expect(page).toHaveURL(/\/hosts$/); } /** * Kern-Anwendungsfall: Schluessel erzeugen, Private Key auf A (Alias zu-b), Public Key auf B, * Dry-Run, anwenden. Endet auf der Schluesselseite; liefert deren Adresse. */ export async function kernAufbauen(page: Page, zweck: string, hostA: string, hostB: string): Promise { await page.goto("/schluessel/neu"); await page.getByLabel("Zweck").fill(zweck); await page.getByRole("button", { name: "Schlüssel erzeugen" }).click(); await expect(page.getByRole("heading", { name: zweck })).toBeVisible({ timeout: 30_000 }); await page.getByRole("button", { name: "Ausrollen" }).first().click(); const privat = page.getByRole("dialog", { name: "Private Key ausrollen" }); await privat.getByLabel("Quell-Host (bekommt den Private Key)").selectOption({ label: hostA }); await privat.getByLabel("Ziel-Host (für known_hosts)").selectOption({ label: hostB }); await privat.getByRole("textbox", { name: "Alias" }).fill("zu-b"); await privat.getByRole("textbox", { name: "HostName" }).fill("ziel-b"); await privat.getByRole("spinbutton", { name: "Port" }).fill("22"); await privat.getByRole("button", { name: "Zuweisen" }).click(); await expect(privat).toBeHidden(); await page.getByRole("button", { name: "Ausrollen" }).nth(1).click(); const oeffentlich = page.getByRole("dialog", { name: "Public Key zuweisen" }); await oeffentlich.getByLabel("Ziel", { exact: true }).selectOption({ label: hostB }); await oeffentlich.getByRole("button", { name: "Zuweisen" }).click(); await expect(oeffentlich).toBeHidden(); await page.getByRole("button", { name: "Dry-Run starten" }).click(); await page.getByRole("button", { name: /Änderung anwenden/ }).click({ timeout: 30_000 }); await expect(page.getByText("Ausgerollt.")).toBeVisible({ timeout: 60_000 }); expect(aufZiel("ziel-a", "ssh -o BatchMode=yes zu-b id -un").trim()).toBe("root"); return page.url(); }