diff --git a/apps/orchestration/src/policy-engine/__test__/e2e/facade.spec.ts b/apps/orchestration/src/policy-engine/__test__/e2e/facade.spec.ts index 99baed11b..58896b61a 100644 --- a/apps/orchestration/src/policy-engine/__test__/e2e/facade.spec.ts +++ b/apps/orchestration/src/policy-engine/__test__/e2e/facade.spec.ts @@ -4,13 +4,13 @@ import { REQUEST_HEADER_ORG_ID } from '@app/orchestration/orchestration.constant' import { AuthorizationRequest, SupportedAction } from '@app/orchestration/policy-engine/core/type/domain.type' -import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/http/persistence/repository/authorization-request.repository' import { SignatureDto } from '@app/orchestration/policy-engine/http/rest/dto/signature.dto' +import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/persistence/repository/authorization-request.repository' import { PolicyEngineModule } from '@app/orchestration/policy-engine/policy-engine.module' import { PersistenceModule } from '@app/orchestration/shared/module/persistence/persistence.module' import { TestPrismaService } from '@app/orchestration/shared/module/persistence/service/test-prisma.service' import { QueueModule } from '@app/orchestration/shared/module/queue/queue.module' -import { TransactionType } from '@narval/authz-shared' +import { TransactionType, hashRequest } from '@narval/authz-shared' import { getQueueToken } from '@nestjs/bull' import { HttpStatus, INestApplication } from '@nestjs/common' import { ConfigModule } from '@nestjs/config' @@ -18,7 +18,7 @@ import { Test, TestingModule } from '@nestjs/testing' import { AuthorizationRequestStatus, Organization } from '@prisma/client/orchestration' import { Queue } from 'bull' import request from 'supertest' -import { hashMessage, stringToHex } from 'viem' +import { stringToHex } from 'viem' const EVALUATIONS_ENDPOINT = '/policy-engine/evaluations' @@ -102,7 +102,7 @@ describe('Policy Engine Cluster Facade', () => { const payload = { action: SupportedAction.SIGN_MESSAGE, request: signMessageRequest, - hash: hashMessage(JSON.stringify(signMessageRequest)), + hash: hashRequest(signMessageRequest), authentication, approvals } @@ -144,7 +144,7 @@ describe('Policy Engine Cluster Facade', () => { } const payload = { action: SupportedAction.SIGN_TRANSACTION, - hash: hashMessage(JSON.stringify(signTransactionRequest)), + hash: hashRequest(signTransactionRequest), request: signTransactionRequest, authentication, approvals @@ -176,7 +176,7 @@ describe('Policy Engine Cluster Facade', () => { } const payload = { action: SupportedAction.SIGN_TRANSACTION, - hash: hashMessage(JSON.stringify(signTransactionRequest)), + hash: hashRequest(signTransactionRequest), request: signTransactionRequest, authentication, approvals @@ -212,7 +212,7 @@ describe('Policy Engine Cluster Facade', () => { status: AuthorizationRequestStatus.PERMITTED, action: SupportedAction.SIGN_MESSAGE, request: signMessageRequest, - hash: hashMessage(JSON.stringify(signMessageRequest)), + hash: hashRequest(signMessageRequest), idempotencyKey: '8dcbb7ad-82a2-4eca-b2f0-b1415c1d4a17', createdAt: new Date(), updatedAt: new Date(), diff --git a/apps/orchestration/src/policy-engine/core/service/authorization-request.service.ts b/apps/orchestration/src/policy-engine/core/service/authorization-request.service.ts index 08baed6d5..658396e75 100644 --- a/apps/orchestration/src/policy-engine/core/service/authorization-request.service.ts +++ b/apps/orchestration/src/policy-engine/core/service/authorization-request.service.ts @@ -3,7 +3,7 @@ import { AuthorizationRequestStatus, CreateAuthorizationRequest } from '@app/orchestration/policy-engine/core/type/domain.type' -import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/http/persistence/repository/authorization-request.repository' +import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/persistence/repository/authorization-request.repository' import { AuthorizationRequestProcessingProducer } from '@app/orchestration/policy-engine/queue/producer/authorization-request-processing.producer' import { ApplicationException } from '@app/orchestration/shared/exception/application.exception' import { HttpService } from '@nestjs/axios' diff --git a/apps/orchestration/src/policy-engine/http/rest/dto/validator/__test__/unit/request-hash.validator.spec.ts b/apps/orchestration/src/policy-engine/http/rest/dto/validator/__test__/unit/request-hash.validator.spec.ts index 30755c73a..eada9b8d6 100644 --- a/apps/orchestration/src/policy-engine/http/rest/dto/validator/__test__/unit/request-hash.validator.spec.ts +++ b/apps/orchestration/src/policy-engine/http/rest/dto/validator/__test__/unit/request-hash.validator.spec.ts @@ -1,4 +1,5 @@ import { RequestHash } from '@app/orchestration/policy-engine/http/rest/dto/validator/request-hash.validator' +import { hashRequest } from '@narval/authz-shared' import { ValidationArguments } from 'class-validator' describe(RequestHash.name, () => { @@ -11,7 +12,7 @@ describe(RequestHash.name, () => { describe('validate', () => { it('returns true if the given hash matches the hash of the request object', () => { const request = { foo: 'bar' } - const hash = RequestHash.hash(request) + const hash = hashRequest(request) const args: ValidationArguments = { targetName: 'AuthorizationRequestDto', object: { request }, @@ -70,7 +71,7 @@ describe(RequestHash.name, () => { const message = validator.defaultMessage(args) - expect(message).toEqual(`${property} is not a valid EIP-191 hash format`) + expect(message).toEqual(`${property} is not a valid hexadecimal SHA256 hash`) }) }) }) diff --git a/apps/orchestration/src/policy-engine/http/rest/dto/validator/request-hash.validator.ts b/apps/orchestration/src/policy-engine/http/rest/dto/validator/request-hash.validator.ts index d7d381619..37a428a4e 100644 --- a/apps/orchestration/src/policy-engine/http/rest/dto/validator/request-hash.validator.ts +++ b/apps/orchestration/src/policy-engine/http/rest/dto/validator/request-hash.validator.ts @@ -1,12 +1,11 @@ -import { stringify } from '@app/orchestration/shared/lib/json' +import { hashRequest } from '@narval/authz-shared' import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator' -import { hashMessage } from 'viem' @ValidatorConstraint({ async: false }) export class RequestHash implements ValidatorConstraintInterface { validate(givenHash: string, args: ValidationArguments) { if ('request' in args.object) { - const hash = RequestHash.hash(args.object.request) + const hash = hashRequest(args.object.request) return givenHash === hash } @@ -15,10 +14,6 @@ export class RequestHash implements ValidatorConstraintInterface { } defaultMessage(args: ValidationArguments) { - return `${args.property} is not a valid EIP-191 hash format` - } - - static hash(request: unknown): string { - return hashMessage(stringify(request)) + return `${args.property} is not a valid hexadecimal SHA256 hash` } } diff --git a/apps/orchestration/src/policy-engine/persistence/schema/sign-transaction-request.schema.ts b/apps/orchestration/src/policy-engine/persistence/schema/sign-transaction-request.schema.ts index d771410c0..c49290258 100644 --- a/apps/orchestration/src/policy-engine/persistence/schema/sign-transaction-request.schema.ts +++ b/apps/orchestration/src/policy-engine/persistence/schema/sign-transaction-request.schema.ts @@ -1,4 +1,4 @@ -import { TransactionType } from '@app/orchestration/policy-engine/core/type/domain.type' +import { TransactionType } from '@narval/authz-shared' import { isAddress, isHex } from 'viem' import { z } from 'zod' diff --git a/apps/orchestration/src/policy-engine/queue/consumer/__test__/integration/authorization-request-processing.consumer.spec.ts b/apps/orchestration/src/policy-engine/queue/consumer/__test__/integration/authorization-request-processing.consumer.spec.ts index 861d83448..b683f814c 100644 --- a/apps/orchestration/src/policy-engine/queue/consumer/__test__/integration/authorization-request-processing.consumer.spec.ts +++ b/apps/orchestration/src/policy-engine/queue/consumer/__test__/integration/authorization-request-processing.consumer.spec.ts @@ -10,7 +10,7 @@ import { AuthorizationRequestStatus, SupportedAction } from '@app/orchestration/policy-engine/core/type/domain.type' -import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/http/persistence/repository/authorization-request.repository' +import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/persistence/repository/authorization-request.repository' import { AuthorizationRequestProcessingConsumer } from '@app/orchestration/policy-engine/queue/consumer/authorization-request-processing.consumer' import { AuthorizationRequestProcessingProducer } from '@app/orchestration/policy-engine/queue/producer/authorization-request-processing.producer' import { PersistenceModule } from '@app/orchestration/shared/module/persistence/persistence.module' diff --git a/apps/orchestration/src/policy-engine/queue/producer/__test__/integration/authorization-request-processing.producer.spec.ts b/apps/orchestration/src/policy-engine/queue/producer/__test__/integration/authorization-request-processing.producer.spec.ts index e64482585..a3258369b 100644 --- a/apps/orchestration/src/policy-engine/queue/producer/__test__/integration/authorization-request-processing.producer.spec.ts +++ b/apps/orchestration/src/policy-engine/queue/producer/__test__/integration/authorization-request-processing.producer.spec.ts @@ -6,7 +6,7 @@ import { AuthorizationRequestStatus, SupportedAction } from '@app/orchestration/policy-engine/core/type/domain.type' -import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/http/persistence/repository/authorization-request.repository' +import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/persistence/repository/authorization-request.repository' import { AuthorizationRequestProcessingProducer, DEFAULT_JOB_OPTIONS diff --git a/apps/orchestration/src/policy-engine/queue/producer/authorization-request-processing.producer.ts b/apps/orchestration/src/policy-engine/queue/producer/authorization-request-processing.producer.ts index 77be92b07..c7f3b0bd2 100644 --- a/apps/orchestration/src/policy-engine/queue/producer/authorization-request-processing.producer.ts +++ b/apps/orchestration/src/policy-engine/queue/producer/authorization-request-processing.producer.ts @@ -8,7 +8,7 @@ import { AuthorizationRequestProcessingJob, AuthorizationRequestStatus } from '@app/orchestration/policy-engine/core/type/domain.type' -import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/http/persistence/repository/authorization-request.repository' +import { AuthorizationRequestRepository } from '@app/orchestration/policy-engine/persistence/repository/authorization-request.repository' import { InjectQueue } from '@nestjs/bull' import { Injectable, Logger } from '@nestjs/common' import { BackoffOptions, Job, Queue } from 'bull' diff --git a/apps/orchestration/src/shared/lib/__test__/unit/json.spec.ts b/apps/orchestration/src/shared/lib/__test__/unit/json.spec.ts deleted file mode 100644 index 493034457..000000000 --- a/apps/orchestration/src/shared/lib/__test__/unit/json.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { stringify } from '@app/orchestration/shared/lib/json' - -describe('json', () => { - describe('stringify', () => { - it('stringifies base primitives exactly like JSON.stringify', () => { - const primitives = { - string: 'example', - number: 123, - boolean: true, - null: null, - undefined: undefined, - symbol: Symbol('example'), - object: { key: 'value' }, - array: [1, 2, 3], - nan: NaN, - infinity: Infinity - } - - expect(stringify(primitives)).toEqual(JSON.stringify(primitives)) - }) - - it('stringifies bigint', () => { - const bigint = { - bigint: BigInt(5_000) - } - - expect(stringify(bigint)).toEqual('{"bigint":"5000"}') - }) - }) -}) diff --git a/apps/orchestration/src/shared/lib/json.ts b/apps/orchestration/src/shared/lib/json.ts deleted file mode 100644 index 1709ecb78..000000000 --- a/apps/orchestration/src/shared/lib/json.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -type Transformation = (key: string, value: any) => any - -type ExtendType = { - replacer: Transformation -} - -const EXTENDED_TYPES: Record = { - bigint: { - replacer: (key: string, value: any) => { - // IMPORTANT: The bigint replacer [1] has a significant limitation that - // hinders its use with a reviver [2]. - // - // In theory, JSON parse and stringify operations should allow for - // round-trip conversion. However, once a bigint is converted to a string, - // it becomes challenging to discern whether the string value was - // originally a number or a bigint. - // - // To address this, one proposed solution is to either prefix the - // stringified bigint with a unique marker, like - // bigint:, or transform the bigint into an object - // with a special key indicating its type, such as { __type: 'bigint', - // value: }. - // - // Personally, I'm not in favor of these methods as transforming the key - // into an object requires the entire system to understand how to process - // it, or it forces consumers to be aware of the format for sending - // correct data. - // - // Given these considerations, I would advise against using the reviver in - // this scenario and only apply the replacer during stringify operations, - // with the understanding that it won't be possible to revert the string - // back to its original bigint form. - // - // Reference - // [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#replacer - // [2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#reviver - if (typeof value === 'bigint') { - return value.toString() - } else { - return value - } - } - } -} - -export const stringify = (value: any) => { - return JSON.stringify(value, (key: string, value: any) => { - const type = typeof value - - if (EXTENDED_TYPES[type]) { - return EXTENDED_TYPES[type].replacer(key, value) - } else { - return value - } - }) -}