mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-15 05:10:44 +08:00
fix(uds-auth): explain why skills get no credentials for a session (0.3.18)
Skill credentials follow the DSH session owner, so a chat created under the emergency account stays owned by administrator after a QR scan and skills cannot get a UAC token there. The generic "scan QR first" error hid this. - Return session_owner_no_uac when the owner is administrator / a local account, telling the user to start a new session after QR sign-in. - Name the owner in no_skill_credentials for UAC owners. - Audit denied outbound calls with session id and owner. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e61f9c6336
commit
f0b4b6cd74
5 changed files with 82 additions and 10 deletions
53
uds-auth/test/agent-auth-owner.test.js
Normal file
53
uds-auth/test/agent-auth-owner.test.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { createAgentAuthHandlers } from '../lib/agent-auth.js'
|
||||
|
||||
const makeRes = () => ({
|
||||
statusCode: 0, headers: {}, body: '',
|
||||
setHeader(k, v) { this.headers[k] = v },
|
||||
end(s) { this.body = s },
|
||||
})
|
||||
const loopbackReq = (payload) => ({
|
||||
method: 'POST',
|
||||
socket: { remoteAddress: '127.0.0.1' },
|
||||
headers: { 'content-type': 'application/json', 'x-dsh-session-id': 's1' },
|
||||
async *[Symbol.asyncIterator]() { yield Buffer.from(JSON.stringify(payload)) },
|
||||
})
|
||||
|
||||
function handlersFor(owner, creds) {
|
||||
return createAgentAuthHandlers({
|
||||
resolveCredentialsForSession: async () => creds,
|
||||
getSessionOwner: () => owner,
|
||||
resolveCallerSession: async () => ({ empNo: owner }),
|
||||
outboundHosts: ['icenterapi.zte.com.cn'],
|
||||
})
|
||||
}
|
||||
|
||||
test('skill outbound on an emergency-owned session names the owner and the way out', async () => {
|
||||
const res = makeRes()
|
||||
await handlersFor('administrator', null).handleOutbound(
|
||||
loopbackReq({ url: 'https://icenterapi.zte.com.cn/x' }), res)
|
||||
assert.equal(res.statusCode, 401)
|
||||
const body = JSON.parse(res.body)
|
||||
assert.equal(body.error, 'session_owner_no_uac')
|
||||
assert.match(body.message, /administrator/)
|
||||
assert.doesNotMatch(body.message, /\{owner\}/)
|
||||
})
|
||||
|
||||
test('skill outbound for a UAC owner with no cached token keeps no_skill_credentials, with owner', async () => {
|
||||
const res = makeRes()
|
||||
await handlersFor('10329667', null).handleOutbound(
|
||||
loopbackReq({ url: 'https://icenterapi.zte.com.cn/x' }), res)
|
||||
assert.equal(res.statusCode, 401)
|
||||
const body = JSON.parse(res.body)
|
||||
assert.equal(body.error, 'no_skill_credentials')
|
||||
assert.match(body.message, /10329667/)
|
||||
})
|
||||
|
||||
test('credentials never cross owners: another account\'s token is refused', async () => {
|
||||
const res = makeRes()
|
||||
await handlersFor('10329667', { empNo: '99999999', token: 't' }).handleOutbound(
|
||||
loopbackReq({ url: 'https://icenterapi.zte.com.cn/x' }), res)
|
||||
assert.equal(res.statusCode, 401)
|
||||
assert.equal(JSON.parse(res.body).error, 'no_skill_credentials')
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue