mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:17:47 +08:00
143 lines
4.6 KiB
JavaScript
143 lines
4.6 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
const {
|
|
WorkerCredentialMutationConflictError,
|
|
} = require('@qinglong/runtime-core/worker-credential');
|
|
const {
|
|
workerCredentialSecretDigest,
|
|
} = require('@qinglong/runtime-core/worker-credential-token');
|
|
const {
|
|
createWorkerCredentialAdministrationService,
|
|
} = require('../dist/worker-credential/workerCredentialAdministration');
|
|
|
|
const NOW = 1_000;
|
|
const PEPPER = Buffer.alloc(32, 1).toString('base64url');
|
|
const PRINCIPAL = {
|
|
subject: { type: 'user', id: 'usr_admin' },
|
|
authenticationId: 'session:admin:1',
|
|
authenticatedAtMs: 900,
|
|
expiresAtMs: 2_000,
|
|
assurance: 'multi_factor',
|
|
};
|
|
|
|
function request(overrides = {}) {
|
|
return {
|
|
mutationId: '123e4567-e89b-42d3-a456-426614174501',
|
|
requestId: 'request-worker-issue-1',
|
|
expectedCurrentVersion: 0,
|
|
credentialId: 'worker_primary',
|
|
workerId: 'edge-router-1',
|
|
principal: PRINCIPAL,
|
|
notBeforeAtMs: NOW,
|
|
expiresAtMs: 2_000,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function repository() {
|
|
let mutation = null;
|
|
const commands = [];
|
|
return {
|
|
commands,
|
|
port: {
|
|
async resolveMutation() { return mutation; },
|
|
async append(command) {
|
|
commands.push(command);
|
|
mutation = {
|
|
credential: command.credential,
|
|
mutation: command.mutation,
|
|
audit: command.audit,
|
|
};
|
|
return {
|
|
status: 'created',
|
|
credential: command.credential,
|
|
mutation: command.mutation,
|
|
};
|
|
},
|
|
},
|
|
setMutation(value) { mutation = value; },
|
|
};
|
|
}
|
|
|
|
test('issues a one-time ql3w token and stores only its Worker-domain digest', async () => {
|
|
const store = repository();
|
|
const generated = Buffer.alloc(32, 7);
|
|
const secret = generated.toString('base64url');
|
|
const service = createWorkerCredentialAdministrationService(
|
|
store.port,
|
|
PEPPER,
|
|
{ now: () => NOW, randomBytes: () => generated },
|
|
);
|
|
const result = await service.issue(request());
|
|
assert.equal(result.status, 'created');
|
|
assert.equal(result.token, `ql3w_worker_primary_${secret}`);
|
|
assert.equal(
|
|
store.commands[0].credential.secretDigest,
|
|
workerCredentialSecretDigest(PEPPER, 'worker_primary', secret),
|
|
);
|
|
assert.equal(store.commands[0].credential.workerId, 'edge-router-1');
|
|
assert.equal(store.commands[0].audit.operationId, 'worker_credential.issue');
|
|
assert.equal(generated.every((byte) => byte === 0), true);
|
|
});
|
|
|
|
test('accepts an approved activation time before delayed execution', async () => {
|
|
const store = repository();
|
|
const service = createWorkerCredentialAdministrationService(
|
|
store.port,
|
|
PEPPER,
|
|
{ now: () => 1_500, randomBytes: () => Buffer.alloc(32, 7) },
|
|
);
|
|
const result = await service.issue(request());
|
|
assert.equal(result.status, 'created');
|
|
assert.equal(result.credential.createdAtMs, 1_500);
|
|
assert.equal(result.credential.notBeforeAtMs, NOW);
|
|
assert.equal(result.credential.expiresAtMs, 2_000);
|
|
});
|
|
|
|
test('semantic replay returns no secret and conflicting replay fails closed', async () => {
|
|
const store = repository();
|
|
let randomCalls = 0;
|
|
const service = createWorkerCredentialAdministrationService(
|
|
store.port,
|
|
PEPPER,
|
|
{
|
|
now: () => NOW,
|
|
randomBytes() { randomCalls += 1; return Buffer.alloc(32, randomCalls); },
|
|
},
|
|
);
|
|
assert.ok((await service.issue(request())).token);
|
|
const replay = await service.issue(request());
|
|
assert.equal(replay.status, 'existing');
|
|
assert.equal(replay.token, null);
|
|
assert.equal(randomCalls, 1);
|
|
await assert.rejects(
|
|
service.issue(request({ workerId: 'other-worker' })),
|
|
WorkerCredentialMutationConflictError,
|
|
);
|
|
});
|
|
|
|
test('rejects widened requests, weak principals and operation/version confusion', async () => {
|
|
const store = repository();
|
|
const service = createWorkerCredentialAdministrationService(
|
|
store.port,
|
|
PEPPER,
|
|
{ now: () => NOW, randomBytes: () => Buffer.alloc(32, 1) },
|
|
);
|
|
await assert.rejects(service.issue(request({ debug: true })), /shape is invalid/);
|
|
await assert.rejects(
|
|
service.issue(request({ principal: { ...PRINCIPAL, assurance: 'single_factor' } })),
|
|
/strong principal/,
|
|
);
|
|
await assert.rejects(
|
|
service.issue(request({ expectedCurrentVersion: 1 })),
|
|
/operation fence is invalid/,
|
|
);
|
|
const { notBeforeAtMs, expiresAtMs, ...revoke } = request({
|
|
mutationId: '123e4567-e89b-42d3-a456-426614174502',
|
|
});
|
|
assert.equal(notBeforeAtMs, NOW);
|
|
assert.equal(expiresAtMs, 2_000);
|
|
await assert.rejects(service.revoke(revoke), /operation fence is invalid/);
|
|
assert.equal(store.commands.length, 0);
|
|
});
|