feat(bus,webui): bus read of credential status; the Console shows it (#1549, row 60)
The credentials read returns service, account, role, date kind, date and state for the capability's business, to human and reader sessions only. It never returns a token, file path or variable name. Author Filbert, reviewers Dewey (27558) and Darkwing (27575), candidate manifest 13541657. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -126,15 +126,38 @@ test('decisions route serves the roles and recent lists from the reader view', a
|
||||
assert.equal(calls.length, before);
|
||||
});
|
||||
|
||||
// Row 60 (#1549): what the bus host loaded, metadata only, for the reader's business.
|
||||
test('credentials route serves the broker credential status from the reader view', async t => {
|
||||
const store = new Store(tmp(t, 'webui-bus-credentials-'));
|
||||
t.after(() => store.close());
|
||||
const status = [
|
||||
{ instance: 'demo/coder', service: 'gitea', date: '2000-01-01', state: 'rotation-due' },
|
||||
{ instance: 'other/coder', service: 'gitea', date: '2099-01-01', state: 'valid' },
|
||||
];
|
||||
const b = new Broker({ store, businesses, credentialStatus: () => status });
|
||||
const reader = b.bindReader({ business: 'demo' });
|
||||
const calls = [];
|
||||
const bus = views({ call: async (verb, args = {}) => { calls.push([verb, args]); return structuredClone(b.request(reader, { verb, args })); } });
|
||||
const web = await startServer({ port: 0, board: 'http://127.0.0.1:9', bus });
|
||||
t.after(() => close(web));
|
||||
const r = await fetch(`http://127.0.0.1:${web.address().port}/api/bus/credentials`);
|
||||
assert.equal(r.status, 200); assert.equal(r.headers.get('cache-control'), 'no-store');
|
||||
const body = await r.json();
|
||||
assert.match(body.at, /^\d{4}-\d\d-\d\dT/);
|
||||
assert.deepEqual(body.rows, [{ service: 'gitea', account: null, role: 'coder', dateKind: 'rotateBy', date: '2000-01-01', state: 'rotation-due' }]);
|
||||
assert.deepEqual(calls, [['credentials', {}]]);
|
||||
});
|
||||
|
||||
test('bus routes are GET-only, have no write verb, and keep the same-origin checks', async t => {
|
||||
const calls = [];
|
||||
const bus = Object.fromEntries(['inbox', 'tasks', 'agents', 'trail', 'decisions'].map(v => [v, async () => { calls.push(v); return []; }]));
|
||||
const bus = Object.fromEntries(['inbox', 'tasks', 'agents', 'trail', 'decisions', 'credentials'].map(v => [v, async () => { calls.push(v); return []; }]));
|
||||
const web = await startServer({ port: 0, board: 'http://127.0.0.1:9', bus });
|
||||
t.after(() => close(web));
|
||||
const base = `http://127.0.0.1:${web.address().port}`;
|
||||
for (const method of ['POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS']) {
|
||||
assert.equal((await fetch(base + '/api/bus/inbox', { method })).status, 405, method);
|
||||
assert.equal((await fetch(base + '/api/bus/decisions?list=roles', { method })).status, 405, method);
|
||||
assert.equal((await fetch(base + '/api/bus/credentials', { method })).status, 405, method);
|
||||
}
|
||||
for (const path of ['/api/bus/decide', '/api/bus/seen', '/api/bus/', '/api/bus/inbox/x', '/api/bus/request', '/api/bus/raise']) {
|
||||
assert.equal((await fetch(base + path)).status, 404, path);
|
||||
@@ -219,6 +242,7 @@ test('busReader takes --business, else the live bus host, else refuses', async t
|
||||
const pinned = busReader({ dataRoot, socket: 'sock', business: 'pinned', cli });
|
||||
assert.equal((await pinned.inbox()).request.business, 'pinned');
|
||||
assert.deepEqual((await pinned.decisions('recent')).request, { business: 'pinned', verb: 'decisions', args: { list: 'recent' } });
|
||||
assert.deepEqual((await pinned.credentials()).request, { business: 'pinned', verb: 'credentials', args: {} });
|
||||
|
||||
const reader = busReader({ dataRoot, socket: 'sock', cli });
|
||||
const code = () => reader.agents().then(() => 'resolved', e => e.code);
|
||||
|
||||
Reference in New Issue
Block a user