diff --git a/packages/access-api/src/service/index.js b/packages/access-api/src/service/index.js index caf897250..debf9e5e1 100644 --- a/packages/access-api/src/service/index.js +++ b/packages/access-api/src/service/index.js @@ -24,12 +24,6 @@ import { providerAddProvider } from './provider-add.js' * } */ export function service(ctx) { - /** - * @param {Ucanto.DID<'key'>} uri - */ - const hasStorageProvider = async (uri) => { - return Boolean(await ctx.models.spaces.get(uri)) - } return { store: uploadApi.createStoreProxy(ctx), upload: uploadApi.createUploadProxy(ctx), @@ -53,7 +47,19 @@ export function service(ctx) { } return accessDelegateProvider({ delegations: ctx.models.delegations, - hasStorageProvider, + hasStorageProvider: async (space) => { + /** @type {import('./access-delegate.js').HasStorageProvider} */ + const registeredViaVoucherRedeem = async (space) => + Boolean(await ctx.models.spaces.get(space)) + /** @type {import('./access-delegate.js').HasStorageProvider} */ + // eslint-disable-next-line unicorn/consistent-function-scoping + const registeredViaProviderAdd = (space) => + ctx.models.provisions.hasStorageProvider(space) + return Boolean( + (await registeredViaProviderAdd(space)) || + (await registeredViaVoucherRedeem(space)) + ) + }, })(...args) }, }, diff --git a/packages/access-api/test/provider-add.test.js b/packages/access-api/test/provider-add.test.js index 5ecf3fc97..aa25064b3 100644 --- a/packages/access-api/test/provider-add.test.js +++ b/packages/access-api/test/provider-add.test.js @@ -64,9 +64,11 @@ for (const accessApiVariant of /** @type {const} */ ([ /** @type {{to:string, url:string}[]} */ const emails = [] const email = createEmail(emails) + const features = new Set(['provider/add', 'access/delegate']) return { spaceWithStorageProvider, emails, + features, ...createTesterFromContext( () => context({ @@ -97,6 +99,94 @@ for (const accessApiVariant of /** @type {const} */ ([ }) }) }) + + if ( + ['provider/add', 'access/delegate'].every((f) => + accessApiVariant.features.has(f) + ) + ) { + it('provider/add allows for access/delegate', async () => { + const space = await principal.ed25519.generate() + const agent = await accessApiVariant.issuer + const service = await accessApiVariant.audience + const accountDid = /** @type {const} */ ('did:mailto:example.com:foo') + + const accountAuthorizesAgentClaim = await ucanto.delegate({ + issuer: principal.Absentee.from({ id: accountDid }), + audience: agent, + capabilities: [ + { + with: 'ucan:*', + can: '*', + }, + ], + }) + const serviceAttestsThatAccountAuthorizesAgent = await ucanto.delegate({ + issuer: service, + audience: agent, + capabilities: [ + { + with: service.did(), + can: 'ucan/attest', + nb: { proof: accountAuthorizesAgentClaim.cid }, + }, + ], + }) + const sessionProofs = [ + accountAuthorizesAgentClaim, + serviceAttestsThatAccountAuthorizesAgent, + ] + const addStorageProvider = await ucanto + .invoke({ + issuer: agent, + audience: service, + capability: { + can: 'provider/add', + with: accountDid, + nb: { + provider: 'did:web:web3.storage:providers:w3up-alpha', + consumer: space.did(), + }, + }, + proofs: [...sessionProofs], + }) + .delegate() + const addStorageProviderResult = await accessApiVariant.invoke( + addStorageProvider + ) + assertNotError(addStorageProviderResult) + + // storage provider added. So we should be able to delegate now + const accessDelegate = await ucanto + .invoke({ + issuer: agent, + audience: service, + capability: { + can: 'access/delegate', + with: space.did(), + nb: { + delegations: {}, + }, + }, + proofs: [ + // space says agent can access/delegate with space + await ucanto.delegate({ + issuer: space, + audience: agent, + capabilities: [ + { + can: 'access/delegate', + with: space.did(), + }, + ], + }), + ], + }) + .delegate() + const accessDelegateResult = await accessApiVariant.invoke(accessDelegate) + assertNotError(accessDelegateResult) + }) + } } /**