461bd63804
The Authentik blueprint that provisions the OAuth2 Provider/Application only lived on CT121's filesystem via ad-hoc scp/pct push -- deploy/authentik/ is now the source of truth, with redeploy steps in docs/oidc-setup.md. Also documents two bugs found and fixed while implementing issue #19: the first RP-Initiated Logout attempt only ended the app-scoped session, and the provider had no property_mappings so the ID token's username claim was missing (fell back to a raw sub hash that looked like a leaked session token). Both are covered by new Playwright regression tests. The deep-check Fingerbank test now skips instead of failing when its target device (192.168.1.106) has since been manually labeled known via the dashboard, rather than assuming it stays unlabeled forever.
80 lines
4.0 KiB
TypeScript
80 lines
4.0 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
|
|
const USERNAME = process.env.LOCAL_USERNAME ?? "admin";
|
|
const PASSWORD = process.env.LOCAL_PASSWORD ?? "";
|
|
|
|
test("deep check button runs a check and shows results for an unknown device", async ({ page }) => {
|
|
test.skip(!PASSWORD, "LOCAL_PASSWORD not set");
|
|
|
|
await page.goto("/");
|
|
await page.getByPlaceholder("Username").fill(USERNAME);
|
|
await page.getByPlaceholder("Password").fill(PASSWORD);
|
|
await page.getByRole("button", { name: "Sign in" }).click();
|
|
await expect(page.locator(".device-table tbody tr").first()).toBeVisible({ timeout: 15_000 });
|
|
|
|
const unknownRow = page.locator(".device-table tbody tr").filter({ hasText: "unknown" }).first();
|
|
await expect(unknownRow).toBeVisible();
|
|
|
|
const button = unknownRow.getByRole("button", { name: /deep check|checking/i });
|
|
await expect(button).toBeVisible();
|
|
await button.click();
|
|
|
|
await expect(button).toHaveText("Checking…");
|
|
// Bounded by the script's own timeouts (well under 15s) plus SSH round trip.
|
|
await expect(page.locator(".deep-check-row").first()).toBeVisible({ timeout: 20_000 });
|
|
await expect(button).toHaveText("Deep check");
|
|
|
|
// Either real findings (SSDP/mDNS/ports) or the explicit "nothing found"
|
|
// message -- either way, a result panel with actual content, not empty.
|
|
const resultRow = page.locator(".deep-check-row").first();
|
|
await expect(resultRow).not.toBeEmpty();
|
|
});
|
|
|
|
test("deep check surfaces a Fingerbank identification with a confidence label", async ({ page }) => {
|
|
test.skip(!PASSWORD, "LOCAL_PASSWORD not set");
|
|
|
|
await page.goto("/");
|
|
await page.getByPlaceholder("Username").fill(USERNAME);
|
|
await page.getByPlaceholder("Password").fill(PASSWORD);
|
|
await page.getByRole("button", { name: "Sign in" }).click();
|
|
// Wait for the table to actually render before checking whether the
|
|
// target device is in it -- checking row.count() too early always finds
|
|
// zero rows and skips the test regardless of whether the device is really
|
|
// on the network. Bit me on the first run of this test.
|
|
await expect(page.locator(".device-table tbody tr").first()).toBeVisible({ timeout: 15_000 });
|
|
|
|
// The Nintendo device (192.168.1.106) reliably gets a Fingerbank match, if
|
|
// it's still on the network and still unknown -- skip rather than fail if
|
|
// it's gone, or if it's since been manually labeled via the dashboard (its
|
|
// "known" state isn't something the test suite controls either).
|
|
const row = page.locator('tr[data-ip="192.168.1.106"]');
|
|
const deepCheckButton = row.getByRole("button", { name: /deep check/i });
|
|
test.skip((await row.count()) === 0, "192.168.1.106 not currently on the network");
|
|
test.skip((await deepCheckButton.count()) === 0, "192.168.1.106 has since been labeled known");
|
|
|
|
await deepCheckButton.click();
|
|
await expect(page.locator(".deep-check-row").first()).toBeVisible({ timeout: 20_000 });
|
|
|
|
const resultText = await page.locator(".deep-check-row").first().textContent();
|
|
expect(resultText).toContain("Fingerbank ID");
|
|
// Confidence label should always accompany a score -- never a bare
|
|
// number, which could be mistaken for a confirmed identification.
|
|
expect(resultText).toMatch(/low confidence|moderate confidence|high confidence|very high confidence/);
|
|
});
|
|
|
|
test("known devices have no deep check button", async ({ page }) => {
|
|
test.skip(!PASSWORD, "LOCAL_PASSWORD not set");
|
|
|
|
await page.goto("/");
|
|
await page.getByPlaceholder("Username").fill(USERNAME);
|
|
await page.getByPlaceholder("Password").fill(PASSWORD);
|
|
await page.getByRole("button", { name: "Sign in" }).click();
|
|
await expect(page.locator(".device-table tbody tr").first()).toBeVisible({ timeout: 15_000 });
|
|
|
|
// .device-badge.known specifically -- "known" is a substring of "unknown",
|
|
// so a text-based filter would match both (bit me on the labeling test too).
|
|
const knownRow = page.locator(".device-table tbody tr").filter({ has: page.locator(".device-badge.known") }).first();
|
|
await expect(knownRow).toBeVisible();
|
|
await expect(knownRow.getByRole("button", { name: /deep check/i })).toHaveCount(0);
|
|
});
|