Skip to content

Commit

Permalink
Added convenience PermissionRequest class (#767)
Browse files Browse the repository at this point in the history
Convenience class so that a dev doesn't have to deal with a
`RecordsWrite` and understand the schema directly.
  • Loading branch information
thehenrytsai authored Jun 21, 2024
1 parent 8446cdb commit 4866891
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 9 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tbd54566975/dwn-sdk-js",
"version": "0.3.8",
"version": "0.3.9",
"description": "A reference implementation of https://identity.foundation/decentralized-web-node/spec/",
"repository": {
"type": "git",
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export { Message } from './core/message.js';
export { MessagesGet, MessagesGetOptions } from './interfaces/messages-get.js';
export { UnionMessageReply } from './core/message-reply.js';
export { MessageStore, MessageStoreOptions } from './types/message-store.js';
export { PermissionGrant } from './protocols/permission-grant.js';
export { PermissionRequest } from './protocols/permission-request.js';
export { PermissionsProtocol } from './protocols/permissions.js';
export { PrivateKeySigner } from './utils/private-key-signer.js';
export { Protocols } from './utils/protocols.js';
Expand Down
4 changes: 0 additions & 4 deletions src/protocols/permission-grant.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { DataEncodedRecordsWriteMessage } from '../types/records-types.js';

import type { PermissionConditions, PermissionGrantData, PermissionScope } from '../types/permission-types.js';

import { Encoder } from '../utils/encoder.js';
Expand Down Expand Up @@ -66,9 +65,6 @@ export class PermissionGrant {
return permissionGrant;
}

/**
* Creates a Permission Grant abstraction for
*/
private constructor(message: DataEncodedRecordsWriteMessage) {
// properties derived from the generic DWN message properties
this.id = message.recordId;
Expand Down
63 changes: 63 additions & 0 deletions src/protocols/permission-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { DataEncodedRecordsWriteMessage } from '../types/records-types.js';
import type { PermissionConditions, PermissionRequestData, PermissionScope } from '../types/permission-types.js';

import { Encoder } from '../utils/encoder.js';
import { Message } from '../core/message.js';


/**
* A class representing a Permission Request for a more convenient abstraction.
*/
export class PermissionRequest {

/**
* The ID of the permission request, which is the record ID DWN message.
*/
public readonly id: string;

/**
* The requester for of the permission.
*/
public readonly requester: string;

/**
* Optional string that communicates what the requested grant would be used for.
*/
public readonly description?: string;

/**
* Whether the requested grant is delegated or not.
* If `true`, the `requestor` will be able to act as the grantor of the permission within the scope of the requested grant.
*/
public readonly delegated?: boolean;

/**
* The scope of the allowed access.
*/
public readonly scope: PermissionScope;

/**
* Optional conditions that must be met when the requested grant is used.
*/
public readonly conditions?: PermissionConditions;

public static async parse(message: DataEncodedRecordsWriteMessage): Promise<PermissionRequest> {
const permissionRequest = new PermissionRequest(message);
return permissionRequest;
}

private constructor(message: DataEncodedRecordsWriteMessage) {
// properties derived from the generic DWN message properties
this.id = message.recordId;
this.requester = Message.getSigner(message)!;

// properties from the data payload itself.
const permissionRequestEncodedData = message.encodedData;
const permissionRequestData = Encoder.base64UrlToObject(permissionRequestEncodedData) as PermissionRequestData;
this.delegated = permissionRequestData.delegated;
this.description = permissionRequestData.description;
this.scope = permissionRequestData.scope;
this.conditions = permissionRequestData.conditions;
}
}

11 changes: 9 additions & 2 deletions src/protocols/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ export class PermissionsProtocol {
public static async createRequest(options: PermissionRequestCreateOptions): Promise<{
recordsWrite: RecordsWrite,
permissionRequestData: PermissionRequestData,
permissionRequestBytes: Uint8Array
permissionRequestBytes: Uint8Array,
dataEncodedMessage: DataEncodedRecordsWriteMessage,
}> {

if (this.isRecordPermissionScope(options.scope) && options.scope.protocol === undefined) {
Expand Down Expand Up @@ -204,10 +205,16 @@ export class PermissionsProtocol {
tags : permissionTags,
});

const dataEncodedMessage: DataEncodedRecordsWriteMessage = {
...recordsWrite.message,
encodedData: Encoder.bytesToBase64Url(permissionRequestBytes)
};

return {
recordsWrite,
permissionRequestData,
permissionRequestBytes
permissionRequestBytes,
dataEncodedMessage
};
}

Expand Down
38 changes: 38 additions & 0 deletions tests/protocols/permission-request.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { RecordsPermissionScope } from '../../src/types/permission-types.js';

import chaiAsPromised from 'chai-as-promised';
import sinon from 'sinon';
import chai, { expect } from 'chai';

import { Jws } from '../../src/utils/jws.js';
import { DwnInterfaceName, DwnMethodName, PermissionRequest, PermissionsProtocol, TestDataGenerator } from '../../src/index.js';

chai.use(chaiAsPromised);

describe('PermissionRequest', () => {
afterEach(() => {
// restores all fakes, stubs, spies etc. not restoring causes a memory leak.
// more info here: https://sinonjs.org/releases/v13/general-setup/
sinon.restore();
});

it('should parse a permission request message into a PermissionRequest', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const scope: RecordsPermissionScope = {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Query,
protocol : 'https://example.com/protocol/test'
};

const permissionRequest = await PermissionsProtocol.createRequest({
signer : Jws.createSigner(alice),
delegated : true,
scope
});

const parsedPermissionRequest = await PermissionRequest.parse(permissionRequest.dataEncodedMessage);
expect (parsedPermissionRequest.id).to.equal(permissionRequest.dataEncodedMessage.recordId);
expect (parsedPermissionRequest.delegated).to.equal(true);
expect (parsedPermissionRequest.scope).to.deep.equal(scope);
});
});

0 comments on commit 4866891

Please sign in to comment.