fix(uds-auth): emergency login workspaces, Desktop key prompts, persistent sessions
Some checks are pending
ci / test (push) Waiting to run
ci / test-postgresql (push) Waiting to run

Emergency (fallback / sealed-box) admins lost the workspace sidebar and saw
"sign-in failed / add API key" on DSH Desktop because the remote.mux opens
anonymously at boot and every early call was rejected terminally.

- gateway: anonymous workspace/follow reaches dsh-acl's empty baseline;
  anonymous streams are parked until the carrier aborts instead of failing;
  anonymous workspace/initializeDefault answers "nothing created"
- request-auth: a dead UDS_SESSION cookie no longer shadows a valid WS ticket
  or bridge (sync + async variants used by all callers)
- dsh-acl: /api/* waits (bounded 15s) for the gateway ACL during Host startup
  instead of 403, so the Desktop welcome read no longer fails
- desktop-bootstrap: key/account projection is best-effort per reference
- client: emit connection/reset after login so boot-time caches (settings
  describe mirror -> Settings > Models) re-read under the real principal
- sessions: absolute lifetime fixed at 7 days (was min(ttl, 7d) = 30 min);
  bridge expiry slides with its session; MemoryStore persisted to
  sessions.json (bearer hash only, userData AES-256-GCM sealed)
- README: session lifetime/persistence and DSH compatibility notes

Also includes previously uncommitted uds-auth 0.3.x work in this tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
oliver 2026-10-12 02:11:09 +08:00
parent fa4d5d6d97
commit df97c5e042
72 changed files with 8436 additions and 1192 deletions

View file

@ -0,0 +1,111 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { createDesktopBootstrap, DESKTOP_BOOTSTRAP_REF } from '../lib/desktop-bootstrap.js'
import { installGatewayPolicy } from '../lib/gateway-policy.js'
function fixture() {
let configured = true, invoked = 0
const services = {
settings: { describe: () => [
{ ns: 'locale', value: { preference: 'zh', hidden: 'LOCALE-SECRET' } },
{ ns: 'llm-deepseek', value: { apiKeyEnv: 'REAL_KEY_NAME', endpoint: 'PRIVATE-ENDPOINT' } },
{ ns: 'uds-auth', value: { initialAdminEmpNo: 'PRIVATE-ADMIN' } },
] },
llm: { listConfigurableProviders: () => [] },
credentials: { describe: async ref => { assert.equal(ref, 'REAL_KEY_NAME'); return { configured, writable: true, value: 'RAW-KEY' } } },
deepseekAccount: { getState: async () => ({ status: 'credential-stored', attempt: { authorizeUrl: 'SECRET-LOGIN-LINK' }, token: 'ACCOUNT-TOKEN' }) },
}
const deny = code => { throw new Error(code) }
const project = createDesktopBootstrap({ getService: name => services[name], deny })
let principal = null, admin = false, live = true
const gateway = {
async *openRemoteEvents() {}, receiveRemoteEventResult() {},
resolveDescriptor: (namespace, method) => ({ namespace, method, parameters: [] }),
prepareInvocation: async () => ({ method: () => { invoked++; return 'full-admin-result' } }),
}
installGatewayPolicy(gateway, { identity: () => principal, assertPrincipal: p => { if (!p || !live) deny('login_required') },
assertSession: () => {}, permissions: () => ({ canAccessSettings: admin }), deny, desktopBootstrap: project })
const invoke = async (namespace, method, args = {}) => (await gateway.prepareInvocation({ namespace, method, args })).method()
return { invoke, services, setConfigured: value => { configured = value },
setPrincipal: value => { principal = value }, setAdmin: value => { admin = value },
revoke: () => { live = false }, invocations: () => invoked }
}
test('unmodified Desktop welcome reads a bounded projection before UDS login', async () => {
const f = fixture()
const settings = await f.invoke('settings', 'describe')
assert.equal(settings.writable, false); assert.equal(settings.hasDocument, false)
assert.equal(settings.namespaces.find(row => row.ns === 'locale').value.preference, 'zh')
const providers = await f.invoke('llm', 'listConfigurableProviders')
const refs = providers.map(provider => settings.namespaces.find(row => row.ns === provider.settingsNs).value.apiKeyEnv)
const credentials = await f.invoke('credentials', 'describe', { refs })
assert.deepEqual(credentials, { [DESKTOP_BOOTSTRAP_REF]: { configured: true, writable: false } })
const account = await f.invoke('account', 'getState')
assert.equal(account.status, 'credential-stored'); assert.equal(account.attempt, null)
assert.equal(f.invocations(), 0, 'anonymous calls never invoke full Remote controllers')
const data = JSON.stringify([settings, providers, credentials, account])
for (const secret of ['REAL_KEY_NAME', 'PRIVATE-ENDPOINT', 'PRIVATE-ADMIN', 'RAW-KEY', 'ACCOUNT-TOKEN', 'SECRET-LOGIN-LINK', 'LOCALE-SECRET']) assert.equal(data.includes(secret), false)
f.setConfigured(false)
assert.equal((await f.invoke('credentials', 'describe', { refs }))[DESKTOP_BOOTSTRAP_REF].configured, false)
})
test('bootstrap cannot enumerate credential refs or access settings writes and sessions', async () => {
const f = fixture()
for (const args of [{ refs: ['REAL_KEY_NAME'] }, { refs: [DESKTOP_BOOTSTRAP_REF, 'OTHER'] }, {}, { refs: 'REAL_KEY_NAME' }]) {
await assert.rejects(f.invoke('credentials', 'describe', args), /forbidden_settings/)
}
for (const [ns, method] of [['settings', 'update'], ['credentials', 'set'], ['account', 'startSignIn'], ['session', 'list'], ['newPlugin', 'describe']]) {
await assert.rejects(f.invoke(ns, method), /login_required/)
}
assert.equal(f.invocations(), 0)
})
test('admin and trusted Host retain full reads; ordinary or revoked principals never receive them', async () => {
const f = fixture()
f.setPrincipal({ empNo: 'u' })
assert.equal((await f.invoke('settings', 'describe')).writable, false)
f.setAdmin(true)
assert.equal(await f.invoke('settings', 'describe'), 'full-admin-result')
f.setAdmin(false)
assert.equal((await f.invoke('settings', 'describe')).writable, false)
f.revoke()
await assert.rejects(f.invoke('settings', 'describe'), /login_required/)
f.setPrincipal(undefined)
assert.equal(await f.invoke('settings', 'describe'), 'full-admin-result')
})
test('custom providers are detected without exposing their namespace or credential names', async () => {
const f = fixture()
f.services.settings.describe = () => [{ ns: 'private-provider', value: { profiles: { one: { apiKeyEnv: 'REAL_KEY_NAME' } } } }]
f.services.llm.listConfigurableProviders = () => [{ settingsNs: 'private-provider', settingsPath: ['profiles', 'one'] }]
const value = await f.invoke('credentials', 'describe', { refs: [DESKTOP_BOOTSTRAP_REF] })
assert.equal(value[DESKTOP_BOOTSTRAP_REF].configured, true)
assert.equal(JSON.stringify(await f.invoke('settings', 'describe')).includes('private-provider'), false)
})
test('projection still reports a stored key when another provider or reference throws', async () => {
const deny = code => { throw new Error(code) }
const services = {
settings: { describe: () => [
{ ns: 'locale', value: {} },
{ ns: 'llm-deepseek', value: { apiKeyEnv: 'BROKEN_REF' } },
{ ns: 'llm-pi-ai', value: { providers: { suanlidao: { apiKeyEnv: 'SUANLIDAO_API_KEY' } } } },
] },
llm: { listConfigurableProviders: () => [
{ provider: 'suanlidao', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'suanlidao'] },
] },
credentials: { describe: async ref => {
if (ref === 'BROKEN_REF') throw new Error('provider exploded')
return { configured: ref === 'SUANLIDAO_API_KEY', writable: true }
} },
deepseekAccount: { getState: async () => { throw new Error('account backend down') } },
}
const project = createDesktopBootstrap({ getService: name => services[name], deny })
const described = await project({ namespace: 'credentials', method: 'describe', args: { refs: [DESKTOP_BOOTSTRAP_REF] } })
assert.equal(described[DESKTOP_BOOTSTRAP_REF].configured, true)
const account = await project({ namespace: 'account', method: 'getState', args: {} })
assert.equal(account.status, 'signed-out')
services.settings.describe = () => { throw new Error('settings not mounted') }
const none = await project({ namespace: 'credentials', method: 'describe', args: { refs: [DESKTOP_BOOTSTRAP_REF] } })
assert.equal(none[DESKTOP_BOOTSTRAP_REF].configured, false)
})