import { test } from 'node:test'; import assert from 'node:assert/strict'; import { isTestDbConfigured, setupTestDb } from '../helpers/setup-db.js'; import { isAdmin, accessibleProjectIds, projectLevel, assertProjectAccess, } from '../../src/auth/authz.js'; const SKIP = !isTestDbConfigured() && 'TEST_DATABASE_URL not set'; // ── isAdmin (pure, no DB) ─────────────────────────────────────────────────── test('isAdmin true only for role admin', () => { assert.equal(isAdmin({ role: 'admin' }), true); assert.equal(isAdmin({ role: 'editor' }), false); assert.equal(isAdmin({ role: 'viewer' }), false); assert.equal(isAdmin(null), false); assert.equal(isAdmin(undefined), false); }); // Seed helpers shared across the DB-backed cases. async function seed(pool) { const proj = async (name) => (await pool.query( `INSERT INTO projects (name, s3_prefix) VALUES ($1, $1) RETURNING id`, [name] )).rows[0].id; const user = async (username, role) => (await pool.query( `INSERT INTO users (username, password_hash, role) VALUES ($1, 'x', $2) RETURNING id`, [username, role] )).rows[0].id; const group = async (name) => (await pool.query(`INSERT INTO groups (name) VALUES ($1) RETURNING id`, [name])).rows[0].id; const grantUser = (pid, uid, level) => pool.query( `INSERT INTO project_access (project_id, subject_type, subject_id, level) VALUES ($1, 'user', $2, $3)`, [pid, uid, level]); const grantGroup = (pid, gid, level) => pool.query( `INSERT INTO project_access (project_id, subject_type, subject_id, level) VALUES ($1, 'group', $2, $3)`, [pid, gid, level]); const addToGroup = (uid, gid) => pool.query(`INSERT INTO user_groups (user_id, group_id) VALUES ($1, $2)`, [uid, gid]); return { proj, user, group, grantUser, grantGroup, addToGroup }; } test('admin sees all projects, every project at edit', { skip: SKIP }, async () => { const pool = await setupTestDb(); try { const s = await seed(pool); await s.proj('Alpha'); await s.proj('Beta'); const admin = { id: await s.user('adm', 'admin'), role: 'admin' }; const acc = await accessibleProjectIds(admin, pool); assert.equal(acc.all, true); assert.equal(await projectLevel(admin, '00000000-0000-4000-8000-000000000001', pool), 'edit'); await assertProjectAccess(admin, '00000000-0000-4000-8000-000000000001', 'edit', pool); // no throw } finally { await pool.end(); } }); test('direct user grant scopes access and respects level', { skip: SKIP }, async () => { const pool = await setupTestDb(); try { const s = await seed(pool); const alpha = await s.proj('Alpha'); const beta = await s.proj('Beta'); const u = { id: await s.user('bob', 'editor'), role: 'editor' }; await s.grantUser(alpha, u.id, 'view'); const acc = await accessibleProjectIds(u, pool); assert.equal(acc.all, false); assert.deepEqual([...acc.ids], [alpha]); assert.equal(await projectLevel(u, alpha, pool), 'view'); assert.equal(await projectLevel(u, beta, pool), null); await assertProjectAccess(u, alpha, 'view', pool); // ok await assert.rejects(() => assertProjectAccess(u, alpha, 'edit', pool), e => e.status === 403); await assert.rejects(() => assertProjectAccess(u, beta, 'view', pool), e => e.status === 403); } finally { await pool.end(); } }); test('group grant flows through membership', { skip: SKIP }, async () => { const pool = await setupTestDb(); try { const s = await seed(pool); const alpha = await s.proj('Alpha'); const u = { id: await s.user('carol', 'viewer'), role: 'viewer' }; const g = await s.group('broadcasters'); await s.addToGroup(u.id, g); await s.grantGroup(alpha, g, 'edit'); assert.equal(await projectLevel(u, alpha, pool), 'edit'); const acc = await accessibleProjectIds(u, pool); assert.deepEqual([...acc.ids], [alpha]); await assertProjectAccess(u, alpha, 'edit', pool); // ok via group } finally { await pool.end(); } }); test('effective level is the max of direct + group grants', { skip: SKIP }, async () => { const pool = await setupTestDb(); try { const s = await seed(pool); const alpha = await s.proj('Alpha'); const u = { id: await s.user('dan', 'editor'), role: 'editor' }; const g = await s.group('team'); await s.addToGroup(u.id, g); await s.grantUser(alpha, u.id, 'view'); // direct: view await s.grantGroup(alpha, g, 'edit'); // group: edit → wins assert.equal(await projectLevel(u, alpha, pool), 'edit'); } finally { await pool.end(); } }); test('user with no grants sees nothing', { skip: SKIP }, async () => { const pool = await setupTestDb(); try { const s = await seed(pool); await s.proj('Alpha'); const u = { id: await s.user('eve', 'viewer'), role: 'viewer' }; const acc = await accessibleProjectIds(u, pool); assert.equal(acc.ids.size, 0); } finally { await pool.end(); } });