Skip to content

Commit

Permalink
Missing JSON Schema properties for Permission Scopes (#781)
Browse files Browse the repository at this point in the history
This PR adds some missing schema properties expected in the various
protocol scopes.

I wrote tests that validate that the messages are processed, but I think
there need to be some additional testing regarding the functionality of
various scopes in a subsequent PR/Issue.
  • Loading branch information
LiranCohen authored Jul 16, 2024
1 parent 6dc12ce commit 4cd136b
Show file tree
Hide file tree
Showing 3 changed files with 337 additions and 1 deletion.
21 changes: 21 additions & 0 deletions json-schemas/permissions/scopes.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@
},
"protocol": {
"type": "string"
},
"contextId": {
"type": "string"
},
"protocolPath": {
"type": "string"
}
}
},
Expand Down Expand Up @@ -156,8 +162,17 @@
"interface": {
"const": "Records"
},
"method": {
"const": "Query"
},
"protocol": {
"type": "string"
},
"contextId": {
"type": "string"
},
"protocolPath": {
"type": "string"
}
}
},
Expand All @@ -177,6 +192,12 @@
},
"protocol": {
"type": "string"
},
"contextId": {
"type": "string"
},
"protocolPath": {
"type": "string"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type { GenericMessage, GenericMessageReply, MessageSort, MessageSubscript
export type { MessagesFilter, MessagesReadMessage as MessagesReadMessage, MessagesReadReply as MessagesReadReply, MessagesReadReplyEntry as MessagesReadReplyEntry, MessagesQueryMessage, MessagesQueryReply, MessagesSubscribeDescriptor, MessagesSubscribeMessage, MessagesSubscribeReply, MessageSubscriptionHandler } from './types/messages-types.js';
export type { Filter, EqualFilter, OneOfFilter, RangeFilter, RangeCriterion, PaginationCursor, QueryOptions } from './types/query-types.js';
export type { ProtocolsConfigureDescriptor, ProtocolDefinition, ProtocolTypes, ProtocolRuleSet, ProtocolsQueryFilter, ProtocolsConfigureMessage, ProtocolsQueryMessage, ProtocolsQueryReply } from './types/protocols-types.js';
export type { EncryptionProperty, RecordsDeleteMessage, RecordsQueryMessage, RecordsQueryReply, RecordsQueryReplyEntry, RecordsReadMessage, RecordsReadReply, RecordsSubscribeDescriptor, RecordsSubscribeMessage, RecordsSubscribeReply, RecordSubscriptionHandler, RecordsWriteDescriptor, RecordsWriteTags, RecordsWriteTagValue, RecordsWriteMessage } from './types/records-types.js';
export type { DataEncodedRecordsWriteMessage, EncryptionProperty, RecordsDeleteMessage, RecordsQueryMessage, RecordsQueryReply, RecordsQueryReplyEntry, RecordsReadMessage, RecordsReadReply, RecordsSubscribeDescriptor, RecordsSubscribeMessage, RecordsSubscribeReply, RecordSubscriptionHandler, RecordsWriteDescriptor, RecordsWriteTags, RecordsWriteTagValue, RecordsWriteMessage } from './types/records-types.js';
export { authenticate } from './core/auth.js';
export { ActiveTenantCheckResult, AllowAllTenantGate, TenantGate } from './core/tenant-gate.js';
export { Cid } from './utils/cid.js';
Expand Down
315 changes: 315 additions & 0 deletions tests/features/permissions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,321 @@ export function testPermissions(): void {
expect(revokeWriteReply.status.code).to.equal(202);
});

// These set of tets are primarily to ensure SchemaValidation passes for the various permission request and grant messages and their scopes
describe('ensure loaded scope properties for permission requests are processed', () => {
it('MessagesQuery', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol
const messagesQueryPermissions = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to query from Alice test-context',
scope : {
interface : DwnInterfaceName.Messages,
method : DwnMethodName.Query,
protocol : 'https://example.com/protocol/test',
}
});

const messagesQueryPermissionsReply = await dwn.processMessage(alice.did, messagesQueryPermissions.recordsWrite.message, {
dataStream: DataStream.fromBytes(messagesQueryPermissions.permissionGrantBytes)
});
expect(messagesQueryPermissionsReply.status.code).to.equal(202);
});

it('MessagesRead', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol
const messagesReadPermissions = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to read from Alice test-context',
scope : {
interface : DwnInterfaceName.Messages,
method : DwnMethodName.Read,
protocol : 'https://example.com/protocol/test',
}
});

const messagesReadPermissionsReply = await dwn.processMessage(alice.did, messagesReadPermissions.recordsWrite.message, {
dataStream: DataStream.fromBytes(messagesReadPermissions.permissionGrantBytes)
});
expect(messagesReadPermissionsReply.status.code).to.equal(202);
});

it('MessagesSubscribe', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol
const messagesSubscribePermissions = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to subscribe from Alice test-context',
scope : {
interface : DwnInterfaceName.Messages,
method : DwnMethodName.Subscribe,
protocol : 'https://example.com/protocol/test',
}
});

const messagesSubscribePermissionsReply = await dwn.processMessage(alice.did, messagesSubscribePermissions.recordsWrite.message, {
dataStream: DataStream.fromBytes(messagesSubscribePermissions.permissionGrantBytes)
});
expect(messagesSubscribePermissionsReply.status.code).to.equal(202);
});

it('RecordsDelete', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol and contextId
const withContextId = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to delete from Alice test-context',
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Delete,
protocol : 'https://example.com/protocol/test',
contextId : 'test-context'
}
});

const withContextIdReply = await dwn.processMessage(alice.did, withContextId.recordsWrite.message, {
dataStream: DataStream.fromBytes(withContextId.permissionGrantBytes)
});
expect(withContextIdReply.status.code).to.equal(202);

// create a permission request with protocol and protocolPath
const withProtocolPath = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to delete from Alice foo/bar',
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Delete,
protocol : 'https://example.com/protocol/test',
protocolPath : 'foo/bar'
}
});

const withProtocolPathReply = await dwn.processMessage(alice.did, withProtocolPath.recordsWrite.message, {
dataStream: DataStream.fromBytes(withProtocolPath.permissionGrantBytes)
});
expect(withProtocolPathReply.status.code).to.equal(202);
});

it('RecordsQuery', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol and contextId scope
const withContextId = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to query from Alice test-context',
delegated : true,
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Query,
protocol : 'https://example.com/protocol/test',
contextId : 'test-context'
}
});

const withContextIdReply = await dwn.processMessage(alice.did, withContextId.recordsWrite.message, {
dataStream: DataStream.fromBytes(withContextId.permissionGrantBytes)
});
expect(withContextIdReply.status.code).to.equal(202);

// create a permission request with protocol and protocolPath scope
const withProtocolPath = await PermissionsProtocol.createRequest({
signer : Jws.createSigner(bob),
description : 'Requesting to query from Alice foo/bar',
delegated : true,
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Query,
protocol : 'https://example.com/protocol/test',
protocolPath : 'foo/bar'
}
});

const withProtocolPathReply = await dwn.processMessage(bob.did, withProtocolPath.recordsWrite.message, {
dataStream: DataStream.fromBytes(withProtocolPath.permissionRequestBytes)
});
expect(withProtocolPathReply.status.code).to.equal(202);
});

it('RecordsRead', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol and contextId scope
const withContextId = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to read to Alice test-context',
delegated : true,
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Read,
protocol : 'https://example.com/protocol/test',
contextId : 'test-context'
}
});

const withContextIdReply = await dwn.processMessage(alice.did, withContextId.recordsWrite.message, {
dataStream: DataStream.fromBytes(withContextId.permissionGrantBytes)
});
expect(withContextIdReply.status.code).to.equal(202);

// create a permission request with protocol and protocolPath scope
const withProtocolPath = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to read to Alice foo/bar',
delegated : true,
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Read,
protocol : 'https://example.com/protocol/test',
protocolPath : 'foo/bar'
}
});

const withProtocolPathReply = await dwn.processMessage(alice.did, withProtocolPath.recordsWrite.message, {
dataStream: DataStream.fromBytes(withProtocolPath.permissionGrantBytes)
});
expect(withProtocolPathReply.status.code).to.equal(202);
});

it('RecordsSubscribe', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol and contextId scope
const withContextId = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to subscribe to Alice test-context',
delegated : true,
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Subscribe,
protocol : 'https://example.com/protocol/test',
contextId : 'test-context'
}
});

const withContextIdReply = await dwn.processMessage(alice.did, withContextId.recordsWrite.message, {
dataStream: DataStream.fromBytes(withContextId.permissionGrantBytes)
});
expect(withContextIdReply.status.code).to.equal(202);

// create a permission request with protocol and protocolPath scope
const withProtocolPath = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to subscribe to Alice foo/bar',
delegated : true,
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Subscribe,
protocol : 'https://example.com/protocol/test',
protocolPath : 'foo/bar'
}
});

const withProtocolPathReply = await dwn.processMessage(alice.did, withProtocolPath.recordsWrite.message, {
dataStream: DataStream.fromBytes(withProtocolPath.permissionGrantBytes)
});
expect(withProtocolPathReply.status.code).to.equal(202);
});

it('RecordsWrite', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol and contextId scope
const withContextId = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to write to Alice test-context',
delegated : true,
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Write,
protocol : 'https://example.com/protocol/test',
contextId : 'test-context'
}
});

const withContextIdReply = await dwn.processMessage(alice.did, withContextId.recordsWrite.message, {
dataStream: DataStream.fromBytes(withContextId.permissionGrantBytes)
});
expect(withContextIdReply.status.code).to.equal(202);

// create a permission request with protocol and protocolPath scope
const withProtocolPath = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to write to Alice foo/bar',
delegated : true,
scope : {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Write,
protocol : 'https://example.com/protocol/test',
protocolPath : 'foo/bar'
}
});

const withProtocolPathReply = await dwn.processMessage(alice.did, withProtocolPath.recordsWrite.message, {
dataStream: DataStream.fromBytes(withProtocolPath.permissionGrantBytes)
});
expect(withProtocolPathReply.status.code).to.equal(202);
});

it('ProtocolsQuery', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();

// create a permission grant with protocol query that is unrestricted
const protocolQueryPermissions = await PermissionsProtocol.createGrant({
signer : Jws.createSigner(alice),
grantedTo : bob.did,
dateExpires : Time.createOffsetTimestamp({ seconds: 100 }),
description : 'Requesting to query from Alice test-context',
scope : {
interface : DwnInterfaceName.Protocols,
method : DwnMethodName.Query,
}
});

const protocolQueryPermissionsReply = await dwn.processMessage(alice.did, protocolQueryPermissions.recordsWrite.message, {
dataStream: DataStream.fromBytes(protocolQueryPermissions.permissionGrantBytes)
});
expect(protocolQueryPermissionsReply.status.code).to.equal(202);
});
});

describe('validateScopeAndTags', async () => {
it('should be called for a Request or Grant record', async () => {
// spy on `validateScope`
Expand Down

0 comments on commit 4cd136b

Please sign in to comment.