48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
/**
|
|
* canExecDecision: the action-level gate the WebSocket terminal path uses so it
|
|
* matches the REST exec endpoint. Free edition / auth-disabled / admin all pass;
|
|
* enterprise requires the env-scoped containers:exec permission. Deps are
|
|
* injected, so no shared modules are mocked (mock.module is process-global).
|
|
*/
|
|
import { describe, it, expect } from 'bun:test';
|
|
import { canExecDecision } from '../src/lib/server/ws-exec-core';
|
|
|
|
const auth = (over: Partial<{ isAdmin: boolean; authDisabled: boolean }> = {}) => ({
|
|
userId: 1,
|
|
username: 'u',
|
|
isAdmin: false,
|
|
authDisabled: false,
|
|
...over
|
|
});
|
|
|
|
const deps = (enterprise: boolean, containers: string[]) => ({
|
|
isEnterprise: async () => enterprise,
|
|
getPerms: async () => ({ containers })
|
|
});
|
|
|
|
describe('canExecDecision', () => {
|
|
it('auth-disabled (bootstrap) -> allow', async () => {
|
|
expect(await canExecDecision(auth({ authDisabled: true }), 1, deps(true, []))).toBe(true);
|
|
});
|
|
|
|
it('admin -> allow', async () => {
|
|
expect(await canExecDecision(auth({ isAdmin: true }), 1, deps(true, []))).toBe(true);
|
|
});
|
|
|
|
it('free edition -> allow for any authed user', async () => {
|
|
expect(await canExecDecision(auth(), 1, deps(false, []))).toBe(true);
|
|
});
|
|
|
|
it('enterprise without the exec permission -> deny', async () => {
|
|
expect(await canExecDecision(auth(), 1, deps(true, ['view', 'logs']))).toBe(false);
|
|
});
|
|
|
|
it('enterprise with the exec permission -> allow', async () => {
|
|
expect(await canExecDecision(auth(), 1, deps(true, ['view', 'logs', 'exec']))).toBe(true);
|
|
});
|
|
|
|
it('enterprise, no envId -> deny (cannot resolve env-scoped perm)', async () => {
|
|
expect(await canExecDecision(auth(), undefined, deps(true, ['exec']))).toBe(false);
|
|
});
|
|
});
|