From e2e6760c4066747e1e43675fcebf371592d7ba25 Mon Sep 17 00:00:00 2001 From: Diane Huxley Date: Mon, 30 Oct 2023 15:33:20 -0700 Subject: [PATCH] Expose DWN PermissionsGrant Message --- packages/agent/package.json | 2 +- packages/agent/src/dwn-manager.ts | 13 ++++-- packages/agent/src/types/agent.ts | 5 ++- packages/api/package.json | 4 +- packages/api/src/dwn-api.ts | 59 +++++++++++++++++++++++++++- packages/api/tests/dwn-api.spec.ts | 35 +++++++++++++++++ packages/identity-agent/package.json | 2 +- packages/proxy-agent/package.json | 2 +- packages/user-agent/package.json | 4 +- 9 files changed, 114 insertions(+), 12 deletions(-) diff --git a/packages/agent/package.json b/packages/agent/package.json index 9e70084cc..064fb562c 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -1,6 +1,6 @@ { "name": "@web5/agent", - "version": "0.2.2", + "version": "0.2.3", "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", diff --git a/packages/agent/src/dwn-manager.ts b/packages/agent/src/dwn-manager.ts index d610cd84c..46eb11683 100644 --- a/packages/agent/src/dwn-manager.ts +++ b/packages/agent/src/dwn-manager.ts @@ -6,6 +6,7 @@ import { RecordsWriteMessage, RecordsWriteOptions, Signer, + PermissionsGrant, } from '@tbd54566975/dwn-sdk-js'; import { Jose } from '@web5/crypto'; @@ -69,6 +70,9 @@ const dwnMessageCreators = { [DwnInterfaceName.Records + DwnMethodName.Query] : RecordsQuery, [DwnInterfaceName.Records + DwnMethodName.Write] : RecordsWrite, [DwnInterfaceName.Records + DwnMethodName.Delete] : RecordsDelete, + [DwnInterfaceName.Permissions + DwnMethodName.Grant] : PermissionsGrant, + [DwnInterfaceName.Permissions + DwnMethodName.Request] : PermissionsGrant, + [DwnInterfaceName.Permissions + DwnMethodName.Revoke] : PermissionsGrant, [DwnInterfaceName.Protocols + DwnMethodName.Query] : ProtocolsQuery, [DwnInterfaceName.Protocols + DwnMethodName.Configure] : ProtocolsConfigure, }; @@ -184,8 +188,11 @@ export class DwnManager { }); dwnRpcRequest.message = message; messageData = data; - + } else if ('message' in request) { + dwnRpcRequest.message = request.message; + messageData = request.dataStream; } else { + // construct message from messageOptions const { message } = await this.constructDwnMessage({ request }); dwnRpcRequest.message = message; messageData = request.dataStream; @@ -224,7 +231,7 @@ export class DwnManager { dwnReply = await this.agent.rpcClient.sendDwnRequest(dwnRpcRequest as DwnRpcRequest); break; } catch(error: unknown) { - const message = (error instanceof Error) ? error.message : 'Uknown error'; + const message = (error instanceof Error) ? error.message : 'Unknown error'; errorMessages.push({ url: dwnUrl, message }); } } @@ -405,7 +412,7 @@ export class DwnManager { author: string, messageOptions: unknown, messageType: string - }): Promise { + }): Promise> { const { author, messageOptions, messageType } = options; const dwnAuthorizationSigner = await this.constructDwnAuthorizationSigner(author); diff --git a/packages/agent/src/types/agent.ts b/packages/agent/src/types/agent.ts index 5ce8f05ee..bbf0e605e 100644 --- a/packages/agent/src/types/agent.ts +++ b/packages/agent/src/types/agent.ts @@ -8,6 +8,7 @@ import type { RecordsDeleteMessage, ProtocolsQueryMessage, ProtocolsConfigureMessage, + GenericMessage, } from '@tbd54566975/dwn-sdk-js'; import { DidResolver } from '@web5/dids'; @@ -57,7 +58,9 @@ export type ProcessDwnRequest = DwnRequest & { store?: boolean; }; -export type SendDwnRequest = DwnRequest & (ProcessDwnRequest | { messageCid: string }) +export type SendDwnRequest = DwnRequest & ( + ProcessDwnRequest | { messageCid: string } | { message: GenericMessage, dataStream?: Blob | ReadableStream | Readable } +); /** * TODO: add JSDoc diff --git a/packages/api/package.json b/packages/api/package.json index 4f6251232..835e5fe01 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -76,10 +76,10 @@ }, "dependencies": { "@tbd54566975/dwn-sdk-js": "0.2.4", - "@web5/agent": "0.2.2", + "@web5/agent": "0.2.3", "@web5/crypto": "0.2.1", "@web5/dids": "0.2.1", - "@web5/user-agent": "0.2.1", + "@web5/user-agent": "0.2.3", "level": "8.0.0", "ms": "2.1.3", "readable-stream": "4.4.2", diff --git a/packages/api/src/dwn-api.ts b/packages/api/src/dwn-api.ts index 11bccc120..358714b8e 100644 --- a/packages/api/src/dwn-api.ts +++ b/packages/api/src/dwn-api.ts @@ -1,5 +1,5 @@ import type { DwnResponse, Web5Agent } from '@web5/agent'; -import type { +import { UnionMessageReply, RecordsReadOptions, RecordsQueryOptions, @@ -11,6 +11,7 @@ import type { ProtocolsConfigureMessage, ProtocolsConfigureOptions, ProtocolsConfigureDescriptor, + Message, } from '@tbd54566975/dwn-sdk-js'; import { isEmptyObject } from '@web5/common'; @@ -19,6 +20,14 @@ import { DwnInterfaceName, DwnMethodName } from '@tbd54566975/dwn-sdk-js'; import { Record } from './record.js'; import { Protocol } from './protocol.js'; import { dataToBlob } from './utils.js'; +import { PermissionsGrant } from '@tbd54566975/dwn-sdk-js'; +import { PermissionsGrantMessage } from '@tbd54566975/dwn-sdk-js'; +import { PermissionsGrantOptions } from '@tbd54566975/dwn-sdk-js'; + +export type PermissionsGrantRequest = { + target?: string; + message: Omit; +} export type ProtocolsConfigureRequest = { message: Omit; @@ -91,6 +100,10 @@ export type RecordsWriteRequest = { store?: boolean; } +export type PermissionGrantRequest = { + message?: Omit, 'authorizationSigner'>; +} + export type RecordsWriteResponse = { status: UnionMessageReply['status']; record?: Record @@ -377,4 +390,48 @@ export class DwnApi { }, }; } + + get permissions() { + return { + grant: async (request: PermissionsGrantRequest): Promise<{ permissionsGrant: PermissionsGrant, status: UnionMessageReply['status'] }> => { + const agentRequest = { + author : this.connectedDid, + messageOptions : request.message, + messageType : DwnInterfaceName.Permissions + DwnMethodName.Grant, + target : request.target || this.connectedDid + }; + + let agentResponse: DwnResponse; + + if (request.target) { + agentResponse = await this.agent.sendDwnRequest(agentRequest); + } else { + agentResponse = await this.agent.processDwnRequest(agentRequest); + } + + const { message, reply: { status } } = agentResponse; + + let permissionsGrant: PermissionsGrant | undefined; + if (200 <= status.code && status.code <= 299) { + permissionsGrant = await PermissionsGrant.parse(message as PermissionsGrantMessage); + } + + return { + permissionsGrant, + status, + }; + }, + + send: async (target: string, message: PermissionsGrant): Promise<{ status: UnionMessageReply['status'] }> => { + const { reply: { status } } = await this.agent.sendDwnRequest({ + messageType : message.message.descriptor.interface + message.message.descriptor.method, + author : message.author, + target : target, + message : message.message, + }); + + return { status }; + }, + }; + } } \ No newline at end of file diff --git a/packages/api/tests/dwn-api.spec.ts b/packages/api/tests/dwn-api.spec.ts index 2374a7899..9e22398fd 100644 --- a/packages/api/tests/dwn-api.spec.ts +++ b/packages/api/tests/dwn-api.spec.ts @@ -1,5 +1,6 @@ import type { PortableDid } from '@web5/dids'; import type { ManagedIdentity } from '@web5/agent'; +import { Temporal } from '@js-temporal/polyfill'; import { expect } from 'chai'; import { TestManagedAgent } from '@web5/agent'; @@ -8,6 +9,7 @@ import { DwnApi } from '../src/dwn-api.js'; import { testDwnUrl } from './test-config.js'; import { TestUserAgent } from './utils/test-user-agent.js'; import emailProtocolDefinition from './fixtures/protocol-definitions/email.json' assert { type: 'json' }; +import { DwnInterfaceName, DwnMethodName } from '@tbd54566975/dwn-sdk-js'; let testDwnUrls: string[] = [testDwnUrl]; @@ -569,4 +571,37 @@ describe('DwnApi', () => { }); }); }); + + describe('permissions.grant()', () => { + describe('agent', () => { + it('returns a PermissionsGrant that can be invoked', async () => { + const { did: bobDid } = await testAgent.createIdentity({ testDwnUrls }); + + const { status, permissionsGrant } = await dwn.permissions.grant({ + message: { + dateExpires : Temporal.Now.instant().toString({ smallestUnit: 'microseconds' }), + grantedBy : aliceDid.did, + grantedFor : aliceDid.did, + grantedTo : bobDid.did, + scope : { + interface : DwnInterfaceName.Records, + method : DwnMethodName.Read, + } + }, + }); + + expect(status.code).to.eq(202); + expect(permissionsGrant).not.to.be.undefined; + + // send to Alice's remote DWN + const { status: statusSend } = await dwn.permissions.send(aliceDid.did, permissionsGrant); + + expect(statusSend.code).to.eq(202); + }); + }); + + describe('target: did', () => { + + }); + }); }); \ No newline at end of file diff --git a/packages/identity-agent/package.json b/packages/identity-agent/package.json index 7d8756429..ce4902f34 100644 --- a/packages/identity-agent/package.json +++ b/packages/identity-agent/package.json @@ -67,7 +67,7 @@ "node": ">=18.0.0" }, "dependencies": { - "@web5/agent": "0.2.2", + "@web5/agent": "0.2.3", "@web5/api": "0.8.2" }, "devDependencies": { diff --git a/packages/proxy-agent/package.json b/packages/proxy-agent/package.json index a1bc5e9a3..7489e7a63 100644 --- a/packages/proxy-agent/package.json +++ b/packages/proxy-agent/package.json @@ -67,7 +67,7 @@ "node": ">=18.0.0" }, "dependencies": { - "@web5/agent": "0.2.2", + "@web5/agent": "0.2.3", "@web5/common": "0.2.1", "@web5/crypto": "0.2.1", "@web5/dids": "0.2.1" diff --git a/packages/user-agent/package.json b/packages/user-agent/package.json index d1e18cd1a..d48356065 100644 --- a/packages/user-agent/package.json +++ b/packages/user-agent/package.json @@ -1,6 +1,6 @@ { "name": "@web5/user-agent", - "version": "0.2.2", + "version": "0.2.3", "type": "module", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", @@ -67,7 +67,7 @@ "node": ">=18.0.0" }, "dependencies": { - "@web5/agent": "0.2.2", + "@web5/agent": "0.2.3", "@web5/common": "0.2.1", "@web5/crypto": "0.2.1", "@web5/dids": "0.2.1"