From 4a3f41ab4a3dbe1f9bdedb60161637a7245cdc19 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 31 Dec 2024 12:49:05 +0530 Subject: [PATCH 1/6] fix: duplicate attributes issue in proof request Signed-off-by: pranalidhanavade --- .../verification/verification.controller.ts | 34 ++++++------------- .../src/interfaces/verification.interface.ts | 2 +- libs/common/src/did.validator.ts | 25 ++++++++++++++ libs/common/src/response-messages/index.ts | 3 +- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index 5c9488687..e4385cd19 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -36,6 +36,7 @@ import { API_Version, ProofRequestType, SortFields } from './enum/verification.e // eslint-disable-next-line @typescript-eslint/no-unused-vars import { user } from '@prisma/client'; import { TrimStringParamPipe } from '@credebl/common/cast.helper'; +import { ProofRequestValidator } from '@credebl/common/did.validator'; @UseFilters(CustomExceptionFilter) @Controller() @@ -193,19 +194,11 @@ export class VerificationController { if (requestType === ProofRequestType.PRESENTATIONEXCHANGE && !requestProof.presentationDefinition) { throw new BadRequestException(`type: ${requestType} requires presentationDefinition`); } - if (requestProof.proofFormats) { - const attributeArray = []; - for (const attrData of requestProof.proofFormats.indy.attributes) { - if (0 === attributeArray.length) { - attributeArray.push(Object.values(attrData)[0]); - } else if (!attributeArray.includes(Object.values(attrData)[0])) { - attributeArray.push(Object.values(attrData)[0]); - } else { - throw new BadRequestException('Please provide unique attribute names'); - } + + if (requestProof.proofFormats) { + ProofRequestValidator.validateAttributes(requestProof.proofFormats.indy.attributes); + } - } - } const version = API_Version.version_neutral; requestProof.version = version; requestProof.orgId = orgId; @@ -255,19 +248,12 @@ export class VerificationController { if (requestTypeV1 === ProofRequestType.PRESENTATIONEXCHANGE && !requestProof.presentationDefinition) { throw new BadRequestException(`type: ${requestTypeV1} requires presentationDefinition`); } - if (requestProof.proofFormats) { - const attributeArrayV1 = []; - for (const attrData of requestProof.proofFormats.indy.attributes) { - if (0 === attributeArrayV1.length) { - attributeArrayV1.push(Object.values(attrData)[0]); - } else if (!attributeArrayV1.includes(Object.values(attrData)[0])) { - attributeArrayV1.push(Object.values(attrData)[0]); - } else { - throw new BadRequestException('Please provide unique attribute names'); - } - } - } + + if (requestProof.proofFormats) { + ProofRequestValidator.validateAttributes(requestProof.proofFormats.indy.attributes); + } + const version = API_Version.VERSION_1; requestProof.version = version; requestProof.orgId = orgId; diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 69c60de46..13279ba92 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -1,7 +1,7 @@ import { AutoAccept } from '@credebl/enum/enum'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; -interface IProofRequestAttribute { +export interface IProofRequestAttribute { attributeName?: string; attributeNames?:string[]; condition?: string; diff --git a/libs/common/src/did.validator.ts b/libs/common/src/did.validator.ts index 4aa0f626a..ff71cbec3 100644 --- a/libs/common/src/did.validator.ts +++ b/libs/common/src/did.validator.ts @@ -1,6 +1,8 @@ import { DidMethod } from '@credebl/enum/enum'; import { IDidCreate } from './interfaces/did.interface'; import { BadRequestException } from '@nestjs/common'; +import { ResponseMessages } from './response-messages'; +import { IProofRequestAttribute } from 'apps/verification/src/interfaces/verification.interface'; export function validateDid(createDid: IDidCreate): void { const errors = []; @@ -38,3 +40,26 @@ export function validateDid(createDid: IDidCreate): void { throw new BadRequestException(errors); } } + +export class ProofRequestValidator { + + static validateAttributes(attributes: IProofRequestAttribute[]): void { + const seenAttributes = new Map(); + + for (const attribute of attributes) { + const key = attribute.schemaId || attribute.credDefId + ? `${attribute.schemaId || ''}:${attribute.credDefId || ''}` + : 'default'; + + if (!seenAttributes.has(key)) { + seenAttributes.set(key, new Set()); + } + + const attributeNames = seenAttributes.get(key); + if (attributeNames.has(attribute.attributeName)) { + throw new BadRequestException(ResponseMessages.verification.error.uniqueAttributes); + } + attributeNames.add(attribute.attributeName); + } + } +} diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 13b43cb9d..5151c7d2c 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -354,7 +354,8 @@ export const ResponseMessages = { batchEmailSend: 'Unable to send email in batches', emailSend: 'Unable to send email to the user', verificationRecordsNotFound: 'Verification records does not exists', - removeVerificationData: 'First you have to remove verification data' + removeVerificationData: 'First you have to remove verification data', + uniqueAttributes:'Please provide unique attribute names' } }, From 424697af53fc65c53964da23dd2a5e63fb861dc2 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 31 Dec 2024 15:40:17 +0530 Subject: [PATCH 2/6] added common validator function file for common validations Signed-off-by: pranalidhanavade --- .../agent-service/agent-service.controller.ts | 4 +- .../cloud-wallet/cloud-wallet.controller.ts | 4 +- .../verification/verification.controller.ts | 6 +- libs/common/src/did.validator.ts | 65 ------------------ libs/common/src/validator.ts | 66 +++++++++++++++++++ 5 files changed, 73 insertions(+), 72 deletions(-) delete mode 100644 libs/common/src/did.validator.ts create mode 100644 libs/common/src/validator.ts diff --git a/apps/api-gateway/src/agent-service/agent-service.controller.ts b/apps/api-gateway/src/agent-service/agent-service.controller.ts index d65d01da1..30e048afc 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -41,7 +41,7 @@ import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler import { Roles } from '../authz/decorators/roles.decorator'; import { OrgRoles } from 'libs/org-roles/enums'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; -import { validateDid } from '@credebl/common/did.validator'; +import { Validator } from '@credebl/common/validator'; import { CreateWalletDto } from './dto/create-wallet.dto'; import { CreateNewDidDto } from './dto/create-new-did.dto'; import { AgentSpinupValidator, TrimStringParamPipe } from '@credebl/common/cast.helper'; @@ -223,7 +223,7 @@ export class AgentController { @User() user: user, @Res() res: Response ): Promise { - await validateDid(createDidDto); + Validator.validateDid(createDidDto); if (createDidDto.seed && seedLength !== createDidDto.seed.length) { this.logger.error(`seed must be at most 32 characters.`); diff --git a/apps/api-gateway/src/cloud-wallet/cloud-wallet.controller.ts b/apps/api-gateway/src/cloud-wallet/cloud-wallet.controller.ts index 63878b854..8c2db22dc 100644 --- a/apps/api-gateway/src/cloud-wallet/cloud-wallet.controller.ts +++ b/apps/api-gateway/src/cloud-wallet/cloud-wallet.controller.ts @@ -14,7 +14,7 @@ import { AuthGuard } from '@nestjs/passport'; import { User } from '../authz/decorators/user.decorator'; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { user } from '@prisma/client'; -import { validateDid } from '@credebl/common/did.validator'; +import { Validator } from '@credebl/common/validator'; import { CommonConstants } from '@credebl/common/common.constant'; import { UserRoleGuard } from '../authz/guards/user-role.guard'; import { AcceptProofRequestDto } from './dtos/accept-proof-request.dto'; @@ -267,7 +267,7 @@ export class CloudWalletController { @User() user: user, @Res() res: Response ): Promise { - await validateDid(createDidDto); + Validator.validateDid(createDidDto); const {email, id} = user; createDidDto.email = email; createDidDto.userId = id; diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index e4385cd19..eb160103c 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -36,7 +36,7 @@ import { API_Version, ProofRequestType, SortFields } from './enum/verification.e // eslint-disable-next-line @typescript-eslint/no-unused-vars import { user } from '@prisma/client'; import { TrimStringParamPipe } from '@credebl/common/cast.helper'; -import { ProofRequestValidator } from '@credebl/common/did.validator'; +import { Validator } from '@credebl/common/validator'; @UseFilters(CustomExceptionFilter) @Controller() @@ -196,7 +196,7 @@ export class VerificationController { } if (requestProof.proofFormats) { - ProofRequestValidator.validateAttributes(requestProof.proofFormats.indy.attributes); + Validator.validateProofAttributes(requestProof.proofFormats.indy.attributes); } const version = API_Version.version_neutral; @@ -251,7 +251,7 @@ export class VerificationController { if (requestProof.proofFormats) { - ProofRequestValidator.validateAttributes(requestProof.proofFormats.indy.attributes); + Validator.validateProofAttributes(requestProof.proofFormats.indy.attributes); } const version = API_Version.VERSION_1; diff --git a/libs/common/src/did.validator.ts b/libs/common/src/did.validator.ts deleted file mode 100644 index ff71cbec3..000000000 --- a/libs/common/src/did.validator.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { DidMethod } from '@credebl/enum/enum'; -import { IDidCreate } from './interfaces/did.interface'; -import { BadRequestException } from '@nestjs/common'; -import { ResponseMessages } from './response-messages'; -import { IProofRequestAttribute } from 'apps/verification/src/interfaces/verification.interface'; - -export function validateDid(createDid: IDidCreate): void { - const errors = []; - - switch (true) { - case DidMethod.WEB === createDid.method && !createDid.domain: - errors.push('domain is required for Web method'); - break; - case (createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && !createDid.network: - errors.push('network is required'); - break; - case (createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && 'ed25519' !== createDid.keyType: - errors.push('Only ed25519 key type is supported'); - break; - case (createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && !('ed25519' === createDid.keyType || 'bls12381g2' === createDid.keyType): - errors.push('Only ed25519 and bls12381g2 key type is supported'); - break; - case DidMethod.INDY === createDid.method && !(createDid.role || createDid.endorserDid): - errors.push('role or endorserDid is required'); - break; - case DidMethod.POLYGON === createDid.method && !createDid.privatekey: - errors.push('privatekey is required for polygon method'); - break; - case DidMethod.POLYGON === createDid.method && createDid.privatekey && 64 !== createDid.privatekey.length: - errors.push('Private key must be exactly 64 characters long'); - break; - case (DidMethod.INDY === createDid.method || DidMethod.KEY === createDid.method || DidMethod.WEB === createDid.method) && (!createDid.seed): - errors.push('seed is required'); - break; - default: - break; - } - - if (0 < errors.length) { - throw new BadRequestException(errors); - } -} - -export class ProofRequestValidator { - - static validateAttributes(attributes: IProofRequestAttribute[]): void { - const seenAttributes = new Map(); - - for (const attribute of attributes) { - const key = attribute.schemaId || attribute.credDefId - ? `${attribute.schemaId || ''}:${attribute.credDefId || ''}` - : 'default'; - - if (!seenAttributes.has(key)) { - seenAttributes.set(key, new Set()); - } - - const attributeNames = seenAttributes.get(key); - if (attributeNames.has(attribute.attributeName)) { - throw new BadRequestException(ResponseMessages.verification.error.uniqueAttributes); - } - attributeNames.add(attribute.attributeName); - } - } -} diff --git a/libs/common/src/validator.ts b/libs/common/src/validator.ts new file mode 100644 index 000000000..cc932a179 --- /dev/null +++ b/libs/common/src/validator.ts @@ -0,0 +1,66 @@ +import { DidMethod } from '@credebl/enum/enum'; +import { IDidCreate } from './interfaces/did.interface'; +import { BadRequestException } from '@nestjs/common'; +import { ResponseMessages } from './response-messages'; +import { IProofRequestAttribute } from 'apps/verification/src/interfaces/verification.interface'; + + +export class Validator { + + static validateDid(createDid: IDidCreate): void { + const errors = []; + + switch (true) { + case DidMethod.WEB === createDid.method && !createDid.domain: + errors.push('domain is required for Web method'); + break; + case (createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && !createDid.network: + errors.push('network is required'); + break; + case (createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && 'ed25519' !== createDid.keyType: + errors.push('Only ed25519 key type is supported'); + break; + case (createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && !('ed25519' === createDid.keyType || 'bls12381g2' === createDid.keyType): + errors.push('Only ed25519 and bls12381g2 key type is supported'); + break; + case DidMethod.INDY === createDid.method && !(createDid.role || createDid.endorserDid): + errors.push('role or endorserDid is required'); + break; + case DidMethod.POLYGON === createDid.method && !createDid.privatekey: + errors.push('privatekey is required for polygon method'); + break; + case DidMethod.POLYGON === createDid.method && createDid.privatekey && 64 !== createDid.privatekey.length: + errors.push('Private key must be exactly 64 characters long'); + break; + case (DidMethod.INDY === createDid.method || DidMethod.KEY === createDid.method || DidMethod.WEB === createDid.method) && (!createDid.seed): + errors.push('seed is required'); + break; + default: + break; + } + + if (0 < errors.length) { + throw new BadRequestException(errors); + } + } + + static validateProofAttributes(attributes: IProofRequestAttribute[]): void { + const seenAttributes = new Map(); + + for (const attribute of attributes) { + const key = attribute.schemaId || attribute.credDefId + ? `${attribute.schemaId || ''}:${attribute.credDefId || ''}` + : 'default'; + + if (!seenAttributes.has(key)) { + seenAttributes.set(key, new Set()); + } + + const attributeNames = seenAttributes.get(key); + if (attributeNames.has(attribute.attributeName)) { + throw new BadRequestException(ResponseMessages.verification.error.uniqueAttributes); + } + attributeNames.add(attribute.attributeName); + } + } +} From 5b41cc160071c7f159e2e3663c220b486726189d Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 31 Dec 2024 16:53:32 +0530 Subject: [PATCH 3/6] added error messages in common file Signed-off-by: pranalidhanavade --- libs/common/src/response-messages/index.ts | 11 ++++++++++- libs/common/src/validator.ts | 16 ++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 5151c7d2c..87a630be6 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -257,7 +257,16 @@ export const ResponseMessages = { failedOrganization: 'Failed to fetch organization agent type details', promiseReject: 'One or more promises were rejected.', orgAgentNotFound: 'Org agent type not found', - walletDoesNotExists: 'Organization wallet does not exists' + walletDoesNotExists: 'Organization wallet does not exists', + requiredDomain: 'domain is required for Web method', + requiredNetwork: 'network is required', + keyType: 'Only ed25519 key type is supported', + keyTypeWeb : 'Only ed25519 and bls12381g2 key type is supported', + requiredEndorserDid: 'role or endorserDid is required', + requiredPrivateKey: 'privatekey is required for polygon method', + privateKeyLength: 'Private key must be exactly 64 characters long', + requiredSeed: 'seed is required' + } }, connection: { diff --git a/libs/common/src/validator.ts b/libs/common/src/validator.ts index cc932a179..7367de976 100644 --- a/libs/common/src/validator.ts +++ b/libs/common/src/validator.ts @@ -12,28 +12,28 @@ export class Validator { switch (true) { case DidMethod.WEB === createDid.method && !createDid.domain: - errors.push('domain is required for Web method'); + errors.push(ResponseMessages.agent.error.requiredDomain); break; case (createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && !createDid.network: - errors.push('network is required'); + errors.push(ResponseMessages.agent.error.requiredNetwork); break; case (createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && 'ed25519' !== createDid.keyType: - errors.push('Only ed25519 key type is supported'); + errors.push(ResponseMessages.agent.error.keyType); break; case (createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && !('ed25519' === createDid.keyType || 'bls12381g2' === createDid.keyType): - errors.push('Only ed25519 and bls12381g2 key type is supported'); + errors.push(ResponseMessages.agent.error.keyTypeWeb); break; case DidMethod.INDY === createDid.method && !(createDid.role || createDid.endorserDid): - errors.push('role or endorserDid is required'); + errors.push(ResponseMessages.agent.error.requiredEndorserDid); break; case DidMethod.POLYGON === createDid.method && !createDid.privatekey: - errors.push('privatekey is required for polygon method'); + errors.push(ResponseMessages.agent.error.requiredPrivateKey); break; case DidMethod.POLYGON === createDid.method && createDid.privatekey && 64 !== createDid.privatekey.length: - errors.push('Private key must be exactly 64 characters long'); + errors.push(ResponseMessages.agent.error.privateKeyLength); break; case (DidMethod.INDY === createDid.method || DidMethod.KEY === createDid.method || DidMethod.WEB === createDid.method) && (!createDid.seed): - errors.push('seed is required'); + errors.push(ResponseMessages.agent.error.requiredSeed); break; default: break; From fbcb6baef5a8652a1bf86ed18af6ab92bcf4e292 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 31 Dec 2024 17:04:13 +0530 Subject: [PATCH 4/6] resolved comments on PR Signed-off-by: pranalidhanavade --- .../src/verification/verification.controller.ts | 8 ++++---- libs/common/src/validator.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index eb160103c..7aa417310 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -195,8 +195,8 @@ export class VerificationController { throw new BadRequestException(`type: ${requestType} requires presentationDefinition`); } - if (requestProof.proofFormats) { - Validator.validateProofAttributes(requestProof.proofFormats.indy.attributes); + if (ProofRequestType.INDY) { + Validator.validateIndyProofAttributes(requestProof.proofFormats.indy.attributes); } const version = API_Version.version_neutral; @@ -250,8 +250,8 @@ export class VerificationController { } - if (requestProof.proofFormats) { - Validator.validateProofAttributes(requestProof.proofFormats.indy.attributes); + if (ProofRequestType.INDY) { + Validator.validateIndyProofAttributes(requestProof.proofFormats.indy.attributes); } const version = API_Version.VERSION_1; diff --git a/libs/common/src/validator.ts b/libs/common/src/validator.ts index 7367de976..73bd49d4f 100644 --- a/libs/common/src/validator.ts +++ b/libs/common/src/validator.ts @@ -44,7 +44,7 @@ export class Validator { } } - static validateProofAttributes(attributes: IProofRequestAttribute[]): void { + static validateIndyProofAttributes(attributes: IProofRequestAttribute[]): void { const seenAttributes = new Map(); for (const attribute of attributes) { From 5bd79edbdfd1697cea7797e1620a8564c87ab757 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Thu, 2 Jan 2025 19:05:19 +0530 Subject: [PATCH 5/6] resolved comments on PR Signed-off-by: pranalidhanavade --- libs/common/src/response-messages/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 87a630be6..fea4ee417 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -258,14 +258,14 @@ export const ResponseMessages = { promiseReject: 'One or more promises were rejected.', orgAgentNotFound: 'Org agent type not found', walletDoesNotExists: 'Organization wallet does not exists', - requiredDomain: 'domain is required for Web method', - requiredNetwork: 'network is required', + requiredDomain: 'Domain is required for Web method', + requiredNetwork: 'Network is required', keyType: 'Only ed25519 key type is supported', keyTypeWeb : 'Only ed25519 and bls12381g2 key type is supported', - requiredEndorserDid: 'role or endorserDid is required', - requiredPrivateKey: 'privatekey is required for polygon method', + requiredEndorserDid: 'Role or endorser DID is required', + requiredPrivateKey: 'Privatekey is required for polygon method', privateKeyLength: 'Private key must be exactly 64 characters long', - requiredSeed: 'seed is required' + requiredSeed: 'Seed is required' } }, From 6646ff49d6b2a16eb71543c5a63d61d339f91940 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Thu, 2 Jan 2025 19:20:12 +0530 Subject: [PATCH 6/6] fix: resolve comments on PR Signed-off-by: pranalidhanavade --- apps/api-gateway/src/verification/verification.controller.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index 7aa417310..533ca9b28 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -195,10 +195,9 @@ export class VerificationController { throw new BadRequestException(`type: ${requestType} requires presentationDefinition`); } - if (ProofRequestType.INDY) { + if (requestType === ProofRequestType.INDY) { Validator.validateIndyProofAttributes(requestProof.proofFormats.indy.attributes); } - const version = API_Version.version_neutral; requestProof.version = version; requestProof.orgId = orgId; @@ -250,7 +249,7 @@ export class VerificationController { } - if (ProofRequestType.INDY) { + if (requestTypeV1 === ProofRequestType.INDY) { Validator.validateIndyProofAttributes(requestProof.proofFormats.indy.attributes); }