mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
fix(test-e2e-playwright): gate shared personas on dashboard + oidc service flags
CI run 26089179898 job 76714878217 (Deploy web-app-bluesky, variant 1)
failed at the mailu Playwright `guest: public-landing → auth chain →
never authenticated` test with 60s timeout × 3 retries. Root cause is
two layered defects:
1. Between Round 1 and Round 2 of the matrix deploy the fresh-purge
sweep removes the web-app-dashboard vhost and container (mandate-
fresh-purged on first contact with a new VARIANT, see commit
3f12fbe83). Round 2 in variant 1 deploys only matomo/mailu/keycloak/
bluesky; the dashboard.infinito.example vhost never comes back.
2. mailu's templates/playwright.env.j2 pins APP_BASE_URL to
`lookup('tls', 'web-app-dashboard', 'url.base')` unconditionally —
it has to, because the shared persona helpers route through the
dashboard main-frame Account menu. In variant 1 (dashboard disabled
for mailu) that URL is dangling: nginx's default_server falls onto
the bluesky-pds backend, which serves a text/plain ASCII banner.
readCspString's `<meta>` fallback in personas/utils/csp.js has no
explicit getAttribute timeout and waits on the test-level 60s.
The shared persona itself has no way to learn that APP_BASE_URL routes
through a dashboard frame whose service is disabled — biber + admin
sidestepped this only because mailu already blocks them via
PERSONA_BIBER_BLOCKED / PERSONA_ADMINISTRATOR_BLOCKED for an unrelated
silent-SSO escape. guest had no equivalent opt-out and timed out.
Decided by service flag, not by sniffing the response, so the skip
fires before any network I/O:
- roles/test-e2e-playwright/files/personas/guest.js: skip when
APP_BASE_URL's host starts with "dashboard." AND
DASHBOARD_SERVICE_ENABLED=false.
- roles/test-e2e-playwright/files/personas/biber.js +
personas/admin.js: same dashboard-frame skip + a new OIDC-required
skip (these personas drive the Keycloak round-trip; without OIDC
they cannot reach an authenticated surface). Skips placed
immediately before the dependent operation, not at the top, so the
reason matches the operation that would actually fail. parity-marker
safeIsEnabled("dashboard") added to the existing oauth2/logout/
matomo block so the env-gate parity guard recognises the new
consumption.
- roles/web-app-mailu/templates/playwright.env.j2: emit
DASHBOARD_SERVICE_ENABLED in the existing OIDC/LOGOUT/MATOMO_SERVICE
_ENABLED block. The other 14 dashboard-frame roles will pick up
this declaration when they hit the same variant-disabled failure.
biber + admin remain unconditional-blocked for mailu via the existing
PERSONA_*_BLOCKED flags; the new skips inside them are forward-looking
for the day the silent-SSO escape lifts and those personas are
un-blocked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
76dfdf78f7
commit
1f6b5f8942
4 files changed
+64
No files matched your search
@@ -60,6 +60,7 @@ async function runAdminFlow(page, opts = {}) {
|
||||
safeIsEnabled("oauth2");
|
||||
safeIsEnabled("logout");
|
||||
safeIsEnabled("matomo");
|
||||
safeIsEnabled("dashboard");
|
||||
|
||||
const canonicalDomain = readEnv("CANONICAL_DOMAIN");
|
||||
const appBaseUrl = normalizeUrl(process.env.APP_BASE_URL);
|
||||
@@ -81,6 +82,30 @@ async function runAdminFlow(page, opts = {}) {
|
||||
|
||||
const oidcEnabled = safeIsEnabled("oidc");
|
||||
|
||||
// The administrator journey relies on the OIDC login chain to
|
||||
// authenticate; when OIDC is off, no Keycloak round-trip happens
|
||||
// and the persona cannot reach an authenticated surface.
|
||||
if (!oidcEnabled) {
|
||||
test.skip(
|
||||
true,
|
||||
"administrator persona requires OIDC, but OIDC_SERVICE_ENABLED=false for this role.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dashboard vhost is purged between variants; skip before any I/O
|
||||
// when APP_BASE_URL routes through a disabled dashboard frame.
|
||||
if (new URL(appBaseUrl).hostname.toLowerCase().startsWith("dashboard.")) {
|
||||
const dashboardEnabled = (process.env.DASHBOARD_SERVICE_ENABLED || "").toLowerCase();
|
||||
if (dashboardEnabled === "false") {
|
||||
test.skip(
|
||||
true,
|
||||
"administrator persona collapsed: APP_BASE_URL routes via the dashboard frame but DASHBOARD_SERVICE_ENABLED=false for this variant.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Direct-app entry: bookmark-style navigation. The OAuth2-Proxy gate
|
||||
// fires on the first request, redirecting unauthenticated requests
|
||||
// to Keycloak; the auth chain is the same regardless of how the user
|
||||
|
||||
@@ -60,6 +60,7 @@ async function runBiberFlow(page, opts = {}) {
|
||||
safeIsEnabled("oauth2");
|
||||
safeIsEnabled("logout");
|
||||
safeIsEnabled("matomo");
|
||||
safeIsEnabled("dashboard");
|
||||
|
||||
const canonicalDomain = readEnv("CANONICAL_DOMAIN");
|
||||
const appBaseUrl = normalizeUrl(process.env.APP_BASE_URL);
|
||||
@@ -80,6 +81,19 @@ async function runBiberFlow(page, opts = {}) {
|
||||
|
||||
await page.context().clearCookies();
|
||||
|
||||
// Dashboard vhost is purged between variants; skip before any I/O
|
||||
// when APP_BASE_URL routes through a disabled dashboard frame.
|
||||
if (new URL(appBaseUrl).hostname.toLowerCase().startsWith("dashboard.")) {
|
||||
const dashboardEnabled = (process.env.DASHBOARD_SERVICE_ENABLED || "").toLowerCase();
|
||||
if (dashboardEnabled === "false") {
|
||||
test.skip(
|
||||
true,
|
||||
"biber persona collapsed: APP_BASE_URL routes via the dashboard frame but DASHBOARD_SERVICE_ENABLED=false for this variant.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Direct-app entry: bookmark-style navigation. The OAuth2-Proxy gate
|
||||
// fires on the first request, redirecting unauthenticated requests
|
||||
// to Keycloak; the auth chain is the same regardless of how the user
|
||||
@@ -88,6 +102,17 @@ async function runBiberFlow(page, opts = {}) {
|
||||
|
||||
const oidcEnabled = safeIsEnabled("oidc");
|
||||
|
||||
// The biber journey relies on the OIDC login chain to authenticate;
|
||||
// when OIDC is off, no Keycloak round-trip happens and the persona
|
||||
// cannot reach an authenticated surface.
|
||||
if (!oidcEnabled) {
|
||||
test.skip(
|
||||
true,
|
||||
"biber persona requires OIDC, but OIDC_SERVICE_ENABLED=false for this role.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Two auth shapes share a single login step:
|
||||
// * oauth2-proxy gate: the goto is intercepted and the page lands
|
||||
// directly on the Keycloak auth endpoint; perform Keycloak login.
|
||||
|
||||
@@ -42,6 +42,19 @@ async function runGuestFlow(page = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dashboard vhost is purged between variants; skip before any I/O
|
||||
// when APP_BASE_URL routes through a disabled dashboard frame.
|
||||
if (new URL(appBaseUrl).hostname.toLowerCase().startsWith("dashboard.")) {
|
||||
const dashboardEnabled = (process.env.DASHBOARD_SERVICE_ENABLED || "").toLowerCase();
|
||||
if (dashboardEnabled === "false") {
|
||||
test.skip(
|
||||
true,
|
||||
"guest persona collapsed: APP_BASE_URL routes via the dashboard frame but DASHBOARD_SERVICE_ENABLED=false for this variant.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
test.setTimeout(60_000);
|
||||
|
||||
const startUrl = appBaseUrl;
|
||||
|
||||
@@ -26,3 +26,4 @@ MATOMO_BASE_URL={{ lookup('tls', 'web-app-matomo', 'url.base') | dotenv_quote }}
|
||||
OIDC_SERVICE_ENABLED={{ lookup('config', application_id, 'services.oidc.enabled') | string | lower }}
|
||||
LOGOUT_SERVICE_ENABLED={{ lookup('config', application_id, 'services.logout.enabled') | string | lower }}
|
||||
MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enabled') | string | lower }}
|
||||
DASHBOARD_SERVICE_ENABLED={{ lookup('config', application_id, 'services.dashboard.enabled') | string | lower }}
|
||||
Reference in new issue
Block a user