import { respecfully, ifWe, ifWeMight, iReckon } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs'; import { getIbGibGlobalThis_IbGib } from '../helpers.mjs'; import { getDeviceDelegateSecretKey, getDeviceDelegateSecret, setDeviceDelegateSecret, getOrCreateDeviceDelegateSecret, clearDeviceDelegateSecret, DEVICE_DELEGATE_SECRET_PREFIX, getDeviceAuthorDelegateSecretKey, getDeviceAuthorDelegateSecret, setDeviceAuthorDelegateSecret, clearDeviceAuthorDelegateSecret, DEVICE_AUTHOR_DELEGATE_SECRET_PREFIX, } from './device-delegate-helper.mjs'; const sir = `[${import.meta.url}]`; // Initialize global db & store names for tests const globalThisIbGib = getIbGibGlobalThis_IbGib(); globalThisIbGib.dbName_hack = 'space_gib_db'; globalThisIbGib.storeName_hack = 'space_gib_store'; // Mock storage substrate for Node test environment const inMemoryStore = new Map(); if (typeof globalThis.indexedDB === 'undefined') { (globalThis as any).indexedDB = { open: (dbName: string) => { const req: any = {}; setTimeout(() => { const db = { objectStoreNames: { contains: () => true }, transaction: (storeName: string) => ({ objectStore: (sName: string) => ({ get: (key: string) => { const getReq: any = {}; setTimeout(() => { const val = inMemoryStore.get(key); getReq.result = val !== undefined ? { value: val } : undefined; if (getReq.onsuccess) getReq.onsuccess({ target: getReq }); }, 0); return getReq; }, put: (datum: any) => { const putReq: any = {}; setTimeout(() => { inMemoryStore.set(datum.key, datum.value); if (putReq.onsuccess) putReq.onsuccess({ target: putReq }); }, 0); return putReq; }, delete: (key: string) => { const delReq: any = {}; setTimeout(() => { inMemoryStore.delete(key); if (delReq.onsuccess) delReq.onsuccess({ target: delReq }); }, 0); return delReq; } }) }), close: () => {} }; req.result = db; if (req.onsuccess) req.onsuccess({ target: req }); }, 0); return req; } }; } await respecfully(sir, 'Device Delegate Storage & Secret Lifecycle', async () => { await ifWe(sir, 'getDeviceDelegateSecretKey constructs deterministic storage key', async () => { const delegateTjpAddr = 'keystone^alice_delegate_tjp_12345'; const key = getDeviceDelegateSecretKey({ delegateTjpAddr }); iReckon(sir, key).willEqual(`${DEVICE_DELEGATE_SECRET_PREFIX}${delegateTjpAddr}`); }); await ifWe(sir, 'setDeviceDelegateSecret and getDeviceDelegateSecret persist and retrieve secret', async () => { const delegateTjpAddr = 'keystone^bob_delegate_tjp_999'; const secret = 'super-secret-512-token-abc'; await setDeviceDelegateSecret({ delegateTjpAddr, delegateSecret: secret }); const retrieved = await getDeviceDelegateSecret({ delegateTjpAddr }); iReckon(sir, retrieved).willEqual(secret); }); await ifWe(sir, 'getOrCreateDeviceDelegateSecret generates high entropy token on first call and caches it', async () => { const delegateTjpAddr = 'keystone^charlie_delegate_tjp_888'; const token1 = await getOrCreateDeviceDelegateSecret({ delegateTjpAddr, deviceTag: 'laptop' }); iReckon(sir, typeof token1 === 'string' && token1.length === 128).willEqual(true); const token2 = await getOrCreateDeviceDelegateSecret({ delegateTjpAddr, deviceTag: 'laptop' }); iReckon(sir, token1).willEqual(token2); }); await ifWe(sir, 'clearDeviceDelegateSecret removes stored secret', async () => { const delegateTjpAddr = 'keystone^david_delegate_tjp_777'; await getOrCreateDeviceDelegateSecret({ delegateTjpAddr }); const beforeClear = await getDeviceDelegateSecret({ delegateTjpAddr }); iReckon(sir, !!beforeClear).willEqual(true); await clearDeviceDelegateSecret({ delegateTjpAddr }); const afterClear = await getDeviceDelegateSecret({ delegateTjpAddr }); iReckon(sir, afterClear).willEqual(undefined); }); await ifWe(sir, 'getDeviceAuthorDelegateSecretKey constructs deterministic storage key', async () => { const delegateTjpAddr = 'keystone^alice_author_delegate_tjp_12345'; const key = getDeviceAuthorDelegateSecretKey({ delegateTjpAddr }); iReckon(sir, key).willEqual(`${DEVICE_AUTHOR_DELEGATE_SECRET_PREFIX}${delegateTjpAddr}`); }); await ifWe(sir, 'setDeviceAuthorDelegateSecret and getDeviceAuthorDelegateSecret persist and retrieve author secret', async () => { const delegateTjpAddr = 'keystone^bob_author_delegate_tjp_999'; const secret = 'super-secret-author-512-token-xyz'; await setDeviceAuthorDelegateSecret({ delegateTjpAddr, delegateSecret: secret }); const retrieved = await getDeviceAuthorDelegateSecret({ delegateTjpAddr }); iReckon(sir, retrieved).willEqual(secret); }); await ifWe(sir, 'getDeviceAuthorDelegateSecret falls back to generic secret if author secret not set', async () => { const delegateTjpAddr = 'keystone^fallback_delegate_tjp_456'; const secret = 'generic-secret-token-123'; await setDeviceDelegateSecret({ delegateTjpAddr, delegateSecret: secret }); const retrieved = await getDeviceAuthorDelegateSecret({ delegateTjpAddr }); iReckon(sir, retrieved).willEqual(secret); }); await ifWe(sir, 'clearDeviceAuthorDelegateSecret and clearDeviceDelegateSecret remove author secrets', async () => { const delegateTjpAddr = 'keystone^author_clear_tjp_777'; const secret = 'author-secret-token-999'; await setDeviceAuthorDelegateSecret({ delegateTjpAddr, delegateSecret: secret }); const before = await getDeviceAuthorDelegateSecret({ delegateTjpAddr }); iReckon(sir, before).willEqual(secret); await clearDeviceAuthorDelegateSecret({ delegateTjpAddr }); const after = await getDeviceAuthorDelegateSecret({ delegateTjpAddr }); iReckon(sir, after).willEqual(undefined); }); await ifWe(sir, 'fails fast when global db or store name is uninitialized', async () => { const delegateTjpAddr = 'keystone^failfast_delegate_tjp_000'; const savedDb = globalThisIbGib.dbName_hack; globalThisIbGib.dbName_hack = undefined as any; let threw = false; try { await getDeviceDelegateSecret({ delegateTjpAddr }); } catch (error) { threw = true; } finally { globalThisIbGib.dbName_hack = savedDb; } iReckon(sir, threw).willEqual(true); }); });