From a2d156b0343acbf28a0c11e69834e55e2b084148 Mon Sep 17 00:00:00 2001 From: Josh Floth Date: Wed, 1 Nov 2023 19:27:01 -0500 Subject: [PATCH 1/2] moving from Error to DwnError --- src/core/auth.ts | 6 ++--- src/core/dwn-error.ts | 34 +++++++++++++++++++++++++- src/core/protocol-authorization.ts | 6 ++--- src/did/did-ion-resolver.ts | 3 ++- src/interfaces/messages-get.ts | 5 ++-- src/interfaces/records-read.ts | 3 ++- src/interfaces/records-write.ts | 33 +++++++++++++------------ src/jose/algorithms/signing/ed25519.ts | 3 ++- src/jose/jws/general/verifier.ts | 2 +- src/schema-validator.ts | 5 ++-- src/utils/cid.ts | 9 ++++--- src/utils/jws.ts | 7 +++--- 12 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/core/auth.ts b/src/core/auth.ts index ae0c5b7d6..b0cb9c70d 100644 --- a/src/core/auth.ts +++ b/src/core/auth.ts @@ -23,7 +23,7 @@ export async function validateMessageSignatureIntegrity( ): Promise<{ descriptorCid: CID, [key: string]: any }> { if (messageSignature.signatures.length !== 1) { - throw new Error('expected no more than 1 signature for authorization purpose'); + throw new DwnError(DwnErrorCode.AuthenticateMoreThanOneAuthoriation,'expected no more than 1 signature for authorization purpose'); } // validate payload integrity @@ -35,7 +35,7 @@ export async function validateMessageSignatureIntegrity( const { descriptorCid } = payloadJson; const expectedDescriptorCid = await Cid.computeCid(messageDescriptor); if (descriptorCid !== expectedDescriptorCid) { - throw new Error(`provided descriptorCid ${descriptorCid} does not match expected CID ${expectedDescriptorCid}`); + throw new DwnError(DwnErrorCode.AuthenticateDescriptorCidMismatch, `provided descriptorCid ${descriptorCid} does not match expected CID ${expectedDescriptorCid}`); } return payloadJson; @@ -77,6 +77,6 @@ export async function authorize(tenant: string, incomingMessage: { author: strin if (incomingMessage.author === tenant) { return; } else { - throw new Error('message failed authorization, permission grant check not yet implemented'); + throw new DwnError(DwnErrorCode.AuthorizationUnknownAuthor, 'message failed authorization, permission grant check not yet implemented'); } } diff --git a/src/core/dwn-error.ts b/src/core/dwn-error.ts index 17eb5a866..72a6c6581 100644 --- a/src/core/dwn-error.ts +++ b/src/core/dwn-error.ts @@ -14,11 +14,17 @@ export class DwnError extends Error { */ export enum DwnErrorCode { AuthenticateJwsMissing = 'AuthenticateJwsMissing', + AuthenticateMoreThanOneAuthoriation = 'AuthenticateMoreThanOneAuthoriation', + AuthenticateDescriptorCidMismatch = 'AuthenticateDescriptorCidMismatch', AuthorizationUnknownAuthor = 'AuthorizationUnknownAuthor', + AuthorizationNotGrantedToAuthor = 'AuthorizationNotGrantedToAuthor', + ComputeCidCodecNotSupported = 'ComputeCidCodecNotSupported', + ComputeCidMultihashNotSupported = 'ComputeCidMultihashNotSupported', DidMethodNotSupported = 'DidMethodNotSupported', DidNotString = 'DidNotString', DidNotValid = 'DidNotValid', DidResolutionFailed = 'DidResolutionFailed', + Ed25519InvalidJwk = 'Ed25519InvalidJwk', GeneralJwsVerifierInvalidSignature = 'GeneralJwsVerifierInvalidSignature', GrantAuthorizationGrantExpired = 'GrantAuthorizationGrantExpired', GrantAuthorizationGrantMissing = 'GrantAuthorizationGrantMissing', @@ -29,6 +35,11 @@ export enum DwnErrorCode { GrantAuthorizationNotGrantedToAuthor = 'GrantAuthorizationNotGrantedToAuthor', GrantAuthorizationGrantNotYetActive = 'GrantAuthorizationGrantNotYetActive', HdKeyDerivationPathInvalid = 'HdKeyDerivationPathInvalid', + JwsVerifySignatureUnsupportedCrv = 'JwsVerifySignatureUnsupportedCrv', + JwsDecodePlainObjectPayloadInvalid = 'JwsDecodePlainObjectPayloadInvalid', + MessageGetInvalidCid = 'MessageGetInvalidCid', + ParseCidCodecNotSupported = 'ParseCidCodecNotSupported', + ParseCidMultihashNotSupported = 'ParseCidMultihashNotSupported', PermissionsGrantGrantedByMismatch = 'PermissionsGrantGrantedByMismatch', PermissionsGrantNotADelegatedGrant = 'PermissionsGrantNotADelegatedGrant', PermissionsGrantScopeContextIdAndProtocolPath = 'PermissionsGrantScopeContextIdAndProtocolPath', @@ -51,6 +62,8 @@ export enum DwnErrorCode { ProtocolAuthorizationMissingRuleSet = 'ProtocolAuthorizationMissingRuleSet', ProtocolAuthorizationParentlessIncorrectProtocolPath = 'ProtocolAuthorizationParentlessIncorrectProtocolPath', ProtocolAuthorizationNotARole = 'ProtocolAuthorizationNotARole', + ProtocolAuthorizationParentNotFound = 'ProtocolAuthorizationParentNotFound', + ProtocolAuthorizationProtocolNotFound = 'ProtocolAuthorizationProtocolNotFound', ProtocolAuthorizationQueryWithoutRole = 'ProtocolAuthorizationQueryWithoutRole', ProtocolAuthorizationRoleMissingRecipient = 'ProtocolAuthorizationRoleMissingRecipient', ProtocolsConfigureContextRoleAtProhibitedProtocolPath = 'ProtocolsConfigureContextRoleAtProhibitedProtocolPath', @@ -77,11 +90,22 @@ export enum DwnErrorCode { RecordsProtocolPathDerivationSchemeMissingProtocol = 'RecordsProtocolPathDerivationSchemeMissingProtocol', RecordsQueryFilterMissingRequiredProperties = 'RecordsQueryFilterMissingRequiredProperties', RecordsReadReturnedMultiple = 'RecordsReadReturnedMultiple', + RecordsReadAuthorizationFailed = 'RecordsReadAuthorizationFailed', RecordsSchemasDerivationSchemeMissingSchema = 'RecordsSchemasDerivationSchemeMissingSchema', + RecordsWriteAttestationIntegrityMoreThanOneSignature = 'RecordsWriteAttestationIntegrityMoreThanOneSignature', + RecordsWriteAttestationIntegrityDescriptorCidMismatch = 'RecordsWriteAttestationIntegrityDescriptorCidMismatch', + RecordsWriteAttestationIntegrityInvalidPayloadProperty = 'RecordsWriteAttestationIntegrityInvalidPayloadProperty', + RecordsWriteAuthorizationFailed = 'RecordsWriteAuthorizationFailed', RecordsWriteCreateMissingSigner = 'RecordsWriteCreateMissingSigner', + RecordsWriteCreateContextIdAndParentIdMutuallyInclusive = 'RecordsWriteCreateContextIdAndParentIdMutuallyInclusive', + RecordsWriteCreateDataAndDataCidMutuallyExclusive = 'RecordsWriteCreateDataAndDataCidMutuallyExclusive', + RecordsWriteCreateDataCidAndDataSizeMutuallyInclusive = 'RecordsWriteCreateDataCidAndDataSizeMutuallyInclusive', + RecordsWriteCreateProtocolAndProtocolPathMutuallyInclusive = 'RecordsWriteCreateProtocolAndProtocolPathMutuallyInclusive', RecordsWriteDataCidMismatch = 'RecordsWriteDataCidMismatch', RecordsWriteDataSizeMismatch = 'RecordsWriteDataSizeMismatch', RecordsWriteGetEntryIdUndefinedAuthor = 'RecordsWriteGetEntryIdUndefinedAuthor', + RecordsWriteGetInitialWriteNotFound = 'RecordsWriteGetInitialWriteNotFound', + RecordsWriteImmutablePropertyChanged = 'RecordsWriteImmutablePropertyChanged', RecordsWriteMissingAuthorizationSigner = 'RecordsWriteMissingAuthorizationSigner', RecordsWriteMissingDataInPrevious = 'RecordsWriteMissingDataInPrevious', RecordsWriteMissingDataAssociation = 'RecordsWriteMissingDataAssociation', @@ -90,13 +114,21 @@ export enum DwnErrorCode { RecordsWriteMissingSchema = 'RecordsWriteMissingSchema', RecordsWriteOwnerAndTenantMismatch = 'RecordsWriteOwnerAndTenantMismatch', RecordsWriteSignAsOwnerUnknownAuthor = 'RecordsWriteSignAsOwnerUnknownAuthor', + RecordsWriteValidateIntegrityAttestationMismatch = 'RecordsWriteValidateIntegrityAttestationMismatch', + RecordsWriteValidateIntegrityContextIdMismatch = 'RecordsWriteValidateIntegrityContextIdMismatch', + RecordsWriteValidateIntegrityContextIdNotInSignerSignaturePayload = 'RecordsWriteValidateIntegrityContextIdNotInSignerSignaturePayload', + RecordsWriteValidateIntegrityDateCreatedMismatch = 'RecordsWriteValidateIntegrityDateCreatedMismatch', RecordsWriteValidateIntegrityDelegatedGrantAndIdExistenceMismatch = 'RecordsWriteValidateIntegrityDelegatedGrantAndIdExistenceMismatch', RecordsWriteValidateIntegrityEncryptionCidMismatch = 'RecordsWriteValidateIntegrityEncryptionCidMismatch', RecordsWriteValidateIntegrityGrantedToAndSignerMismatch = 'RecordsWriteValidateIntegrityGrantedToAndSignerMismatch', + RecordsWriteValidateIntegrityRecordIdUnauthorized = 'RecordsWriteValidateIntegrityRecordIdUnauthorized', + SchemaValidatorSchemaNotFound = 'SchemaValidatorSchemaNotFound', + SchemaValidationFailure = 'SchemaValidationFailure', Secp256k1KeyNotValid = 'Secp256k1KeyNotValid', TimestampInvalid = 'TimestampInvalid', UrlProtocolNotNormalized = 'UrlProtocolNotNormalized', UrlProtocolNotNormalizable = 'UrlProtocolNotNormalizable', UrlSchemaNotNormalized = 'UrlSchemaNotNormalized', - UrlSchemaNotNormalizable = 'UrlSchemaNotNormalizable' + UrlSchemaNotNormalizable = 'UrlSchemaNotNormalizable', + VerifierValidPublicKeyNotFound = 'VerifierValidPublicKeyNotFound', }; diff --git a/src/core/protocol-authorization.ts b/src/core/protocol-authorization.ts index fdb66fb6a..1c86dc5eb 100644 --- a/src/core/protocol-authorization.ts +++ b/src/core/protocol-authorization.ts @@ -259,7 +259,7 @@ export class ProtocolAuthorization { const { messages: protocols } = await messageStore.query(tenant, [ query ]); if (protocols.length === 0) { - throw new Error(`unable to find protocol definition for ${protocolUri}`); + throw new DwnError(DwnErrorCode.ProtocolAuthorizationProtocolNotFound, `unable to find protocol definition for ${protocolUri}`); } const protocolMessage = protocols[0] as ProtocolsConfigureMessage; @@ -303,7 +303,7 @@ export class ProtocolAuthorization { // We already check the immediate parent in `verifyProtocolPath`, so if it triggers, // it means a bug that caused an invalid message to be saved to the DWN. if (parentMessages.length === 0) { - throw new Error(`no parent found with ID ${currentParentId}`); + throw new DwnError(DwnErrorCode.ProtocolAuthorizationParentNotFound, `no parent found with ID ${currentParentId}`); } const parent = parentMessages[0] as RecordsWriteMessage; @@ -526,7 +526,7 @@ export class ProtocolAuthorization { // We have already checked that the message is not from tenant, owner, or permissionsGrant if (actionRules === undefined) { - throw new Error(`no action rule defined for ${incomingMessageMethod}, ${author} is unauthorized`); + throw new DwnError(DwnErrorCode.ProtocolAuthorizationActionNotAllowed, `no action rule defined for ${incomingMessageMethod}, ${author} is unauthorized`); } const invokedRole = incomingMessage.signerSignaturePayload?.protocolRole; diff --git a/src/did/did-ion-resolver.ts b/src/did/did-ion-resolver.ts index 7efb46a58..dd2380bc2 100644 --- a/src/did/did-ion-resolver.ts +++ b/src/did/did-ion-resolver.ts @@ -1,3 +1,4 @@ +import { DwnError, DwnErrorCode } from '../index.js'; import type { DidMethodResolver, DidResolutionResult } from './did-resolver.js'; import crossFetch from 'cross-fetch'; @@ -29,7 +30,7 @@ export class DidIonResolver implements DidMethodResolver { const response = await fetch(resolutionUrl); if (response.status !== 200) { - throw new Error(`unable to resolve ${did}, got http status ${response.status}`); + throw new DwnError(DwnErrorCode.DidResolutionFailed, `unable to resolve ${did}, got http status ${response.status}`); } const didResolutionResult = await response.json(); diff --git a/src/interfaces/messages-get.ts b/src/interfaces/messages-get.ts index 8e461d6fd..5608cea20 100644 --- a/src/interfaces/messages-get.ts +++ b/src/interfaces/messages-get.ts @@ -5,6 +5,7 @@ import { Cid } from '../utils/cid.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; +import { DwnError, DwnErrorCode } from '../index.js'; export type MessagesGetOptions = { messageCids: string[]; @@ -43,14 +44,14 @@ export class MessagesGet extends Message { /** * validates the provided cids * @param messageCids - the cids in question - * @throws {Error} if an invalid cid is found. + * @throws {DwnError} if an invalid cid is found. */ private static validateMessageCids(messageCids: string[]): void { for (const cid of messageCids) { try { Cid.parseCid(cid); } catch (_) { - throw new Error(`${cid} is not a valid CID`); + throw new DwnError(DwnErrorCode.MessageGetInvalidCid, `${cid} is not a valid CID`); } } } diff --git a/src/interfaces/records-read.ts b/src/interfaces/records-read.ts index 23d708596..182f12667 100644 --- a/src/interfaces/records-read.ts +++ b/src/interfaces/records-read.ts @@ -11,6 +11,7 @@ import { removeUndefinedProperties } from '../utils/object.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; +import { DwnError, DwnErrorCode } from '../index.js'; export type RecordsReadOptions = { filter: RecordsFilter; @@ -85,7 +86,7 @@ export class RecordsRead extends Message { } else if (descriptor.protocol !== undefined) { await ProtocolAuthorization.authorizeRead(tenant, this, newestRecordsWrite, messageStore); } else { - throw new Error('message failed authorization'); + throw new DwnError(DwnErrorCode.RecordsReadAuthorizationFailed, 'message failed authorization'); } } } diff --git a/src/interfaces/records-write.ts b/src/interfaces/records-write.ts index cefc6ef18..86a878eee 100644 --- a/src/interfaces/records-write.ts +++ b/src/interfaces/records-write.ts @@ -249,21 +249,21 @@ export class RecordsWrite { public static async create(options: RecordsWriteOptions): Promise { if ((options.protocol === undefined && options.protocolPath !== undefined) || (options.protocol !== undefined && options.protocolPath === undefined)) { - throw new Error('`protocol` and `protocolPath` must both be defined or undefined at the same time'); + throw new DwnError(DwnErrorCode.RecordsWriteCreateProtocolAndProtocolPathMutuallyInclusive, '`protocol` and `protocolPath` must both be defined or undefined at the same time'); } if ((options.data === undefined && options.dataCid === undefined) || (options.data !== undefined && options.dataCid !== undefined)) { - throw new Error('one and only one parameter between `data` and `dataCid` is allowed'); + throw new DwnError(DwnErrorCode.RecordsWriteCreateDataAndDataCidMutuallyExclusive, 'one and only one parameter between `data` and `dataCid` is allowed'); } if ((options.dataCid === undefined && options.dataSize !== undefined) || (options.dataCid !== undefined && options.dataSize === undefined)) { - throw new Error('`dataCid` and `dataSize` must both be defined or undefined at the same time'); + throw new DwnError(DwnErrorCode.RecordsWriteCreateDataCidAndDataSizeMutuallyInclusive, '`dataCid` and `dataSize` must both be defined or undefined at the same time'); } if (options.parentId !== undefined && options.contextId === undefined) { - throw new Error('`contextId` must also be given when `parentId` is specified'); + throw new DwnError(DwnErrorCode.RecordsWriteCreateContextIdAndParentIdMutuallyInclusive, '`contextId` must also be given when `parentId` is specified'); } if (options.signer === undefined && options.delegatedGrant !== undefined) { @@ -528,7 +528,7 @@ export class RecordsWrite { } else if (this.message.descriptor.protocol !== undefined) { await ProtocolAuthorization.authorizeWrite(tenant, this, messageStore); } else { - throw new Error('message failed authorization'); + throw new DwnError(DwnErrorCode.RecordsWriteAuthorizationFailed, 'message failed authorization'); } } @@ -544,7 +544,7 @@ export class RecordsWrite { const dateRecordCreated = this.message.descriptor.dateCreated; const messageTimestamp = this.message.descriptor.messageTimestamp; if (messageTimestamp !== dateRecordCreated) { - throw new Error(`messageTimestamp ${messageTimestamp} must match dateCreated ${dateRecordCreated} for the initial write`); + throw new DwnError(DwnErrorCode.RecordsWriteValidateIntegrityDateCreatedMismatch, `messageTimestamp ${messageTimestamp} must match dateCreated ${dateRecordCreated} for the initial write`); } // if the message is also a protocol context root, the `contextId` must match the expected deterministic value @@ -553,7 +553,7 @@ export class RecordsWrite { const expectedContextId = await this.getEntryId(); if (this.message.contextId !== expectedContextId) { - throw new Error(`contextId in message: ${this.message.contextId} does not match deterministic contextId: ${expectedContextId}`); + throw new DwnError(DwnErrorCode.RecordsWriteValidateIntegrityContextIdMismatch, `contextId in message: ${this.message.contextId} does not match deterministic contextId: ${expectedContextId}`); } } } @@ -563,14 +563,16 @@ export class RecordsWrite { // make sure the `recordId` in message is the same as the `recordId` in signer signature payload if (this.message.recordId !== signerSignaturePayload.recordId) { - throw new Error( + throw new DwnError( + DwnErrorCode.RecordsWriteValidateIntegrityRecordIdUnauthorized, `recordId in message ${this.message.recordId} does not match recordId in authorization: ${signerSignaturePayload.recordId}` ); } // if `contextId` is given in message, make sure the same `contextId` is in the signer signature payload if (this.message.contextId !== signerSignaturePayload.contextId) { - throw new Error( + throw new DwnError( + DwnErrorCode.RecordsWriteValidateIntegrityContextIdNotInSignerSignaturePayload, `contextId in message ${this.message.contextId} does not match contextId in authorization: ${signerSignaturePayload.contextId}` ); } @@ -603,7 +605,8 @@ export class RecordsWrite { const expectedAttestationCid = await Cid.computeCid(this.message.attestation); const actualAttestationCid = signerSignaturePayload.attestationCid; if (actualAttestationCid !== expectedAttestationCid) { - throw new Error( + throw new DwnError( + DwnErrorCode.RecordsWriteValidateIntegrityAttestationMismatch, `CID ${expectedAttestationCid} of attestation property in message does not match attestationCid in authorization: ${actualAttestationCid}` ); } @@ -646,7 +649,7 @@ export class RecordsWrite { // TODO: multi-attesters to be unblocked by #205 - Revisit database interfaces (https://github.com/TBD54566975/dwn-sdk-js/issues/205) if (message.attestation.signatures.length !== 1) { - throw new Error(`Currently implementation only supports 1 attester, but got ${message.attestation.signatures.length}`); + throw new DwnError(DwnErrorCode.RecordsWriteAttestationIntegrityMoreThanOneSignature, `Currently implementation only supports 1 attester, but got ${message.attestation.signatures.length}`); } const payloadJson = Jws.decodePlainObjectPayload(message.attestation); @@ -655,13 +658,13 @@ export class RecordsWrite { // `descriptorCid` validation - ensure that the provided descriptorCid matches the CID of the actual message const expectedDescriptorCid = await Cid.computeCid(message.descriptor); if (descriptorCid !== expectedDescriptorCid) { - throw new Error(`descriptorCid ${descriptorCid} does not match expected descriptorCid ${expectedDescriptorCid}`); + throw new DwnError(DwnErrorCode.RecordsWriteAttestationIntegrityDescriptorCidMismatch, `descriptorCid ${descriptorCid} does not match expected descriptorCid ${expectedDescriptorCid}`); } // check to ensure that no other unexpected properties exist in payload. const propertyCount = Object.keys(payloadJson).length; if (propertyCount > 1) { - throw new Error(`Only 'descriptorCid' is allowed in attestation payload, but got ${propertyCount} properties.`); + throw new DwnError(DwnErrorCode.RecordsWriteAttestationIntegrityInvalidPayloadProperty, `Only 'descriptorCid' is allowed in attestation payload, but got ${propertyCount} properties.`); } }; @@ -869,7 +872,7 @@ export class RecordsWrite { } } - throw new Error(`initial write is not found`); + throw new DwnError(DwnErrorCode.RecordsWriteGetInitialWriteNotFound, `initial write is not found`); } /** @@ -892,7 +895,7 @@ export class RecordsWrite { const valueInExistingWrite = (existingWriteMessage.descriptor as any)[descriptorPropertyName]; const valueInNewMessage = (newMessage.descriptor as any)[descriptorPropertyName]; if (valueInNewMessage !== valueInExistingWrite) { - throw new Error(`${descriptorPropertyName} is an immutable property: cannot change '${valueInExistingWrite}' to '${valueInNewMessage}'`); + throw new DwnError(DwnErrorCode.RecordsWriteImmutablePropertyChanged, `${descriptorPropertyName} is an immutable property: cannot change '${valueInExistingWrite}' to '${valueInNewMessage}'`); } } } diff --git a/src/jose/algorithms/signing/ed25519.ts b/src/jose/algorithms/signing/ed25519.ts index 6211700a5..cef069f3b 100644 --- a/src/jose/algorithms/signing/ed25519.ts +++ b/src/jose/algorithms/signing/ed25519.ts @@ -2,10 +2,11 @@ import * as Ed25519 from '@noble/ed25519'; import type { PrivateJwk, PublicJwk, SignatureAlgorithm } from '../../../types/jose-types.js'; import { Encoder } from '../../../utils/encoder.js'; +import { DwnError, DwnErrorCode } from '../../../index.js'; function validateKey(jwk: PrivateJwk | PublicJwk): void { if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') { - throw new Error('invalid jwk. kty MUST be OKP. crv MUST be Ed25519'); + throw new DwnError(DwnErrorCode.Ed25519InvalidJwk, 'invalid jwk. kty MUST be OKP. crv MUST be Ed25519'); } } diff --git a/src/jose/jws/general/verifier.ts b/src/jose/jws/general/verifier.ts index 498959830..b6acc371c 100644 --- a/src/jose/jws/general/verifier.ts +++ b/src/jose/jws/general/verifier.ts @@ -76,7 +76,7 @@ export class GeneralJwsVerifier { } if (!verificationMethod) { - throw new Error('public key needed to verify signature not found in DID Document'); + throw new DwnError(DwnErrorCode.VerifierValidPublicKeyNotFound, 'public key needed to verify signature not found in DID Document'); } validateJsonSchema('JwkVerificationMethod', verificationMethod); diff --git a/src/schema-validator.ts b/src/schema-validator.ts index 0e420b470..ac540d29b 100644 --- a/src/schema-validator.ts +++ b/src/schema-validator.ts @@ -1,4 +1,5 @@ import * as precompiledValidators from '../generated/precompiled-validators.js'; +import { DwnError, DwnErrorCode } from './index.js'; /** * Validates the given payload using JSON schema keyed by the given schema name. Throws if the given payload fails validation. @@ -11,7 +12,7 @@ export function validateJsonSchema(schemaName: string, payload: any): void { const validateFn = (precompiledValidators as any)[schemaName]; if (!validateFn) { - throw new Error(`schema for ${schemaName} not found.`); + throw new DwnError(DwnErrorCode.SchemaValidatorSchemaNotFound, `schema for ${schemaName} not found.`); } validateFn(payload); @@ -29,5 +30,5 @@ export function validateJsonSchema(schemaName: string, payload: any): void { instancePath = schemaName; } - throw new Error(`${instancePath}: ${message}`); + throw new DwnError(DwnErrorCode.SchemaValidationFailure, `${instancePath}: ${message}`); } \ No newline at end of file diff --git a/src/utils/cid.ts b/src/utils/cid.ts index 34a6a1805..12c9f7e23 100644 --- a/src/utils/cid.ts +++ b/src/utils/cid.ts @@ -6,6 +6,7 @@ import { CID } from 'multiformats/cid'; import { importer } from 'ipfs-unixfs-importer'; import { MemoryBlockstore } from 'blockstore-core'; import { sha256 } from 'multiformats/hashes/sha2'; +import { DwnError, DwnErrorCode } from '../index.js'; // a map of all supported CID hashing algorithms. This map is used to select the appropriate hasher // when generating a CID to compare against a provided CID @@ -40,12 +41,12 @@ export class Cid { ): Promise { const codec = codecs[codecCode]; if (!codec) { - throw new Error(`codec [${codecCode}] not supported`); + throw new DwnError(DwnErrorCode.ComputeCidCodecNotSupported, `codec [${codecCode}] not supported`); } const hasher = hashers[multihashCode]; if (!hasher) { - throw new Error(`multihash code [${multihashCode}] not supported`); + throw new DwnError(DwnErrorCode.ComputeCidMultihashNotSupported, `multihash code [${multihashCode}] not supported`); } const payloadBytes = codec.encode(payload); @@ -62,11 +63,11 @@ export class Cid { const cid: CID = CID.parse(str).toV1(); if (!codecs[cid.code]) { - throw new Error(`codec [${cid.code}] not supported`); + throw new DwnError(DwnErrorCode.ParseCidCodecNotSupported, `codec [${cid.code}] not supported`); } if (!hashers[cid.multihash.code]) { - throw new Error(`multihash code [${cid.multihash.code}] not supported`); + throw new DwnError(DwnErrorCode.ParseCidMultihashNotSupported, `multihash code [${cid.multihash.code}] not supported`); } return cid; diff --git a/src/utils/jws.ts b/src/utils/jws.ts index 61416a38d..fc44ed154 100644 --- a/src/utils/jws.ts +++ b/src/utils/jws.ts @@ -8,6 +8,7 @@ import isPlainObject from 'lodash/isPlainObject.js'; import { Encoder } from './encoder.js'; import { PrivateKeySigner } from './private-key-signer.js'; import { signatureAlgorithms } from '../jose/algorithms/signing/signature-algorithms.js'; +import { DwnError, DwnErrorCode } from '../index.js'; /** @@ -39,7 +40,7 @@ export class Jws { const signatureAlgorithm = signatureAlgorithms[jwkPublic.crv]; if (!signatureAlgorithm) { - throw new Error(`unsupported crv. crv must be one of ${Object.keys(signatureAlgorithms)}`); + throw new DwnError(DwnErrorCode.JwsVerifySignatureUnsupportedCrv, `unsupported crv. crv must be one of ${Object.keys(signatureAlgorithms)}`); } const payload = Encoder.stringToBytes(`${signatureEntry.protected}.${base64UrlPayload}`); @@ -56,11 +57,11 @@ export class Jws { try { payloadJson = Encoder.base64UrlToObject(jws.payload); } catch { - throw new Error('payload is not a JSON object'); + throw new DwnError(DwnErrorCode.JwsDecodePlainObjectPayloadInvalid, 'payload is not a JSON object'); } if (!isPlainObject(payloadJson)) { - throw new Error('signed payload must be a plain object'); + throw new DwnError(DwnErrorCode.JwsDecodePlainObjectPayloadInvalid, 'signed payload must be a plain object'); } return payloadJson; From f237d1cc492fea98d71990bbe4d4dbacdca2d6b2 Mon Sep 17 00:00:00 2001 From: Josh Floth Date: Wed, 1 Nov 2023 19:51:01 -0500 Subject: [PATCH 2/2] linting --- src/core/auth.ts | 7 ++-- src/core/protocol-authorization.ts | 13 +++++--- src/did/did-ion-resolver.ts | 2 +- src/interfaces/messages-get.ts | 2 +- src/interfaces/records-read.ts | 2 +- src/interfaces/records-write.ts | 52 ++++++++++++++++++++---------- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/core/auth.ts b/src/core/auth.ts index bcc9a9f5d..1fa678fd8 100644 --- a/src/core/auth.ts +++ b/src/core/auth.ts @@ -23,7 +23,7 @@ export async function validateMessageSignatureIntegrity( ): Promise<{ descriptorCid: CID, [key: string]: any }> { if (messageSignature.signatures.length !== 1) { - throw new DwnError(DwnErrorCode.AuthenticateMoreThanOneAuthoriation,'expected no more than 1 signature for authorization purpose'); + throw new DwnError(DwnErrorCode.AuthenticateMoreThanOneAuthoriation, 'expected no more than 1 signature for authorization purpose'); } // validate payload integrity @@ -35,7 +35,10 @@ export async function validateMessageSignatureIntegrity( const { descriptorCid } = payloadJson; const expectedDescriptorCid = await Cid.computeCid(messageDescriptor); if (descriptorCid !== expectedDescriptorCid) { - throw new DwnError(DwnErrorCode.AuthenticateDescriptorCidMismatch, `provided descriptorCid ${descriptorCid} does not match expected CID ${expectedDescriptorCid}`); + throw new DwnError( + DwnErrorCode.AuthenticateDescriptorCidMismatch, + `provided descriptorCid ${descriptorCid} does not match expected CID ${expectedDescriptorCid}` + ); } return payloadJson; diff --git a/src/core/protocol-authorization.ts b/src/core/protocol-authorization.ts index 38685987b..4564cd373 100644 --- a/src/core/protocol-authorization.ts +++ b/src/core/protocol-authorization.ts @@ -256,7 +256,7 @@ export class ProtocolAuthorization { method : DwnMethodName.Configure, protocol : protocolUri }; - const { messages: protocols } = await messageStore.query(tenant, [ query ]); + const { messages: protocols } = await messageStore.query(tenant, [query]); if (protocols.length === 0) { throw new DwnError(DwnErrorCode.ProtocolAuthorizationProtocolNotFound, `unable to find protocol definition for ${protocolUri}`); @@ -298,7 +298,7 @@ export class ProtocolAuthorization { contextId, recordId : currentParentId }; - const { messages: parentMessages } = await messageStore.query(tenant, [ query ]); + const { messages: parentMessages } = await messageStore.query(tenant, [query]); // We already check the immediate parent in `verifyProtocolPath`, so if it triggers, // it means a bug that caused an invalid message to be saved to the DWN. @@ -360,7 +360,7 @@ export class ProtocolAuthorization { contextId, recordId : parentId }; - const { messages: parentMessages } = await messageStore.query(tenant, [ query ]); + const { messages: parentMessages } = await messageStore.query(tenant, [query]); const parentProtocolPath = (parentMessages as RecordsWriteMessage[])[0]?.descriptor?.protocolPath; const actualProtocolPath = `${parentProtocolPath}/${declaredTypeName}`; if (parentProtocolPath === undefined || actualProtocolPath !== declaredProtocolPath) { @@ -503,7 +503,7 @@ export class ProtocolAuthorization { return [ProtocolAction.Update]; } - // default: + // default: // not reachable in typescript } } @@ -526,7 +526,10 @@ export class ProtocolAuthorization { // We have already checked that the message is not from tenant, owner, or permissionsGrant if (actionRules === undefined) { - throw new DwnError(DwnErrorCode.ProtocolAuthorizationActionNotAllowed, `no action rule defined for ${incomingMessageMethod}, ${author} is unauthorized`); + throw new DwnError( + DwnErrorCode.ProtocolAuthorizationActionNotAllowed, + `no action rule defined for ${incomingMessageMethod}, ${author} is unauthorized` + ); } const invokedRole = incomingMessage.signaturePayload?.protocolRole; diff --git a/src/did/did-ion-resolver.ts b/src/did/did-ion-resolver.ts index dd2380bc2..a3fb517dc 100644 --- a/src/did/did-ion-resolver.ts +++ b/src/did/did-ion-resolver.ts @@ -1,5 +1,5 @@ -import { DwnError, DwnErrorCode } from '../index.js'; import type { DidMethodResolver, DidResolutionResult } from './did-resolver.js'; +import { DwnError, DwnErrorCode } from '../index.js'; import crossFetch from 'cross-fetch'; // supports fetch in: node, browsers, and browser extensions. diff --git a/src/interfaces/messages-get.ts b/src/interfaces/messages-get.ts index 163647720..c95bcb4b6 100644 --- a/src/interfaces/messages-get.ts +++ b/src/interfaces/messages-get.ts @@ -3,9 +3,9 @@ import type { MessagesGetDescriptor, MessagesGetMessage } from '../types/message import { Cid } from '../utils/cid.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; +import { DwnError, DwnErrorCode } from '../index.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; -import { DwnError, DwnErrorCode } from '../index.js'; export type MessagesGetOptions = { messageCids: string[]; diff --git a/src/interfaces/records-read.ts b/src/interfaces/records-read.ts index 645edd69a..90b3c1b4c 100644 --- a/src/interfaces/records-read.ts +++ b/src/interfaces/records-read.ts @@ -9,9 +9,9 @@ import { Records } from '../utils/records.js'; import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js'; import { removeUndefinedProperties } from '../utils/object.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; +import { DwnError, DwnErrorCode } from '../index.js'; import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; -import { DwnError, DwnErrorCode } from '../index.js'; export type RecordsReadOptions = { filter: RecordsFilter; diff --git a/src/interfaces/records-write.ts b/src/interfaces/records-write.ts index c747f092a..0a8f0465e 100644 --- a/src/interfaces/records-write.ts +++ b/src/interfaces/records-write.ts @@ -248,17 +248,17 @@ export class RecordsWrite { */ public static async create(options: RecordsWriteOptions): Promise { if ((options.protocol === undefined && options.protocolPath !== undefined) || - (options.protocol !== undefined && options.protocolPath === undefined)) { + (options.protocol !== undefined && options.protocolPath === undefined)) { throw new DwnError(DwnErrorCode.RecordsWriteCreateProtocolAndProtocolPathMutuallyInclusive, '`protocol` and `protocolPath` must both be defined or undefined at the same time'); } if ((options.data === undefined && options.dataCid === undefined) || - (options.data !== undefined && options.dataCid !== undefined)) { + (options.data !== undefined && options.dataCid !== undefined)) { throw new DwnError(DwnErrorCode.RecordsWriteCreateDataAndDataCidMutuallyExclusive, 'one and only one parameter between `data` and `dataCid` is allowed'); } if ((options.dataCid === undefined && options.dataSize !== undefined) || - (options.dataCid !== undefined && options.dataSize === undefined)) { + (options.dataCid !== undefined && options.dataSize === undefined)) { throw new DwnError(DwnErrorCode.RecordsWriteCreateDataCidAndDataSizeMutuallyInclusive, '`dataCid` and `dataSize` must both be defined or undefined at the same time'); } @@ -509,7 +509,7 @@ export class RecordsWrite { if (this.owner !== undefined && this.owner !== tenant) { throw new DwnError( DwnErrorCode.RecordsWriteOwnerAndTenantMismatch, - `Owner ${this.owner } must be the same as tenant ${tenant} when specified.` + `Owner ${this.owner} must be the same as tenant ${tenant} when specified.` ); } @@ -541,7 +541,10 @@ export class RecordsWrite { const dateRecordCreated = this.message.descriptor.dateCreated; const messageTimestamp = this.message.descriptor.messageTimestamp; if (messageTimestamp !== dateRecordCreated) { - throw new DwnError(DwnErrorCode.RecordsWriteValidateIntegrityDateCreatedMismatch, `messageTimestamp ${messageTimestamp} must match dateCreated ${dateRecordCreated} for the initial write`); + throw new DwnError( + DwnErrorCode.RecordsWriteValidateIntegrityDateCreatedMismatch, + `messageTimestamp ${messageTimestamp} must match dateCreated ${dateRecordCreated} for the initial write` + ); } // if the message is also a protocol context root, the `contextId` must match the expected deterministic value @@ -550,7 +553,10 @@ export class RecordsWrite { const expectedContextId = await this.getEntryId(); if (this.message.contextId !== expectedContextId) { - throw new DwnError(DwnErrorCode.RecordsWriteValidateIntegrityContextIdMismatch, `contextId in message: ${this.message.contextId} does not match deterministic contextId: ${expectedContextId}`); + throw new DwnError( + DwnErrorCode.RecordsWriteValidateIntegrityContextIdMismatch, + `contextId in message: ${this.message.contextId} does not match deterministic contextId: ${expectedContextId}` + ); } } } @@ -630,7 +636,7 @@ export class RecordsWrite { validateTimestamp(this.message.descriptor.messageTimestamp); validateTimestamp(this.message.descriptor.dateCreated); - if (this.message.descriptor.datePublished){ + if (this.message.descriptor.datePublished) { validateTimestamp(this.message.descriptor.datePublished); } } @@ -646,7 +652,10 @@ export class RecordsWrite { // TODO: multi-attesters to be unblocked by #205 - Revisit database interfaces (https://github.com/TBD54566975/dwn-sdk-js/issues/205) if (message.attestation.signatures.length !== 1) { - throw new DwnError(DwnErrorCode.RecordsWriteAttestationIntegrityMoreThanOneSignature, `Currently implementation only supports 1 attester, but got ${message.attestation.signatures.length}`); + throw new DwnError( + DwnErrorCode.RecordsWriteAttestationIntegrityMoreThanOneSignature, + `Currently implementation only supports 1 attester, but got ${message.attestation.signatures.length}` + ); } const payloadJson = Jws.decodePlainObjectPayload(message.attestation); @@ -655,13 +664,19 @@ export class RecordsWrite { // `descriptorCid` validation - ensure that the provided descriptorCid matches the CID of the actual message const expectedDescriptorCid = await Cid.computeCid(message.descriptor); if (descriptorCid !== expectedDescriptorCid) { - throw new DwnError(DwnErrorCode.RecordsWriteAttestationIntegrityDescriptorCidMismatch, `descriptorCid ${descriptorCid} does not match expected descriptorCid ${expectedDescriptorCid}`); + throw new DwnError( + DwnErrorCode.RecordsWriteAttestationIntegrityDescriptorCidMismatch, + `descriptorCid ${descriptorCid} does not match expected descriptorCid ${expectedDescriptorCid}` + ); } // check to ensure that no other unexpected properties exist in payload. const propertyCount = Object.keys(payloadJson).length; if (propertyCount > 1) { - throw new DwnError(DwnErrorCode.RecordsWriteAttestationIntegrityInvalidPayloadProperty, `Only 'descriptorCid' is allowed in attestation payload, but got ${propertyCount} properties.`); + throw new DwnError( + DwnErrorCode.RecordsWriteAttestationIntegrityInvalidPayloadProperty, + `Only 'descriptorCid' is allowed in attestation payload, but got ${propertyCount} properties.` + ); } }; @@ -705,7 +720,7 @@ export class RecordsWrite { const query = { entryId: this.message.recordId }; - const { messages: result } = await messageStore.query(tenant, [ query ]); + const { messages: result } = await messageStore.query(tenant, [query]); const initialRecordsWrite = await RecordsWrite.parse(result[0] as RecordsWriteMessage); return initialRecordsWrite.author === this.author; @@ -717,7 +732,7 @@ export class RecordsWrite { public static async isInitialWrite(message: GenericMessage): Promise { // can't be the initial write if the message is not a Records Write if (message.descriptor.interface !== DwnInterfaceName.Records || - message.descriptor.method !== DwnMethodName.Write) { + message.descriptor.method !== DwnMethodName.Write) { return false; } @@ -757,14 +772,14 @@ export class RecordsWrite { const keyEncryption: EncryptedKey[] = []; for (const keyEncryptionInput of encryptionInput.keyEncryptionInputs) { - if (keyEncryptionInput.derivationScheme ===  KeyDerivationScheme.ProtocolPath && descriptor.protocol === undefined) { + if (keyEncryptionInput.derivationScheme === KeyDerivationScheme.ProtocolPath && descriptor.protocol === undefined) { throw new DwnError( DwnErrorCode.RecordsWriteMissingProtocol, '`protocols` encryption scheme cannot be applied to record without the `protocol` property.' ); } - if (keyEncryptionInput.derivationScheme ===  KeyDerivationScheme.Schemas && descriptor.schema === undefined) { + if (keyEncryptionInput.derivationScheme === KeyDerivationScheme.Schemas && descriptor.schema === undefined) { throw new DwnError( DwnErrorCode.RecordsWriteMissingSchema, '`schemas` encryption scheme cannot be applied to record without the `schema` property.' @@ -793,7 +808,7 @@ export class RecordsWrite { // we need to attach the actual public key if derivation scheme is protocol-context, // so that the responder to this message is able to encrypt the message/symmetric key using the same protocol-context derived public key, // without needing the knowledge of the corresponding private key - if (keyEncryptionInput.derivationScheme ===  KeyDerivationScheme.ProtocolContext) { + if (keyEncryptionInput.derivationScheme === KeyDerivationScheme.ProtocolContext) { encryptedKeyData.derivedPublicKey = keyEncryptionInput.publicKey; } @@ -866,7 +881,7 @@ export class RecordsWrite { /** * Gets the initial write from the given list or record write. */ - public static async getInitialWrite(messages: GenericMessage[]): Promise{ + public static async getInitialWrite(messages: GenericMessage[]): Promise { for (const message of messages) { if (await RecordsWrite.isInitialWrite(message)) { return message as RecordsWriteMessage; @@ -896,7 +911,10 @@ export class RecordsWrite { const valueInExistingWrite = (existingWriteMessage.descriptor as any)[descriptorPropertyName]; const valueInNewMessage = (newMessage.descriptor as any)[descriptorPropertyName]; if (valueInNewMessage !== valueInExistingWrite) { - throw new DwnError(DwnErrorCode.RecordsWriteImmutablePropertyChanged, `${descriptorPropertyName} is an immutable property: cannot change '${valueInExistingWrite}' to '${valueInNewMessage}'`); + throw new DwnError( + DwnErrorCode.RecordsWriteImmutablePropertyChanged, + `${descriptorPropertyName} is an immutable property: cannot change '${valueInExistingWrite}' to '${valueInNewMessage}'` + ); } } }