Skip to content

Commit

Permalink
Protocol action "of" validation (#520)
Browse files Browse the repository at this point in the history
* move conditional property validation to javascript for protocol action rules

* fix tests

* replaced reuse of error code

* npm audit fix get-func-name (#521)

---------

Co-authored-by: Henry Tsai <[email protected]>
Co-authored-by: Diane Huxley <[email protected]>
  • Loading branch information
3 people authored Sep 29, 2023
1 parent d5d418b commit 9e86e41
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 55 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Here's to a thrilling Hacktoberfest voyage with us! 🎉
# Decentralized Web Node (DWN) SDK <!-- omit in toc -->

Code Coverage
![Statements](https://img.shields.io/badge/statements-97.77%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-95.03%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.26%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.77%25-brightgreen.svg?style=flat)
![Statements](https://img.shields.io/badge/statements-97.78%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-95.05%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.26%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.78%25-brightgreen.svg?style=flat)

- [Introduction](#introduction)
- [Installation](#installation)
Expand Down
24 changes: 1 addition & 23 deletions json-schemas/interface-methods/protocol-rule-set.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,7 @@
"who": {
"type": "string",
"enum": [
"anyone"
]
},
"can": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"required": [
"who",
"of",
"can"
],
"additionalProperties": false,
"properties": {
"who": {
"type": "string",
"enum": [
"anyone",
"author",
"recipient"
]
Expand Down
2 changes: 2 additions & 0 deletions src/core/dwn-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export enum DwnErrorCode {
ProtocolAuthorizationNotARole = 'ProtocolAuthorizationNotARole',
ProtocolsConfigureGlobalRoleAtProhibitedProtocolPath = 'ProtocolsConfigureGlobalRoleAtProhibitedProtocolPath',
ProtocolsConfigureInvalidRole = 'ProtocolsConfigureInvalidRole',
ProtocolsConfigureInvalidActionMissingOf = 'ProtocolsConfigureInvalidActionMissingOf',
ProtocolsConfigureInvalidActionOfNotAllowed = 'ProtocolsConfigureInvalidActionOfNotAllowed',
ProtocolsConfigureUnauthorized = 'ProtocolsConfigureUnauthorized',
ProtocolsQueryUnauthorized = 'ProtocolsQueryUnauthorized',
RecordsDecryptNoMatchingKeyEncryptedFound = 'RecordsDecryptNoMatchingKeyEncryptedFound',
Expand Down
23 changes: 20 additions & 3 deletions src/interfaces/protocols-configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js';
import { normalizeProtocolUrl, normalizeSchemaUrl, validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../utils/url.js';

export type ProtocolsConfigureOptions = {
messageTimestamp? : string;
definition : ProtocolDefinition;
messageTimestamp?: string;
definition: ProtocolDefinition;
authorizationSigner: Signer;
permissionsGrantId?: string;
};
Expand Down Expand Up @@ -92,15 +92,32 @@ export class ProtocolsConfigure extends Message<ProtocolsConfigureMessage> {
);
}

// Validate that all `role` properties contain protocol paths $globalRole records
// Validate $actions in the ruleset
const actions = ruleSet.$actions ?? [];
for (const action of actions) {
// Validate that all `role` properties contain protocol paths $globalRole records
if (action.role !== undefined && !globalRoles.includes(action.role)) {
throw new DwnError(
DwnErrorCode.ProtocolsConfigureInvalidRole,
`Invalid role '${action.role}' found at protocol path '${protocolPath}'`
);
}

// Validate that if `who` is set to `anyone` then `of` is not set
if (action.who === 'anyone' && action.of) {
throw new DwnError(
DwnErrorCode.ProtocolsConfigureInvalidActionOfNotAllowed,
`'of' is not allowed at protocol path (${protocolPath})`
);
}

// Validate that if `who` is not set to `anyone` then `of` is set
if (action.who !== undefined && ['author', 'recipient'].includes(action.who) && !action.of) {
throw new DwnError(
DwnErrorCode.ProtocolsConfigureInvalidActionMissingOf,
`'of' is required at protocol path (${protocolPath})`
);
}
}

// Validate nested rule sets
Expand Down
59 changes: 59 additions & 0 deletions tests/interfaces/protocols-configure.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,65 @@ describe('ProtocolsConfigure', () => {
await expect(createProtocolsConfigurePromise)
.to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidRole);
});

it('rejects protocol definitions with actions that contain `of` and `who` is `anyone`', async () => {
const definition = {
published : true,
protocol : 'http://example.com',
types : {
message: {},
},
structure: {
message: {
$actions: [{
who : 'anyone',
of : 'message', // Not allowed
can : 'read'
}]
}
}
};

const alice = await TestDataGenerator.generatePersona();

const createProtocolsConfigurePromise = ProtocolsConfigure.create({
authorizationSigner: Jws.createSigner(alice),
definition
});

await expect(createProtocolsConfigurePromise)
.to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidActionOfNotAllowed);
});

it('rejects protocol definitions with actions that don\'t contain `of` and `who` is `author` or `recipient`', async () => {
const definition = {
published : true,
protocol : 'http://example.com',
types : {
message: {},
},
structure: {
message: {
$actions: [{
who : 'author',
// of : 'message', // Intentionally missing
can : 'read'
}]
}
}
};

const alice = await TestDataGenerator.generatePersona();

const createProtocolsConfigurePromise = ProtocolsConfigure.create({
authorizationSigner: Jws.createSigner(alice),
definition
});

await expect(createProtocolsConfigurePromise)
.to.be.rejectedWith(DwnErrorCode.ProtocolsConfigureInvalidActionMissingOf);
});

});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,33 +67,5 @@ describe('ProtocolsConfigure schema definition', () => {
}).throws('/$actions/0');
}
});

it('#183 - should throw if required `of` is missing in rule-set', async () => {
const invalidRuleSet = {
$actions: [{
who : 'author',
// of: 'thread', // intentionally missing
can : 'read'
}]
};

expect(() => {
validateJsonSchema('ProtocolRuleSet', invalidRuleSet);
}).throws('/$actions/0');
});

it('#183 - should throw if `of` is present in `anyone` rule-set', async () => {
const invalidRuleSet = {
$actions: [{
who : 'anyone',
of : 'thread', // intentionally present
can : 'read'
}]
};

expect(() => {
validateJsonSchema('ProtocolRuleSet', invalidRuleSet);
}).throws('/$actions/0');
});
});
});

0 comments on commit 9e86e41

Please sign in to comment.