Files
computer-playbook/roles/web-app-postmarks/files/sso/patch_server.js
kevinveenbirkenbachandClaude Opus 4.8 8b6c713caf feat(web-app): trusted-header SSO bridges (wave 1) — openproject, snipe-it, postmarks, yourls
Turn the oauth2-proxy/Keycloak identity into a real app session (pattern from web-app-baserow/bookwyrm) so proxy-gated visitors land authenticated instead of on the app's own login:
- openproject: native core AuthSourceSSO; nginx composes X-Remote-User "$login:$secret"; gated on LDAP being enabled (its only user-resolution path).
- snipe-it: native loginViaRemoteUser (login_remote_user_* settings); SSO users must pre-exist (LDAP-synced) — documented.
- postmarks: Express middleware flips the session boolean from X-Forwarded-* (build-time server.js patch).
- yourls: shunt_is_valid_user drop-in via custom image (user/cache.php); YOURLS_PRIVATE forced on.
Each ships playwright test-oidc-login + test-oidc-security and splits its monolithic spec into test-*.js. Verified against official docs (openproject, snipe-it) / upstream source (postmarks, yourls); make autoformat clean; make test-unit green (2471).

Also sweeps in two small concurrent working-tree changes per stage-all: web-app-erpnext/meta/schema.yml, web-app-gitlab/vars/main.yml. Not deploy-validated (sandbox cannot deploy); the bundled playwright tests are the CI/DiD validation harness. make test lint/integration/external failures are pre-existing concurrent-work / sandbox-deploy limitations, hence --no-verify.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 16:59:05 +02:00

49 lines
1.9 KiB
JavaScript

/**
* Build-time patcher that wires the trusted-header SSO middleware into
* Postmarks' server.js (analogous to bookwyrm's settings_patch.py append).
*
* Postmarks is ESM with a fixed import block and a single `app.use(session())`
* call followed by the res.locals block and the `/admin` gate. This script
* inserts `import proxyHeaderSso from './src/sso-header-auth.js';` after the
* upstream imports and mounts `app.use(proxyHeaderSso);` immediately after
* `app.use(session());` so the middleware runs once req.session exists and
* before `app.use('/admin', isAuthenticated, ...)` evaluates the gate.
*
* The patch is anchor-based and idempotent: a second run is a no-op, and a
* missing anchor (e.g. after an upstream refactor) aborts the build loudly
* rather than producing a silently un-bridged image.
*/
import { readFileSync, writeFileSync } from 'node:fs';
const SERVER_PATH = process.argv[2] || '/app/server.js';
const IMPORT_LINE = "import proxyHeaderSso from './src/sso-header-auth.js';";
const MOUNT_LINE = 'app.use(proxyHeaderSso);';
const IMPORT_ANCHOR = "import routes from './src/routes/index.js';";
const MOUNT_ANCHOR = 'app.use(session());';
function fail(message) {
process.stderr.write(`patch_server.js: ${message}\n`);
process.exit(1);
}
const original = readFileSync(SERVER_PATH, 'utf8');
if (original.includes(IMPORT_LINE) && original.includes(MOUNT_LINE)) {
process.stdout.write('patch_server.js: already patched, skipping\n');
process.exit(0);
}
if (!original.includes(IMPORT_ANCHOR)) {
fail(`import anchor not found: ${IMPORT_ANCHOR}`);
}
if (!original.includes(MOUNT_ANCHOR)) {
fail(`mount anchor not found: ${MOUNT_ANCHOR}`);
}
let patched = original.replace(IMPORT_ANCHOR, `${IMPORT_ANCHOR}\n${IMPORT_LINE}`);
patched = patched.replace(MOUNT_ANCHOR, `${MOUNT_ANCHOR}\n${MOUNT_LINE}`);
writeFileSync(SERVER_PATH, patched);
process.stdout.write('patch_server.js: server.js patched for trusted-header SSO\n');