mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
New lint test_no_raw_goto.py forbids raw `page.goto(...)` in role playwright specs + persona flows (the gotoOnion wrapper in personas/utils is exempt) so navigation retries transient Tor-transport errors over onion. Migrates every raw call across ~75 roles (343 sites) to `gotoOnion(page, ...)` and teaches gotoOnion to detect relative onion URLs (`isOnionCanonical`). gotoOnion is a behaviour-identical drop-in on clearnet (single attempt, same return). Verified: no-raw-goto lint green, eslint 0 errors, make test green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
93 lines
3.8 KiB
JavaScript
93 lines
3.8 KiB
JavaScript
const { test, expect } = require("@playwright/test");
|
|
const { resolveTimeout } = require("../timeouts");
|
|
|
|
const { skipUnlessAddonEnabled } = require("../addon-gating");
|
|
const { skipUnlessServiceEnabled } = require("../service-gating");
|
|
const { decodeDotenvQuotedValue, normalizeBaseUrl, gotoOnion } = require("../personas");
|
|
|
|
test.use({ ignoreHTTPSErrors: true });
|
|
|
|
const appBaseUrl = normalizeBaseUrl(process.env.APP_BASE_URL || "");
|
|
const canonicalDomain = decodeDotenvQuotedValue(process.env.CANONICAL_DOMAIN || "");
|
|
|
|
// PluggableAuth is the auth framework backing OpenIDConnect. The role wires it
|
|
// with EnableAutoLogin=true / EnableLocalLogin=false, so a wiki where the
|
|
// framework is actually loaded AND bound to the OIDC plugin must NOT serve the
|
|
// stock local username/password login form on Special:UserLogin — it must
|
|
// instead hand off to the OIDC issuer (Keycloak) or render only the
|
|
// PluggableAuth SSO affordance. This test fails if PluggableAuth is absent or
|
|
// not coupled to the IdP.
|
|
test("PluggableAuth: framework replaces local login and is bound to the OIDC issuer", async ({
|
|
page,
|
|
}) => {
|
|
skipUnlessAddonEnabled("PluggableAuth");
|
|
skipUnlessServiceEnabled("sso");
|
|
|
|
expect(appBaseUrl, "APP_BASE_URL must be set").toBeTruthy();
|
|
expect(canonicalDomain, "CANONICAL_DOMAIN must be set").toBeTruthy();
|
|
|
|
await page.context().clearCookies();
|
|
|
|
const loginUrl = `${appBaseUrl}/index.php?title=Special:UserLogin`;
|
|
await gotoOnion(page, loginUrl, { waitUntil: "domcontentloaded", timeout: resolveTimeout(60_000) });
|
|
|
|
// Let an EnableAutoLogin handoff to the IdP settle.
|
|
await page
|
|
.waitForLoadState("networkidle", { timeout: resolveTimeout(60_000) })
|
|
.catch(() => {});
|
|
|
|
const finalUrl = page.url();
|
|
const redirectedAway = !finalUrl.includes("Special:UserLogin");
|
|
|
|
if (redirectedAway) {
|
|
// EnableAutoLogin handed off to the configured OIDC issuer. Prove the
|
|
// landing page is genuinely an OAuth2/OIDC authorization surface coupled to
|
|
// this wiki, not an arbitrary redirect: the OIDC client sends the wiki's
|
|
// redirect_uri back to itself and Keycloak renders a recognizable login.
|
|
const url = new URL(finalUrl);
|
|
const looksLikeAuthEndpoint =
|
|
/\/(auth|authorize|protocol\/openid-connect\/auth|oauth2?\/authorize)/i.test(
|
|
url.pathname,
|
|
) || url.searchParams.has("redirect_uri");
|
|
|
|
expect(
|
|
looksLikeAuthEndpoint,
|
|
`auto-login should land on the OIDC authorization endpoint, got ${finalUrl}`,
|
|
).toBeTruthy();
|
|
|
|
// The OAuth request must carry this wiki as the client redirect target,
|
|
// proving the clientID/providerURL coupling from $wgPluggableAuth_Config.
|
|
const redirectUri = url.searchParams.get("redirect_uri") || "";
|
|
if (redirectUri) {
|
|
expect(
|
|
decodeURIComponent(redirectUri),
|
|
"OIDC redirect_uri must point back at this wiki",
|
|
).toContain(canonicalDomain);
|
|
}
|
|
|
|
await expect(page.locator("body")).toContainText(
|
|
/sign in|log ?in|username|password|keycloak|openid/i,
|
|
{ timeout: resolveTimeout(60_000) },
|
|
);
|
|
return;
|
|
}
|
|
|
|
// No auto-redirect: we are still on Special:UserLogin. PluggableAuth with
|
|
// EnableLocalLogin=false must have suppressed the stock local credential form
|
|
// and exposed only the SSO affordance bound to OIDC.
|
|
const body = page.locator("body");
|
|
|
|
await expect(body).toContainText(
|
|
/single sign[- ]?on|openid|\bsso\b|log ?in with|continue with|keycloak/i,
|
|
{ timeout: resolveTimeout(60_000) },
|
|
);
|
|
|
|
// Stock MediaWiki local login has a password input named "wpPassword". With
|
|
// EnableLocalLogin=false the framework removes it; its presence would mean
|
|
// PluggableAuth is not actually driving the login surface.
|
|
await expect(
|
|
page.locator('input[name="wpPassword"]'),
|
|
"local password field must be absent when PluggableAuth owns login",
|
|
).toHaveCount(0);
|
|
});
|