From f552036913cf7172e93e83e27fd4af6f7b6a4673 Mon Sep 17 00:00:00 2001 From: Oli Evans Date: Wed, 29 Nov 2023 11:21:52 +0000 Subject: [PATCH] fix: sync space names from proofs (#1193) space names are stored as facts in proofs in the special `ucan:*` delegation from email to agent e.g. ```yaml iss: did:mailto:protocol.ai:oli att: - can: * with: ucan:* fct: - {"access/confirm":{"/":"bafyreidadrwbxueiuhtbph2tj2m7a6nizpx7sbtqotbfkkjsoytn3dg2t4"},"access/request":{"/":"bafyreigmlliovbpxka2borbdaw6zpsai4qzmzv5nv7syl7nagecxjcub6e"}} prf: - {"iss":"did:key:z6Mku56ywWspVnzkcVQbeSkw6egVRSNKVSYgS38HhKcUJRiN","aud":"did:mailto:protocol.ai:oli","v":"0.9.1","s":{"/":{"bytes":"7aEDQPgUVQFVOoLmijx8O7Li9zyc7iIHEDnCwiqr+LcV9CyHd/KzUg1Ige7Okh+ipVMOKy2EvG8dGjWfsdAAbUVPMwo"}},"exp":null,"att":[{"can":"*","with":"did:key:z6Mku56ywWspVnzkcVQbeSkw6egVRSNKVSYgS38HhKcUJRiN"}],"fct":[{"space":{"name":"fruits"}}],"prf":[],"/":"bafyreihhlz5yjci6h7vdrlgn5xnyc65e3unp3dx2yrspepwoftaxszxpxa"} ``` this will be fixed more robustly by the proposed re-imagining of the client as a stateless view over the stored ucans, but i'd like to land this fix asap, as it's currently you dont' get to see your existing space names when calling login in a new agent. fixes: https://github.com/web3-storage/w3up/issues/1176 License: MIT --------- Signed-off-by: Oli Evans --- packages/access-client/src/agent.js | 58 +++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/packages/access-client/src/agent.js b/packages/access-client/src/agent.js index 520b13e6f..793621ec6 100644 --- a/packages/access-client/src/agent.js +++ b/packages/access-client/src/agent.js @@ -7,7 +7,14 @@ import { attest } from '@web3-storage/capabilities/ucan' import * as Access from './access.js' import * as Space from './space.js' -import { invoke, delegate, DID, Delegation, Schema } from '@ucanto/core' +import { + invoke, + delegate, + DID, + Delegation, + Schema, + isDelegation, +} from '@ucanto/core' import { isExpired, isTooEarly, canDelegateCapability } from './delegations.js' import { AgentData, getSessionProofs } from './agent-data.js' import { UCAN } from '@web3-storage/capabilities' @@ -621,24 +628,45 @@ export async function addSpacesFromDelegations(agent, delegations) { }) } - for (const delegation of delegations) { - // We only consider delegations to this agent as those are only spaces that - // this agent will be able to interact with. - if (delegation.audience.did() === agent.did()) { - // TODO: we need a more robust way to determine which spaces a user has access to - // it may or may not involve look at delegations - const allows = ucanto.Delegation.allows(delegation) - - for (const [did, value] of Object.entries(allows)) { - // If we discovered a delegation to any DID, we add it to the spaces list. - if (did.startsWith('did:key') && Object.keys(value).length > 0) { - await data.addSpace(/** @type {API.DID} */ (did), { - name: '', - }) + // spaces we find along the way. + const spaces = new Map() + // only consider ucans with this agent as the audience + const ours = delegations.filter((x) => x.audience.did() === agent.did()) + // space names are stored as facts in proofs in the special `ucan:*` delegation from email to agent. + const ucanStars = ours.filter( + (x) => x.capabilities[0].can === '*' && x.capabilities[0].with === 'ucan:*' + ) + for (const delegation of ucanStars) { + for (const proof of delegation.proofs) { + if ( + !isDelegation(proof) || + !proof.capabilities[0].with.startsWith('did:key') + ) { + continue + } + const space = Space.fromDelegation(proof) + spaces.set(space.did(), space.meta) + } + } + + // Find any other spaces the user may have access to + for (const delegation of ours) { + // TODO: we need a more robust way to determine which spaces a user has access to + // it may or may not involve look at delegations + const allows = ucanto.Delegation.allows(delegation) + for (const [resource, value] of Object.entries(allows)) { + // If we discovered a delegation to any DID, we add it to the spaces list. + if (resource.startsWith('did:key') && Object.keys(value).length > 0) { + if (!spaces.has(resource)) { + spaces.set(resource, {}) } } } } + + for (const [did, meta] of spaces) { + await data.addSpace(did, meta) + } } /**