pediatric-ai-scribe-v3/test/oidc-roles.test.js
Daniel f748e02063
Some checks failed
Forgejo Docker Build / Root app tests (push) Successful in 49s
Forgejo Docker Build / Build Docker image (push) Successful in 8s
Forgejo Docker Build / End-to-end (browser) (push) Failing after 8s
feat: roles follow the SSO's groups, on every sign-in
Both PedsHub apps now take admin and moderator from the same two Authentik
groups. oidc.admin_groups and oidc.moderator_groups name them; unset means
local roles stand. Applied at every sign-in so removal at the SSO demotes
here, and never applied to the last admin — a group edit must not be able
to lock everyone out of the panel.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-13 13:54:12 +02:00

34 lines
2 KiB
JavaScript

// Roles come from the SSO's groups on every sign-in, when the admin has said
// which groups mean what; the last admin is never demoted by a claim.
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const src = fs.readFileSync(path.join(__dirname, '..', 'src/routes/oidc.js'), 'utf8');
function withSettings(settings) {
const db = { getSetting: async k => settings[k] || '' };
const slice = src.slice(src.indexOf('function parseGroupList'), src.indexOf('// ── GET /api/auth/oidc — initiate'));
return new Function('db', slice + '; return roleFromGroups;')(db);
}
test('groups map to roles: admin beats moderator, neither is a user, off when unset', async () => {
const on = withSettings({ 'oidc.admin_groups': 'pedshub-admins', 'oidc.moderator_groups': 'pedshub-moderators, editors' });
assert.equal(await on(['pedshub-members', 'pedshub-admins', 'pedshub-moderators']), 'admin');
assert.equal(await on(['pedshub-members', 'Editors']), 'moderator', 'case-blind');
assert.equal(await on(['pedshub-members']), 'user');
assert.equal(await on('pedshub-members pedshub-moderators'), 'moderator', 'a string claim is a list');
assert.equal(await on(undefined), null, 'no claim: leave the role alone');
const off = withSettings({});
assert.equal(await off(['pedshub-admins']), null, 'unset settings: local roles stand');
});
test('the callback applies the mapping every sign-in and spares the last admin', () => {
const cb = src.slice(src.indexOf("router.get('/oidc/callback'"));
assert.match(cb, /var mapped = await roleFromGroups\(claims\.groups\);/);
assert.match(cb, /if \(mapped && mapped !== user\.role\)/);
assert.match(cb, /SELECT COUNT\(\*\) AS count FROM users WHERE role = 'admin' AND disabled IS NOT TRUE/);
assert.match(cb, /not demoting the last admin/);
assert.match(cb, /UPDATE users SET role = \? WHERE id = \?/);
assert.match(src, /'oidc\.admin_groups', 'oidc\.moderator_groups'\]/);
});