import test from 'brittle' import { Roles, roleAllows, MethodRoles, Methods, PROTOCOL_VERSION } from '../shared/protocol.js' import { resolveRole, assertAllowed } from '../server/core/acl.js' test('PROTOCOL_VERSION is frozen semver integer', (t) => { t.is(typeof PROTOCOL_VERSION, 'number') t.ok(PROTOCOL_VERSION >= 2) }) test('viewer can list but not remove', (t) => { t.ok(roleAllows(Roles.viewer, Methods.listContainers)) t.ok(roleAllows(Roles.viewer, Methods.ping)) t.ok(roleAllows(Roles.viewer, Methods.containerTop)) t.not(roleAllows(Roles.viewer, Methods.removeContainer)) t.not(roleAllows(Roles.viewer, Methods.pruneContainers)) t.not(roleAllows(Roles.viewer, Methods.killContainer)) }) test('operator can kill but not prune', (t) => { t.ok(roleAllows(Roles.operator, Methods.killContainer)) t.ok(roleAllows(Roles.operator, Methods.startContainer)) t.ok(roleAllows(Roles.operator, Methods.pullImage)) t.not(roleAllows(Roles.operator, Methods.pruneContainers)) t.not(roleAllows(Roles.operator, Methods.removeImage)) }) test('admin can everything mapped', (t) => { for (const method of Object.values(Methods)) { t.ok(roleAllows(Roles.admin, method), method) } }) test('unknown methods require admin', (t) => { t.not(roleAllows(Roles.viewer, 'secretBackdoor')) t.ok(roleAllows(Roles.admin, 'secretBackdoor')) }) test('MethodRoles covers core Methods', (t) => { const required = [ Methods.listContainers, Methods.killContainer, Methods.containerTop, Methods.updateContainer, Methods.pruneImages, Methods.getSystemDf, Methods.handshake, ] for (const m of required) { t.ok(MethodRoles[m], `MethodRoles missing ${m}`) } }) test('assertAllowed throws PERMISSION_DENIED', (t) => { try { assertAllowed(Roles.viewer, Methods.removeContainer) t.fail('should throw') } catch (err) { t.is(err.code, 'PERMISSION_DENIED') } }) test('resolveRole defaults to admin without env policy', (t) => { // Without PEARDOCK_ADMIN_KEYS / DEFAULT_ROLE override in this process const role = resolveRole('abc123') t.ok([Roles.admin, Roles.operator, Roles.viewer].includes(role)) }) test('every Methods entry has MethodRoles mapping', (t) => { for (const method of Object.values(Methods)) { t.ok(MethodRoles[method], `missing MethodRoles for ${method}`) } }) test('viewer can search images (read-only)', (t) => { t.ok(roleAllows(Roles.viewer, Methods.searchImages)) t.ok(roleAllows(Roles.admin, Methods.recreateContainer)) t.absent(roleAllows(Roles.operator, Methods.recreateContainer)) t.ok(roleAllows(Roles.admin, Methods.systemPrune)) t.absent(roleAllows(Roles.operator, Methods.systemPrune)) t.ok(roleAllows(Roles.viewer, Methods.browseVolume)) t.ok(roleAllows(Roles.viewer, Methods.listSchedules)) t.ok(roleAllows(Roles.admin, Methods.upsertSchedule)) t.ok(roleAllows(Roles.operator, Methods.scaleService)) t.ok(roleAllows(Roles.operator, Methods.updateContainer)) })