forked from snxraven/peardock
Implement volume browse, stack env file, service scale, create secret/config, disconnect network, registry Hub search, server schedules, resource editor, and fleet environment tags. Mark roadmap Tracks A–F complete.
86 lines
2.9 KiB
JavaScript
86 lines
2.9 KiB
JavaScript
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))
|
|
})
|