From dc0eb5198d1c76d3da66384c14e1a3259ddc2f53 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:31:07 -0800 Subject: [PATCH 1/5] access/delegate checks hasStorageProvider(space) in a way that provider/add allows access/delegate --- packages/access-api/src/service/index.js | 20 ++-- packages/access-api/test/provider-add.test.js | 96 +++++++++++++++++++ 2 files changed, 109 insertions(+), 7 deletions(-) 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..5f07c38d0 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,100 @@ 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 issuer = await accessApiVariant.issuer + const service = await accessApiVariant.audience + const accountDid = /** @type {const} */ ('did:mailto:example.com:foo') + const serviceSessionAttest = await ucanto.delegate({ + issuer: service, + audience: issuer, + capabilities: [ + { + can: 'provider/add', + with: accountDid, + }, + { + can: 'access/delegate', + with: space.did(), + }, + ], + }) + const sessionProofs = [ + await ucanto.delegate({ + issuer: service, + audience: issuer, + capabilities: [ + { + can: 'ucan/attest', + with: accountDid, + nb: { + // note: whole delegation is also included in 'proofs' + proof: serviceSessionAttest.cid, + }, + }, + ], + }), + serviceSessionAttest, + ] + const addStorageProvider = await ucanto + .invoke({ + issuer, + audience: service, + capability: { + can: 'provider/add', + with: accountDid, + nb: { + provider: 'did:web:web3.storage:providers:w3up-alpha', + consumer: space.did(), + }, + }, + proofs: [ + // space says issuer can provider/add with this account + await ucanto.delegate({ + issuer: space, + audience: issuer, + capabilities: [ + { + can: 'provider/add', + with: accountDid, + }, + ], + }), + ...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, + audience: service, + capability: { + can: 'access/delegate', + with: space.did(), + nb: { + delegations: {}, + }, + }, + proofs: [...sessionProofs], + }) + .delegate() + const accessDelegateResult = await accessApiVariant.invoke(accessDelegate) + assertNotError(accessDelegateResult) + }) + } } /** From 811fa09187a9adaac4fcbfe145442197fae60fb0 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:03:27 -0800 Subject: [PATCH 2/5] fix: provider/add test to (hopefully) properly create authorization (#492) --- packages/access-api/test/provider-add.test.js | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/packages/access-api/test/provider-add.test.js b/packages/access-api/test/provider-add.test.js index 5f07c38d0..5edf7eaff 100644 --- a/packages/access-api/test/provider-add.test.js +++ b/packages/access-api/test/provider-add.test.js @@ -107,43 +107,38 @@ for (const accessApiVariant of /** @type {const} */ ([ ) { it('provider/add allows for access/delegate', async () => { const space = await principal.ed25519.generate() - const issuer = await accessApiVariant.issuer + const agent = await accessApiVariant.issuer const service = await accessApiVariant.audience const accountDid = /** @type {const} */ ('did:mailto:example.com:foo') - const serviceSessionAttest = await ucanto.delegate({ - issuer: service, - audience: issuer, + + const accountAuthorizesIssuerClaim = await ucanto.delegate({ + issuer: principal.Absentee.from({ id: accountDid }), + audience: agent, capabilities: [ { - can: 'provider/add', - with: accountDid, + with: 'ucan:*', + can: '*', }, + ], + }) + const serviceAttestsThatAccountAuthorizesIssuer = await ucanto.delegate({ + issuer: service, + audience: agent, + capabilities: [ { - can: 'access/delegate', - with: space.did(), + with: service.did(), + can: 'ucan/attest', + nb: { proof: accountAuthorizesIssuerClaim.cid }, }, ], }) const sessionProofs = [ - await ucanto.delegate({ - issuer: service, - audience: issuer, - capabilities: [ - { - can: 'ucan/attest', - with: accountDid, - nb: { - // note: whole delegation is also included in 'proofs' - proof: serviceSessionAttest.cid, - }, - }, - ], - }), - serviceSessionAttest, + accountAuthorizesIssuerClaim, + serviceAttestsThatAccountAuthorizesIssuer, ] const addStorageProvider = await ucanto .invoke({ - issuer, + issuer: agent, audience: service, capability: { can: 'provider/add', @@ -154,10 +149,11 @@ for (const accessApiVariant of /** @type {const} */ ([ }, }, proofs: [ + ...sessionProofs, // space says issuer can provider/add with this account await ucanto.delegate({ issuer: space, - audience: issuer, + audience: agent, capabilities: [ { can: 'provider/add', @@ -165,7 +161,6 @@ for (const accessApiVariant of /** @type {const} */ ([ }, ], }), - ...sessionProofs, ], }) .delegate() @@ -177,7 +172,7 @@ for (const accessApiVariant of /** @type {const} */ ([ // storage provider added. So we should be able to delegate now const accessDelegate = await ucanto .invoke({ - issuer, + issuer: agent, audience: service, capability: { can: 'access/delegate', @@ -186,7 +181,19 @@ for (const accessApiVariant of /** @type {const} */ ([ delegations: {}, }, }, - proofs: [...sessionProofs], + proofs: [ + ...sessionProofs, + await ucanto.delegate({ + issuer: space, + audience: agent, + capabilities: [ + { + can: 'access/delegate', + with: space.did(), + }, + ], + }), + ], }) .delegate() const accessDelegateResult = await accessApiVariant.invoke(accessDelegate) From 90afea4e7eb68353d0938e5adda76fe0cd8a12c0 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:06:10 -0800 Subject: [PATCH 3/5] remove unnecessary authorization from space to provider/add with account --- packages/access-api/test/provider-add.test.js | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/access-api/test/provider-add.test.js b/packages/access-api/test/provider-add.test.js index 5edf7eaff..19acb674c 100644 --- a/packages/access-api/test/provider-add.test.js +++ b/packages/access-api/test/provider-add.test.js @@ -148,20 +148,7 @@ for (const accessApiVariant of /** @type {const} */ ([ consumer: space.did(), }, }, - proofs: [ - ...sessionProofs, - // space says issuer can provider/add with this account - await ucanto.delegate({ - issuer: space, - audience: agent, - capabilities: [ - { - can: 'provider/add', - with: accountDid, - }, - ], - }), - ], + proofs: [...sessionProofs], }) .delegate() const addStorageProviderResult = await accessApiVariant.invoke( @@ -183,6 +170,7 @@ for (const accessApiVariant of /** @type {const} */ ([ }, proofs: [ ...sessionProofs, + // space says agent can access/delegate with space await ucanto.delegate({ issuer: space, audience: agent, From 0d92ac1fd60cddea0296a41fa2fb727ade298bdd Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:14:23 -0800 Subject: [PATCH 4/5] variable rename --- packages/access-api/test/provider-add.test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/access-api/test/provider-add.test.js b/packages/access-api/test/provider-add.test.js index 19acb674c..176f533d2 100644 --- a/packages/access-api/test/provider-add.test.js +++ b/packages/access-api/test/provider-add.test.js @@ -111,7 +111,7 @@ for (const accessApiVariant of /** @type {const} */ ([ const service = await accessApiVariant.audience const accountDid = /** @type {const} */ ('did:mailto:example.com:foo') - const accountAuthorizesIssuerClaim = await ucanto.delegate({ + const accountAuthorizesAgentClaim = await ucanto.delegate({ issuer: principal.Absentee.from({ id: accountDid }), audience: agent, capabilities: [ @@ -121,20 +121,20 @@ for (const accessApiVariant of /** @type {const} */ ([ }, ], }) - const serviceAttestsThatAccountAuthorizesIssuer = await ucanto.delegate({ + const serviceAttestsThatAccountAuthorizesAgent = await ucanto.delegate({ issuer: service, audience: agent, capabilities: [ { with: service.did(), can: 'ucan/attest', - nb: { proof: accountAuthorizesIssuerClaim.cid }, + nb: { proof: accountAuthorizesAgentClaim.cid }, }, ], }) const sessionProofs = [ - accountAuthorizesIssuerClaim, - serviceAttestsThatAccountAuthorizesIssuer, + accountAuthorizesAgentClaim, + serviceAttestsThatAccountAuthorizesAgent, ] const addStorageProvider = await ucanto .invoke({ From 940d24cd9f2626e69fc2e2210559c4ad9516576d Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:52:29 -0800 Subject: [PATCH 5/5] remove unused proofs from provider/add test --- packages/access-api/test/provider-add.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/access-api/test/provider-add.test.js b/packages/access-api/test/provider-add.test.js index 176f533d2..aa25064b3 100644 --- a/packages/access-api/test/provider-add.test.js +++ b/packages/access-api/test/provider-add.test.js @@ -169,7 +169,6 @@ for (const accessApiVariant of /** @type {const} */ ([ }, }, proofs: [ - ...sessionProofs, // space says agent can access/delegate with space await ucanto.delegate({ issuer: space,