Skip to content

Commit

Permalink
Stricten typescript in protocol-authorization.ts (#277)
Browse files Browse the repository at this point in the history
  • Loading branch information
Diane Huxley authored Mar 28, 2023
1 parent 81bcc55 commit 04345b0
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 15 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Decentralized Web Node (DWN) SDK

Code Coverage
![Statements](https://img.shields.io/badge/statements-93.64%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-92.8%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-90.4%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-93.64%25-brightgreen.svg?style=flat)
![Statements](https://img.shields.io/badge/statements-95.1%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-93.05%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-94.09%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-95.1%25-brightgreen.svg?style=flat)

## Introduction

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,4 @@
"url": "https://github.com/TBD54566975/dwn-sdk-js/issues"
},
"homepage": "https://github.com/TBD54566975/dwn-sdk-js#readme"
}
}
28 changes: 15 additions & 13 deletions src/core/protocol-authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import type { RecordsWriteMessage } from '../interfaces/records/types.js';
import type { ProtocolDefinition, ProtocolRuleSet, ProtocolsConfigureMessage } from '../interfaces/protocols/types.js';

import { DwnInterfaceName, DwnMethodName, Message } from './message.js';
import { Filter } from './types.js';

const methodToAllowedActionMap = {
const methodToAllowedActionMap: Record<string, string> = {
[DwnMethodName.Write]: 'write',
};

Expand All @@ -31,13 +32,14 @@ export class ProtocolAuthorization {
const recordSchemaToLabelMap: Map<string, string> = new Map();
for (const schemaLabel in protocolDefinition.labels) {
const schema = protocolDefinition.labels[schemaLabel].schema;
recordSchemaToLabelMap[schema] = schemaLabel;
recordSchemaToLabelMap.set(schema, schemaLabel);
}

// get the rule set for the inbound message
const inboundMessageRuleSet = ProtocolAuthorization.getRuleSet(
recordsWrite.message,
protocolDefinition, ancestorMessageChain,
protocolDefinition,
ancestorMessageChain,
recordSchemaToLabelMap
);

Expand All @@ -57,10 +59,10 @@ export class ProtocolAuthorization {
*/
private static async fetchProtocolDefinition(tenant: string, recordsWrite: RecordsWrite, messageStore: MessageStore): Promise<ProtocolDefinition> {
// get the protocol URI
const protocolUri = recordsWrite.message.descriptor.protocol;
const protocolUri = recordsWrite.message.descriptor.protocol!;

// fetch the corresponding protocol definition
const query = {
const query: Filter = {
interface : DwnInterfaceName.Protocols,
method : DwnMethodName.Configure,
protocol : protocolUri
Expand All @@ -83,14 +85,14 @@ export class ProtocolAuthorization {
: Promise<RecordsWriteMessage[]> {
const ancestorMessageChain: RecordsWriteMessage[] = [];

const protocol = recordsWrite.message.descriptor.protocol;
const contextId = recordsWrite.message.contextId;
const protocol = recordsWrite.message.descriptor.protocol!;
const contextId = recordsWrite.message.contextId!;

// keep walking up the chain from the inbound message's parent, until there is no more parent
let currentParentId = recordsWrite.message.descriptor.parentId;
while (currentParentId !== undefined) {
// fetch parent
const query = {
const query: Filter = {
interface : DwnInterfaceName.Records,
method : DwnMethodName.Write,
protocol,
Expand Down Expand Up @@ -126,17 +128,17 @@ export class ProtocolAuthorization {

// walk down the ancestor message chain from the root ancestor record and match against the corresponding rule set at each level
// to make sure the chain structure is allowed
let allowedRecordsAtCurrentLevel = protocolDefinition.records;
let allowedRecordsAtCurrentLevel: { [key: string]: ProtocolRuleSet} | undefined = protocolDefinition.records;
let currentMessageIndex = 0;
while (true) {
const currentRecordSchema = messageChain[currentMessageIndex].descriptor.schema;
const currentRecordType = recordSchemaToLabelMap[currentRecordSchema];
const currentRecordType = recordSchemaToLabelMap.get(currentRecordSchema!);

if (currentRecordType === undefined) {
throw new Error(`record with schema '${currentRecordSchema}' not allowed in protocol`);
}

if (!(currentRecordType in allowedRecordsAtCurrentLevel)) {
if (allowedRecordsAtCurrentLevel === undefined || !(currentRecordType in allowedRecordsAtCurrentLevel)) {
throw new Error(`record with schema: '${currentRecordSchema}' not allowed in structure level ${currentMessageIndex}`);
}

Expand Down Expand Up @@ -202,7 +204,7 @@ export class ProtocolAuthorization {
}
}

let allowedActions: string[];
let allowedActions: string[] = [];
if (allowRule.anyone !== undefined) {
allowedActions = allowRule.anyone.to;
} else if (allowRule.recipient !== undefined) {
Expand Down Expand Up @@ -262,7 +264,7 @@ export class ProtocolAuthorization {
const expectedAncestorType = expectedAncestors[i];
const ancestorMessage = ancestorMessageChain[i];

const actualAncestorType = recordSchemaToLabelMap[ancestorMessage.descriptor.schema];
const actualAncestorType = recordSchemaToLabelMap.get(ancestorMessage.descriptor.schema!);
if (actualAncestorType !== expectedAncestorType) {
throw new Error(`mismatching record schema: expecting ${expectedAncestorType} but actual ${actualAncestorType}`);
}
Expand Down
52 changes: 52 additions & 0 deletions tests/interfaces/records/handlers/records-write.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,58 @@ describe('RecordsWriteHandler.handle()', () => {
expect(reply.status.detail).to.contain('not allowed in structure level');
});


it('should fail authorization if record schema is not allowed at the hierarchical level attempted for the RecordsWrite', async () => {
const alice = await DidKeyResolver.generate();

const protocol = 'chatProtocol';
const protocolDefinition = {
labels: {
email: {
schema: 'emailSchema'
},
sms: {
schema: 'smsSchema'
}
},
records: {
email: {},
sms: {}
}
};
const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
requester: alice,
protocol,
protocolDefinition
});

const protocolConfigureReply = await dwn.processMessage(alice.did, protocolConfig.message, protocolConfig.dataStream);
expect(protocolConfigureReply.status.code).to.equal(202);

const emailRecordsWrite = await TestDataGenerator.generateRecordsWrite({
requester : alice,
recipientDid : alice.did,
protocol,
schema : protocolDefinition.labels.email.schema,
data: Encoder.stringToBytes('any data'),
});
await dwn.processMessage(alice.did, emailRecordsWrite.message, emailRecordsWrite.dataStream);

const smsSchemaResponse = await TestDataGenerator.generateRecordsWrite({
requester : alice,
recipientDid : alice.did,
protocol,
schema : protocolDefinition.labels.sms.schema, // SMS are allowed, but not as a child record of emails
data: Encoder.stringToBytes('any other data'),
parentId: emailRecordsWrite.message.recordId,
contextId: await emailRecordsWrite.recordsWrite.getEntryId()
});
const reply = await dwn.processMessage(alice.did, smsSchemaResponse.message, smsSchemaResponse.dataStream);

expect(reply.status.code).to.equal(401);
expect(reply.status.detail).to.contain('record with schema: \'smsSchema\' not allowed in structure level 1');
});

it('should only allow DWN owner to write if record does not have an allow rule defined', async () => {
const alice = await DidKeyResolver.generate();

Expand Down

0 comments on commit 04345b0

Please sign in to comment.