Skip to content

Commit ba253a4

Browse files
authored
#179 - removed 'target' property from message data model
1 parent a59d519 commit ba253a4

33 files changed

+328
-462
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Decentralized Web Node (DWN) SDK
44

55
Code Coverage
6-
![Statements](https://img.shields.io/badge/statements-92.79%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-91.98%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-89.3%25-yellow.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-92.79%25-brightgreen.svg?style=flat)
6+
![Statements](https://img.shields.io/badge/statements-92.65%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-91.98%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-89.3%25-yellow.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-92.65%25-brightgreen.svg?style=flat)
77

88
## Introduction
99

src/core/auth.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { BaseMessage } from './types.js';
22

33
import { CID } from 'multiformats';
4-
import { Did } from '../did/did.js';
54
import { DidResolver } from '../did/did-resolver.js';
65
import { GeneralJws } from '../jose/jws/general/types.js';
76
import { GeneralJwsVerifier } from '../jose/jws/general/verifier.js';
@@ -20,32 +19,29 @@ type AuthorizationPayloadConstraints = {
2019
* @throws {Error} if auth fails
2120
*/
2221
export async function canonicalAuth(
22+
tenant: string,
2323
incomingMessage: Message,
2424
didResolver: DidResolver
2525
): Promise<void> {
2626
await authenticate(incomingMessage.message.authorization, didResolver);
27-
await authorize(incomingMessage);
27+
await authorize(tenant, incomingMessage);
2828
}
2929

3030
/**
3131
* Validates the data integrity of the `authorization` property.
32-
* NOTE: `target` and `descriptorCid` are both checked by default
3332
* NOTE signature is not verified.
3433
*/
3534
export async function validateAuthorizationIntegrity(
3635
message: BaseMessage,
3736
authorizationPayloadConstraints?: AuthorizationPayloadConstraints
38-
): Promise<{ target: string, descriptorCid: CID, [key: string]: any }> {
37+
): Promise<{ descriptorCid: CID, [key: string]: any }> {
3938

4039
if (message.authorization.signatures.length !== 1) {
4140
throw new Error('expected no more than 1 signature for authorization');
4241
}
4342

4443
const payloadJson = GeneralJwsVerifier.decodePlainObjectPayload(message.authorization);
45-
const { target, descriptorCid } = payloadJson;
46-
47-
// `target` validation
48-
Did.validate(target);
44+
const { descriptorCid } = payloadJson;
4945

5046
// `descriptorCid` validation - ensure that the provided descriptorCid matches the CID of the actual message
5147
const providedDescriptorCid = parseCid(descriptorCid); // parseCid throws an exception if parsing fails
@@ -57,7 +53,6 @@ export async function validateAuthorizationIntegrity(
5753
// check to ensure that no other unexpected properties exist in payload.
5854
const allowedProperties = authorizationPayloadConstraints?.allowedProperties ?? new Set();
5955
const customProperties = { ...payloadJson };
60-
delete customProperties.target;
6156
delete customProperties.descriptorCid;
6257
for (const propertyName in customProperties) {
6358
{
@@ -89,9 +84,9 @@ export async function authenticate(jws: GeneralJws, didResolver: DidResolver): P
8984
* Authorizes the incoming message.
9085
* @throws {Error} if fails authentication
9186
*/
92-
export async function authorize(incomingMessage: Message): Promise<void> {
93-
// if author/requester is the same as the target DID, we can directly grant access
94-
if (incomingMessage.author === incomingMessage.target) {
87+
export async function authorize(tenant: string, incomingMessage: Message): Promise<void> {
88+
// if author/requester is the same as the target tenant, we can directly grant access
89+
if (incomingMessage.author === tenant) {
9590
return;
9691
} else {
9792
throw new Error('message failed authorization, permission grant check not yet implemented');

src/core/message.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,12 @@ export abstract class Message {
2424

2525
// commonly used properties for extra convenience;
2626
readonly author: string;
27-
readonly target: string;
2827

2928
constructor(message: BaseMessage) {
3029
this.message = message;
3130
this.authorizationPayload = GeneralJwsVerifier.decodePlainObjectPayload(message.authorization);
3231

3332
this.author = Message.getAuthor(message);
34-
this.target = this.authorizationPayload.target;
3533
}
3634

3735
/**
@@ -112,19 +110,17 @@ export abstract class Message {
112110
/**
113111
* Signs the provided message to be used an `authorization` property. Signed payload includes the CID of the message's descriptor by default
114112
* along with any additional payload properties provided
115-
* @param target - the logical DID where this message will be sent to
116113
* @param descriptor - the message to sign
117114
* @param signatureInput - the signature material to use (e.g. key and header data)
118115
* @returns General JWS signature used as an `authorization` property.
119116
*/
120117
public static async signAsAuthorization(
121-
target: string,
122118
descriptor: Descriptor,
123119
signatureInput: SignatureInput
124120
): Promise<GeneralJws> {
125121
const descriptorCid = await generateCid(descriptor);
126122

127-
const authPayload: BaseDecodedAuthorizationPayload = { target, descriptorCid: descriptorCid.toString() };
123+
const authPayload: BaseDecodedAuthorizationPayload = { descriptorCid: descriptorCid.toString() };
128124
const authPayloadStr = JSON.stringify(authPayload);
129125
const authPayloadBytes = new TextEncoder().encode(authPayloadStr);
130126

src/core/protocol-authorization.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@ export class ProtocolAuthorization {
1616
* @throws {Error} if authorization fails.
1717
*/
1818
public static async authorize(
19+
tenant: string,
1920
recordsWrite: RecordsWrite,
2021
requesterDid: string,
2122
messageStore: MessageStore
2223
): Promise<void> {
2324
// fetch the protocol definition
24-
const protocolDefinition = await ProtocolAuthorization.fetchProtocolDefinition(recordsWrite, messageStore);
25+
const protocolDefinition = await ProtocolAuthorization.fetchProtocolDefinition(tenant, recordsWrite, messageStore);
2526

2627
// fetch ancestor message chain
27-
const ancestorMessageChain: RecordsWriteMessage[] = await ProtocolAuthorization.constructAncestorMessageChain(recordsWrite, messageStore);
28+
const ancestorMessageChain: RecordsWriteMessage[] = await ProtocolAuthorization.constructAncestorMessageChain(tenant, recordsWrite, messageStore);
2829

2930
// record schema -> schema label map
3031
const recordSchemaToLabelMap: Map<string, string> = new Map();
@@ -41,12 +42,11 @@ export class ProtocolAuthorization {
4142
);
4243

4344
// verify the requester of the inbound message against allowed requester rule
44-
ProtocolAuthorization.verifyAllowedRequester(
45-
requesterDid, recordsWrite.target, inboundMessageRuleSet, ancestorMessageChain, recordSchemaToLabelMap
45+
ProtocolAuthorization.verifyAllowedRequester(tenant, requesterDid, inboundMessageRuleSet, ancestorMessageChain, recordSchemaToLabelMap
4646
);
4747

4848
// verify method invoked against the allowed actions
49-
ProtocolAuthorization.verifyAllowedActions(requesterDid, recordsWrite, inboundMessageRuleSet);
49+
ProtocolAuthorization.verifyAllowedActions(tenant, requesterDid, recordsWrite, inboundMessageRuleSet);
5050

5151
// verify allowed condition of the write
5252
await ProtocolAuthorization.verifyActionCondition(recordsWrite, messageStore);
@@ -55,13 +55,13 @@ export class ProtocolAuthorization {
5555
/**
5656
* Fetches the protocol definition based on the protocol specified in the given message.
5757
*/
58-
private static async fetchProtocolDefinition(recordsWrite: RecordsWrite, messageStore: MessageStore): Promise<ProtocolDefinition> {
58+
private static async fetchProtocolDefinition(tenant: string, recordsWrite: RecordsWrite, messageStore: MessageStore): Promise<ProtocolDefinition> {
5959
// get the protocol URI
6060
const protocolUri = recordsWrite.message.descriptor.protocol;
6161

6262
// fetch the corresponding protocol definition
6363
const query = {
64-
target : recordsWrite.target,
64+
tenant,
6565
method : DwnMethodName.ProtocolsConfigure,
6666
protocol : protocolUri
6767
};
@@ -79,7 +79,7 @@ export class ProtocolAuthorization {
7979
* Constructs a chain of ancestor messages
8080
* @returns the ancestor chain of messages where the first element is the root of the chain; returns empty array if no parent is specified.
8181
*/
82-
private static async constructAncestorMessageChain(recordsWrite: RecordsWrite, messageStore: MessageStore)
82+
private static async constructAncestorMessageChain(tenant: string, recordsWrite: RecordsWrite, messageStore: MessageStore)
8383
: Promise<RecordsWriteMessage[]> {
8484
const ancestorMessageChain: RecordsWriteMessage[] = [];
8585

@@ -91,7 +91,7 @@ export class ProtocolAuthorization {
9191
while (currentParentId !== undefined) {
9292
// fetch parent
9393
const query = {
94-
target : recordsWrite.target,
94+
tenant,
9595
method : DwnMethodName.RecordsWrite,
9696
protocol,
9797
contextId,
@@ -157,16 +157,16 @@ export class ProtocolAuthorization {
157157
* @throws {Error} if requester not allowed.
158158
*/
159159
private static verifyAllowedRequester(
160+
tenant: string,
160161
requesterDid: string,
161-
targetDid: string,
162162
inboundMessageRuleSet: ProtocolRuleSet,
163163
ancestorMessageChain: RecordsWriteMessage[],
164164
recordSchemaToLabelMap: Map<string, string>
165165
): void {
166166
const allowRule = inboundMessageRuleSet.allow;
167167
if (allowRule === undefined) {
168-
// if no allow rule is defined, still allow if requester is the same as target, but throw otherwise
169-
if (requesterDid !== targetDid) {
168+
// if no allow rule is defined, still allow if requester is the same as target tenant, but throw otherwise
169+
if (requesterDid !== tenant) {
170170
throw new Error(`no allow rule defined for requester, ${requesterDid} is unauthorized`);
171171
}
172172
} else if (allowRule.anyone !== undefined) {
@@ -189,13 +189,13 @@ export class ProtocolAuthorization {
189189
* Verifies the actions specified in the given message matches the allowed actions in the rule set.
190190
* @throws {Error} if action not allowed.
191191
*/
192-
private static verifyAllowedActions(requesterDid: string, incomingMessage: Message, inboundMessageRuleSet: ProtocolRuleSet): void {
192+
private static verifyAllowedActions(tenant: string, requesterDid: string, incomingMessage: Message, inboundMessageRuleSet: ProtocolRuleSet): void {
193193
const allowRule = inboundMessageRuleSet.allow;
194194
const incomingMessageMethod = incomingMessage.message.descriptor.method;
195195

196196
if (allowRule === undefined) {
197197
// if no allow rule is defined, owner of DWN can do everything
198-
if (requesterDid === incomingMessage.target) {
198+
if (requesterDid === tenant) {
199199
return;
200200
} else {
201201
throw new Error(`no allow rule defined for ${incomingMessageMethod}, ${requesterDid} is unauthorized`);

src/core/response.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ type ResponseOptions = {
1515
*/
1616
export class Response {
1717
// present ONLY if there is a general request-related issue
18-
// e.g. malformed request, invalid target. `status` and `replies` are mutually exclusive
18+
// e.g. malformed request.
19+
// `status` and `replies` are mutually exclusive
1920
status?: ResponseStatus;
2021
// responses to individual messages provided within a request
2122
replies?: MessageReply[];

src/core/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export type BaseMessage = {
1212
* Type of common decoded `authorization`property payload.
1313
*/
1414
export type BaseDecodedAuthorizationPayload = {
15-
target: string;
1615
descriptorCid: string;
1716
};
1817

src/dwn.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import { MessageStoreLevel } from './store/message-store-level.js';
1111
import { Request } from './core/request.js';
1212
import { Response } from './core/response.js';
1313

14-
import { CollectionsInterface } from './interfaces/records/records-interface.js';
1514
import { PermissionsInterface } from './interfaces/permissions/permissions-interface.js';
1615
import { ProtocolsInterface } from './interfaces/protocols/protocols-interface.js';
16+
import { RecordsInterface } from './interfaces/records/records-interface.js';
1717

1818
export class Dwn {
1919
static methodHandlers: { [key:string]: MethodHandler } = {
20-
...CollectionsInterface.methodHandlers,
20+
...RecordsInterface.methodHandlers,
2121
...PermissionsInterface.methodHandlers,
2222
...ProtocolsInterface.methodHandlers
2323
};
@@ -59,7 +59,11 @@ export class Dwn {
5959
return this.messageStore.close();
6060
}
6161

62-
async processRequest(rawRequest: Uint8Array): Promise<Response> {
62+
/**
63+
* Processes the given DWN request and returns with a DWN response.
64+
* @param tenant The tenant DID to route the given request to.
65+
*/
66+
async processRequest(tenant: string, rawRequest: Uint8Array): Promise<Response> {
6367
let request: RequestSchema;
6468
try {
6569
const requestString = Encoder.bytesToString(rawRequest);
@@ -81,7 +85,7 @@ export class Dwn {
8185
for (const message of request.messages) {
8286
let result;
8387
try {
84-
result = await this.processMessage(message);
88+
result = await this.processMessage(tenant, message);
8589
} catch (error) {
8690
result = new MessageReply({
8791
status: { code: 500, detail: error.message }
@@ -95,9 +99,10 @@ export class Dwn {
9599
}
96100

97101
/**
98-
* Processes the given DWN message.
102+
* Processes the given DWN message and returns with a reply.
103+
* @param tenant The tenant DID to route the given message to.
99104
*/
100-
async processMessage(rawMessage: any): Promise<MessageReply> {
105+
async processMessage(tenant: string, rawMessage: any): Promise<MessageReply> {
101106
const dwnMethod = rawMessage?.descriptor?.method;
102107
if (dwnMethod === undefined) {
103108
return new MessageReply({
@@ -116,7 +121,7 @@ export class Dwn {
116121

117122
const interfaceMethodHandler = Dwn.methodHandlers[dwnMethod];
118123

119-
const methodHandlerReply = await interfaceMethodHandler(rawMessage as BaseMessage, this.messageStore, this.DidResolver);
124+
const methodHandlerReply = await interfaceMethodHandler(tenant, rawMessage as BaseMessage, this.messageStore, this.DidResolver);
120125
return methodHandlerReply;
121126
}
122127
};

src/interfaces/hooks/messages/hooks-write.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { DwnMethodName, Message } from '../../../core/message.js';
1010
* Input to `HookssWrite.create()`.
1111
*/
1212
export type HooksWriteOptions = AuthCreateOptions & {
13-
target: string,
1413
dateCreated?: string,
1514
/**
1615
* leave as `undefined` for customer handler.
@@ -49,7 +48,7 @@ export class HooksWrite extends Message {
4948

5049
Message.validateJsonSchema({ descriptor, authorization: { } });
5150

52-
const authorization = await Message.signAsAuthorization(options.target, descriptor, options.signatureInput);
51+
const authorization = await Message.signAsAuthorization(descriptor, options.signatureInput);
5352
const message = { descriptor, authorization };
5453

5554
const hooksWrite = new HooksWrite(message);

src/interfaces/permissions/handlers/permissions-request.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,31 @@ import { MessageReply } from '../../../core/message-reply.js';
66
import { PermissionsRequest } from '../messages/permissions-request.js';
77

88
export const handlePermissionsRequest: MethodHandler = async (
9+
tenant,
910
message,
1011
messageStore,
1112
didResolver
1213
): Promise<MessageReply> => {
1314
const permissionRequest = await PermissionsRequest.parse(message as PermissionsRequestMessage);
14-
const { author, target } = permissionRequest;
15+
const { author } = permissionRequest;
1516

16-
if (permissionRequest.target !== permissionRequest.grantedBy && permissionRequest.target !== permissionRequest.grantedTo) {
17+
if (tenant !== permissionRequest.grantedBy && tenant !== permissionRequest.grantedTo) {
1718
return new MessageReply({
1819
status: { code: 400, detail: 'grantedBy or grantedTo must be the targeted message recipient' }
1920
});
2021
}
2122

22-
await canonicalAuth(permissionRequest, didResolver);
23+
await canonicalAuth(tenant, permissionRequest, didResolver);
2324

2425
if (author !== permissionRequest.grantedTo) {
2526
throw new Error('grantee must be signer');
2627
}
2728

28-
const index = { author, target, ... message.descriptor };
29+
const index = {
30+
tenant,
31+
author,
32+
... message.descriptor
33+
};
2934
await messageStore.put(message, index);
3035

3136
return new MessageReply({

0 commit comments

Comments
 (0)