diff --git a/README.md b/README.md index 99f295542..89f25b0be 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Decentralized Web Node (DWN) SDK Code Coverage -![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) +![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) ## Introduction diff --git a/src/core/auth.ts b/src/core/auth.ts index 65948ffaf..5732f0c99 100644 --- a/src/core/auth.ts +++ b/src/core/auth.ts @@ -1,7 +1,6 @@ import type { BaseMessage } from './types.js'; import { CID } from 'multiformats'; -import { Did } from '../did/did.js'; import { DidResolver } from '../did/did-resolver.js'; import { GeneralJws } from '../jose/jws/general/types.js'; import { GeneralJwsVerifier } from '../jose/jws/general/verifier.js'; @@ -20,32 +19,29 @@ type AuthorizationPayloadConstraints = { * @throws {Error} if auth fails */ export async function canonicalAuth( + tenant: string, incomingMessage: Message, didResolver: DidResolver ): Promise { await authenticate(incomingMessage.message.authorization, didResolver); - await authorize(incomingMessage); + await authorize(tenant, incomingMessage); } /** * Validates the data integrity of the `authorization` property. - * NOTE: `target` and `descriptorCid` are both checked by default * NOTE signature is not verified. */ export async function validateAuthorizationIntegrity( message: BaseMessage, authorizationPayloadConstraints?: AuthorizationPayloadConstraints -): Promise<{ target: string, descriptorCid: CID, [key: string]: any }> { +): Promise<{ descriptorCid: CID, [key: string]: any }> { if (message.authorization.signatures.length !== 1) { throw new Error('expected no more than 1 signature for authorization'); } const payloadJson = GeneralJwsVerifier.decodePlainObjectPayload(message.authorization); - const { target, descriptorCid } = payloadJson; - - // `target` validation - Did.validate(target); + const { descriptorCid } = payloadJson; // `descriptorCid` validation - ensure that the provided descriptorCid matches the CID of the actual message const providedDescriptorCid = parseCid(descriptorCid); // parseCid throws an exception if parsing fails @@ -57,7 +53,6 @@ export async function validateAuthorizationIntegrity( // check to ensure that no other unexpected properties exist in payload. const allowedProperties = authorizationPayloadConstraints?.allowedProperties ?? new Set(); const customProperties = { ...payloadJson }; - delete customProperties.target; delete customProperties.descriptorCid; for (const propertyName in customProperties) { { @@ -89,9 +84,9 @@ export async function authenticate(jws: GeneralJws, didResolver: DidResolver): P * Authorizes the incoming message. * @throws {Error} if fails authentication */ -export async function authorize(incomingMessage: Message): Promise { - // if author/requester is the same as the target DID, we can directly grant access - if (incomingMessage.author === incomingMessage.target) { +export async function authorize(tenant: string, incomingMessage: Message): Promise { + // if author/requester is the same as the target tenant, we can directly grant access + if (incomingMessage.author === tenant) { return; } else { throw new Error('message failed authorization, permission grant check not yet implemented'); diff --git a/src/core/message.ts b/src/core/message.ts index 1ccf7b963..e0607491a 100644 --- a/src/core/message.ts +++ b/src/core/message.ts @@ -24,14 +24,12 @@ export abstract class Message { // commonly used properties for extra convenience; readonly author: string; - readonly target: string; constructor(message: BaseMessage) { this.message = message; this.authorizationPayload = GeneralJwsVerifier.decodePlainObjectPayload(message.authorization); this.author = Message.getAuthor(message); - this.target = this.authorizationPayload.target; } /** @@ -112,19 +110,17 @@ export abstract class Message { /** * Signs the provided message to be used an `authorization` property. Signed payload includes the CID of the message's descriptor by default * along with any additional payload properties provided - * @param target - the logical DID where this message will be sent to * @param descriptor - the message to sign * @param signatureInput - the signature material to use (e.g. key and header data) * @returns General JWS signature used as an `authorization` property. */ public static async signAsAuthorization( - target: string, descriptor: Descriptor, signatureInput: SignatureInput ): Promise { const descriptorCid = await generateCid(descriptor); - const authPayload: BaseDecodedAuthorizationPayload = { target, descriptorCid: descriptorCid.toString() }; + const authPayload: BaseDecodedAuthorizationPayload = { descriptorCid: descriptorCid.toString() }; const authPayloadStr = JSON.stringify(authPayload); const authPayloadBytes = new TextEncoder().encode(authPayloadStr); diff --git a/src/core/protocol-authorization.ts b/src/core/protocol-authorization.ts index 6e000e923..70109cbd0 100644 --- a/src/core/protocol-authorization.ts +++ b/src/core/protocol-authorization.ts @@ -16,15 +16,16 @@ export class ProtocolAuthorization { * @throws {Error} if authorization fails. */ public static async authorize( + tenant: string, recordsWrite: RecordsWrite, requesterDid: string, messageStore: MessageStore ): Promise { // fetch the protocol definition - const protocolDefinition = await ProtocolAuthorization.fetchProtocolDefinition(recordsWrite, messageStore); + const protocolDefinition = await ProtocolAuthorization.fetchProtocolDefinition(tenant, recordsWrite, messageStore); // fetch ancestor message chain - const ancestorMessageChain: RecordsWriteMessage[] = await ProtocolAuthorization.constructAncestorMessageChain(recordsWrite, messageStore); + const ancestorMessageChain: RecordsWriteMessage[] = await ProtocolAuthorization.constructAncestorMessageChain(tenant, recordsWrite, messageStore); // record schema -> schema label map const recordSchemaToLabelMap: Map = new Map(); @@ -41,12 +42,11 @@ export class ProtocolAuthorization { ); // verify the requester of the inbound message against allowed requester rule - ProtocolAuthorization.verifyAllowedRequester( - requesterDid, recordsWrite.target, inboundMessageRuleSet, ancestorMessageChain, recordSchemaToLabelMap + ProtocolAuthorization.verifyAllowedRequester(tenant, requesterDid, inboundMessageRuleSet, ancestorMessageChain, recordSchemaToLabelMap ); // verify method invoked against the allowed actions - ProtocolAuthorization.verifyAllowedActions(requesterDid, recordsWrite, inboundMessageRuleSet); + ProtocolAuthorization.verifyAllowedActions(tenant, requesterDid, recordsWrite, inboundMessageRuleSet); // verify allowed condition of the write await ProtocolAuthorization.verifyActionCondition(recordsWrite, messageStore); @@ -55,13 +55,13 @@ export class ProtocolAuthorization { /** * Fetches the protocol definition based on the protocol specified in the given message. */ - private static async fetchProtocolDefinition(recordsWrite: RecordsWrite, messageStore: MessageStore): Promise { + private static async fetchProtocolDefinition(tenant: string, recordsWrite: RecordsWrite, messageStore: MessageStore): Promise { // get the protocol URI const protocolUri = recordsWrite.message.descriptor.protocol; // fetch the corresponding protocol definition const query = { - target : recordsWrite.target, + tenant, method : DwnMethodName.ProtocolsConfigure, protocol : protocolUri }; @@ -79,7 +79,7 @@ export class ProtocolAuthorization { * Constructs a chain of ancestor messages * @returns the ancestor chain of messages where the first element is the root of the chain; returns empty array if no parent is specified. */ - private static async constructAncestorMessageChain(recordsWrite: RecordsWrite, messageStore: MessageStore) + private static async constructAncestorMessageChain(tenant: string, recordsWrite: RecordsWrite, messageStore: MessageStore) : Promise { const ancestorMessageChain: RecordsWriteMessage[] = []; @@ -91,7 +91,7 @@ export class ProtocolAuthorization { while (currentParentId !== undefined) { // fetch parent const query = { - target : recordsWrite.target, + tenant, method : DwnMethodName.RecordsWrite, protocol, contextId, @@ -157,16 +157,16 @@ export class ProtocolAuthorization { * @throws {Error} if requester not allowed. */ private static verifyAllowedRequester( + tenant: string, requesterDid: string, - targetDid: string, inboundMessageRuleSet: ProtocolRuleSet, ancestorMessageChain: RecordsWriteMessage[], recordSchemaToLabelMap: Map ): void { const allowRule = inboundMessageRuleSet.allow; if (allowRule === undefined) { - // if no allow rule is defined, still allow if requester is the same as target, but throw otherwise - if (requesterDid !== targetDid) { + // if no allow rule is defined, still allow if requester is the same as target tenant, but throw otherwise + if (requesterDid !== tenant) { throw new Error(`no allow rule defined for requester, ${requesterDid} is unauthorized`); } } else if (allowRule.anyone !== undefined) { @@ -189,13 +189,13 @@ export class ProtocolAuthorization { * Verifies the actions specified in the given message matches the allowed actions in the rule set. * @throws {Error} if action not allowed. */ - private static verifyAllowedActions(requesterDid: string, incomingMessage: Message, inboundMessageRuleSet: ProtocolRuleSet): void { + private static verifyAllowedActions(tenant: string, requesterDid: string, incomingMessage: Message, inboundMessageRuleSet: ProtocolRuleSet): void { const allowRule = inboundMessageRuleSet.allow; const incomingMessageMethod = incomingMessage.message.descriptor.method; if (allowRule === undefined) { // if no allow rule is defined, owner of DWN can do everything - if (requesterDid === incomingMessage.target) { + if (requesterDid === tenant) { return; } else { throw new Error(`no allow rule defined for ${incomingMessageMethod}, ${requesterDid} is unauthorized`); diff --git a/src/core/response.ts b/src/core/response.ts index 5584d2fc5..f87044f31 100644 --- a/src/core/response.ts +++ b/src/core/response.ts @@ -15,7 +15,8 @@ type ResponseOptions = { */ export class Response { // present ONLY if there is a general request-related issue - // e.g. malformed request, invalid target. `status` and `replies` are mutually exclusive + // e.g. malformed request. + // `status` and `replies` are mutually exclusive status?: ResponseStatus; // responses to individual messages provided within a request replies?: MessageReply[]; diff --git a/src/core/types.ts b/src/core/types.ts index 0e5837b85..c793a4c99 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -12,7 +12,6 @@ export type BaseMessage = { * Type of common decoded `authorization`property payload. */ export type BaseDecodedAuthorizationPayload = { - target: string; descriptorCid: string; }; diff --git a/src/dwn.ts b/src/dwn.ts index dc1522788..1c3d4e4d5 100644 --- a/src/dwn.ts +++ b/src/dwn.ts @@ -11,13 +11,13 @@ import { MessageStoreLevel } from './store/message-store-level.js'; import { Request } from './core/request.js'; import { Response } from './core/response.js'; -import { CollectionsInterface } from './interfaces/records/records-interface.js'; import { PermissionsInterface } from './interfaces/permissions/permissions-interface.js'; import { ProtocolsInterface } from './interfaces/protocols/protocols-interface.js'; +import { RecordsInterface } from './interfaces/records/records-interface.js'; export class Dwn { static methodHandlers: { [key:string]: MethodHandler } = { - ...CollectionsInterface.methodHandlers, + ...RecordsInterface.methodHandlers, ...PermissionsInterface.methodHandlers, ...ProtocolsInterface.methodHandlers }; @@ -59,7 +59,11 @@ export class Dwn { return this.messageStore.close(); } - async processRequest(rawRequest: Uint8Array): Promise { + /** + * Processes the given DWN request and returns with a DWN response. + * @param tenant The tenant DID to route the given request to. + */ + async processRequest(tenant: string, rawRequest: Uint8Array): Promise { let request: RequestSchema; try { const requestString = Encoder.bytesToString(rawRequest); @@ -81,7 +85,7 @@ export class Dwn { for (const message of request.messages) { let result; try { - result = await this.processMessage(message); + result = await this.processMessage(tenant, message); } catch (error) { result = new MessageReply({ status: { code: 500, detail: error.message } @@ -95,9 +99,10 @@ export class Dwn { } /** - * Processes the given DWN message. + * Processes the given DWN message and returns with a reply. + * @param tenant The tenant DID to route the given message to. */ - async processMessage(rawMessage: any): Promise { + async processMessage(tenant: string, rawMessage: any): Promise { const dwnMethod = rawMessage?.descriptor?.method; if (dwnMethod === undefined) { return new MessageReply({ @@ -116,7 +121,7 @@ export class Dwn { const interfaceMethodHandler = Dwn.methodHandlers[dwnMethod]; - const methodHandlerReply = await interfaceMethodHandler(rawMessage as BaseMessage, this.messageStore, this.DidResolver); + const methodHandlerReply = await interfaceMethodHandler(tenant, rawMessage as BaseMessage, this.messageStore, this.DidResolver); return methodHandlerReply; } }; diff --git a/src/interfaces/hooks/messages/hooks-write.ts b/src/interfaces/hooks/messages/hooks-write.ts index e2fcac365..cef20af99 100644 --- a/src/interfaces/hooks/messages/hooks-write.ts +++ b/src/interfaces/hooks/messages/hooks-write.ts @@ -10,7 +10,6 @@ import { DwnMethodName, Message } from '../../../core/message.js'; * Input to `HookssWrite.create()`. */ export type HooksWriteOptions = AuthCreateOptions & { - target: string, dateCreated?: string, /** * leave as `undefined` for customer handler. @@ -49,7 +48,7 @@ export class HooksWrite extends Message { Message.validateJsonSchema({ descriptor, authorization: { } }); - const authorization = await Message.signAsAuthorization(options.target, descriptor, options.signatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.signatureInput); const message = { descriptor, authorization }; const hooksWrite = new HooksWrite(message); diff --git a/src/interfaces/permissions/handlers/permissions-request.ts b/src/interfaces/permissions/handlers/permissions-request.ts index 58ed0284a..37bb15708 100644 --- a/src/interfaces/permissions/handlers/permissions-request.ts +++ b/src/interfaces/permissions/handlers/permissions-request.ts @@ -6,26 +6,31 @@ import { MessageReply } from '../../../core/message-reply.js'; import { PermissionsRequest } from '../messages/permissions-request.js'; export const handlePermissionsRequest: MethodHandler = async ( + tenant, message, messageStore, didResolver ): Promise => { const permissionRequest = await PermissionsRequest.parse(message as PermissionsRequestMessage); - const { author, target } = permissionRequest; + const { author } = permissionRequest; - if (permissionRequest.target !== permissionRequest.grantedBy && permissionRequest.target !== permissionRequest.grantedTo) { + if (tenant !== permissionRequest.grantedBy && tenant !== permissionRequest.grantedTo) { return new MessageReply({ status: { code: 400, detail: 'grantedBy or grantedTo must be the targeted message recipient' } }); } - await canonicalAuth(permissionRequest, didResolver); + await canonicalAuth(tenant, permissionRequest, didResolver); if (author !== permissionRequest.grantedTo) { throw new Error('grantee must be signer'); } - const index = { author, target, ... message.descriptor }; + const index = { + tenant, + author, + ... message.descriptor + }; await messageStore.put(message, index); return new MessageReply({ diff --git a/src/interfaces/permissions/messages/permissions-grant.ts b/src/interfaces/permissions/messages/permissions-grant.ts index 24e383cf0..417cef148 100644 --- a/src/interfaces/permissions/messages/permissions-grant.ts +++ b/src/interfaces/permissions/messages/permissions-grant.ts @@ -11,7 +11,6 @@ import { v4 as uuidv4 } from 'uuid'; import { DEFAULT_CONDITIONS, PermissionsRequest } from './permissions-request'; type PermissionsGrantOptions = AuthCreateOptions & { - target: string, dateCreated?: string; conditions?: PermissionConditions; description: string; @@ -47,7 +46,7 @@ export class PermissionsGrant extends Message { Message.validateJsonSchema({ descriptor, authorization: { } }); - const authorization = await Message.signAsAuthorization(options.target, descriptor, options.signatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.signatureInput); const message: PermissionsGrantMessage = { descriptor, authorization }; return new PermissionsGrant(message); @@ -56,13 +55,11 @@ export class PermissionsGrant extends Message { /** * generates a PermissionsGrant using the provided PermissionsRequest - * @param target - the DID of the DWN to this grant message will be sent to * @param permissionsRequest * @param signatureInput - the private key and additional signature material of the grantor * @param conditionOverrides - any conditions that the grantor may want to override */ static async fromPermissionsRequest( - target: string, permissionsRequest: PermissionsRequest, signatureInput: SignatureInput, conditionOverrides: Partial = {} @@ -70,7 +67,6 @@ export class PermissionsGrant extends Message { const conditions = { ...permissionsRequest.conditions, ...conditionOverrides }; return PermissionsGrant.create({ - target, conditions : conditions, description : permissionsRequest.description, grantedBy : permissionsRequest.grantedBy, @@ -83,12 +79,11 @@ export class PermissionsGrant extends Message { /** * delegates the permission to the DID provided - * @param target - the DID of the DWN to this grant message will be sent to * @param to - the DID of the grantee * @param signatureInput - the private key and additional signature material of this permission's `grantedTo` * @throws {Error} - if the permission cannot be delegated */ - async delegate(target: string, to: string, signatureInput: SignatureInput): Promise { + async delegate(to: string, signatureInput: SignatureInput): Promise { // throw an exception if the permission cannot be delegated if (!this.conditions.delegation) { throw new Error('this permission cannot be delegated'); @@ -96,7 +91,6 @@ export class PermissionsGrant extends Message { // `grantedBy` of the delegated permission will be `grantedTo` of the permission being delegated because the grantee is the delegator const delegatedGrant = await PermissionsGrant.create({ - target, conditions : this.conditions, description : this.description, grantedBy : this.grantedTo, diff --git a/src/interfaces/permissions/messages/permissions-request.ts b/src/interfaces/permissions/messages/permissions-request.ts index 8d32baa74..2dce099a1 100644 --- a/src/interfaces/permissions/messages/permissions-request.ts +++ b/src/interfaces/permissions/messages/permissions-request.ts @@ -8,7 +8,6 @@ import { v4 as uuidv4 } from 'uuid'; import { validateAuthorizationIntegrity } from '../../../core/auth.js'; type PermissionsRequestOptions = AuthCreateOptions & { - target: string; dateCreated?: string; conditions?: PermissionConditions; description: string; @@ -49,7 +48,7 @@ export class PermissionsRequest extends Message { Message.validateJsonSchema({ descriptor, authorization: { } }); - const auth = await Message.signAsAuthorization(options.target, descriptor, options.signatureInput); + const auth = await Message.signAsAuthorization(descriptor, options.signatureInput); const message: PermissionsRequestMessage = { descriptor, authorization: auth }; return new PermissionsRequest(message); diff --git a/src/interfaces/protocols/handlers/protocols-configure.ts b/src/interfaces/protocols/handlers/protocols-configure.ts index 04f3d58d9..ef9ab20c5 100644 --- a/src/interfaces/protocols/handlers/protocols-configure.ts +++ b/src/interfaces/protocols/handlers/protocols-configure.ts @@ -8,6 +8,7 @@ import { ProtocolsConfigure } from '../messages/protocols-configure.js'; import { DwnMethodName, Message } from '../../../core/message.js'; export const handleProtocolsConfigure: MethodHandler = async ( + tenant, message, messageStore, didResolver @@ -25,7 +26,7 @@ export const handleProtocolsConfigure: MethodHandler = async ( // authentication & authorization try { - await canonicalAuth(protocolsConfigure, didResolver); + await canonicalAuth(tenant, protocolsConfigure, didResolver); } catch (e) { return new MessageReply({ status: { code: 401, detail: e.message } @@ -34,7 +35,7 @@ export const handleProtocolsConfigure: MethodHandler = async ( // attempt to get existing protocol const query = { - target : protocolsConfigure.target, + tenant, method : DwnMethodName.ProtocolsConfigure, protocol : incomingMessage.descriptor.protocol }; @@ -48,11 +49,15 @@ export const handleProtocolsConfigure: MethodHandler = async ( newestMessage = incomingMessage; } - // write the incoming message to DB if incoming message is largest + // write the incoming message to DB if incoming message is newest let messageReply: MessageReply; if (incomingMessageIsNewest) { - const { author, target } = protocolsConfigure; - const index = { author, target, ... message.descriptor }; + const { author } = protocolsConfigure; + const index = { + tenant, + author, + ... message.descriptor + }; await messageStore.put(message, index); messageReply = new MessageReply({ diff --git a/src/interfaces/protocols/handlers/protocols-query.ts b/src/interfaces/protocols/handlers/protocols-query.ts index b16e2927e..4aeae0b64 100644 --- a/src/interfaces/protocols/handlers/protocols-query.ts +++ b/src/interfaces/protocols/handlers/protocols-query.ts @@ -8,6 +8,7 @@ import { ProtocolsQuery } from '../messages/protocols-query.js'; import { removeUndefinedProperties } from '../../../utils/object.js'; export const handleProtocolsQuery: MethodHandler = async ( + tenant, message, messageStore, didResolver @@ -24,7 +25,7 @@ export const handleProtocolsQuery: MethodHandler = async ( } try { - await canonicalAuth(protocolsQuery, didResolver); + await canonicalAuth(tenant, protocolsQuery, didResolver); } catch (e) { return new MessageReply({ status: { code: 401, detail: e.message } @@ -32,8 +33,8 @@ export const handleProtocolsQuery: MethodHandler = async ( } const query = { - target : protocolsQuery.target, - method : DwnMethodName.ProtocolsConfigure, + tenant, + method: DwnMethodName.ProtocolsConfigure, ...incomingMessage.descriptor.filter }; removeUndefinedProperties(query); diff --git a/src/interfaces/protocols/messages/protocols-configure.ts b/src/interfaces/protocols/messages/protocols-configure.ts index 15b3d21c6..20503b69a 100644 --- a/src/interfaces/protocols/messages/protocols-configure.ts +++ b/src/interfaces/protocols/messages/protocols-configure.ts @@ -7,7 +7,6 @@ import { validateAuthorizationIntegrity } from '../../../core/auth.js'; import { DwnMethodName, Message } from '../../../core/message.js'; export type ProtocolsConfigureOptions = AuthCreateOptions & { - target: string; dateCreated? : string; protocol: string; definition : ProtocolDefinition; @@ -36,7 +35,7 @@ export class ProtocolsConfigure extends Message { Message.validateJsonSchema({ descriptor, authorization: { } }); - const authorization = await Message.signAsAuthorization(options.target, descriptor, options.signatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.signatureInput); const message = { descriptor, authorization }; const protocolsConfigure = new ProtocolsConfigure(message); diff --git a/src/interfaces/protocols/messages/protocols-query.ts b/src/interfaces/protocols/messages/protocols-query.ts index 3a0a7e8fe..3e3daa01a 100644 --- a/src/interfaces/protocols/messages/protocols-query.ts +++ b/src/interfaces/protocols/messages/protocols-query.ts @@ -8,7 +8,6 @@ import { validateAuthorizationIntegrity } from '../../../core/auth.js'; import { DwnMethodName, Message } from '../../../core/message.js'; export type ProtocolsQueryOptions = AuthCreateOptions & { - target: string; dateCreated?: string; filter?: { protocol: string; @@ -41,7 +40,7 @@ export class ProtocolsQuery extends Message { Message.validateJsonSchema({ descriptor, authorization: { } }); - const authorization = await Message.signAsAuthorization(options.target, descriptor, options.signatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.signatureInput); const message = { descriptor, authorization }; const protocolsQuery = new ProtocolsQuery(message); diff --git a/src/interfaces/records/handlers/records-query.ts b/src/interfaces/records/handlers/records-query.ts index 8245a02e1..5dd8d4f55 100644 --- a/src/interfaces/records/handlers/records-query.ts +++ b/src/interfaces/records/handlers/records-query.ts @@ -11,6 +11,7 @@ import { removeUndefinedProperties } from '../../../utils/object.js'; import { DateSort, RecordsQuery } from '../messages/records-query.js'; export const handleRecordsQuery: MethodHandler = async ( + tenant, message, messageStore, didResolver @@ -26,7 +27,7 @@ export const handleRecordsQuery: MethodHandler = async ( try { await authenticate(message.authorization, didResolver); - await recordsQuery.authorize(); + await recordsQuery.authorize(tenant); } catch (e) { return new MessageReply({ status: { code: 401, detail: e.message } @@ -34,10 +35,10 @@ export const handleRecordsQuery: MethodHandler = async ( } let records: BaseMessage[]; - if (recordsQuery.author === recordsQuery.target) { - records = await fetchRecordsAsOwner(recordsQuery, messageStore); + if (recordsQuery.author === tenant) { + records = await fetchRecordsAsOwner(tenant, recordsQuery, messageStore); } else { - records = await fetchRecordsAsNonOwner(recordsQuery, messageStore); + records = await fetchRecordsAsNonOwner(tenant, recordsQuery, messageStore); } // sort if `dataSort` is specified @@ -61,10 +62,10 @@ export const handleRecordsQuery: MethodHandler = async ( /** * Fetches the records as the owner of the DWN with no additional filtering. */ -async function fetchRecordsAsOwner(recordsQuery: RecordsQuery, messageStore: MessageStore): Promise { +async function fetchRecordsAsOwner(tenant: string, recordsQuery: RecordsQuery, messageStore: MessageStore): Promise { // fetch all published records matching the query const includeCriteria = { - target : recordsQuery.target, + tenant, method : DwnMethodName.RecordsWrite, isLatestBaseState : 'true', ...recordsQuery.message.descriptor.filter @@ -80,11 +81,11 @@ async function fetchRecordsAsOwner(recordsQuery: RecordsQuery, messageStore: Mes * 1. published records; and * 2. unpublished records intended for the requester (where `recipient` is the requester) */ -async function fetchRecordsAsNonOwner(recordsQuery: RecordsQuery, messageStore: MessageStore) +async function fetchRecordsAsNonOwner(tenant: string, recordsQuery: RecordsQuery, messageStore: MessageStore) : Promise { - const publishedRecords = await fetchPublishedRecords(recordsQuery, messageStore); - const unpublishedRecordsForRequester = await fetchUnpublishedRecordsForRequester(recordsQuery, messageStore); - const unpublishedRecordsByRequester = await fetchUnpublishedRecordsByRequester(recordsQuery, messageStore); + const publishedRecords = await fetchPublishedRecords(tenant, recordsQuery, messageStore); + const unpublishedRecordsForRequester = await fetchUnpublishedRecordsForRequester(tenant, recordsQuery, messageStore); + const unpublishedRecordsByRequester = await fetchUnpublishedRecordsByRequester(tenant, recordsQuery, messageStore); const records = [...publishedRecords, ...unpublishedRecordsForRequester, ...unpublishedRecordsByRequester]; return records; } @@ -92,10 +93,10 @@ async function fetchRecordsAsNonOwner(recordsQuery: RecordsQuery, messageStore: /** * Fetches only published records. */ -async function fetchPublishedRecords(recordsQuery: RecordsQuery, messageStore: MessageStore): Promise { +async function fetchPublishedRecords(tenant: string, recordsQuery: RecordsQuery, messageStore: MessageStore): Promise { // fetch all published records matching the query const includeCriteria = { - target : recordsQuery.target, + tenant, method : DwnMethodName.RecordsWrite, published : 'true', isLatestBaseState : 'true', @@ -110,11 +111,11 @@ async function fetchPublishedRecords(recordsQuery: RecordsQuery, messageStore: M /** * Fetches only unpublished records that are intended for the requester (where `recipient` is the requester). */ -async function fetchUnpublishedRecordsForRequester(recordsQuery: RecordsQuery, messageStore: MessageStore) +async function fetchUnpublishedRecordsForRequester(tenant: string, recordsQuery: RecordsQuery, messageStore: MessageStore) : Promise { // include records where recipient is requester const includeCriteria = { - target : recordsQuery.target, + tenant, recipient : recordsQuery.author, method : DwnMethodName.RecordsWrite, isLatestBaseState : 'true', @@ -130,11 +131,11 @@ async function fetchUnpublishedRecordsForRequester(recordsQuery: RecordsQuery, m /** * Fetches only unpublished records that are authored by the requester. */ -async function fetchUnpublishedRecordsByRequester(recordsQuery: RecordsQuery, messageStore: MessageStore) +async function fetchUnpublishedRecordsByRequester(tenant: string, recordsQuery: RecordsQuery, messageStore: MessageStore) : Promise { // include records where recipient is requester const includeCriteria = { - target : recordsQuery.target, + tenant, author : recordsQuery.author, method : DwnMethodName.RecordsWrite, isLatestBaseState : 'true', diff --git a/src/interfaces/records/handlers/records-write.ts b/src/interfaces/records/handlers/records-write.ts index b52ced28e..cab2df138 100644 --- a/src/interfaces/records/handlers/records-write.ts +++ b/src/interfaces/records/handlers/records-write.ts @@ -8,6 +8,7 @@ import { RecordsWrite } from '../messages/records-write.js'; import { DwnMethodName, Message } from '../../../core/message.js'; export const handleRecordsWrite: MethodHandler = async ( + tenant, message, messageStore, didResolver @@ -26,7 +27,7 @@ export const handleRecordsWrite: MethodHandler = async ( // authentication & authorization try { await authenticate(message.authorization, didResolver); - await recordsWrite.authorize(messageStore); + await recordsWrite.authorize(tenant, messageStore); } catch (e) { return new MessageReply({ status: { code: 401, detail: e.message } @@ -35,7 +36,7 @@ export const handleRecordsWrite: MethodHandler = async ( // get existing records matching the `recordId` const query = { - target : recordsWrite.target, + tenant, method : DwnMethodName.RecordsWrite, recordId : incomingMessage.recordId }; @@ -74,7 +75,7 @@ export const handleRecordsWrite: MethodHandler = async ( let messageReply: MessageReply; if (incomingMessageIsNewest) { const isLatestBaseState = true; - const indexes = await constructIndexes(recordsWrite, isLatestBaseState); + const indexes = await constructIndexes(tenant, recordsWrite, isLatestBaseState); await messageStore.put(incomingMessage, indexes); @@ -105,7 +106,7 @@ export const handleRecordsWrite: MethodHandler = async ( if (existingMessageIsInitialWrite) { const existingRecordsWrite = await RecordsWrite.parse(message); const isLatestBaseState = false; - const indexes = await constructIndexes(existingRecordsWrite, isLatestBaseState); + const indexes = await constructIndexes(tenant, existingRecordsWrite, isLatestBaseState); await messageStore.put(message, indexes); } } @@ -114,17 +115,17 @@ export const handleRecordsWrite: MethodHandler = async ( return messageReply; }; -export async function constructIndexes(recordsWrite: RecordsWrite, isLatestBaseState: boolean): Promise<{ [key: string]: string }> { +export async function constructIndexes(tenant: string, recordsWrite: RecordsWrite, isLatestBaseState: boolean): Promise<{ [key: string]: string }> { const message = recordsWrite.message; const descriptor = { ...message.descriptor }; delete descriptor.published; // handle `published` specifically further down const indexes: { [key: string]: any } = { + tenant, // NOTE: underlying search-index library does not support boolean, so converting boolean to string before storing // https://github.com/TBD54566975/dwn-sdk-js/issues/170 isLatestBaseState : isLatestBaseState.toString(), author : recordsWrite.author, - target : recordsWrite.target, recordId : message.recordId, entryId : await RecordsWrite.getEntryId(recordsWrite.author, recordsWrite.message.descriptor), ...descriptor diff --git a/src/interfaces/records/messages/records-query.ts b/src/interfaces/records/messages/records-query.ts index 70a5abdf8..0e8d71b16 100644 --- a/src/interfaces/records/messages/records-query.ts +++ b/src/interfaces/records/messages/records-query.ts @@ -15,7 +15,6 @@ export enum DateSort { } export type RecordsQueryOptions = AuthCreateOptions & { - target: string; dateCreated?: string; filter: { recipient?: string; @@ -55,15 +54,15 @@ export class RecordsQuery extends Message { Message.validateJsonSchema({ descriptor, authorization: { } }); - const authorization = await Message.signAsAuthorization(options.target, descriptor, options.signatureInput); + const authorization = await Message.signAsAuthorization(descriptor, options.signatureInput); const message = { descriptor, authorization }; return new RecordsQuery(message); } - public async authorize(): Promise { + public async authorize(tenant: string): Promise { // DWN owner can do any query - if (this.author === this.target) { + if (this.author === tenant) { return; } diff --git a/src/interfaces/records/messages/records-write.ts b/src/interfaces/records/messages/records-write.ts index 327bd1451..2ffa2764c 100644 --- a/src/interfaces/records/messages/records-write.ts +++ b/src/interfaces/records/messages/records-write.ts @@ -16,8 +16,7 @@ import { GeneralJws, SignatureInput } from '../../../jose/jws/general/types.js'; import { generateCid, getDagPbCid } from '../../../utils/cid.js'; export type RecordsWriteOptions = AuthCreateOptions & { - target: string; - recipient: string; + recipient?: string; protocol?: string; contextId?: string; schema?: string; @@ -32,7 +31,6 @@ export type RecordsWriteOptions = AuthCreateOptions & { }; export type CreateFromOptions = AuthCreateOptions & { - target: string, unsignedRecordsWriteMessage: UnsignedRecordsWriteMessage, data?: Uint8Array; published?: boolean; @@ -41,7 +39,10 @@ export type CreateFromOptions = AuthCreateOptions & { }; export class RecordsWrite extends Message { - readonly message: RecordsWriteMessage; // a more specific type than the base type defined in parent class + /** + * RecordsWrite message adhering to the DWN specification. + */ + readonly message: RecordsWriteMessage; private constructor(message: RecordsWriteMessage) { super(message); @@ -111,7 +112,6 @@ export class RecordsWrite extends Message { const encodedData = Encoder.bytesToBase64Url(options.data); const authorization = await RecordsWrite.signAsRecordsWriteAuthorization( - options.target, recordId, contextId, descriptor, @@ -172,7 +172,6 @@ export class RecordsWrite extends Message { const createOptions: RecordsWriteOptions = { // immutable properties below, just inherit from the message given - target : options.target, recipient : unsignedMessage.descriptor.recipient, recordId : unsignedMessage.recordId, dateCreated : unsignedMessage.descriptor.dateCreated, @@ -194,11 +193,11 @@ export class RecordsWrite extends Message { return recordsWrite; } - public async authorize(messageStore: MessageStore): Promise { + public async authorize(tenant: string, messageStore: MessageStore): Promise { if (this.message.descriptor.protocol !== undefined) { - await ProtocolAuthorization.authorize(this, this.author, messageStore); + await ProtocolAuthorization.authorize(tenant, this, this.author, messageStore); } else { - await authorize(this); + await authorize(tenant, this); } } @@ -294,7 +293,6 @@ export class RecordsWrite extends Message { * Creates the `authorization` property for a RecordsWrite message. */ private static async signAsRecordsWriteAuthorization( - target: string, recordId: string, contextId: string | undefined, descriptor: RecordsWriteDescriptor, @@ -303,7 +301,6 @@ export class RecordsWrite extends Message { const descriptorCid = await generateCid(descriptor); const authorizationPayload: RecordsWriteAuthorizationPayload = { - target, recordId, descriptorCid: descriptorCid.toString() }; diff --git a/src/interfaces/records/records-interface.ts b/src/interfaces/records/records-interface.ts index 4a51bb99a..7563c4d71 100644 --- a/src/interfaces/records/records-interface.ts +++ b/src/interfaces/records/records-interface.ts @@ -4,7 +4,7 @@ import { handleRecordsWrite } from './handlers/records-write.js'; import { RecordsQuery } from './messages/records-query.js'; import { RecordsWrite } from './messages/records-write.js'; -export const CollectionsInterface = { +export const RecordsInterface = { methodHandlers: { [DwnMethodName.RecordsQuery] : handleRecordsQuery, [DwnMethodName.RecordsWrite] : handleRecordsWrite diff --git a/src/interfaces/records/types.ts b/src/interfaces/records/types.ts index 29872e35d..9bc8992d3 100644 --- a/src/interfaces/records/types.ts +++ b/src/interfaces/records/types.ts @@ -49,7 +49,6 @@ export type RecordsQueryDescriptor = { }; export type RecordsWriteAuthorizationPayload = { - target: string; recordId: string; contextId?: string; descriptorCid: string; diff --git a/src/interfaces/types.ts b/src/interfaces/types.ts index 495672f29..d790b3403 100644 --- a/src/interfaces/types.ts +++ b/src/interfaces/types.ts @@ -5,6 +5,7 @@ import type { MessageStore } from '../store/message-store.js'; import { DidResolver } from '../did/did-resolver.js'; export type MethodHandler = ( + tenant: string, message: BaseMessage, messageStore: MessageStore, didResolver: DidResolver) => Promise; diff --git a/src/store/message-store-level.ts b/src/store/message-store-level.ts index 7df33bede..7dd33330c 100644 --- a/src/store/message-store-level.ts +++ b/src/store/message-store-level.ts @@ -113,7 +113,7 @@ export class MessageStoreLevel implements MessageStore { async delete(cid: CID): Promise { - // TODO: Implement data deletion in Collections - https://github.com/TBD54566975/dwn-sdk-js/issues/84 + // TODO: Implement data deletion in Records - https://github.com/TBD54566975/dwn-sdk-js/issues/84 await this.db.delete(cid); await this.index.DELETE(cid.toString()); diff --git a/tests/core/protocol-authorization.spec.ts b/tests/core/protocol-authorization.spec.ts index 8fb74c3ee..652287cf3 100644 --- a/tests/core/protocol-authorization.spec.ts +++ b/tests/core/protocol-authorization.spec.ts @@ -22,14 +22,14 @@ describe('Protocol-Based Authorization', async () => { }); describe('verifyAllowedActions()', async () => { - it('should throw if requester DID is not the target DWN owner when no allow rule defined', async () => { + it('should throw if requester DID is not the target tenant when no allow rule defined', async () => { const alice = await TestDataGenerator.generatePersona(); const bob = await TestDataGenerator.generatePersona(); - const { recordsWrite } = await TestDataGenerator.generateRecordsWriteMessage({ requester: bob, target: alice }); + const { recordsWrite } = await TestDataGenerator.generateRecordsWriteMessage({ requester: bob }); const ruleSet: ProtocolRuleSet = {}; expect(() => { - ProtocolAuthorization['verifyAllowedActions'](bob.did, recordsWrite, ruleSet); + ProtocolAuthorization['verifyAllowedActions'](alice.did, bob.did, recordsWrite, ruleSet); }).throws('no allow rule defined for RecordsWrite'); }); @@ -45,7 +45,7 @@ describe('Protocol-Based Authorization', async () => { }; expect(() => { - ProtocolAuthorization['verifyAllowedActions'](did, recordsWrite, ruleSet); + ProtocolAuthorization['verifyAllowedActions'](did, did, recordsWrite, ruleSet); }).throws('not in list of allowed actions'); }); }); diff --git a/tests/dwn.spec.ts b/tests/dwn.spec.ts index 3814853e2..5a6efa9cd 100644 --- a/tests/dwn.spec.ts +++ b/tests/dwn.spec.ts @@ -41,14 +41,13 @@ describe('DWN', () => { const alice = await DidKeyResolver.generate(); const messageData = await TestDataGenerator.generateRecordsWriteMessage({ - requester : alice, - target : alice + requester: alice, }); const dwnConfig: Config = { messageStore }; const dwn = await Dwn.create(dwnConfig); - const reply = await dwn.processMessage(messageData.message); + const reply = await dwn.processMessage(alice.did, messageData.message); expect(reply.status.code).to.equal(202); }); @@ -72,7 +71,8 @@ describe('DWN', () => { }; const dwn = await Dwn.create(dwnConfig); - const reply = await dwn.processMessage(message); + const tenant = requester.did; + const reply = await dwn.processMessage(tenant, message); expect(reply.status.code).to.equal(200); expect(reply.entries).to.be.empty; @@ -90,7 +90,8 @@ describe('DWN', () => { const validateJsonSchemaSpy = sinon.spy(Message, 'validateJsonSchema'); - const reply = await dwn.processMessage(invalidMessage); + const alice = await DidKeyResolver.generate(); + const reply = await dwn.processMessage(alice.did, invalidMessage); sinon.assert.calledOnce(validateJsonSchemaSpy); diff --git a/tests/interfaces/permissions/messages/permissions-request.spec.ts b/tests/interfaces/permissions/messages/permissions-request.spec.ts index a23b9e1bf..550ea1cfd 100644 --- a/tests/interfaces/permissions/messages/permissions-request.spec.ts +++ b/tests/interfaces/permissions/messages/permissions-request.spec.ts @@ -65,7 +65,6 @@ describe('PermissionsRequest', () => { }; const message = await PermissionsRequest.create({ - target : 'did:jank:bob', description : 'drugs', grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', @@ -91,7 +90,6 @@ describe('PermissionsRequest', () => { }; const message = await PermissionsRequest.create({ - target : 'did:jank:bob', description : 'drugs', grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', diff --git a/tests/interfaces/protocols/handlers/protocols-configure.spec.ts b/tests/interfaces/protocols/handlers/protocols-configure.spec.ts index 9a82893d6..07352e391 100644 --- a/tests/interfaces/protocols/handlers/protocols-configure.spec.ts +++ b/tests/interfaces/protocols/handlers/protocols-configure.spec.ts @@ -44,6 +44,7 @@ describe('handleProtocolsQuery()', () => { it('should return 400 if failed to parse the message', async () => { const { requester, message, protocolsConfigure } = await TestDataGenerator.generateProtocolsConfigureMessage(); + const tenant = requester.did; // intentionally create more than one signature, which is not allowed const extraRandomPersona = await TestDataGenerator.generatePersona(); @@ -57,7 +58,7 @@ describe('handleProtocolsQuery()', () => { const didResolverStub = TestStubGenerator.createDidResolverStub(requester); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - const reply = await handleProtocolsConfigure(message, messageStoreStub, didResolverStub); + const reply = await handleProtocolsConfigure(tenant, message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('expected no more than 1 signature'); @@ -66,9 +67,9 @@ describe('handleProtocolsQuery()', () => { it('should return 401 if auth fails', async () => { const alice = await DidKeyResolver.generate(); alice.keyId = 'wrongValue'; // to fail authentication - const { message } = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, target: alice }); + const { message } = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice }); - const reply = await handleProtocolsConfigure(message, messageStore, didResolver); + const reply = await handleProtocolsConfigure(alice.did, message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.contain('not a valid DID'); }); @@ -77,9 +78,9 @@ describe('handleProtocolsQuery()', () => { // generate three versions of the same protocol message const alice = await DidKeyResolver.generate(); const protocol = 'exampleProtocol'; - const messageData1 = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, target: alice, protocol }); - const messageData2 = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, target: alice, protocol }); - const messageData3 = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, target: alice, protocol }); + const messageData1 = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, protocol }); + const messageData2 = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, protocol }); + const messageData3 = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, protocol }); const messageDataWithCid = []; for (const messageData of [messageData1, messageData2, messageData3]) { @@ -96,20 +97,20 @@ describe('handleProtocolsQuery()', () => { = messageDataWithCid.sort((messageDataA, messageDataB) => { return lexicographicalCompare(messageDataA.cid, messageDataB.cid); }); // write the protocol with the middle lexicographic value - let reply = await handleProtocolsConfigure(messageDataWithMediumLexicographicValue.message, messageStore, didResolver); + let reply = await handleProtocolsConfigure(alice.did, messageDataWithMediumLexicographicValue.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); // test that the protocol with the smallest lexicographic value cannot be written - reply = await handleProtocolsConfigure(messageDataWithSmallestLexicographicValue.message, messageStore, didResolver); + reply = await handleProtocolsConfigure(alice.did, messageDataWithSmallestLexicographicValue.message, messageStore, didResolver); expect(reply.status.code).to.equal(409); // test that the protocol with the largest lexicographic value can be written - reply = await handleProtocolsConfigure(messageDataWithLargestLexicographicValue.message, messageStore, didResolver); + reply = await handleProtocolsConfigure(alice.did, messageDataWithLargestLexicographicValue.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); // test that old protocol message is removed from DB and only the newer protocol message remains - const queryMessageData = await TestDataGenerator.generateProtocolsQueryMessage({ requester: alice, target: alice, filter: { protocol } }); - reply = await handleProtocolsQuery(queryMessageData.message, messageStore, didResolver); + const queryMessageData = await TestDataGenerator.generateProtocolsQueryMessage({ requester: alice, filter: { protocol } }); + reply = await handleProtocolsQuery(alice.did, queryMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(200); expect(reply.entries.length).to.equal(1); diff --git a/tests/interfaces/protocols/handlers/protocols-query.spec.ts b/tests/interfaces/protocols/handlers/protocols-query.spec.ts index 3f6ef7886..dda3e38f2 100644 --- a/tests/interfaces/protocols/handlers/protocols-query.spec.ts +++ b/tests/interfaces/protocols/handlers/protocols-query.spec.ts @@ -47,33 +47,31 @@ describe('handleProtocolsQuery()', () => { const didResolverStub = TestStubGenerator.createDidResolverStub(alice); // insert three messages into DB, two with matching protocol - const message1Data = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, target: alice }); - const message2Data = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, target: alice }); - const message3Data = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice, target: alice }); + const message1Data = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice }); + const message2Data = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice }); + const message3Data = await TestDataGenerator.generateProtocolsConfigureMessage({ requester: alice }); - await handleProtocolsConfigure(message1Data.message, messageStore, didResolverStub); - await handleProtocolsConfigure(message2Data.message, messageStore, didResolverStub); - await handleProtocolsConfigure(message3Data.message, messageStore, didResolverStub); + await handleProtocolsConfigure(alice.did, message1Data.message, messageStore, didResolverStub); + await handleProtocolsConfigure(alice.did, message2Data.message, messageStore, didResolverStub); + await handleProtocolsConfigure(alice.did, message3Data.message, messageStore, didResolverStub); // testing singular conditional query const queryMessageData = await TestDataGenerator.generateProtocolsQueryMessage({ requester : alice, - target : alice, filter : { protocol: message1Data.message.descriptor.protocol } }); - const reply = await handleProtocolsQuery(queryMessageData.message, messageStore, didResolverStub); + const reply = await handleProtocolsQuery(alice.did, queryMessageData.message, messageStore, didResolverStub); expect(reply.status.code).to.equal(200); expect(reply.entries?.length).to.equal(1); // only 1 entry should match the query on protocol // testing fetch-all query without filter const queryMessageData2 = await TestDataGenerator.generateProtocolsQueryMessage({ - requester : alice, - target : alice + requester: alice }); - const reply2 = await handleProtocolsQuery(queryMessageData2.message, messageStore, didResolverStub); + const reply2 = await handleProtocolsQuery(alice.did, queryMessageData2.message, messageStore, didResolverStub); expect(reply2.status.code).to.equal(200); expect(reply2.entries?.length).to.equal(3); // expecting all 3 entries written above match the query @@ -81,6 +79,7 @@ describe('handleProtocolsQuery()', () => { it('should return 400 if failed to parse the message', async () => { const { requester, message, protocolsQuery } = await TestDataGenerator.generateProtocolsQueryMessage(); + const tenant = requester.did; // replace `authorization` with incorrect `descriptorCid`, even though signature is still valid const incorrectDescriptorCid = await TestDataGenerator.randomCborSha256Cid(); @@ -93,7 +92,7 @@ describe('handleProtocolsQuery()', () => { const didResolverStub = TestStubGenerator.createDidResolverStub(requester); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - const reply = await handleProtocolsQuery(message, messageStoreStub, didResolverStub); + const reply = await handleProtocolsQuery(tenant, message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain(`${incorrectDescriptorCid} does not match expected CID`); @@ -102,9 +101,9 @@ describe('handleProtocolsQuery()', () => { it('should return 401 if auth fails', async () => { const alice = await DidKeyResolver.generate(); alice.keyId = 'wrongValue'; // to fail authentication - const messageData = await TestDataGenerator.generateProtocolsQueryMessage({ requester: alice, target: alice }); + const messageData = await TestDataGenerator.generateProtocolsQueryMessage({ requester: alice }); - const reply = await handleProtocolsQuery(messageData.message, messageStore, didResolver); + const reply = await handleProtocolsQuery(alice.did, messageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.contain('not a valid DID'); diff --git a/tests/interfaces/records/handlers/records-query.spec.ts b/tests/interfaces/records/handlers/records-query.spec.ts index 8d9aed135..04b7c0bc0 100644 --- a/tests/interfaces/records/handlers/records-query.spec.ts +++ b/tests/interfaces/records/handlers/records-query.spec.ts @@ -46,25 +46,25 @@ describe('handleRecordsQuery()', () => { // insert three messages into DB, two with matching protocol const alice = await TestDataGenerator.generatePersona(); const dataFormat = 'myAwesomeDataFormat'; - const write1Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, target: alice }); - const write2Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, target: alice, dataFormat, schema: 'schema1' }); - const write3Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, target: alice, dataFormat, schema: 'schema2' }); + const write1Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice }); + const write2Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, dataFormat, schema: 'schema1' }); + const write3Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, dataFormat, schema: 'schema2' }); // setting up a stub method resolver const didResolverStub = TestStubGenerator.createDidResolverStub(alice); // insert data - const writeReply1 = await handleRecordsWrite(write1Data.message, messageStore, didResolverStub); - const writeReply2 = await handleRecordsWrite(write2Data.message, messageStore, didResolverStub); - const writeReply3 = await handleRecordsWrite(write3Data.message, messageStore, didResolverStub); + const writeReply1 = await handleRecordsWrite(alice.did, write1Data.message, messageStore, didResolverStub); + const writeReply2 = await handleRecordsWrite(alice.did, write2Data.message, messageStore, didResolverStub); + const writeReply3 = await handleRecordsWrite(alice.did, write3Data.message, messageStore, didResolverStub); expect(writeReply1.status.code).to.equal(202); expect(writeReply2.status.code).to.equal(202); expect(writeReply3.status.code).to.equal(202); // testing singular conditional query - const messageData = await TestDataGenerator.generateRecordsQueryMessage({ requester: alice, target: alice, filter: { dataFormat } }); + const messageData = await TestDataGenerator.generateRecordsQueryMessage({ requester: alice, filter: { dataFormat } }); - const reply = await handleRecordsQuery(messageData.message, messageStore, didResolverStub); + const reply = await handleRecordsQuery(alice.did, messageData.message, messageStore, didResolverStub); expect(reply.status.code).to.equal(200); expect(reply.entries?.length).to.equal(2); // only 2 entries should match the query on protocol @@ -72,14 +72,13 @@ describe('handleRecordsQuery()', () => { // testing multi-conditional query, reuse data generated above for bob const messageData2 = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { dataFormat, schema: 'schema1' } }); - const reply2 = await handleRecordsQuery(messageData2.message, messageStore, didResolverStub); + const reply2 = await handleRecordsQuery(alice.did, messageData2.message, messageStore, didResolverStub); expect(reply2.status.code).to.equal(200); expect(reply2.entries?.length).to.equal(1); // only 1 entry should match the query @@ -88,21 +87,20 @@ describe('handleRecordsQuery()', () => { it('should not include `authorization` in returned records', async () => { // insert three messages into DB, two with matching protocol const alice = await TestDataGenerator.generatePersona(); - const { message } = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, target: alice }); + const { message } = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice }); // setting up a stub method resolver const didResolverStub = TestStubGenerator.createDidResolverStub(alice); - const writeReply = await handleRecordsWrite(message, messageStore, didResolverStub); + const writeReply = await handleRecordsWrite(alice.did, message, messageStore, didResolverStub); expect(writeReply.status.code).to.equal(202); const queryData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { schema: message.descriptor.schema } }); - const queryReply = await handleRecordsQuery(queryData.message, messageStore, didResolverStub); + const queryReply = await handleRecordsQuery(alice.did, queryData.message, messageStore, didResolverStub); expect(queryReply.status.code).to.equal(200); expect(queryReply.entries?.length).to.equal(1); expect(queryReply.entries[0]['authorization']).to.equal(undefined); @@ -113,29 +111,28 @@ describe('handleRecordsQuery()', () => { const alice = await TestDataGenerator.generatePersona(); const schema = 'aSchema'; const publishedWriteData = await TestDataGenerator.generateRecordsWriteMessage({ - requester: alice, target: alice, schema, published: true + requester: alice, schema, published: true }); const unpublishedWriteData = await TestDataGenerator.generateRecordsWriteMessage({ - requester: alice, target: alice, schema + requester: alice, schema }); // setting up a stub method resolver const didResolverStub = TestStubGenerator.createDidResolverStub(alice); // insert data - const publishedWriteReply = await handleRecordsWrite(publishedWriteData.message, messageStore, didResolverStub); - const unpublishedWriteReply = await handleRecordsWrite(unpublishedWriteData.message, messageStore, didResolverStub); + const publishedWriteReply = await handleRecordsWrite(alice.did, publishedWriteData.message, messageStore, didResolverStub); + const unpublishedWriteReply = await handleRecordsWrite(alice.did, unpublishedWriteData.message, messageStore, didResolverStub); expect(publishedWriteReply.status.code).to.equal(202); expect(unpublishedWriteReply.status.code).to.equal(202); // test published date ascending sort does not include any records that is not published const publishedAscendingQueryData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, dateSort : DateSort.PublishedAscending, filter : { schema } }); - const publishedAscendingQueryReply = await handleRecordsQuery(publishedAscendingQueryData.message, messageStore, didResolverStub); + const publishedAscendingQueryReply = await handleRecordsQuery(alice.did, publishedAscendingQueryData.message, messageStore, didResolverStub); expect(publishedAscendingQueryReply.entries?.length).to.equal(1); expect(publishedAscendingQueryReply.entries[0].descriptor['datePublished']).to.equal(publishedWriteData.message.descriptor.datePublished); @@ -143,11 +140,10 @@ describe('handleRecordsQuery()', () => { // test published date scending sort does not include any records that is not published const publishedDescendingQueryData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, dateSort : DateSort.PublishedDescending, filter : { schema } }); - const publishedDescendingQueryReply = await handleRecordsQuery(publishedDescendingQueryData.message, messageStore, didResolverStub); + const publishedDescendingQueryReply = await handleRecordsQuery(alice.did, publishedDescendingQueryData.message, messageStore, didResolverStub); expect(publishedDescendingQueryReply.entries?.length).to.equal(1); expect(publishedDescendingQueryReply.entries[0].descriptor['datePublished']).to.equal(publishedWriteData.message.descriptor.datePublished); @@ -158,17 +154,17 @@ describe('handleRecordsQuery()', () => { const alice = await TestDataGenerator.generatePersona(); const schema = 'aSchema'; const published = true; - const write1Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, target: alice, schema, published }); - const write2Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, target: alice, schema, published }); - const write3Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, target: alice, schema, published }); + const write1Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, schema, published }); + const write2Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, schema, published }); + const write3Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, schema, published }); // setting up a stub method resolver const didResolverStub = TestStubGenerator.createDidResolverStub(alice); // insert data, intentionally out of order - const writeReply2 = await handleRecordsWrite(write2Data.message, messageStore, didResolverStub); - const writeReply1 = await handleRecordsWrite(write1Data.message, messageStore, didResolverStub); - const writeReply3 = await handleRecordsWrite(write3Data.message, messageStore, didResolverStub); + const writeReply2 = await handleRecordsWrite(alice.did, write2Data.message, messageStore, didResolverStub); + const writeReply1 = await handleRecordsWrite(alice.did, write1Data.message, messageStore, didResolverStub); + const writeReply3 = await handleRecordsWrite(alice.did, write3Data.message, messageStore, didResolverStub); expect(writeReply1.status.code).to.equal(202); expect(writeReply2.status.code).to.equal(202); expect(writeReply3.status.code).to.equal(202); @@ -176,11 +172,10 @@ describe('handleRecordsQuery()', () => { // createdAscending test const createdAscendingQueryData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, dateSort : DateSort.CreatedAscending, filter : { schema } }); - const createdAscendingQueryReply = await handleRecordsQuery(createdAscendingQueryData.message, messageStore, didResolverStub); + const createdAscendingQueryReply = await handleRecordsQuery(alice.did, createdAscendingQueryData.message, messageStore, didResolverStub); expect(createdAscendingQueryReply.entries[0].descriptor['dateCreated']).to.equal(write1Data.message.descriptor.dateCreated); expect(createdAscendingQueryReply.entries[1].descriptor['dateCreated']).to.equal(write2Data.message.descriptor.dateCreated); @@ -189,11 +184,10 @@ describe('handleRecordsQuery()', () => { // createdDescending test const createdDescendingQueryData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, dateSort : DateSort.CreatedDescending, filter : { schema } }); - const createdDescendingQueryReply = await handleRecordsQuery(createdDescendingQueryData.message, messageStore, didResolverStub); + const createdDescendingQueryReply = await handleRecordsQuery(alice.did, createdDescendingQueryData.message, messageStore, didResolverStub); expect(createdDescendingQueryReply.entries[0].descriptor['dateCreated']).to.equal(write3Data.message.descriptor.dateCreated); expect(createdDescendingQueryReply.entries[1].descriptor['dateCreated']).to.equal(write2Data.message.descriptor.dateCreated); @@ -202,11 +196,10 @@ describe('handleRecordsQuery()', () => { // publishedAscending test const publishedAscendingQueryData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, dateSort : DateSort.PublishedAscending, filter : { schema } }); - const publishedAscendingQueryReply = await handleRecordsQuery(publishedAscendingQueryData.message, messageStore, didResolverStub); + const publishedAscendingQueryReply = await handleRecordsQuery(alice.did, publishedAscendingQueryData.message, messageStore, didResolverStub); expect(publishedAscendingQueryReply.entries[0].descriptor['datePublished']).to.equal(write1Data.message.descriptor.datePublished); expect(publishedAscendingQueryReply.entries[1].descriptor['datePublished']).to.equal(write2Data.message.descriptor.datePublished); @@ -215,11 +208,10 @@ describe('handleRecordsQuery()', () => { // publishedDescending test const publishedDescendingQueryData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, dateSort : DateSort.PublishedDescending, filter : { schema } }); - const publishedDescendingQueryReply = await handleRecordsQuery(publishedDescendingQueryData.message, messageStore, didResolverStub); + const publishedDescendingQueryReply = await handleRecordsQuery(alice.did, publishedDescendingQueryData.message, messageStore, didResolverStub); expect(publishedDescendingQueryReply.entries[0].descriptor['datePublished']).to.equal(write3Data.message.descriptor.datePublished); expect(publishedDescendingQueryReply.entries[1].descriptor['datePublished']).to.equal(write2Data.message.descriptor.datePublished); @@ -236,23 +228,23 @@ describe('handleRecordsQuery()', () => { const bob = await DidKeyResolver.generate(); const schema = 'schema1'; const record1Data = await TestDataGenerator.generateRecordsWriteMessage( - { requester: alice, target: alice, schema, data: Encoder.stringToBytes('1') } + { requester: alice, schema, data: Encoder.stringToBytes('1') } ); const record2Data = await TestDataGenerator.generateRecordsWriteMessage( - { requester: alice, target: alice, schema, data: Encoder.stringToBytes('2'), recipientDid: bob.did } + { requester: alice, schema, data: Encoder.stringToBytes('2'), recipientDid: bob.did } ); const record3Data = await TestDataGenerator.generateRecordsWriteMessage( - { requester: bob, target: alice, recipientDid: alice.did, schema, data: Encoder.stringToBytes('3') } + { requester: bob, recipientDid: alice.did, schema, data: Encoder.stringToBytes('3') } ); const record4Data = await TestDataGenerator.generateRecordsWriteMessage( - { requester: alice, target: alice, schema, data: Encoder.stringToBytes('4'), published: true } + { requester: alice, schema, data: Encoder.stringToBytes('4'), published: true } ); // directly inserting data to datastore so that we don't have to setup to grant Bob permission to write to Alice's DWN - const additionalIndexes1 = await constructIndexes(record1Data.recordsWrite, true); - const additionalIndexes2 = await constructIndexes(record2Data.recordsWrite, true); - const additionalIndexes3 = await constructIndexes(record3Data.recordsWrite, true); - const additionalIndexes4 = await constructIndexes(record4Data.recordsWrite, true); + const additionalIndexes1 = await constructIndexes(alice.did, record1Data.recordsWrite, true); + const additionalIndexes2 = await constructIndexes(alice.did, record2Data.recordsWrite, true); + const additionalIndexes3 = await constructIndexes(alice.did, record3Data.recordsWrite, true); + const additionalIndexes4 = await constructIndexes(alice.did, record4Data.recordsWrite, true); await messageStore.put(record1Data.message, additionalIndexes1); await messageStore.put(record2Data.message, additionalIndexes2); await messageStore.put(record3Data.message, additionalIndexes3); @@ -261,11 +253,10 @@ describe('handleRecordsQuery()', () => { // test correctness for Bob's query const bobQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester : bob, - target : alice, filter : { schema } }); - const replyToBob = await handleRecordsQuery(bobQueryMessageData.message, messageStore, didResolver); + const replyToBob = await handleRecordsQuery(alice.did, bobQueryMessageData.message, messageStore, didResolver); expect(replyToBob.status.code).to.equal(200); expect(replyToBob.entries?.length).to.equal(3); // expect 3 records @@ -280,11 +271,10 @@ describe('handleRecordsQuery()', () => { // test correctness for Alice's query const aliceQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { schema } }); - const replyToAliceQuery = await handleRecordsQuery(aliceQueryMessageData.message, messageStore, didResolver); + const replyToAliceQuery = await handleRecordsQuery(alice.did, aliceQueryMessageData.message, messageStore, didResolver); expect(replyToAliceQuery.status.code).to.equal(200); expect(replyToAliceQuery.entries?.length).to.equal(4); // expect all 4 records @@ -296,29 +286,27 @@ describe('handleRecordsQuery()', () => { const bob = await DidKeyResolver.generate(); const schema = 'schema1'; const unpublishedRecordsWrite = await TestDataGenerator.generateRecordsWriteMessage( - { requester: alice, target: alice, schema, data: Encoder.stringToBytes('1'), published: false } // explicitly setting `published` to `false` + { requester: alice, schema, data: Encoder.stringToBytes('1'), published: false } // explicitly setting `published` to `false` ); - const result1 = await handleRecordsWrite(unpublishedRecordsWrite.message, messageStore, didResolver); + const result1 = await handleRecordsWrite(alice.did, unpublishedRecordsWrite.message, messageStore, didResolver); expect(result1.status.code).to.equal(202); // alice should be able to see the unpublished record const queryByAlice = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { schema } }); - const replyToAliceQuery = await handleRecordsQuery(queryByAlice.message, messageStore, didResolver); + const replyToAliceQuery = await handleRecordsQuery(alice.did, queryByAlice.message, messageStore, didResolver); expect(replyToAliceQuery.status.code).to.equal(200); expect(replyToAliceQuery.entries?.length).to.equal(1); // actual test: bob should not be able to see unpublished record const queryByBob = await TestDataGenerator.generateRecordsQueryMessage({ requester : bob, - target : alice, filter : { schema } }); - const replyToBobQuery = await handleRecordsQuery(queryByBob.message, messageStore, didResolver); + const replyToBobQuery = await handleRecordsQuery(alice.did, queryByBob.message, messageStore, didResolver); expect(replyToBobQuery.status.code).to.equal(200); expect(replyToBobQuery.entries?.length).to.equal(0); }); @@ -330,11 +318,10 @@ describe('handleRecordsQuery()', () => { const bobQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester : bob, - target : alice, filter : { recipient: carol.did } // bob querying carol's records }); - const replyToBobQuery = await handleRecordsQuery(bobQueryMessageData.message, messageStore, didResolver); + const replyToBobQuery = await handleRecordsQuery(alice.did, bobQueryMessageData.message, messageStore, didResolver); expect(replyToBobQuery.status.code).to.equal(401); expect(replyToBobQuery.status.detail).to.contain('not allowed to query records'); @@ -346,11 +333,10 @@ describe('handleRecordsQuery()', () => { const bobQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { recipient: bob.did } // alice as the DWN owner querying bob's records }); - const replyToBobQuery = await handleRecordsQuery(bobQueryMessageData.message, messageStore, didResolver); + const replyToBobQuery = await handleRecordsQuery(alice.did, bobQueryMessageData.message, messageStore, didResolver); expect(replyToBobQuery.status.code).to.equal(200); }); @@ -360,21 +346,20 @@ describe('handleRecordsQuery()', () => { const alice = await DidKeyResolver.generate(); const bob = await DidKeyResolver.generate(); const schema = 'myAwesomeSchema'; - const recordsWriteMessage1Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, target: alice, schema }); - const recordsWriteMessage2Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: bob, target: bob, schema }); + const recordsWriteMessage1Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: alice, schema }); + const recordsWriteMessage2Data = await TestDataGenerator.generateRecordsWriteMessage({ requester: bob, schema }); const aliceQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { schema } }); // insert data into 2 different tenants const didResolver = new DidResolver([new DidKeyResolver()]); - await handleRecordsWrite(recordsWriteMessage1Data.message, messageStore, didResolver); - await handleRecordsWrite(recordsWriteMessage2Data.message, messageStore, didResolver); + await handleRecordsWrite(alice.did, recordsWriteMessage1Data.message, messageStore, didResolver); + await handleRecordsWrite(bob.did, recordsWriteMessage2Data.message, messageStore, didResolver); - const reply = await handleRecordsQuery(aliceQueryMessageData.message, messageStore, didResolver); + const reply = await handleRecordsQuery(alice.did, aliceQueryMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(200); expect(reply.entries?.length).to.equal(1); @@ -390,13 +375,15 @@ describe('handleRecordsQuery()', () => { const didResolverStub = TestStubGenerator.createDidResolverStub(mismatchingPersona); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - const reply = await handleRecordsQuery(message, messageStoreStub, didResolverStub); + const tenant = requester.did; + const reply = await handleRecordsQuery(tenant, message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(401); }); it('should return 400 if fail parsing the message', async () => { const { requester, message } = await TestDataGenerator.generateRecordsQueryMessage(); + const tenant = requester.did; // setting up a stub method resolver & message store const didResolverStub = TestStubGenerator.createDidResolverStub(requester); @@ -404,7 +391,7 @@ describe('handleRecordsQuery()', () => { // stub the `parse()` function to throw an error sinon.stub(RecordsQuery, 'parse').throws('anyError'); - const reply = await handleRecordsQuery(message, messageStoreStub, didResolverStub); + const reply = await handleRecordsQuery(tenant, message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(400); }); diff --git a/tests/interfaces/records/handlers/records-write.spec.ts b/tests/interfaces/records/handlers/records-write.spec.ts index 90cb6720b..a95ef1acb 100644 --- a/tests/interfaces/records/handlers/records-write.spec.ts +++ b/tests/interfaces/records/handlers/records-write.spec.ts @@ -51,26 +51,24 @@ describe('handleRecordsWrite()', () => { it('should only be able to overwrite existing record if new record has a later `dateModified` value', async () => { // write a message into DB - const requester = await TestDataGenerator.generatePersona(); - const target = requester; + const requester = await DidKeyResolver.generate(); const data1 = new TextEncoder().encode('data1'); - const recordsWriteMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester, target, data: data1 }); + const recordsWriteMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester, data: data1 }); - // setting up a stub did resolver - const didResolverStub = TestStubGenerator.createDidResolverStub(requester); + const didResolver = new DidResolver(); - const recordsWriteReply = await handleRecordsWrite(recordsWriteMessageData.message, messageStore, didResolverStub); + const tenant = requester.did; + const recordsWriteReply = await handleRecordsWrite(tenant, recordsWriteMessageData.message, messageStore, didResolver); expect(recordsWriteReply.status.code).to.equal(202); const recordId = recordsWriteMessageData.message.recordId; const recordsQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester, - target, filter: { recordId } }); // verify the message written can be queried - const recordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolverStub); + const recordsQueryReply = await handleRecordsQuery(tenant, recordsQueryMessageData.message, messageStore, didResolver); expect(recordsQueryReply.status.code).to.equal(200); expect(recordsQueryReply.entries?.length).to.equal(1); expect((recordsQueryReply.entries![0] as RecordsWriteMessage).encodedData).to.equal(base64url.baseEncode(data1)); @@ -88,22 +86,22 @@ describe('handleRecordsWrite()', () => { // sanity check that old data and new data are different expect(newDataEncoded).to.not.equal(recordsWriteMessageData.message.encodedData); - const newRecordsWriteReply = await handleRecordsWrite(newRecordsWrite.message, messageStore, didResolverStub); + const newRecordsWriteReply = await handleRecordsWrite(tenant, newRecordsWrite.message, messageStore, didResolver); expect(newRecordsWriteReply.status.code).to.equal(202); // verify new record has overwritten the existing record - const newRecordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolverStub); + const newRecordsQueryReply = await handleRecordsQuery(tenant, recordsQueryMessageData.message, messageStore, didResolver); expect(newRecordsQueryReply.status.code).to.equal(200); expect(newRecordsQueryReply.entries?.length).to.equal(1); expect((newRecordsQueryReply.entries![0] as RecordsWriteMessage).encodedData).to.equal(newDataEncoded); // try to write the older message to store again and verify that it is not accepted - const thirdRecordsWriteReply = await handleRecordsWrite(recordsWriteMessageData.message, messageStore, didResolverStub); + const thirdRecordsWriteReply = await handleRecordsWrite(tenant, recordsWriteMessageData.message, messageStore, didResolver); expect(thirdRecordsWriteReply.status.code).to.equal(409); // expecting to fail // expecting unchanged - const thirdRecordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolverStub); + const thirdRecordsQueryReply = await handleRecordsQuery(tenant, recordsQueryMessageData.message, messageStore, didResolver); expect(thirdRecordsQueryReply.status.code).to.equal(200); expect(thirdRecordsQueryReply.entries?.length).to.equal(1); expect((thirdRecordsQueryReply.entries![0] as RecordsWriteMessage).encodedData).to.equal(newDataEncoded); @@ -112,10 +110,9 @@ describe('handleRecordsWrite()', () => { it('should only be able to overwrite existing record if new message CID is larger when `dateModified` value is the same', async () => { // start by writing an originating message const requester = await TestDataGenerator.generatePersona(); - const target = requester; + const tenant = requester.did; const originatingMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester, - target, data: Encoder.stringToBytes('unused') }); @@ -123,7 +120,7 @@ describe('handleRecordsWrite()', () => { const didResolverStub = TestStubGenerator.createDidResolverStub(requester); // sanity check that originating message got written - const originatingMessageWriteReply = await handleRecordsWrite(originatingMessageData.message, messageStore, didResolverStub); + const originatingMessageWriteReply = await handleRecordsWrite(tenant, originatingMessageData.message, messageStore, didResolverStub); expect(originatingMessageWriteReply.status.code).to.equal(202); // generate two new RecordsWrite messages with the same `dateModified` value @@ -154,44 +151,39 @@ describe('handleRecordsWrite()', () => { } // write the message with the smaller lexicographical message CID first - const recordsWriteReply = await handleRecordsWrite(smallerCollectionWrite.message, messageStore, didResolverStub); + const recordsWriteReply = await handleRecordsWrite(tenant, smallerCollectionWrite.message, messageStore, didResolverStub); expect(recordsWriteReply.status.code).to.equal(202); // query to fetch the record const recordsQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester, - target, filter: { recordId: originatingMessageData.message.recordId } }); // verify the data is written - const recordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolverStub); + const recordsQueryReply = await handleRecordsQuery(tenant, recordsQueryMessageData.message, messageStore, didResolverStub); expect(recordsQueryReply.status.code).to.equal(200); expect(recordsQueryReply.entries?.length).to.equal(1); expect((recordsQueryReply.entries![0] as RecordsWriteMessage).descriptor.dataCid) .to.equal(smallerCollectionWrite.message.descriptor.dataCid); // attempt to write the message with larger lexicographical message CID - const newRecordsWriteReply = await handleRecordsWrite(largerCollectionWrite.message, messageStore, didResolverStub); + const newRecordsWriteReply = await handleRecordsWrite(tenant, largerCollectionWrite.message, messageStore, didResolverStub); expect(newRecordsWriteReply.status.code).to.equal(202); // verify new record has overwritten the existing record - const newRecordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolverStub); + const newRecordsQueryReply = await handleRecordsQuery(tenant, recordsQueryMessageData.message, messageStore, didResolverStub); expect(newRecordsQueryReply.status.code).to.equal(200); expect(newRecordsQueryReply.entries?.length).to.equal(1); expect((newRecordsQueryReply.entries![0] as RecordsWriteMessage).descriptor.dataCid) .to.equal(largerCollectionWrite.message.descriptor.dataCid); // try to write the message with smaller lexicographical message CID again - const thirdRecordsWriteReply = await handleRecordsWrite( - smallerCollectionWrite.message, - messageStore, - didResolverStub - ); + const thirdRecordsWriteReply = await handleRecordsWrite(tenant, smallerCollectionWrite.message, messageStore, didResolverStub ); expect(thirdRecordsWriteReply.status.code).to.equal(409); // expecting to fail // verify the message in store is still the one with larger lexicographical message CID - const thirdRecordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolverStub); + const thirdRecordsQueryReply = await handleRecordsQuery(tenant, recordsQueryMessageData.message, messageStore, didResolverStub); expect(thirdRecordsQueryReply.status.code).to.equal(200); expect(thirdRecordsQueryReply.entries?.length).to.equal(1); expect((thirdRecordsQueryReply.entries![0] as RecordsWriteMessage).descriptor.dataCid) @@ -200,8 +192,9 @@ describe('handleRecordsWrite()', () => { it('should not allow changes to immutable properties', async () => { const initialWriteData = await TestDataGenerator.generateRecordsWriteMessage(); + const tenant = initialWriteData.requester.did; const didResolverStub = TestStubGenerator.createDidResolverStub(initialWriteData.requester); - const initialWriteReply = await handleRecordsWrite(initialWriteData.message, messageStore, didResolverStub); + const initialWriteReply = await handleRecordsWrite(tenant, initialWriteData.message, messageStore, didResolverStub); expect(initialWriteReply.status.code).to.equal(202); const recordId = initialWriteData.message.recordId; @@ -211,14 +204,13 @@ describe('handleRecordsWrite()', () => { // dateCreated test let childMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : initialWriteData.requester, - target : initialWriteData.target, recordId, schema, dateCreated : getCurrentTimeInHighPrecision(), // should not be allowed to be modified dataFormat : initialWriteData.message.descriptor.dataFormat }); - let reply = await handleRecordsWrite(childMessageData.message, messageStore, didResolverStub); + let reply = await handleRecordsWrite(tenant, childMessageData.message, messageStore, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('dateCreated is an immutable property'); @@ -226,14 +218,13 @@ describe('handleRecordsWrite()', () => { // schema test childMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : initialWriteData.requester, - target : initialWriteData.target, recordId, schema : 'should-not-allowed-to-be-modified', dateCreated, dataFormat : initialWriteData.message.descriptor.dataFormat }); - reply = await handleRecordsWrite(childMessageData.message, messageStore, didResolverStub); + reply = await handleRecordsWrite(tenant, childMessageData.message, messageStore, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('schema is an immutable property'); @@ -241,51 +232,49 @@ describe('handleRecordsWrite()', () => { // dataFormat test childMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : initialWriteData.requester, - target : initialWriteData.target, recordId, schema, dateCreated, dataFormat : 'should-not-be-allowed-to-change' }); - reply = await handleRecordsWrite(childMessageData.message, messageStore, didResolverStub); + reply = await handleRecordsWrite(tenant, childMessageData.message, messageStore, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('dataFormat is an immutable property'); }); - describe('initial write tests', () => { + describe('initial write & subsequent tests', () => { describe('createFrom()', () => { it('should accept a publish RecordsWrite using createFrom() without specifying datePublished', async () => { const { message, requester, recordsWrite } = await TestDataGenerator.generateRecordsWriteMessage({ published: false }); + const tenant = requester.did; // setting up a stub DID resolver const didResolverStub = TestStubGenerator.createDidResolverStub(requester); - const reply = await handleRecordsWrite(message, messageStore, didResolverStub); + const reply = await handleRecordsWrite(tenant, message, messageStore, didResolverStub); expect(reply.status.code).to.equal(202); const newWrite = await RecordsWrite.createFrom({ - target : requester.did, unsignedRecordsWriteMessage : recordsWrite.message, published : true, signatureInput : TestDataGenerator.createSignatureInputFromPersona(requester) }); - const newWriteReply = await handleRecordsWrite(newWrite.message, messageStore, didResolverStub); + const newWriteReply = await handleRecordsWrite(tenant, newWrite.message, messageStore, didResolverStub); expect(newWriteReply.status.code).to.equal(202); // verify the new record state can be queried const recordsQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester, - target : requester, - filter : { recordId: message.recordId } + filter: { recordId: message.recordId } }); - const recordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolverStub); + const recordsQueryReply = await handleRecordsQuery(tenant, recordsQueryMessageData.message, messageStore, didResolverStub); expect(recordsQueryReply.status.code).to.equal(200); expect(recordsQueryReply.entries?.length).to.equal(1); expect((recordsQueryReply.entries![0] as RecordsWriteMessage).descriptor.published).to.equal(true); @@ -295,33 +284,32 @@ describe('handleRecordsWrite()', () => { const { message, requester, recordsWrite } = await TestDataGenerator.generateRecordsWriteMessage({ published: true }); + const tenant = requester.did; // setting up a stub DID resolver const didResolverStub = TestStubGenerator.createDidResolverStub(requester); - const reply = await handleRecordsWrite(message, messageStore, didResolverStub); + const reply = await handleRecordsWrite(tenant, message, messageStore, didResolverStub); expect(reply.status.code).to.equal(202); const newData = Encoder.stringToBytes('new data'); const newWrite = await RecordsWrite.createFrom({ - target : requester.did, unsignedRecordsWriteMessage : recordsWrite.message, data : newData, signatureInput : TestDataGenerator.createSignatureInputFromPersona(requester) }); - const newWriteReply = await handleRecordsWrite(newWrite.message, messageStore, didResolverStub); + const newWriteReply = await handleRecordsWrite(tenant, newWrite.message, messageStore, didResolverStub); expect(newWriteReply.status.code).to.equal(202); // verify the new record state can be queried const recordsQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester, - target : requester, - filter : { recordId: message.recordId } + filter: { recordId: message.recordId } }); - const recordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolverStub); + const recordsQueryReply = await handleRecordsQuery(tenant, recordsQueryMessageData.message, messageStore, didResolverStub); expect(recordsQueryReply.status.code).to.equal(200); expect(recordsQueryReply.entries?.length).to.equal(1); @@ -338,9 +326,10 @@ describe('handleRecordsWrite()', () => { recordId, data: Encoder.stringToBytes('anything') // simulating modification of a message }); + const tenant = requester.did; const didResolverStub = TestStubGenerator.createDidResolverStub(requester); - const reply = await handleRecordsWrite(message, messageStore, didResolverStub); + const reply = await handleRecordsWrite(tenant, message, messageStore, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('initial write is not found'); @@ -351,9 +340,10 @@ describe('handleRecordsWrite()', () => { dateCreated : '2023-01-10T10:20:30.405060', dateModified : getCurrentTimeInHighPrecision() // this always generate a different timestamp }); + const tenant = requester.did; const didResolverStub = TestStubGenerator.createDidResolverStub(requester); - const reply = await handleRecordsWrite(message, messageStore, didResolverStub); + const reply = await handleRecordsWrite(tenant, message, messageStore, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('must match dateCreated'); @@ -368,7 +358,7 @@ describe('handleRecordsWrite()', () => { const didResolverStub = sinon.createStubInstance(DidResolver); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - const reply = await handleRecordsWrite(message, messageStoreStub, didResolverStub); + const reply = await handleRecordsWrite('unused-tenant-DID', message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('does not match deterministic contextId'); }); @@ -401,16 +391,15 @@ describe('handleRecordsWrite()', () => { const alice = await TestDataGenerator.generatePersona(); const protocolsConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - requester : alice, - target : alice, + requester: alice, protocol, protocolDefinition }); // setting up a stub did resolver - const aliceDidResolverStub = TestStubGenerator.createDidResolverStub(alice); + const aliceDidResolver = TestStubGenerator.createDidResolverStub(alice); - const protocolWriteReply = await handleProtocolsConfigure(protocolsConfigureMessageData.message, messageStore, aliceDidResolverStub); + const protocolWriteReply = await handleProtocolsConfigure(alice.did, protocolsConfigureMessageData.message, messageStore, aliceDidResolver); expect(protocolWriteReply.status.code).to.equal(202); // generate a `RecordsWrite` message from bob allowed by anyone @@ -419,7 +408,6 @@ describe('handleRecordsWrite()', () => { const emailMessageDataFromBob = await TestDataGenerator.generateRecordsWriteMessage( { requester : bob, - target : alice, protocol, schema : 'email', data : bobData @@ -428,16 +416,15 @@ describe('handleRecordsWrite()', () => { const bobDidResolverStub = TestStubGenerator.createDidResolverStub(bob); - const bobWriteReply = await handleRecordsWrite(emailMessageDataFromBob.message, messageStore, bobDidResolverStub); + const bobWriteReply = await handleRecordsWrite(alice.did, emailMessageDataFromBob.message, messageStore, bobDidResolverStub); expect(bobWriteReply.status.code).to.equal(202); // verify bob's message got written to the DB const messageDataForQueryingBobsWrite = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { recordId: emailMessageDataFromBob.message.recordId } }); - const bobRecordQueryReply = await handleRecordsQuery(messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); + const bobRecordQueryReply = await handleRecordsQuery(alice.did, messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolver); expect(bobRecordQueryReply.status.code).to.equal(200); expect(bobRecordQueryReply.entries?.length).to.equal(1); expect((bobRecordQueryReply.entries![0] as RecordsWriteMessage).encodedData).to.equal(base64url.baseEncode(bobData)); @@ -454,40 +441,37 @@ describe('handleRecordsWrite()', () => { const alice = await TestDataGenerator.generatePersona(); const protocolsConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - requester : alice, - target : alice, + requester: alice, protocol, protocolDefinition }); // setting up a stub did resolver - const aliceDidResolverStub = TestStubGenerator.createDidResolverStub(alice); + const aliceDidResolver = TestStubGenerator.createDidResolverStub(alice); - const protocolWriteReply = await handleProtocolsConfigure(protocolsConfigureMessageData.message, messageStore, aliceDidResolverStub); + const protocolWriteReply = await handleProtocolsConfigure(alice.did, protocolsConfigureMessageData.message, messageStore, aliceDidResolver); expect(protocolWriteReply.status.code).to.equal(202); // write a credential application to Alice's DWN to simulate that she has sent a credential application to a VC issuer const vcIssuer = await TestDataGenerator.generatePersona(); const encodedCredentialApplication = new TextEncoder().encode('credential application data'); - const credentialApplicationMessageData = await TestDataGenerator.generateRecordsWriteMessage({ + const credentialApplicationData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : alice, recipientDid : vcIssuer.did, protocol, schema : credentialApplicationSchema, data : encodedCredentialApplication }); - const credentialApplicationContextId = await credentialApplicationMessageData.recordsWrite.getEntryId(); + const credentialApplicationContextId = await credentialApplicationData.recordsWrite.getEntryId(); - const credentialApplicationReply = await handleRecordsWrite(credentialApplicationMessageData.message, messageStore, aliceDidResolverStub); + const credentialApplicationReply = await handleRecordsWrite(alice.did, credentialApplicationData.message, messageStore, aliceDidResolver); expect(credentialApplicationReply.status.code).to.equal(202); // generate a credential application response message from VC issuer const encodedCredentialResponse = new TextEncoder().encode('credential response data'); - const credentialResponseMessageData = await TestDataGenerator.generateRecordsWriteMessage( + const credentialResponseData = await TestDataGenerator.generateRecordsWriteMessage( { requester : vcIssuer, - target : alice, recipientDid : alice.did, protocol, contextId : credentialApplicationContextId, @@ -499,19 +483,19 @@ describe('handleRecordsWrite()', () => { const vcIssuerDidResolverStub = TestStubGenerator.createDidResolverStub(vcIssuer); - const credentialResponseReply = await handleRecordsWrite(credentialResponseMessageData.message, messageStore, vcIssuerDidResolverStub); + const credentialResponseReply = await handleRecordsWrite(alice.did, credentialResponseData.message, messageStore, vcIssuerDidResolverStub); expect(credentialResponseReply.status.code).to.equal(202); // verify VC issuer's message got written to the DB const messageDataForQueryingCredentialResponse = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, - filter : { recordId: credentialResponseMessageData.message.recordId } + filter : { recordId: credentialResponseData.message.recordId } }); const applicationResponseQueryReply = await handleRecordsQuery( + alice.did, messageDataForQueryingCredentialResponse.message, messageStore, - aliceDidResolverStub + aliceDidResolver ); expect(applicationResponseQueryReply.status.code).to.equal(200); expect(applicationResponseQueryReply.entries?.length).to.equal(1); @@ -544,9 +528,8 @@ describe('handleRecordsWrite()', () => { }; const alice = await TestDataGenerator.generatePersona(); - const protocolsConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - requester : alice, - target : alice, + const protocolsConfigureData = await TestDataGenerator.generateProtocolsConfigureMessage({ + requester: alice, protocol, protocolDefinition }); @@ -554,7 +537,7 @@ describe('handleRecordsWrite()', () => { // setting up a stub did resolver const aliceDidResolverStub = TestStubGenerator.createDidResolverStub(alice); - const protocolWriteReply = await handleProtocolsConfigure(protocolsConfigureMessageData.message, messageStore, aliceDidResolverStub); + const protocolWriteReply = await handleProtocolsConfigure(alice.did, protocolsConfigureData.message, messageStore, aliceDidResolverStub); expect(protocolWriteReply.status.code).to.equal(202); // generate a `RecordsWrite` message from bob @@ -563,7 +546,6 @@ describe('handleRecordsWrite()', () => { const notesMessageDataFromBob = await TestDataGenerator.generateRecordsWriteMessage( { requester : bob, - target : alice, protocol, schema : 'notes', data : bobData @@ -572,16 +554,15 @@ describe('handleRecordsWrite()', () => { const bobDidResolverStub = TestStubGenerator.createDidResolverStub(bob); - const bobWriteReply = await handleRecordsWrite(notesMessageDataFromBob.message, messageStore, bobDidResolverStub); + const bobWriteReply = await handleRecordsWrite(alice.did, notesMessageDataFromBob.message, messageStore, bobDidResolverStub); expect(bobWriteReply.status.code).to.equal(202); // verify bob's message got written to the DB const messageDataForQueryingBobsWrite = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { recordId: notesMessageDataFromBob.message.recordId } }); - const bobRecordQueryReply = await handleRecordsQuery(messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); + const bobRecordQueryReply = await handleRecordsQuery(alice.did, messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); expect(bobRecordQueryReply.status.code).to.equal(200); expect(bobRecordQueryReply.entries?.length).to.equal(1); expect((bobRecordQueryReply.entries![0] as RecordsWriteMessage).encodedData).to.equal(base64url.baseEncode(bobData)); @@ -594,11 +575,11 @@ describe('handleRecordsWrite()', () => { data : newNotesBytes }); - const newWriteReply = await handleRecordsWrite(newNotesMessageFromBob.message, messageStore, bobDidResolverStub); + const newWriteReply = await handleRecordsWrite(alice.did, newNotesMessageFromBob.message, messageStore, bobDidResolverStub); expect(newWriteReply.status.code).to.equal(202); // verify bob's message got written to the DB - const newRecordQueryReply = await handleRecordsQuery(messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); + const newRecordQueryReply = await handleRecordsQuery(alice.did, messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); expect(newRecordQueryReply.status.code).to.equal(200); expect(newRecordQueryReply.entries?.length).to.equal(1); expect((newRecordQueryReply.entries![0] as RecordsWriteMessage).encodedData).to.equal(Encoder.bytesToBase64Url(newNotesBytes)); @@ -629,9 +610,8 @@ describe('handleRecordsWrite()', () => { }; const alice = await TestDataGenerator.generatePersona(); - const protocolsConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - requester : alice, - target : alice, + const protocolsConfigureData = await TestDataGenerator.generateProtocolsConfigureMessage({ + requester: alice, protocol, protocolDefinition }); @@ -639,7 +619,7 @@ describe('handleRecordsWrite()', () => { // setting up a stub did resolver const aliceDidResolverStub = TestStubGenerator.createDidResolverStub(alice); - const protocolWriteReply = await handleProtocolsConfigure(protocolsConfigureMessageData.message, messageStore, aliceDidResolverStub); + const protocolWriteReply = await handleProtocolsConfigure(alice.did, protocolsConfigureData.message, messageStore, aliceDidResolverStub); expect(protocolWriteReply.status.code).to.equal(202); // generate a `RecordsWrite` message from bob @@ -648,7 +628,6 @@ describe('handleRecordsWrite()', () => { const notesMessageDataFromBob = await TestDataGenerator.generateRecordsWriteMessage( { requester : bob, - target : alice, protocol, schema : 'notes', data : bobData @@ -657,16 +636,15 @@ describe('handleRecordsWrite()', () => { const bobDidResolverStub = TestStubGenerator.createDidResolverStub(bob); - const bobWriteReply = await handleRecordsWrite(notesMessageDataFromBob.message, messageStore, bobDidResolverStub); + const bobWriteReply = await handleRecordsWrite(alice.did, notesMessageDataFromBob.message, messageStore, bobDidResolverStub); expect(bobWriteReply.status.code).to.equal(202); // verify bob's message got written to the DB const messageDataForQueryingBobsWrite = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { recordId: notesMessageDataFromBob.message.recordId } }); - const bobRecordQueryReply = await handleRecordsQuery(messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); + const bobRecordQueryReply = await handleRecordsQuery(alice.did, messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); expect(bobRecordQueryReply.status.code).to.equal(200); expect(bobRecordQueryReply.entries?.length).to.equal(1); expect((bobRecordQueryReply.entries![0] as RecordsWriteMessage).encodedData).to.equal(base64url.baseEncode(bobData)); @@ -677,7 +655,6 @@ describe('handleRecordsWrite()', () => { const newNotesMessageDataFromBob = await TestDataGenerator.generateRecordsWriteMessage( { requester : carol, - target : alice, protocol, schema : 'notes', data : newNotesData, @@ -686,7 +663,7 @@ describe('handleRecordsWrite()', () => { ); const carolDidResolverStub = TestStubGenerator.createDidResolverStub(carol); - const carolWriteReply = await handleRecordsWrite(newNotesMessageDataFromBob.message, messageStore, carolDidResolverStub); + const carolWriteReply = await handleRecordsWrite(alice.did, newNotesMessageDataFromBob.message, messageStore, carolDidResolverStub); expect(carolWriteReply.status.code).to.equal(401); expect(carolWriteReply.status.detail).to.contain('must match to author of initial write'); }); @@ -719,9 +696,8 @@ describe('handleRecordsWrite()', () => { }; const alice = await TestDataGenerator.generatePersona(); - const protocolsConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - requester : alice, - target : alice, + const protocolsConfigureData = await TestDataGenerator.generateProtocolsConfigureMessage({ + requester: alice, protocol, protocolDefinition }); @@ -729,7 +705,7 @@ describe('handleRecordsWrite()', () => { // setting up a stub did resolver const aliceDidResolverStub = TestStubGenerator.createDidResolverStub(alice); - const protocolWriteReply = await handleProtocolsConfigure(protocolsConfigureMessageData.message, messageStore, aliceDidResolverStub); + const protocolWriteReply = await handleProtocolsConfigure(alice.did, protocolsConfigureData.message, messageStore, aliceDidResolverStub); expect(protocolWriteReply.status.code).to.equal(202); // generate a `RecordsWrite` message from bob @@ -738,7 +714,6 @@ describe('handleRecordsWrite()', () => { const notesMessageDataFromBob = await TestDataGenerator.generateRecordsWriteMessage( { requester : bob, - target : alice, protocol, schema : 'notes', data : bobData @@ -747,16 +722,15 @@ describe('handleRecordsWrite()', () => { const bobDidResolverStub = TestStubGenerator.createDidResolverStub(bob); - const bobWriteReply = await handleRecordsWrite(notesMessageDataFromBob.message, messageStore, bobDidResolverStub); + const bobWriteReply = await handleRecordsWrite(alice.did, notesMessageDataFromBob.message, messageStore, bobDidResolverStub); expect(bobWriteReply.status.code).to.equal(202); // verify bob's message got written to the DB const messageDataForQueryingBobsWrite = await TestDataGenerator.generateRecordsQueryMessage({ requester : alice, - target : alice, filter : { recordId: notesMessageDataFromBob.message.recordId } }); - const bobRecordQueryReply = await handleRecordsQuery(messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); + const bobRecordQueryReply = await handleRecordsQuery(alice.did, messageDataForQueryingBobsWrite.message, messageStore, aliceDidResolverStub); expect(bobRecordQueryReply.status.code).to.equal(200); expect(bobRecordQueryReply.entries?.length).to.equal(1); expect((bobRecordQueryReply.entries![0] as RecordsWriteMessage).encodedData).to.equal(base64url.baseEncode(bobData)); @@ -765,7 +739,7 @@ describe('handleRecordsWrite()', () => { const newNotesMessageDataFromBob = await TestDataGenerator.generateRecordsWriteMessage( { requester : bob, - target : alice, + dateCreated : notesMessageDataFromBob.message.descriptor.dateCreated, protocol, schema : 'notes', data : bobData, @@ -774,7 +748,7 @@ describe('handleRecordsWrite()', () => { } ); - const newWriteReply = await handleRecordsWrite(newNotesMessageDataFromBob.message, messageStore, bobDidResolverStub); + const newWriteReply = await handleRecordsWrite(alice.did, newNotesMessageDataFromBob.message, messageStore, bobDidResolverStub); expect(newWriteReply.status.code).to.equal(400); expect(newWriteReply.status.detail).to.contain('recipient is an immutable property'); }); @@ -790,9 +764,8 @@ describe('handleRecordsWrite()', () => { const alice = await TestDataGenerator.generatePersona(); - const protocolsConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - requester : alice, - target : alice, + const protocolsConfigureData = await TestDataGenerator.generateProtocolsConfigureMessage({ + requester: alice, protocol, protocolDefinition }); @@ -800,32 +773,30 @@ describe('handleRecordsWrite()', () => { // setting up a stub did resolver const aliceDidResolverStub = TestStubGenerator.createDidResolverStub(alice); - const protocolWriteReply = await handleProtocolsConfigure(protocolsConfigureMessageData.message, messageStore, aliceDidResolverStub); + const protocolWriteReply = await handleProtocolsConfigure(alice.did, protocolsConfigureData.message, messageStore, aliceDidResolverStub); expect(protocolWriteReply.status.code).to.equal(202); // write a credential application to Alice's DWN to simulate that she has sent a credential application to a VC issuer const vcIssuer = await TestDataGenerator.generatePersona(); const encodedCredentialApplication = new TextEncoder().encode('credential application data'); - const credentialApplicationMessageData = await TestDataGenerator.generateRecordsWriteMessage({ + const credentialApplicationData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : alice, recipientDid : vcIssuer.did, protocol, schema : credentialApplicationSchema, data : encodedCredentialApplication }); - const credentialApplicationContextId = await credentialApplicationMessageData.recordsWrite.getEntryId(); + const credentialApplicationContextId = await credentialApplicationData.recordsWrite.getEntryId(); - const credentialApplicationReply = await handleRecordsWrite(credentialApplicationMessageData.message, messageStore, aliceDidResolverStub); + const credentialApplicationReply = await handleRecordsWrite(alice.did, credentialApplicationData.message, messageStore, aliceDidResolverStub); expect(credentialApplicationReply.status.code).to.equal(202); // generate a credential application response message from a fake VC issuer const fakeVcIssuer = await TestDataGenerator.generatePersona(); const encodedCredentialResponse = new TextEncoder().encode('credential response data'); - const credentialResponseMessageData = await TestDataGenerator.generateRecordsWriteMessage( + const credentialResponseData = await TestDataGenerator.generateRecordsWriteMessage( { requester : fakeVcIssuer, - target : alice, recipientDid : alice.did, protocol, contextId : credentialApplicationContextId, @@ -837,7 +808,7 @@ describe('handleRecordsWrite()', () => { const vcIssuerDidResolverStub = TestStubGenerator.createDidResolverStub(fakeVcIssuer); - const credentialResponseReply = await handleRecordsWrite(credentialResponseMessageData.message, messageStore, vcIssuerDidResolverStub); + const credentialResponseReply = await handleRecordsWrite(alice.did, credentialResponseData.message, messageStore, vcIssuerDidResolverStub); expect(credentialResponseReply.status.code).to.equal(401); expect(credentialResponseReply.status.detail).to.contain('unexpected inbound message author'); }); @@ -848,13 +819,12 @@ describe('handleRecordsWrite()', () => { const data = Encoder.stringToBytes('any data'); const credentialApplicationMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : alice, recipientDid : alice.did, protocol, data }); - const reply = await handleRecordsWrite(credentialApplicationMessageData.message, messageStore, didResolver); + const reply = await handleRecordsWrite(alice.did, credentialApplicationMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.contain('unable to find protocol definition'); }); @@ -864,26 +834,24 @@ describe('handleRecordsWrite()', () => { const protocol = 'https://identity.foundation/decentralized-web-node/protocols/credential-issuance'; const protocolConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - target : alice, requester : alice, protocol, protocolDefinition : credentialIssuanceProtocolDefinition }); - const protocolConfigureReply = await handleProtocolsConfigure(protocolConfigureMessageData.message, messageStore, didResolver); + const protocolConfigureReply = await handleProtocolsConfigure(alice.did, protocolConfigureMessageData.message, messageStore, didResolver); expect(protocolConfigureReply.status.code).to.equal(202); const data = Encoder.stringToBytes('any data'); const credentialApplicationMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : alice, recipientDid : alice.did, protocol, schema : 'unexpectedSchema', data }); - const reply = await handleRecordsWrite(credentialApplicationMessageData.message, messageStore, didResolver); + const reply = await handleRecordsWrite(alice.did, credentialApplicationMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.equal('record with schema \'unexpectedSchema\' not allowed in protocol'); }); @@ -894,27 +862,25 @@ describe('handleRecordsWrite()', () => { const protocol = 'https://identity.foundation/decentralized-web-node/protocols/credential-issuance'; const protocolDefinition = credentialIssuanceProtocolDefinition; const protocolConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - target : alice, - requester : alice, + requester: alice, protocol, protocolDefinition }); const credentialResponseSchema = protocolDefinition.labels.credentialResponse.schema; - const protocolConfigureReply = await handleProtocolsConfigure(protocolConfigureMessageData.message, messageStore, didResolver); + const protocolConfigureReply = await handleProtocolsConfigure(alice.did, protocolConfigureMessageData.message, messageStore, didResolver); expect(protocolConfigureReply.status.code).to.equal(202); const data = Encoder.stringToBytes('any data'); const credentialApplicationMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : alice, recipientDid : alice.did, protocol, schema : credentialResponseSchema, // this is an known schema type, but not allowed for a protocol root record data }); - const reply = await handleRecordsWrite(credentialApplicationMessageData.message, messageStore, didResolver); + const reply = await handleRecordsWrite(alice.did, credentialApplicationMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.contain('not allowed in structure level'); }); @@ -935,41 +901,38 @@ describe('handleRecordsWrite()', () => { } }; const protocolConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - target : alice, - requester : alice, + requester: alice, protocol, protocolDefinition }); - const protocolConfigureReply = await handleProtocolsConfigure(protocolConfigureMessageData.message, messageStore, didResolver); + const protocolConfigureReply = await handleProtocolsConfigure(alice.did, protocolConfigureMessageData.message, messageStore, didResolver); expect(protocolConfigureReply.status.code).to.equal(202); // test that Alice is allowed to write to her own DWN const data = Encoder.stringToBytes('any data'); const aliceWriteMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : alice, recipientDid : alice.did, protocol, schema : 'private-note', data }); - let reply = await handleRecordsWrite(aliceWriteMessageData.message, messageStore, didResolver); + let reply = await handleRecordsWrite(alice.did, aliceWriteMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); // test that Bob is not allowed to write to Alice's DWN const bob = await DidKeyResolver.generate(); const bobWriteMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : bob, - target : alice, recipientDid : alice.did, protocol, schema : 'private-note', data }); - reply = await handleRecordsWrite(bobWriteMessageData.message, messageStore, didResolver); + reply = await handleRecordsWrite(alice.did, bobWriteMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.contain('no allow rule defined for requester'); }); @@ -986,20 +949,18 @@ describe('handleRecordsWrite()', () => { // write the VC issuance protocol const protocol = 'https://identity.foundation/decentralized-web-node/protocols/credential-issuance'; const protocolConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - target : alice, requester : alice, protocol, protocolDefinition : invalidProtocolDefinition }); - const protocolConfigureReply = await handleProtocolsConfigure(protocolConfigureMessageData.message, messageStore, didResolver); + const protocolConfigureReply = await handleProtocolsConfigure(alice.did, protocolConfigureMessageData.message, messageStore, didResolver); expect(protocolConfigureReply.status.code).to.equal(202); // simulate Alice's VC applications with both issuer const data = Encoder.stringToBytes('irrelevant'); const messageDataWithIssuerA = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : alice, recipientDid : issuer.did, schema : credentialIssuanceProtocolDefinition.labels.credentialApplication.schema, protocol, @@ -1007,13 +968,12 @@ describe('handleRecordsWrite()', () => { }); const contextId = await messageDataWithIssuerA.recordsWrite.getEntryId(); - let reply = await handleRecordsWrite(messageDataWithIssuerA.message, messageStore, didResolver); + let reply = await handleRecordsWrite(alice.did, messageDataWithIssuerA.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); // simulate issuer attempting to respond to Alice's VC application const invalidResponseDataByIssuerA = await TestDataGenerator.generateRecordsWriteMessage({ requester : issuer, - target : alice, recipientDid : alice.did, schema : credentialIssuanceProtocolDefinition.labels.credentialResponse.schema, contextId, @@ -1022,7 +982,7 @@ describe('handleRecordsWrite()', () => { data }); - reply = await handleRecordsWrite(invalidResponseDataByIssuerA.message, messageStore, didResolver); + reply = await handleRecordsWrite(alice.did, invalidResponseDataByIssuerA.message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.contain('path to expected recipient is longer than actual length of ancestor message chain'); }); @@ -1039,20 +999,18 @@ describe('handleRecordsWrite()', () => { // write the VC issuance protocol const protocol = 'https://identity.foundation/decentralized-web-node/protocols/credential-issuance'; const protocolConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - target : alice, requester : alice, protocol, protocolDefinition : invalidProtocolDefinition }); - const protocolConfigureReply = await handleProtocolsConfigure(protocolConfigureMessageData.message, messageStore, didResolver); + const protocolConfigureReply = await handleProtocolsConfigure(alice.did, protocolConfigureMessageData.message, messageStore, didResolver); expect(protocolConfigureReply.status.code).to.equal(202); // simulate Alice's VC application to an issuer const data = Encoder.stringToBytes('irrelevant'); const messageDataWithIssuerA = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : alice, recipientDid : issuer.did, schema : credentialIssuanceProtocolDefinition.labels.credentialApplication.schema, protocol, @@ -1060,13 +1018,12 @@ describe('handleRecordsWrite()', () => { }); const contextId = await messageDataWithIssuerA.recordsWrite.getEntryId(); - let reply = await handleRecordsWrite(messageDataWithIssuerA.message, messageStore, didResolver); + let reply = await handleRecordsWrite(alice.did, messageDataWithIssuerA.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); // simulate issuer attempting to respond to Alice's VC application const invalidResponseDataByIssuerA = await TestDataGenerator.generateRecordsWriteMessage({ requester : issuer, - target : alice, recipientDid : alice.did, schema : credentialIssuanceProtocolDefinition.labels.credentialResponse.schema, contextId, @@ -1075,7 +1032,7 @@ describe('handleRecordsWrite()', () => { data }); - reply = await handleRecordsWrite(invalidResponseDataByIssuerA.message, messageStore, didResolver); + reply = await handleRecordsWrite(alice.did, invalidResponseDataByIssuerA.message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.contain('mismatching record schema'); }); @@ -1093,20 +1050,18 @@ describe('handleRecordsWrite()', () => { // write the DEX protocol in the PFI const protocolConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - target : pfi, requester : pfi, protocol, protocolDefinition : protocolDefinition }); - const protocolConfigureReply = await handleProtocolsConfigure(protocolConfigureMessageData.message, messageStore, didResolver); + const protocolConfigureReply = await handleProtocolsConfigure(pfi.did, protocolConfigureMessageData.message, messageStore, didResolver); expect(protocolConfigureReply.status.code).to.equal(202); // simulate Alice's ask and PFI's offer already occurred const data = Encoder.stringToBytes('irrelevant'); const askMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : pfi, recipientDid : pfi.did, schema : 'ask', protocol, @@ -1114,12 +1069,11 @@ describe('handleRecordsWrite()', () => { }); const contextId = await askMessageData.recordsWrite.getEntryId(); - let reply = await handleRecordsWrite(askMessageData.message, messageStore, didResolver); + let reply = await handleRecordsWrite(pfi.did, askMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); const offerMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : pfi, - target : pfi, recipientDid : alice.did, schema : 'offer', contextId, @@ -1128,13 +1082,12 @@ describe('handleRecordsWrite()', () => { data }); - reply = await handleRecordsWrite(offerMessageData.message, messageStore, didResolver); + reply = await handleRecordsWrite(pfi.did, offerMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); // the actual test: making sure fulfillment message is accepted const fulfillmentMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : pfi, recipientDid : pfi.did, schema : 'fulfillment', contextId, @@ -1142,18 +1095,17 @@ describe('handleRecordsWrite()', () => { protocol, data }); - reply = await handleRecordsWrite(fulfillmentMessageData.message, messageStore, didResolver); + reply = await handleRecordsWrite(pfi.did, fulfillmentMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); // verify the fulfillment message is stored const recordsQueryMessageData = await TestDataGenerator.generateRecordsQueryMessage({ requester : pfi, - target : pfi, filter : { recordId: fulfillmentMessageData.message.recordId } }); // verify the data is written - const recordsQueryReply = await handleRecordsQuery(recordsQueryMessageData.message, messageStore, didResolver); + const recordsQueryReply = await handleRecordsQuery(pfi.did, recordsQueryMessageData.message, messageStore, didResolver); expect(recordsQueryReply.status.code).to.equal(200); expect(recordsQueryReply.entries?.length).to.equal(1); expect((recordsQueryReply.entries![0] as RecordsWriteMessage).descriptor.dataCid) @@ -1174,20 +1126,18 @@ describe('handleRecordsWrite()', () => { // write the DEX protocol in the PFI const protocolConfigureMessageData = await TestDataGenerator.generateProtocolsConfigureMessage({ - target : pfi, requester : pfi, protocol, protocolDefinition : protocolDefinition }); - const protocolConfigureReply = await handleProtocolsConfigure(protocolConfigureMessageData.message, messageStore, didResolver); + const protocolConfigureReply = await handleProtocolsConfigure(pfi.did, protocolConfigureMessageData.message, messageStore, didResolver); expect(protocolConfigureReply.status.code).to.equal(202); // simulate Alice's ask const data = Encoder.stringToBytes('irrelevant'); const askMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : pfi, recipientDid : pfi.did, schema : 'ask', protocol, @@ -1195,13 +1145,12 @@ describe('handleRecordsWrite()', () => { }); const contextId = await askMessageData.recordsWrite.getEntryId(); - let reply = await handleRecordsWrite(askMessageData.message, messageStore, didResolver); + let reply = await handleRecordsWrite(pfi.did, askMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(202); // the actual test: making sure fulfillment message fails const fulfillmentMessageData = await TestDataGenerator.generateRecordsWriteMessage({ requester : alice, - target : pfi, recipientDid : pfi.did, schema : 'fulfillment', contextId, @@ -1209,7 +1158,7 @@ describe('handleRecordsWrite()', () => { protocol, data }); - reply = await handleRecordsWrite(fulfillmentMessageData.message, messageStore, didResolver); + reply = await handleRecordsWrite(pfi.did, fulfillmentMessageData.message, messageStore, didResolver); expect(reply.status.code).to.equal(401); expect(reply.status.detail).to.contain('no parent found'); }); @@ -1227,9 +1176,10 @@ describe('handleRecordsWrite()', () => { const signer = await GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput]); message.authorization = signer.getJws(); + const tenant = requester.did; const didResolverStub = TestStubGenerator.createDidResolverStub(requester); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - const reply = await handleRecordsWrite(message, messageStoreStub, didResolverStub); + const reply = await handleRecordsWrite(tenant, message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('does not match recordId in authorization'); @@ -1247,9 +1197,10 @@ describe('handleRecordsWrite()', () => { const signer = await GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput]); message.authorization = signer.getJws(); + const tenant = requester.did; const didResolverStub = sinon.createStubInstance(DidResolver); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - const reply = await handleRecordsWrite(message, messageStoreStub, didResolverStub); + const reply = await handleRecordsWrite(tenant, message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.contain('does not match contextId in authorization'); @@ -1262,7 +1213,7 @@ describe('handleRecordsWrite()', () => { const didResolverStub = sinon.createStubInstance(DidResolver); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - const reply = await handleRecordsWrite(messageData.message, messageStoreStub, didResolverStub); + const reply = await handleRecordsWrite(messageData.requester.did, messageData.message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(400); expect(reply.status.detail).to.equal('actual CID of data and `dataCid` in descriptor mismatch'); @@ -1277,22 +1228,23 @@ describe('handleRecordsWrite()', () => { const didResolverStub = TestStubGenerator.createDidResolverStub(mismatchingPersona); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); + const tenant = requester.did; - const reply = await handleRecordsWrite(message, messageStoreStub, didResolverStub); + const reply = await handleRecordsWrite(tenant, message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(401); }); it('should return 401 if an authorized requester is attempting write', async () => { const requester = await TestDataGenerator.generatePersona(); - const target = await TestDataGenerator.generatePersona(); - const { message } = await TestDataGenerator.generateRecordsWriteMessage({ requester, target }); + const { message } = await TestDataGenerator.generateRecordsWriteMessage({ requester }); // setting up a stub did resolver & message store const didResolverStub = TestStubGenerator.createDidResolverStub(requester); const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - const reply = await handleRecordsWrite(message, messageStoreStub, didResolverStub); + const tenant = await TestDataGenerator.generatePersona(); + const reply = await handleRecordsWrite(tenant.did, message, messageStoreStub, didResolverStub); expect(reply.status.code).to.equal(401); }); diff --git a/tests/interfaces/records/messages/records-write.spec.ts b/tests/interfaces/records/messages/records-write.spec.ts index a6f38b888..60e3de70f 100644 --- a/tests/interfaces/records/messages/records-write.spec.ts +++ b/tests/interfaces/records/messages/records-write.spec.ts @@ -19,7 +19,6 @@ describe('RecordsWrite', () => { const alice = await TestDataGenerator.generatePersona(); const options = { - target : alice.did, recipient : alice.did, data : TestDataGenerator.randomBytes(10), dataFormat : 'application/json', @@ -39,14 +38,13 @@ describe('RecordsWrite', () => { const messageStoreStub = sinon.createStubInstance(MessageStoreLevel); - await recordsWrite.authorize(messageStoreStub); + await recordsWrite.authorize(alice.did, messageStoreStub); }); it('should be able to auto-fill `datePublished` when `published` set to `true` but `datePublished` not given', async () => { const alice = await TestDataGenerator.generatePersona(); const options = { - target : alice.did, recipient : alice.did, data : TestDataGenerator.randomBytes(10), dataFormat : 'application/json', @@ -69,7 +67,6 @@ describe('RecordsWrite', () => { }); const write = await RecordsWrite.createFrom({ - target : requester.did, unsignedRecordsWriteMessage : recordsWrite.message, datePublished : getCurrentTimeInHighPrecision(), signatureInput : TestDataGenerator.createSignatureInputFromPersona(requester) diff --git a/tests/store/message-store.spec.ts b/tests/store/message-store.spec.ts index 3a2ad9fe7..8583cb82d 100644 --- a/tests/store/message-store.spec.ts +++ b/tests/store/message-store.spec.ts @@ -1,3 +1,4 @@ +import { DidKeyResolver } from '../../src/index.js'; import { expect } from 'chai'; import { generateCid } from '../../src/utils/cid.js'; import { Message } from '../../src/core/message.js'; @@ -85,37 +86,30 @@ describe('MessageStoreLevel Tests', () => { expect(resultCid.equals(expectedCid)).to.be.true; }); - it('adds tenant to index', async () => { - const { target, message } = await TestDataGenerator.generatePermissionsRequestMessage(); - - await messageStore.put(message, { target }); - - const results = await messageStore.query({ target }); - expect(results.length).to.equal(1); - }); - // https://github.com/TBD54566975/dwn-sdk-js/issues/170 it('#170 - should be able to update (delete and insert new) indexes to an existing message', async () => { - const { target, message } = await TestDataGenerator.generateRecordsWriteMessage(); + const alice = await DidKeyResolver.generate(); + + const { message } = await TestDataGenerator.generateRecordsWriteMessage(); // inserting the message indicating it is the 'latest' in the index - await messageStore.put(message, { target: target.did, latest: 'true' }); + await messageStore.put(message, { tenant: alice.did, latest: 'true' }); - const results1 = await messageStore.query({ target: target.did, latest: 'true' }); + const results1 = await messageStore.query({ tenant: alice.did, latest: 'true' }); expect(results1.length).to.equal(1); - const results2 = await messageStore.query({ target: target.did, latest: 'false' }); + const results2 = await messageStore.query({ tenant: alice.did, latest: 'false' }); expect(results2.length).to.equal(0); // deleting the existing indexes and replacing it indicating it is no longer the 'latest' const cid = await Message.getCid(message); await messageStore.delete(cid); - await messageStore.put(message, { target: target.did, latest: 'false' }); + await messageStore.put(message, { tenant: alice.did, latest: 'false' }); - const results3 = await messageStore.query({ target: target.did, latest: 'true' }); + const results3 = await messageStore.query({ tenant: alice.did, latest: 'true' }); expect(results3.length).to.equal(0); - const results4 = await messageStore.query({ target: target.did, latest: 'false' }); + const results4 = await messageStore.query({ tenant: alice.did, latest: 'false' }); expect(results4.length).to.equal(1); }); diff --git a/tests/utils/test-data-generator.ts b/tests/utils/test-data-generator.ts index 4b1ad52b1..b984fc2f8 100644 --- a/tests/utils/test-data-generator.ts +++ b/tests/utils/test-data-generator.ts @@ -42,7 +42,6 @@ export type Persona = { export type GenerateProtocolsConfigureMessageInput = { requester?: Persona; - target?: Persona; dateCreated?: string; protocol?: string; protocolDefinition?: ProtocolDefinition; @@ -50,14 +49,12 @@ export type GenerateProtocolsConfigureMessageInput = { export type GenerateProtocolsConfigureMessageOutput = { requester: Persona; - target: Persona; message: ProtocolsConfigureMessage; protocolsConfigure: ProtocolsConfigure; }; export type GenerateProtocolsQueryMessageInput = { requester?: Persona; - target?: Persona; dateCreated?: string; filter?: { protocol: string; @@ -66,14 +63,12 @@ export type GenerateProtocolsQueryMessageInput = { export type GenerateProtocolsQueryMessageOutput = { requester: Persona; - target: Persona; message: ProtocolsQueryMessage; protocolsQuery: ProtocolsQuery; }; export type GenerateRecordsWriteMessageInput = { requester?: Persona; - target?: Persona; recipientDid?: string; protocol?: string; contextId?: string; @@ -99,14 +94,12 @@ export type generateFromRecordsWriteInput = { export type GenerateRecordsWriteMessageOutput = { requester: Persona; - target: Persona; message: RecordsWriteMessage; recordsWrite: RecordsWrite; }; export type GenerateRecordsQueryMessageInput = { requester?: Persona; - target?: Persona; dateCreated?: string; filter?: { recipient?: string; @@ -122,13 +115,11 @@ export type GenerateRecordsQueryMessageInput = { export type GenerateRecordsQueryMessageOutput = { requester: Persona; - target: Persona; message: RecordsQueryMessage; }; export type GenerateHooksWriteMessageInput = { requester?: Persona; - target?: Persona; dateCreated?: string; filter?: { method: string; @@ -138,7 +129,6 @@ export type GenerateHooksWriteMessageInput = { export type GenerateHooksWriteMessageOutput = { requester: Persona; - target: Persona; message: HooksWriteMessage; }; @@ -198,7 +188,7 @@ export class TestDataGenerator { input?: GenerateProtocolsConfigureMessageInput ): Promise { - const { requester, target } = await TestDataGenerator.generateRequesterAndTargetPersonas(input); + const requester = input?.requester ?? await TestDataGenerator.generatePersona(); // generate protocol definition if not given let definition = input?.protocolDefinition; @@ -216,7 +206,6 @@ export class TestDataGenerator { const signatureInput = TestDataGenerator.createSignatureInputFromPersona(requester); const options: ProtocolsConfigureOptions = { - target : target.did, dateCreated : input?.dateCreated, protocol : input?.protocol ?? TestDataGenerator.randomString(20), definition, @@ -227,7 +216,6 @@ export class TestDataGenerator { return { requester, - target, message: protocolsConfigure.message, protocolsConfigure }; @@ -238,12 +226,11 @@ export class TestDataGenerator { */ public static async generateProtocolsQueryMessage(input?: GenerateProtocolsQueryMessageInput): Promise { // generate requester persona if not given - const { requester, target } = await TestDataGenerator.generateRequesterAndTargetPersonas(input); + const requester = input?.requester ?? await TestDataGenerator.generatePersona(); const signatureInput = TestDataGenerator.createSignatureInputFromPersona(requester); const options: ProtocolsQueryOptions = { - target : target.did, dateCreated : input?.dateCreated, filter : input?.filter, signatureInput @@ -254,7 +241,6 @@ export class TestDataGenerator { return { requester, - target, message: protocolsQuery.message, protocolsQuery }; @@ -263,20 +249,17 @@ export class TestDataGenerator { /** * Generates a RecordsWrite message for testing. * Optional parameters are generated if not given. - * If `requester` and `target` are both not given, use the same persona to pass authorization in tests by default. * Implementation currently uses `RecordsWrite.create()`. */ public static async generateRecordsWriteMessage(input?: GenerateRecordsWriteMessageInput): Promise { - - const { requester, target } = await TestDataGenerator.generateRequesterAndTargetPersonas(input); + const requester = input?.requester ?? await TestDataGenerator.generatePersona(); const signatureInput = TestDataGenerator.createSignatureInputFromPersona(requester); const data = input?.data ?? TestDataGenerator.randomBytes(32); const options: RecordsWriteOptions = { - target : target.did, - recipient : input?.recipientDid ?? target.did, // use target if recipient is not explicitly set + recipient : input?.recipientDid, protocol : input?.protocol, contextId : input?.contextId, schema : input?.schema ?? TestDataGenerator.randomString(20), @@ -296,7 +279,6 @@ export class TestDataGenerator { const message = recordsWrite.message as RecordsWriteMessage; return { - target, requester, message, recordsWrite @@ -316,7 +298,6 @@ export class TestDataGenerator { const datePublished = input.datePublished ?? (published ? currentTime : undefined); const options: CreateFromOptions = { - target : input.existingWrite.target, unsignedRecordsWriteMessage : input.existingWrite.message, data : input.data ?? TestDataGenerator.randomBytes(32), published, @@ -333,12 +314,11 @@ export class TestDataGenerator { * Generates a RecordsQuery message for testing. */ public static async generateRecordsQueryMessage(input?: GenerateRecordsQueryMessageInput): Promise { - const { requester, target } = await TestDataGenerator.generateRequesterAndTargetPersonas(input); + const requester = input?.requester ?? await TestDataGenerator.generatePersona(); const signatureInput = TestDataGenerator.createSignatureInputFromPersona(requester); const options: RecordsQueryOptions = { - target : target.did, dateCreated : input?.dateCreated, signatureInput, filter : input?.filter ?? { schema: TestDataGenerator.randomString(10) }, // must have one filter property if no filter is given @@ -350,7 +330,6 @@ export class TestDataGenerator { const message = recordsQuery.message as RecordsQueryMessage; return { - target, requester, message }; @@ -360,13 +339,11 @@ export class TestDataGenerator { * Generates a HooksWrite message for testing. */ public static async generateHooksWriteMessage(input?: GenerateHooksWriteMessageInput): Promise { - - const { requester, target } = await TestDataGenerator.generateRequesterAndTargetPersonas(input); + const requester = input?.requester ?? await TestDataGenerator.generatePersona(); const signatureInput = TestDataGenerator.createSignatureInputFromPersona(requester); const options: HooksWriteOptions = { - target : target.did, dateCreated : input?.dateCreated, signatureInput, filter : input?.filter ?? { method: 'RecordsWrite' }, // hardcode to filter on `RecordsWrite` if no filter is given @@ -376,7 +353,6 @@ export class TestDataGenerator { const hooksWrite = await HooksWrite.create(options); return { - target, requester, message: hooksWrite.message }; @@ -385,11 +361,9 @@ export class TestDataGenerator { /** * Generates a PermissionsRequest message for testing. */ - public static async generatePermissionsRequestMessage(): Promise<{ target, message: BaseMessage }> { + public static async generatePermissionsRequestMessage(): Promise<{ message: BaseMessage }> { const { privateJwk } = await ed25519.generateKeyPair(); - const target = 'did:jank:alice'; const permissionRequest = await PermissionsRequest.create({ - target, dateCreated : getCurrentTimeInHighPrecision(), description : 'drugs', grantedBy : 'did:jank:bob', @@ -398,7 +372,7 @@ export class TestDataGenerator { signatureInput : { privateJwk: privateJwk, protectedHeader: { alg: privateJwk.alg as string, kid: 'whatev' } } }); - return { target, message: permissionRequest.message }; + return { message: permissionRequest.message }; } /** @@ -452,36 +426,4 @@ export class TestDataGenerator { didDocumentMetadata: {} }; } - - /** - * Gets the method name from the given DID. - */ - private static getDidMethodName(did: string): string { - const segments = did.split(':', 3); - if (segments.length < 3) { - throw new Error(`${did} is not a valid DID`); - } - - return segments[1]; - } - - /** - * Generates requester and target personas if not given. - * If `requester` and `target` are both not given, use the same persona to pass authorization in tests by default. - */ - private static async generateRequesterAndTargetPersonas( - input?: { requester?: Persona, target?: Persona } - ): Promise<{ requester: Persona, target: Persona }> { - // generate requester & target persona if not given - let requester = input?.requester ?? await TestDataGenerator.generatePersona(); - const target = input?.target ?? await TestDataGenerator.generatePersona(); - - // if `requester` and `target` are both not given, use the same persona to pass authorization in tests by default - if (input?.requester === undefined && - input?.target === undefined) { - requester = target; - } - - return { requester, target }; - } } \ No newline at end of file