const assert = require('node:assert/strict'); const test = require('node:test'); const db = require('../../src/db/models'); const AccessPolicy = require('../../src/services/access-policy'); const AccessPolicyAuditService = require('../../src/services/access-policy-audit'); const suffix = `${Date.now()}-${process.pid}`; test.after(async () => { await db.sequelize.close(); }); async function authenticateWithTimeout(timeoutMs = 1500) { let timeoutId; const timeout = new Promise((_, reject) => { timeoutId = setTimeout( () => reject(new Error(`Database unavailable after ${timeoutMs}ms`)), timeoutMs, ); }); try { await Promise.race([db.sequelize.authenticate(), timeout]); } finally { clearTimeout(timeoutId); } } async function withTransaction(t, callback) { try { await authenticateWithTimeout(); } catch (error) { t.skip(`Database unavailable: ${error.message}`); return; } const transaction = await db.sequelize.transaction(); try { await callback(transaction); } finally { await transaction.rollback(); } } async function createRole(name, transaction) { return db.roles.create({ name }, { transaction }); } async function createPermission(name, transaction) { return db.permissions.create({ name }, { transaction }); } async function createUser({ email, role }, transaction) { const user = await db.users.create( { email, password: 'not-used-in-test', emailVerified: true, }, { transaction }, ); await user.setApp_role(role, { transaction }); return user; } async function createProject({ slug, visibility }, transaction) { return db.projects.create( { name: `Test ${slug}`, slug, production_presentation_visibility: visibility, }, { transaction }, ); } test('guest can view public production presentation only', async (t) => { await withTransaction(t, async (transaction) => { const publicProject = await createProject( { slug: `test-public-runtime-access-${suffix}`, visibility: 'public', }, transaction, ); const privateProject = await createProject( { slug: `test-private-runtime-access-${suffix}`, visibility: 'private', }, transaction, ); assert.equal( await AccessPolicy.canViewProductionPresentation( null, publicProject.slug, { transaction }, ), true, ); assert.equal( await AccessPolicy.canViewProductionPresentation( null, privateProject.slug, { transaction }, ), false, ); }); }); test('public user can view granted private production presentation', async (t) => { await withTransaction(t, async (transaction) => { const publicRole = await createRole('Public', transaction); const publicUser = await createUser( { email: `public-granted-${suffix}@example.test`, role: publicRole, }, transaction, ); const privateProject = await createProject( { slug: `test-private-granted-runtime-access-${suffix}`, visibility: 'private', }, transaction, ); await db.production_presentation_access.create( { userId: publicUser.id, projectId: privateProject.id, }, { transaction }, ); const authUser = await db.users.findOne({ where: { id: publicUser.id }, include: [ { association: 'app_role', include: [{ association: 'permissions' }] }, { association: 'custom_permissions' }, ], transaction, }); assert.equal( await AccessPolicy.canViewProductionPresentation( authUser.get({ plain: true }), privateProject.slug, { transaction }, ), true, ); }); }); test('internal user with permission can use admin api and view private presentation', async (t) => { await withTransaction(t, async (transaction) => { const role = await createRole('Content Reviewer', transaction); const permission = await createPermission( `TEST_READ_PROJECTS_${suffix}`, transaction, ); await role.setPermissions([permission], { transaction }); const internalUser = await createUser( { email: `internal-access-${suffix}@example.test`, role, }, transaction, ); const privateProject = await createProject( { slug: `test-private-internal-runtime-access-${suffix}`, visibility: 'private', }, transaction, ); const authUser = await db.users.findOne({ where: { id: internalUser.id }, include: [ { association: 'app_role', include: [{ association: 'permissions' }] }, { association: 'custom_permissions' }, ], transaction, }); const plainUser = authUser.get({ plain: true }); assert.equal(AccessPolicy.canUseAdminApi(plainUser), true); assert.equal( await AccessPolicy.canViewProductionPresentation( plainUser, privateProject.slug, { transaction }, ), true, ); }); }); test('audit finds and cleanup removes stale Public grants', async (t) => { await withTransaction(t, async (transaction) => { const publicRole = await createRole('Public', transaction); const internalRole = await createRole('Tour Designer', transaction); const permission = await createPermission( `TEST_READ_USERS_${suffix}`, transaction, ); await publicRole.setPermissions([permission], { transaction }); const publicUser = await createUser( { email: `public-stale-${suffix}@example.test`, role: publicRole, }, transaction, ); await publicUser.setCustom_permissions([permission], { transaction }); const internalUser = await createUser( { email: `internal-stale-grant-${suffix}@example.test`, role: internalRole, }, transaction, ); const privateProject = await createProject( { slug: `test-private-stale-grant-${suffix}`, visibility: 'private', }, transaction, ); await db.production_presentation_access.create( { userId: internalUser.id, projectId: privateProject.id, }, { transaction }, ); const report = await AccessPolicyAuditService.findViolations({ transaction, }); assert.ok(report.publicRolePermissions.length >= 1); assert.ok(report.publicUsersWithCustomPermissions.length >= 1); assert.ok(report.productionPresentationAccessForNonPublicUsers.length >= 1); const cleanup = await AccessPolicyAuditService.cleanupViolations({ transaction, }); assert.ok(cleanup.removedPublicRolePermissions >= 1); assert.ok(cleanup.clearedPublicUserCustomPermissions >= 1); assert.ok(cleanup.removedNonPublicProductionPresentationGrants >= 1); const after = await AccessPolicyAuditService.findViolations({ transaction, }); assert.equal(AccessPolicyAuditService.hasViolations(after), false); }); });