Skip to content

Commit

Permalink
#599 - Removed remaining circular dependencies and integrated madge i…
Browse files Browse the repository at this point in the history
…n build (#601)

1. Fixed remaining more tricky circular dependencies
1. Added `madge` to detect future circular dependencies
  • Loading branch information
thehenrytsai committed Nov 7, 2023
1 parent c175fa2 commit e3af260
Show file tree
Hide file tree
Showing 58 changed files with 1,786 additions and 561 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/integrity-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ jobs:
node-version: ${{ matrix.node-version }}
# https://docs.npmjs.com/cli/v8/commands/npm-ci
- run: npm clean-install
# check the licences allow list to avoid incorrectly licensed dependencies creeping in:
# check the licenses allow list to avoid incorrectly licensed dependencies creeping in:
- run: npm run license-check
# builds all bundles
- run: npm run build
# run circular dependency check, job will fail if there is an error
- run: npm run madge
# runs linter. Job will fail if there are any warnings or errors
- run: npm run lint
# runs tests in node environment
Expand Down
1,360 changes: 1,279 additions & 81 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
"karma-mocha-reporter": "2.2.5",
"karma-webkit-launcher": "2.1.0",
"license-report": "6.3.0",
"madge": "^6.1.0",
"mkdirp": "1.0.4",
"mocha": "10.1.0",
"mockdate": "3.0.5",
Expand Down Expand Up @@ -153,10 +154,11 @@
"compile-validators": "node ./build/compile-validators.js",
"lint": "eslint . --ext .ts --max-warnings 0",
"lint:fix": "eslint . --ext .ts --fix",
"madge": "madge --circular ./src/index.ts",
"test:node": "npm run compile-validators && tsc && c8 mocha \"dist/esm/tests/**/*.spec.js\"",
"test:browser": "npm run compile-validators && cross-env karma start karma.conf.cjs",
"test:browser-debug": "npm run compile-validators && cross-env karma start karma.conf.debug.cjs",
"license-check": "license-report --only=prod > license-report.json && node ./build/license-check.cjs",
"publish:unstable": "./build/publish-unstable.sh"
}
}
}
40 changes: 1 addition & 39 deletions src/core/auth.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,10 @@
import type { AuthorizationModel } from '../types/message-types.js';
import type { DidResolver } from '../did/did-resolver.js';
import type { GeneralJws } from '../types/jws-types.js';
import type { AuthorizationModel, Descriptor, GenericSignaturePayload } from '../types/message-types.js';

import { Cid } from '../utils/cid.js';
import { GeneralJwsVerifier } from '../jose/jws/general/verifier.js';
import { Jws } from '../utils/jws.js';
import { PermissionsGrant } from '../interfaces/permissions-grant.js';
import { validateJsonSchema } from '../schema-validator.js';
import { DwnError, DwnErrorCode } from './dwn-error.js';

/**
* Validates the structural integrity of the message signature given.
* NOTE: signature is not verified.
* @param payloadJsonSchemaKey The key to look up the JSON schema referenced in `compile-validators.js` and perform payload schema validation on.
* @returns the parsed JSON payload object if validation succeeds.
*/
export async function validateMessageSignatureIntegrity(
messageSignature: GeneralJws,
messageDescriptor: Descriptor,
payloadJsonSchemaKey: string = 'GenericSignaturePayload',
): Promise<GenericSignaturePayload> {

if (messageSignature.signatures.length !== 1) {
throw new DwnError(DwnErrorCode.AuthenticationMoreThanOneSignatureNotSupported, 'expected no more than 1 signature for authorization purpose');
}

// validate payload integrity
const payloadJson = Jws.decodePlainObjectPayload(messageSignature);

validateJsonSchema(payloadJsonSchemaKey, payloadJson);

// `descriptorCid` validation - ensure that the provided descriptorCid matches the CID of the actual message
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}`
);
}

return payloadJson;
}

/**
* Verifies all the signature(s) within the authorization property.
*
Expand Down
3 changes: 2 additions & 1 deletion src/core/grant-authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import type { GenericMessage } from '../types/message-types.js';
import type { MessageStore } from '../types/message-store.js';
import type { PermissionsGrantMessage } from '../types/permissions-types.js';

import { Message } from './message.js';
import { DwnError, DwnErrorCode } from './dwn-error.js';
import { DwnInterfaceName, DwnMethodName, Message } from './message.js';
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';

export class GrantAuthorization {

Expand Down
59 changes: 37 additions & 22 deletions src/core/message.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DelegatedGrantMessage } from '../types/permissions-types.js';
import type { DelegatedGrantMessage } from '../types/delegated-grant-message.js';
import type { GeneralJws } from '../types/jws-types.js';
import type { Signer } from '../types/signer.js';
import type { AuthorizationModel, Descriptor, GenericMessage, GenericSignaturePayload } from '../types/message-types.js';
Expand All @@ -10,27 +10,7 @@ import { Jws } from '../utils/jws.js';
import { lexicographicalCompare } from '../utils/string.js';
import { removeUndefinedProperties } from '../utils/object.js';
import { validateJsonSchema } from '../schema-validator.js';

export enum DwnInterfaceName {
Events = 'Events',
Messages = 'Messages',
Permissions = 'Permissions',
Protocols = 'Protocols',
Records = 'Records'
}

export enum DwnMethodName {
Configure = 'Configure',
Create = 'Create',
Get = 'Get',
Grant = 'Grant',
Query = 'Query',
Read = 'Read',
Request = 'Request',
Revoke = 'Revoke',
Write = 'Write',
Delete = 'Delete'
}
import { DwnError, DwnErrorCode } from './dwn-error.js';

export abstract class Message<M extends GenericMessage> {
readonly message: M;
Expand Down Expand Up @@ -223,4 +203,39 @@ export abstract class Message<M extends GenericMessage> {
// compare the `dataCid` instead, the < and > operators compare strings in lexicographical order
return Message.compareCid(a, b);
}


/**
* Validates the structural integrity of the message signature given.
* NOTE: signature is not verified.
* @param payloadJsonSchemaKey The key to look up the JSON schema referenced in `compile-validators.js` and perform payload schema validation on.
* @returns the parsed JSON payload object if validation succeeds.
*/
public static async validateMessageSignatureIntegrity(
messageSignature: GeneralJws,
messageDescriptor: Descriptor,
payloadJsonSchemaKey: string = 'GenericSignaturePayload',
): Promise<GenericSignaturePayload> {

if (messageSignature.signatures.length !== 1) {
throw new DwnError(DwnErrorCode.AuthenticationMoreThanOneSignatureNotSupported, 'expected no more than 1 signature for authorization purpose');
}

// validate payload integrity
const payloadJson = Jws.decodePlainObjectPayload(messageSignature);

validateJsonSchema(payloadJsonSchemaKey, payloadJson);

// `descriptorCid` validation - ensure that the provided descriptorCid matches the CID of the actual message
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}`
);
}

return payloadJson;
}
}
14 changes: 8 additions & 6 deletions src/core/protocol-authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { ProtocolActionRule, ProtocolDefinition, ProtocolRuleSet, Protocols

import { RecordsWrite } from '../interfaces/records-write.js';
import { DwnError, DwnErrorCode } from './dwn-error.js';
import { DwnInterfaceName, DwnMethodName } from './message.js';
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
import { ProtocolAction, ProtocolActor } from '../types/protocols-types.js';

export class ProtocolAuthorization {
Expand Down Expand Up @@ -268,27 +268,29 @@ export class ProtocolAuthorization {

/**
* Constructs a chain of ancestor messages
* @param newestRecordsWrite The newest RecordsWrite associated with the recordId being written.
* This will be the incoming RecordsWrite itself if the incoming message is a RecordsWrite.
* @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(
tenant: string,
incomingMessage: RecordsDelete | RecordsRead | RecordsWrite,
recordsWrite: RecordsWrite,
newestRecordsWrite: RecordsWrite,
messageStore: MessageStore
)
: Promise<RecordsWriteMessage[]> {
const ancestorMessageChain: RecordsWriteMessage[] = [];

if (incomingMessage.message.descriptor.method !== DwnMethodName.Write) {
// Unless inboundMessage is a Write, recordsWrite is also an ancestor message
ancestorMessageChain.push(recordsWrite.message);
ancestorMessageChain.push(newestRecordsWrite.message);
}

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

// keep walking up the chain from the inbound message's parent, until there is no more parent
let currentParentId = recordsWrite.message.descriptor.parentId;
let currentParentId = newestRecordsWrite.message.descriptor.parentId;
while (currentParentId !== undefined) {
// fetch parent
const query: Filter = {
Expand Down
5 changes: 3 additions & 2 deletions src/core/records-grant-authorization.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { MessageStore } from '../types/message-store.js';
import type { PermissionsGrantMessage } from '../types/permissions-types.js';
import type { RecordsPermissionScope } from '../types/permissions-grant-descriptor.js';
import type { RecordsRead } from '../interfaces/records-read.js';
import type { RecordsWrite } from '../interfaces/records-write.js';
import type { PermissionsGrantMessage, RecordsPermissionScope } from '../types/permissions-types.js';

import { GrantAuthorization } from './grant-authorization.js';
import { PermissionsConditionPublication } from '../types/permissions-types.js';
import { PermissionsConditionPublication } from '../types/permissions-grant-descriptor.js';
import { DwnError, DwnErrorCode } from './dwn-error.js';

export class RecordsGrantAuthorization {
Expand Down
2 changes: 1 addition & 1 deletion src/did/did-ion-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DidMethodResolver, DidResolutionResult } from './did-resolver.js';
import type { DidMethodResolver, DidResolutionResult } from '../types/did-types.js';
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';

import crossFetch from 'cross-fetch';
Expand Down
2 changes: 1 addition & 1 deletion src/did/did-key-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import varint from 'varint';
import type { DidDocument, DidMethodResolver, DidResolutionResult } from './did-resolver.js';
import type { DidDocument, DidMethodResolver, DidResolutionResult } from '../types/did-types.js';

import { base58btc } from 'multiformats/bases/base58';
import { Did } from './did.js';
Expand Down
94 changes: 1 addition & 93 deletions src/did/did-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Cache } from '../types/cache.js';
import type { PublicJwk } from '../types/jose-types.js';
import type { DidMethodResolver, DidResolutionResult } from '../types/did-types.js';

import { Did } from './did.js';
import { DidIonResolver } from './did-ion-resolver.js';
Expand Down Expand Up @@ -89,95 +89,3 @@ export class DidResolver {
console.groupEnd();
}
}

/**
* A generalized interface that can be implemented for individual
* DID methods
*/
export interface DidMethodResolver {
/**
* @returns the DID method supported by {@link DidMethodResolver.resolve}
*/
method(): string;

/**
* attempts to resolve the DID provided into its respective DID Document.
* More info on resolving DIDs can be found
* {@link https://www.w3.org/TR/did-core/#resolution here}
* @param did - the DID to resolve
* @throws {Error} if unable to resolve the DID
*/
resolve(did: string): Promise<DidResolutionResult>;
}

export type DidDocument = {
'@context'?: 'https://www.w3.org/ns/did/v1' | string | string[]
id: string
alsoKnownAs?: string[]
controller?: string | string[]
verificationMethod?: VerificationMethod[]
service?: ServiceEndpoint[]
authentication?: VerificationMethod[] | string[]
assertionMethod?: VerificationMethod[] | string[]
keyAgreement?: VerificationMethod[] | string[]
capabilityInvocation?: VerificationMethod[] | string[]
capabilityDelegation?: VerificationMethod[] | string[]
};

export type DwnServiceEndpoint = {
nodes: string[]
};

export type ServiceEndpoint = {
id: string
type: string
serviceEndpoint: string | DwnServiceEndpoint
description?: string
};

export type VerificationMethod = {
id: string
// one of the valid verification method types as per
// https://www.w3.org/TR/did-spec-registries/#verification-method-types
type: string
// DID of the key's controller
controller: string
// a JSON Web Key that conforms to https://datatracker.ietf.org/doc/html/rfc7517
publicKeyJwk?: PublicJwk
};

export type DidResolutionResult = {
'@context'?: 'https://w3id.org/did-resolution/v1' | string | string[]
didResolutionMetadata: DidResolutionMetadata
didDocument?: DidDocument
didDocumentMetadata: DidDocumentMetadata
};

export type DidResolutionMetadata = {
contentType?: string
error?: 'invalidDid' | 'notFound' | 'representationNotSupported' |
'unsupportedDidMethod' | string
};

export type DidDocumentMetadata = {
// indicates the timestamp of the Create operation. ISO8601 timestamp
created?: string
// indicates the timestamp of the last Update operation for the document version which was
// resolved. ISO8601 timestamp
updated?: string
// indicates whether the DID has been deactivated
deactivated?: boolean
// indicates the version of the last Update operation for the document version which
// was resolved
versionId?: string
// indicates the timestamp of the next Update operation if the resolved document version
// is not the latest version of the document.
nextUpdate?: string
// indicates the version of the next Update operation if the resolved document version
// is not the latest version of the document.
nextVersionId?: string
// @see https://www.w3.org/TR/did-core/#dfn-equivalentid
equivalentId?: string
// @see https://www.w3.org/TR/did-core/#dfn-canonicalid
canonicalId?: string
};
3 changes: 2 additions & 1 deletion src/dwn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { RecordsDeleteMessage, RecordsQueryMessage, RecordsQueryReply, Reco
import { AllowAllTenantGate } from './core/tenant-gate.js';
import { DidResolver } from './did/did-resolver.js';
import { EventsGetHandler } from './handlers/events-get.js';
import { Message } from './core/message.js';
import { messageReplyFromError } from './core/message-reply.js';
import { MessagesGetHandler } from './handlers/messages-get.js';
import { PermissionsGrantHandler } from './handlers/permissions-grant.js';
Expand All @@ -27,7 +28,7 @@ import { RecordsDeleteHandler } from './handlers/records-delete.js';
import { RecordsQueryHandler } from './handlers/records-query.js';
import { RecordsReadHandler } from './handlers/records-read.js';
import { RecordsWriteHandler } from './handlers/records-write.js';
import { DwnInterfaceName, DwnMethodName, Message } from './core/message.js';
import { DwnInterfaceName, DwnMethodName } from './enums/dwn-interface-method.js';

export class Dwn {
private methodHandlers: { [key:string]: MethodHandler };
Expand Down
20 changes: 20 additions & 0 deletions src/enums/dwn-interface-method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export enum DwnInterfaceName {
Events = 'Events',
Messages = 'Messages',
Permissions = 'Permissions',
Protocols = 'Protocols',
Records = 'Records'
}

export enum DwnMethodName {
Configure = 'Configure',
Create = 'Create',
Get = 'Get',
Grant = 'Grant',
Query = 'Query',
Read = 'Read',
Request = 'Request',
Revoke = 'Revoke',
Write = 'Write',
Delete = 'Delete'
}

0 comments on commit e3af260

Please sign in to comment.