Files
jaredandClaude Fable 5.1 cbb91a306d feat(ui): TDS migration stage 1 — EJS layout, CSP, vendored design system, shared runtime
- Vendor web_template base.css/base.js (v1.2 bbec859) under public/web_template with sync script
- Add views/layout.ejs (fixed upstream EJS comment delimiters, mobile drawer, cmd palette,
  keys help, WS status, theme toggle) and lib/render.js two-step renderer
- Add page routes for /, /workers, /workflows, /executions, /quick, /scheduler (stub views)
- helmet with strict nonce CSP (script-src-attr 'none'), /csp-report, report-only toggle
- lib/pageauth.js: shared users upsert + HTML 401/403 for page routes
- PULSE_DEV_READONLY guard disabling background writers for local testing
- public/assets/app.js shared runtime (Pulse namespace, delegated actions, WS singleton,
  themed confirm modal) and app.css
- eslint globals for browser files; ignore vendored assets

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HamVMDrA8RqhyxmUHgiqRp
2026-09-08 21:46:24 -04:00

71 lines
2.2 KiB
JavaScript

// Authelia SSO helpers shared by the JSON API middleware (server.js authenticateSSO)
// and the HTML page middleware (authenticatePage).
const crypto = require('crypto');
const { renderError } = require('./render');
const ALLOWED_GROUPS = ['admin', 'employee'];
// Upsert the SSO user into the `users` table. Extracted verbatim from authenticateSSO.
async function upsertUser(pool, headers) {
const userId = crypto.randomUUID();
await pool.query(
`INSERT INTO users (id, username, display_name, email, groups, last_login)
VALUES (?, ?, ?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE
display_name=VALUES(display_name),
email=VALUES(email),
groups=VALUES(groups),
last_login=NOW()`,
[
userId,
headers['remote-user'],
headers['remote-name'],
headers['remote-email'],
headers['remote-groups']
]
);
}
// Express middleware factory for HTML page routes: same auth rules as the API,
// but failures render a themed HTML page instead of JSON.
function makeAuthenticatePage(pool) {
return async function authenticatePage(req, res, next) {
const remoteUser = req.headers['remote-user'];
const remoteName = req.headers['remote-name'];
const remoteEmail = req.headers['remote-email'];
const remoteGroups = req.headers['remote-groups'];
if (!remoteUser) {
return renderError(req, res, 401, 'Not authenticated',
'Not authenticated — access via Authelia SSO (auth.lotusguild.org).');
}
const groups = remoteGroups ? remoteGroups.split(',').map(g => g.trim()) : [];
const hasAccess = groups.some(g => ALLOWED_GROUPS.includes(g));
if (!hasAccess) {
return renderError(req, res, 403, 'Access denied',
'You must be in the admin or employee group to use this service.');
}
try {
await upsertUser(pool, req.headers);
} catch (error) {
console.error('Error updating user:', error);
}
req.user = {
username: remoteUser,
name: remoteName || remoteUser,
email: remoteEmail || '',
groups: groups,
isAdmin: groups.includes('admin')
};
next();
};
}
module.exports = { upsertUser, makeAuthenticatePage, ALLOWED_GROUPS };