Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added convenience PermissionRequest class #767

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});
});