mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +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>
141 lines
5.2 KiB
JavaScript
141 lines
5.2 KiB
JavaScript
const { test, expect } = require("@playwright/test");
|
|
const { resolveTimeout } = require("./timeouts");
|
|
|
|
const { installCspViolationObserver, gotoOnion } = require("./personas");
|
|
const { skipUnlessServiceEnabled } = require("./service-gating");
|
|
|
|
// Auto-provisioned LDAP/Keycloak groups drive WordPress roles via the
|
|
// OIDC `groups` claim, consumed by the mu-plugin
|
|
// infinito-oidc-rbac-mapper.php. Test three roles across the privilege
|
|
// spectrum serially so a regression in one mapping does not mask another.
|
|
const RBAC_ROLE_SEQUENCE = ["subscriber", "editor", "administrator"];
|
|
|
|
// Keycloak group path is hierarchical:
|
|
// /roles/web-app-wordpress/<role> (Single-Site) or
|
|
// /roles/web-app-wordpress/<tenant>/<role> (Multisite).
|
|
// RBAC_GROUP_PATH_PREFIX renders `/roles/web-app-wordpress/`; the spec
|
|
// appends the role segment. Multisite scenarios opt in via
|
|
// services.wordpress.multisite.enabled = true.
|
|
|
|
exports.register = function (shared) {
|
|
for (const role of RBAC_ROLE_SEQUENCE) {
|
|
test(`rbac: membership in ${role} group grants WordPress ${role} role`, async ({
|
|
browser,
|
|
}) => {
|
|
skipUnlessServiceEnabled("sso");
|
|
skipUnlessServiceEnabled("ldap");
|
|
test.skip(
|
|
shared.env.multisiteEnabled,
|
|
"WORDPRESS_MULTISITE_ENABLED=true; Single-Site RBAC scenarios run only when Multisite is disabled"
|
|
);
|
|
const groupPath = `${shared.env.rbacGroupPathPrefix}${role}`;
|
|
let biberAddedToGroup = false;
|
|
|
|
// Each identity runs in its own isolated context so WP session
|
|
// cookies, Keycloak SSO cookies, and OIDC post-logout redirect
|
|
// state cannot leak between super-admin, biber, and wp-admin hops.
|
|
const newCtx = async () => {
|
|
const ctx = await browser.newContext({
|
|
ignoreHTTPSErrors: true,
|
|
viewport: { width: 1440, height: 1100 },
|
|
});
|
|
const p = await ctx.newPage();
|
|
await installCspViolationObserver(p);
|
|
return { ctx, page: p };
|
|
};
|
|
|
|
try {
|
|
const adminKc = await newCtx();
|
|
try {
|
|
await gotoOnion(adminKc.page, `${shared.env.keycloakBaseUrl}/admin/master/console/`);
|
|
await shared.fillKeycloakLoginForm(
|
|
adminKc.page,
|
|
shared.env.superAdminUsername,
|
|
shared.env.superAdminPassword
|
|
);
|
|
await expect
|
|
.poll(() => adminKc.page.url(), {
|
|
timeout: resolveTimeout(60_000),
|
|
message: "Expected to land in the Keycloak admin console",
|
|
})
|
|
.toContain("/admin/master/console/");
|
|
biberAddedToGroup = await shared.keycloakAdminAddUserToGroup(
|
|
adminKc.page,
|
|
shared.env.keycloakBaseUrl,
|
|
shared.env.realmName,
|
|
groupPath,
|
|
shared.env.biberUsername
|
|
);
|
|
} finally {
|
|
await adminKc.ctx.close().catch(() => {});
|
|
}
|
|
|
|
const biberWp = await newCtx();
|
|
try {
|
|
await shared.wpAdminLoginViaOidc(
|
|
biberWp.page,
|
|
shared.env.wpBaseUrl,
|
|
shared.env.biberUsername,
|
|
shared.env.biberPassword
|
|
);
|
|
} finally {
|
|
await biberWp.ctx.close().catch(() => {});
|
|
}
|
|
|
|
const wpAdmin = await newCtx();
|
|
try {
|
|
await shared.wpAdminLoginViaOidc(
|
|
wpAdmin.page,
|
|
shared.env.wpBaseUrl,
|
|
shared.env.adminUsername,
|
|
shared.env.adminPassword
|
|
);
|
|
await gotoOnion(wpAdmin.page, `${shared.env.wpBaseUrl}/wp-admin/users.php`, {
|
|
waitUntil: "domcontentloaded",
|
|
});
|
|
const biberRow = wpAdmin.page
|
|
.locator("tr")
|
|
.filter({ hasText: new RegExp(shared.env.biberUsername, "i") })
|
|
.first();
|
|
await expect(
|
|
biberRow,
|
|
`Expected biber row to be visible on /wp-admin/users.php`
|
|
).toBeVisible({ timeout: resolveTimeout(30_000) });
|
|
const rowText = (await biberRow.textContent()) || "";
|
|
const expectedLabel = role.charAt(0).toUpperCase() + role.slice(1);
|
|
expect(
|
|
rowText.includes(expectedLabel),
|
|
`biber's row on /wp-admin/users.php MUST show WordPress role "${expectedLabel}" after OIDC login; row text: ${rowText}`
|
|
).toBe(true);
|
|
} finally {
|
|
await wpAdmin.ctx.close().catch(() => {});
|
|
}
|
|
} finally {
|
|
// Idempotency: only teardown when this test performed the join.
|
|
// If biber was already a member at start, leave the membership
|
|
// untouched.
|
|
if (biberAddedToGroup) {
|
|
try {
|
|
const reqCtx = await browser.newContext({ ignoreHTTPSErrors: true });
|
|
try {
|
|
await shared.keycloakRemoveUserFromGroupViaRest(
|
|
reqCtx.request,
|
|
shared.env.keycloakBaseUrl,
|
|
shared.env.realmName,
|
|
shared.env.superAdminUsername,
|
|
shared.env.superAdminPassword,
|
|
groupPath,
|
|
shared.env.biberUsername
|
|
);
|
|
} finally {
|
|
await reqCtx.close().catch(() => {});
|
|
}
|
|
} catch (err) {
|
|
console.warn(`Cleanup removal of biber from ${groupPath} failed: ${err}`);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
};
|