mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 04:13:17 +08:00
Add manual IM bot credential access
This commit is contained in:
parent
d5d2c18ce0
commit
7123fce47a
42 changed files with 2458 additions and 889 deletions
|
|
@ -19,6 +19,7 @@ test('client exposes the fixed DingTalk RPC channel and endpoint names', () => {
|
|||
beginProvisioning: 'provision.begin',
|
||||
pollProvisioning: 'provision.poll',
|
||||
cancelProvisioning: 'provision.cancel',
|
||||
bindCredentials: 'bot.bind-credentials',
|
||||
reconnectBot: 'bot.reconnect',
|
||||
deleteBot: 'bot.delete',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -148,6 +148,30 @@ test('successful QR poll stores secret then config then starts runtime without p
|
|||
await controller.close();
|
||||
});
|
||||
|
||||
test('manual DingTalk Client ID and Client Secret binding stores credentials off the public plane', async () => {
|
||||
const events = [];
|
||||
const credentials = credentialsFixture(events);
|
||||
const configs = configFixture([], events);
|
||||
const runtimes = runtimeFactory({ events });
|
||||
const controller = new DingtalkController({
|
||||
deviceAuth: successfulDeviceAuth(),
|
||||
credentials: credentials.provider,
|
||||
configStore: configs.store,
|
||||
createRuntime: runtimes.createRuntime,
|
||||
clock: () => 1_000,
|
||||
});
|
||||
|
||||
const status = await controller.bindCredentials({
|
||||
clientId: 'manual-ding-client',
|
||||
clientSecret: 'manual-ding-secret',
|
||||
});
|
||||
assert.equal(status.totals.connected, 1);
|
||||
assert.equal([...credentials.values.values()][0], 'manual-ding-secret');
|
||||
assert.equal(runtimes.runtimes[0].clientSecret, 'manual-ding-secret');
|
||||
assert.doesNotMatch(JSON.stringify(status), /manual-ding-client|manual-ding-secret|secretRef/);
|
||||
await controller.close();
|
||||
});
|
||||
|
||||
test('scanning the same client ID is idempotent and replaces its one runtime', async () => {
|
||||
const events = [];
|
||||
const credentials = credentialsFixture(events);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ function controller() {
|
|||
startProvisioning() {},
|
||||
registrationStatus() {},
|
||||
cancelProvisioning() {},
|
||||
bindCredentials() {},
|
||||
reconnectBot() {},
|
||||
deleteBot() {},
|
||||
approveSender() {},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ function controller(overrides = {}) {
|
|||
}),
|
||||
registrationStatus: async () => ({ attemptId: 'attempt_1', status: 'pending' }),
|
||||
cancelProvisioning: async () => ({ attemptId: 'attempt_1', status: 'cancelled' }),
|
||||
bindCredentials: async () => ({ bots: [] }),
|
||||
reconnectBot: async () => ({ bots: [] }),
|
||||
deleteBot: async () => ({ bots: [] }),
|
||||
approveSender: async () => ({ bots: [] }),
|
||||
|
|
@ -76,6 +77,24 @@ test('RPC validates mutating requests before invoking the controller', async ()
|
|||
assert.equal(calls, 0);
|
||||
});
|
||||
|
||||
test('credential RPC accepts Client ID fields while keeping Client Secret host-only', async () => {
|
||||
let received;
|
||||
const handler = createDingtalkRpcHandler(controller({
|
||||
bindCredentials: async (payload) => {
|
||||
received = payload;
|
||||
return { bots: [], clientSecret: payload.clientSecret };
|
||||
},
|
||||
}));
|
||||
|
||||
const result = await handler(DINGTALK_ENDPOINTS.bindCredentials, {
|
||||
clientId: 'manual-client', clientSecret: 'manual-secret',
|
||||
});
|
||||
assert.equal(result.ok, true);
|
||||
assert.deepEqual(received, { clientId: 'manual-client', clientSecret: 'manual-secret' });
|
||||
assert.doesNotMatch(JSON.stringify(result), /manual-secret|clientSecret/);
|
||||
assert.equal((await handler(DINGTALK_ENDPOINTS.bindCredentials, { clientId: 'manual-client' })).ok, false);
|
||||
});
|
||||
|
||||
test('RPC is registered for loopback clients only', () => {
|
||||
const registrations = [];
|
||||
const dispose = () => {};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue