From 5d2069646be6eee0cb69818d18ad6664ebd32e61 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Fri, 2 Feb 2024 16:35:10 +0530 Subject: [PATCH 001/231] refactor: added the docker shell script for spinup the agent Signed-off-by: KulkarniShashank --- Dockerfiles/Dockerfile.agent-provisioning | 16 +- .../AFJ/scripts/docker_start_agent.sh | 259 ++++++++++++++++++ .../AFJ/scripts/start_agent.sh | 2 +- 3 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 apps/agent-provisioning/AFJ/scripts/docker_start_agent.sh diff --git a/Dockerfiles/Dockerfile.agent-provisioning b/Dockerfiles/Dockerfile.agent-provisioning index 5c9c4f19d..d23333fa0 100644 --- a/Dockerfiles/Dockerfile.agent-provisioning +++ b/Dockerfiles/Dockerfile.agent-provisioning @@ -9,7 +9,12 @@ FROM node:18-alpine as build RUN npm install -g pnpm --ignore-scripts \ && apk update \ && apk add openssh-client \ - && apk add aws-cli + && apk add aws-cli \ + && apk add docker \ + && apk add docker-compose + +RUN docker --version && \ + docker-compose --version # Set the working directory WORKDIR /app @@ -40,7 +45,10 @@ FROM node:18-alpine as prod RUN npm install -g pnpm --ignore-scripts \ && apk update \ && apk add openssh-client \ - && apk add aws-cli + && apk add aws-cli \ + && apk add docker \ + && apk add docker-compose + WORKDIR /app @@ -49,6 +57,7 @@ RUN mkdir -p ./agent-provisioning/AFJ/agent-config RUN mkdir -p ./agent-provisioning/AFJ/port-file RUN mkdir -p ./agent-provisioning/AFJ/token + # Copy the compiled code COPY --from=build /app/dist/apps/agent-provisioning/ ./dist/apps/agent-provisioning/ COPY --from=build /app/node_modules ./node_modules @@ -58,15 +67,16 @@ COPY --from=build /app/apps/agent-provisioning/AFJ/port-file ./agent-provisionin # Set permissions RUN chmod +x /app/agent-provisioning/AFJ/scripts/start_agent.sh RUN chmod +x /app/agent-provisioning/AFJ/scripts/start_agent_ecs.sh +RUN chmod +x /app/agent-provisioning/AFJ/scripts/docker_start_agent.sh RUN chmod 777 /app/agent-provisioning/AFJ/endpoints RUN chmod 777 /app/agent-provisioning/AFJ/agent-config RUN chmod 777 /app/agent-provisioning/AFJ/token + # Copy the libs folder COPY libs/ ./libs/ # Set the command to run the microservice CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/agent-provisioning/main.js"] - # docker build -t agent-provisioning-service -f Dockerfiles/Dockerfile.agent-provisioning . # docker run -d --env-file .env --name agent-provisioning-service docker.io/library/agent-provisioning-service \ No newline at end of file diff --git a/apps/agent-provisioning/AFJ/scripts/docker_start_agent.sh b/apps/agent-provisioning/AFJ/scripts/docker_start_agent.sh new file mode 100644 index 000000000..520a138ad --- /dev/null +++ b/apps/agent-provisioning/AFJ/scripts/docker_start_agent.sh @@ -0,0 +1,259 @@ +#!/bin/sh + +START_TIME=$(date +%s) + +AGENCY=$1 +EXTERNAL_IP=$2 +WALLET_NAME=$3 +WALLET_PASSWORD=$4 +RANDOM_SEED=$5 +WEBHOOK_HOST=$6 +WALLET_STORAGE_HOST=$7 +WALLET_STORAGE_PORT=$8 +WALLET_STORAGE_USER=$9 +WALLET_STORAGE_PASSWORD=${10} +CONTAINER_NAME=${11} +PROTOCOL=${12} +TENANT=${13} +AFJ_VERSION=${14} +INDY_LEDGER=${15} + +echo "AGENCY: $AGENCY" +echo "EXTERNAL_IP: $EXTERNAL_IP" +echo "WALLET_NAME: $WALLET_NAME" +echo "WALLET_PASSWORD: $WALLET_PASSWORD" +echo "RANDOM_SEED: $RANDOM_SEED" +echo "WEBHOOK_HOST: $WEBHOOK_HOST" +echo "WALLET_STORAGE_HOST: $WALLET_STORAGE_HOST" +echo "WALLET_STORAGE_PORT: $WALLET_STORAGE_PORT" +echo "WALLET_STORAGE_USER: $WALLET_STORAGE_USER" +echo "WALLET_STORAGE_PASSWORD: $WALLET_STORAGE_PASSWORD" +echo "CONTAINER_NAME: $CONTAINER_NAME" +echo "PROTOCOL: $PROTOCOL" +echo "TENANT: $TENANT" +echo "AFJ_VERSION: $AFJ_VERSION" +echo "INDY_LEDGER: $INDY_LEDGER" + +ADMIN_PORT_FILE="$PWD/agent-provisioning/AFJ/port-file/last-admin-port.txt" +INBOUND_PORT_FILE="$PWD/agent-provisioning/AFJ/port-file/last-inbound-port.txt" +ADMIN_PORT=8001 +INBOUND_PORT=9001 + +increment_port() { + local port="$1" + local lower_limit="$2" + + while [ "$port" -le "$lower_limit" ]; do + port=$((port + 1)) # Increment the port using arithmetic expansion + done + + echo "$port" +} + +# Check if admin port file exists and if not, create and initialize it +if [ ! -e "$ADMIN_PORT_FILE" ]; then + echo "$ADMIN_PORT" > "$ADMIN_PORT_FILE" +fi + +# Read the last used admin port number from the file +last_used_admin_port=$(cat "$ADMIN_PORT_FILE") +echo "Last used admin port: $last_used_admin_port" + +# Increment the admin port number starting from the last used port +last_used_admin_port=$(increment_port "$last_used_admin_port" "$last_used_admin_port") + +# Save the updated admin port number back to the file and update the global variable +echo "$last_used_admin_port" > "$ADMIN_PORT_FILE" +ADMIN_PORT="$last_used_admin_port" + +# Check if inbound port file exists and if not, create and initialize it +if [ ! -e "$INBOUND_PORT_FILE" ]; then + echo "$INBOUND_PORT" > "$INBOUND_PORT_FILE" +fi + +# Read the last used inbound port number from the file +last_used_inbound_port=$(cat "$INBOUND_PORT_FILE") +echo "Last used inbound port: $last_used_inbound_port" + +# Increment the inbound port number starting from the last used port +last_used_inbound_port=$(increment_port "$last_used_inbound_port" "$last_used_inbound_port") + +# Save the updated inbound port number back to the file and update the global variable +echo "$last_used_inbound_port" > "$INBOUND_PORT_FILE" +INBOUND_PORT="$last_used_inbound_port" + +echo "Last used admin port: $ADMIN_PORT" +echo "Last used inbound port: $INBOUND_PORT" + +echo "AGENT SPIN-UP STARTED" + +if [ -d "${PWD}/agent-provisioning/AFJ/endpoints" ]; then + echo "Endpoints directory exists." +else + echo "Error: Endpoints directory does not exists." + mkdir ${PWD}/agent-provisioning/AFJ/endpoints +fi + +if [ -d "${PWD}/agent-provisioning/AFJ/agent-config" ]; then + echo "Endpoints directory exists." +else + echo "Error: Endpoints directory does not exists." + mkdir ${PWD}/agent-provisioning/AFJ/agent-config +fi + +AGENT_ENDPOINT="${PROTOCOL}://${EXTERNAL_IP}:${INBOUND_PORT}" + +echo "-----$AGENT_ENDPOINT----" +CONFIG_FILE="${PWD}/agent-provisioning/AFJ/agent-config/${AGENCY}_${CONTAINER_NAME}.json" + +echo "--CONFIG_FILE----${CONFIG_FILE}" + +# Check if the file exists +if [ -f "$CONFIG_FILE" ]; then + # If it exists, remove the file + rm "$CONFIG_FILE" +fi + + +cat <${CONFIG_FILE} +{ + "label": "${AGENCY}_${CONTAINER_NAME}", + "walletId": "$WALLET_NAME", + "walletKey": "$WALLET_PASSWORD", + "walletType": "postgres", + "walletUrl": "$WALLET_STORAGE_HOST:$WALLET_STORAGE_PORT", + "walletAccount": "$WALLET_STORAGE_USER", + "walletPassword": "$WALLET_STORAGE_PASSWORD", + "walletAdminAccount": "$WALLET_STORAGE_USER", + "walletAdminPassword": "$WALLET_STORAGE_PASSWORD", + "walletScheme": "DatabasePerWallet", + "indyLedger": $INDY_LEDGER, + "endpoint": [ + "$AGENT_ENDPOINT" + ], + "autoAcceptConnections": true, + "autoAcceptCredentials": "contentApproved", + "autoAcceptProofs": "contentApproved", + "logLevel": 5, + "inboundTransport": [ + { + "transport": "$PROTOCOL", + "port": "$INBOUND_PORT" + } + ], + "outboundTransport": [ + "$PROTOCOL" + ], + "webhookUrl": "$WEBHOOK_HOST/wh/$AGENCY", + "adminPort": "$ADMIN_PORT", + "tenancy": $TENANT +} +EOF + +FILE_NAME="docker-compose_${AGENCY}_${CONTAINER_NAME}.yaml" + +DOCKER_COMPOSE="${PWD}/agent-provisioning/AFJ/${FILE_NAME}" + +# Check if the file exists +if [ -f "$DOCKER_COMPOSE" ]; then + # If it exists, remove the file + rm "$DOCKER_COMPOSE" +fi +cat <${DOCKER_COMPOSE} +version: '3' + +services: + agent: + image: $AFJ_VERSION + + container_name: ${AGENCY}_${CONTAINER_NAME} + restart: always + environment: + AFJ_REST_LOG_LEVEL: 1 + ports: + - ${INBOUND_PORT}:${INBOUND_PORT} + - ${ADMIN_PORT}:${ADMIN_PORT} + + volumes: + - ./agent-config/${AGENCY}_${CONTAINER_NAME}.json:/config.json + + command: --auto-accept-connections --config /config.json + +volumes: + pgdata: + agent-indy_client: + agent-tmp: +EOF + +if [ $? -eq 0 ]; then + cd agent-provisioning/AFJ + echo "docker-compose generated successfully!" + echo "=================" + echo "spinning up the container" + echo "=================" + echo "container-name::::::${CONTAINER_NAME}" + echo "file-name::::::$FILE_NAME" + + docker-compose -f $FILE_NAME up -d + if [ $? -eq 0 ]; then + + n=0 + until [ "$n" -ge 6 ]; do + if netstat -tln | grep ${ADMIN_PORT} >/dev/null; then + + AGENTURL="http://${EXTERNAL_IP}:${ADMIN_PORT}/agent" + agentResponse=$(curl -s -o /dev/null -w "%{http_code}" $AGENTURL) + + if [ "$agentResponse" = "200" ]; then + echo "Agent is running" && break + else + echo "Agent is not running" + n=$((n + 1)) + sleep 10 + fi + else + echo "No response from agent" + n=$((n + 1)) + sleep 10 + fi + done + + echo "Creating agent config" + # Capture the logs from the container + container_logs=$(docker logs $(docker ps -q --filter "name=${AGENCY}_${CONTAINER_NAME}")) + + # Extract the token from the logs using sed + token=$(echo "$container_logs" | sed -nE 's/.*API Toekn: ([^ ]+).*/\1/p') + + # Print the extracted token + echo "Token: $token" + + ENDPOINT="${PWD}/endpoints/${AGENCY}_${CONTAINER_NAME}.json" + + # Check if the file exists + if [ -f "$ENDPOINT" ]; then + # If it exists, remove the file + rm "$ENDPOINT" + fi + cat <${ENDPOINT} + { + "CONTROLLER_ENDPOINT":"${EXTERNAL_IP}:${ADMIN_PORT}" + } +EOF + + cat <${PWD}/token/${AGENCY}_${CONTAINER_NAME}.json + { + "token" : "$token" + } +EOF + echo "Agent config created" + else + echo "===============" + echo "ERROR : Failed to spin up the agent!" + echo "===============" && exit 125 + fi +else + echo "ERROR : Failed to execute!" && exit 125 +fi + +echo "Total time elapsed: $(date -ud "@$(($(date +%s) - $START_TIME))" +%T) (HH:MM:SS)" \ No newline at end of file diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh index ea5b17f28..79fa19fc1 100644 --- a/apps/agent-provisioning/AFJ/scripts/start_agent.sh +++ b/apps/agent-provisioning/AFJ/scripts/start_agent.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh START_TIME=$(date +%s) From ee3ab6d95cee1d6686bf1bf9cf6e45cff5a9c8ff Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Fri, 9 Feb 2024 19:44:29 +0530 Subject: [PATCH 002/231] Solved bulk issuance bug for oob issue Signed-off-by: KulkarniShashank --- apps/issuance/src/issuance.service.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index c38ccf305..a0c61cf94 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -346,7 +346,9 @@ export class IssuanceService { comment, credentialDefinitionId, orgId, - protocolVersion + protocolVersion, + attributes, + emailId } = outOfBandCredential; const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); @@ -383,7 +385,7 @@ export class IssuanceService { protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { - attributes: iterator.attributes, + attributes: iterator.attributes || attributes, credentialDefinitionId } }, @@ -468,6 +470,8 @@ export class IssuanceService { const batchPromises = batch.map((iterator, index) => sendEmailForCredentialOffer(iterator, iterator.emailId, index)); emailPromises.push(Promise.all(batchPromises)); } + } else { + emailPromises.push(sendEmailForCredentialOffer({}, emailId, 1)); } const results = await Promise.all(emailPromises); From 96b301df898d1bb546a3816b62dd9e77d09652fc Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Fri, 9 Feb 2024 21:57:23 +0530 Subject: [PATCH 003/231] fix:not able to create cred-def after initialy rejecting Signed-off-by: pranalidhanavade --- apps/ecosystem/src/ecosystem.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 10236182f..55010e785 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -766,8 +766,13 @@ export class EcosystemService { this.ecosystemRepository.getAgentDetails(getEcosystemLeadDetails.orgId), this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); + + const existsCredDef = credDefRequestExist?.filter(tag => tag.status === endorsementTransactionStatus.REQUESTED || + tag.status === endorsementTransactionStatus.SIGNED || + tag.status === endorsementTransactionStatus.SUBMITED + ) ?? []; - if (0 !== credDefRequestExist.length) { + if (0 < existsCredDef.length) { throw new ConflictException(ResponseMessages.ecosystem.error.credDefAlreadyExist); } From 0b1dc5d71e21e81cb96e277c16b76dc267b46fe9 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Tue, 13 Feb 2024 13:19:08 +0530 Subject: [PATCH 004/231] feat: replace existing file with new file while bulk issuance Signed-off-by: bhavanakarwade --- .../src/issuance/issuance.controller.ts | 55 ++++++++++++++++--- .../src/issuance/issuance.service.ts | 6 +- apps/issuance/src/issuance.controller.ts | 4 +- apps/issuance/src/issuance.service.ts | 24 +++++--- 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 745c8716e..47b0b49bd 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -232,7 +232,7 @@ export class IssuanceController { const reqPayload: RequestPayload = { credDefId: credentialDefinitionId, fileKey, - fileName: fileDetails['fileName'].split('.csv')[0] + fileName: file.filename || file.originalname }; const importCsvDetails = await this.issueCredentialService.importCsv(reqPayload); @@ -326,21 +326,60 @@ export class IssuanceController { summary: 'bulk issue credential', description: 'bulk issue credential' }) + @ApiConsumes('multipart/form-data') + @ApiBody({ + schema: { + type: 'object', + nullable: false, + required: ['file'], + properties: { + file: { + type: 'string', + format: 'binary' + } + } + }, + required: true + }) + @UseInterceptors(FileInterceptor('file')) + async issueBulkCredentials( @Param('requestId') requestId: string, @Param('orgId') orgId: string, + @Query('credDefId') credentialDefinitionId: string, + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + @Body() fileDetails: any, + @UploadedFile() file: Express.Multer.File, @Res() res: Response, @Body() clientDetails: ClientDetails, @User() user: user ): Promise { clientDetails.userId = user.id; - const bulkIssuanceDetails = await this.issueCredentialService.issueBulkCredential(requestId, orgId, clientDetails); - const finalResponse: IResponseType = { - statusCode: HttpStatus.CREATED, - message: ResponseMessages.issuance.success.bulkIssuance, - data: bulkIssuanceDetails.response - }; - return res.status(HttpStatus.CREATED).json(finalResponse); + if (file) { + const fileKey: string = uuidv4(); + try { + + await this.awsService.uploadCsvFile(fileKey, file.buffer); + } catch (error) { + throw new RpcException(error.response ? error.response : error); + } + + const reqPayload: RequestPayload = { + credDefId: credentialDefinitionId, + fileKey, + fileName: file?.filename || file.originalname || fileDetails?.file?.filename + }; + + const bulkIssuanceDetails = await this.issueCredentialService.issueBulkCredential(requestId, orgId, clientDetails, reqPayload); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.issuance.success.bulkIssuance, + data: bulkIssuanceDetails + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + + } } @Get('/orgs/:orgId/bulk/files') diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index 11267f2f8..42b1f03ea 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -108,9 +108,9 @@ export class IssuanceService extends BaseService { return this.sendNats(this.issuanceProxy, 'issued-file-data', payload); } - async issueBulkCredential(requestId: string, orgId: string, clientDetails: ClientDetails): Promise<{ response: object }> { - const payload = { requestId, orgId, clientDetails }; - return this.sendNats(this.issuanceProxy, 'issue-bulk-credentials', payload); + async issueBulkCredential(requestId: string, orgId: string, clientDetails: ClientDetails, reqPayload: RequestPayload): Promise { + const payload = { requestId, orgId, clientDetails, reqPayload }; + return this.sendNatsMessage(this.issuanceProxy, 'issue-bulk-credentials', payload); } async retryBulkCredential(fileId: string, orgId: string, clientId: string): Promise<{ response: object }> { diff --git a/apps/issuance/src/issuance.controller.ts b/apps/issuance/src/issuance.controller.ts index 544212ac4..aa666829d 100644 --- a/apps/issuance/src/issuance.controller.ts +++ b/apps/issuance/src/issuance.controller.ts @@ -82,8 +82,8 @@ export class IssuanceController { @MessagePattern({ cmd: 'issue-bulk-credentials' }) - async issueBulkCredentials(payload: { requestId: string, orgId: string, clientDetails: ClientDetails }): Promise { - return this.issuanceService.issueBulkCredential(payload.requestId, payload.orgId, payload.clientDetails); + async issueBulkCredentials(payload: { requestId: string, orgId: string, clientDetails: ClientDetails, reqPayload: ImportFileDetails }): Promise { + return this.issuanceService.issueBulkCredential(payload.requestId, payload.orgId, payload.clientDetails, payload.reqPayload); } @MessagePattern({ cmd: 'retry-bulk-credentials' }) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index a0c61cf94..9c2825715 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -397,6 +397,8 @@ export class IssuanceService { label: outOfBandCredential.label || undefined }; + this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); + const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, apiKey); if (!credentialCreateOfferDetails) { @@ -436,8 +438,9 @@ export class IssuanceService { disposition: 'attachment' } ]; - + const isEmailSent = await sendEmail(this.emailData); + this.logger.log(`isEmailSent ::: ${JSON.stringify(isEmailSent)}`); if (!isEmailSent) { errors.push(new InternalServerErrorException(ResponseMessages.issuance.error.emailSend)); @@ -636,7 +639,7 @@ export class IssuanceService { } - async importAndPreviewDataForIssuance(importFileDetails: ImportFileDetails): Promise { + async importAndPreviewDataForIssuance(importFileDetails: ImportFileDetails, requestId?: string): Promise { try { const credDefResponse = @@ -672,9 +675,7 @@ export class IssuanceService { // Output invalid emails if (0 < invalidEmails.length) { - throw new BadRequestException(`Invalid emails found in the chosen file`); - } const fileData: string[] = parsedData.data.map(Object.values); @@ -703,7 +704,7 @@ export class IssuanceService { const newCacheKey = uuidv4(); - await this.cacheManager.set(newCacheKey, JSON.stringify(resData), 3600); + await this.cacheManager.set(requestId ? requestId : newCacheKey, JSON.stringify(resData), 60000); return newCacheKey; @@ -806,7 +807,7 @@ export class IssuanceService { return new Promise(resolve => setTimeout(resolve, ms)); } - async issueBulkCredential(requestId: string, orgId: string, clientDetails: ClientDetails): Promise { + async issueBulkCredential(requestId: string, orgId: string, clientDetails: ClientDetails, reqPayload: ImportFileDetails): Promise { const fileUpload: { lastChangedDateTime: Date; name?: string; @@ -829,11 +830,17 @@ export class IssuanceService { } try { - const cachedData = await this.cacheManager.get(requestId); + let cachedData = await this.cacheManager.get(requestId); if (!cachedData) { throw new BadRequestException(ResponseMessages.issuance.error.cacheTimeOut); } + if (cachedData) { + await this.cacheManager.del(requestId); + await this.importAndPreviewDataForIssuance(reqPayload, requestId); + // await this.cacheManager.set(requestId, reqPayload); + cachedData = await this.cacheManager.get(requestId); + } const parsedData = JSON.parse(cachedData as string).fileData.data; const parsedPrimeDetails = JSON.parse(cachedData as string); @@ -1025,8 +1032,8 @@ export class IssuanceService { 0 === errorCount ? FileUploadStatus.completed : FileUploadStatus.partially_completed; if (!jobDetails.isRetry) { - this.cacheManager.del(jobDetails.cacheId); socket.emit('bulk-issuance-process-completed', {clientId: jobDetails.clientId, fileUploadId: jobDetails.fileUploadId}); + this.cacheManager.del(jobDetails.cacheId); } else { socket.emit('bulk-issuance-process-retry-completed', { clientId: jobDetails.clientId }); } @@ -1035,7 +1042,6 @@ export class IssuanceService { status, lastChangedDateTime: new Date() }); - } } catch (error) { this.logger.error(`Error in completing bulk issuance process: ${error}`); From 5cae39789dd9277d13e040423fbef8ebca2f60a5 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:00:09 +0530 Subject: [PATCH 005/231] fix: added logger (#508) Signed-off-by: bhavanakarwade --- apps/connection/src/connection.service.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 004a29527..b064518ed 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -55,16 +55,12 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); } - let logoImageUrl; - if (organisation.logoUrl) { - logoImageUrl = organisation.logoUrl; - } - + this.logger.log(`logoUrl:::, ${organisation.logoUrl}`); const connectionPayload = { multiUseInvitation: multiUseInvitation || true, autoAcceptConnection: autoAcceptConnection || true, alias: alias || undefined, - imageUrl: logoImageUrl ? logoImageUrl : imageUrl ? imageUrl : undefined, + imageUrl: organisation.logoUrl || imageUrl || undefined, label: organisation.name, goal: goal || undefined, goalCode: goalCode || undefined, From a698bea4bcb0b4feb566a2fbaebf77eaabdd5469 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Tue, 13 Feb 2024 17:53:05 +0530 Subject: [PATCH 006/231] feat: selective bulk issuance Signed-off-by: bhavanakarwade --- .../src/issuance/dtos/issuance.dto.ts | 12 ++++++++- .../src/issuance/issuance.controller.ts | 26 +++++++++---------- .../interfaces/issuance.interfaces.ts | 5 ++-- apps/issuance/src/issuance.controller.ts | 4 +-- apps/issuance/src/issuance.service.ts | 6 ++--- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index c51deb603..9c4a68942 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -309,6 +309,16 @@ export class ClientDetails { @Type(() => String) clientId = ''; - userId?: string; + @ApiProperty({ required: false, example: 'issue-data.csv' }) + @IsOptional() + @Type(() => String) + fileName = ''; + @ApiProperty({ required: false }) + @IsOptional() + @Type(() => Boolean) + isSelectiveIssuance?: boolean = false; + + userId?: string; + } \ No newline at end of file diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 47b0b49bd..9e8fad833 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -221,6 +221,7 @@ export class IssuanceController { @Res() res: Response ): Promise { try { + if (file) { const fileKey: string = uuidv4(); try { @@ -232,7 +233,7 @@ export class IssuanceController { const reqPayload: RequestPayload = { credDefId: credentialDefinitionId, fileKey, - fileName: file.filename || file.originalname + fileName: fileDetails['fileName'] || file?.filename || file?.originalname }; const importCsvDetails = await this.issueCredentialService.importCsv(reqPayload); @@ -346,30 +347,31 @@ export class IssuanceController { async issueBulkCredentials( @Param('requestId') requestId: string, @Param('orgId') orgId: string, - @Query('credDefId') credentialDefinitionId: string, - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - @Body() fileDetails: any, - @UploadedFile() file: Express.Multer.File, @Res() res: Response, @Body() clientDetails: ClientDetails, - @User() user: user + @User() user: user, + @Query('credDefId') credentialDefinitionId?: string, + @Body() fileDetails?: object, + @UploadedFile() file?: Express.Multer.File ): Promise { + clientDetails.userId = user.id; - if (file) { + let reqPayload: RequestPayload; + + if (file && clientDetails?.isSelectiveIssuance) { const fileKey: string = uuidv4(); try { - await this.awsService.uploadCsvFile(fileKey, file.buffer); } catch (error) { throw new RpcException(error.response ? error.response : error); } - const reqPayload: RequestPayload = { + reqPayload = { credDefId: credentialDefinitionId, fileKey, - fileName: file?.filename || file.originalname || fileDetails?.file?.filename + fileName: fileDetails['fileName'] || file?.filename || file?.originalname }; - + } const bulkIssuanceDetails = await this.issueCredentialService.issueBulkCredential(requestId, orgId, clientDetails, reqPayload); const finalResponse: IResponse = { @@ -378,8 +380,6 @@ export class IssuanceController { data: bulkIssuanceDetails }; return res.status(HttpStatus.CREATED).json(finalResponse); - - } } @Get('/orgs/:orgId/bulk/files') diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 3eeaacb74..f75aa5fc9 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -188,10 +188,11 @@ export interface FileUploadData { jobId: string; } -export interface ClientDetails { +export interface IClientDetails { clientId: string; - userId?: string; + isSelectiveIssuance?: boolean; + fileName?: string; } export interface IIssuedCredentialsSearchInterface { issuedCredentialsSearchCriteria: IIssuedCredentialsSearchCriteria; diff --git a/apps/issuance/src/issuance.controller.ts b/apps/issuance/src/issuance.controller.ts index aa666829d..1c3d0cd85 100644 --- a/apps/issuance/src/issuance.controller.ts +++ b/apps/issuance/src/issuance.controller.ts @@ -1,6 +1,6 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; -import { ClientDetails, ICreateOfferResponse, IIssuance, IIssueCredentials, IIssueCredentialsDefinitions, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOffer, PreviewRequest } from '../interfaces/issuance.interfaces'; +import { IClientDetails, ICreateOfferResponse, IIssuance, IIssueCredentials, IIssueCredentialsDefinitions, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOffer, PreviewRequest } from '../interfaces/issuance.interfaces'; import { IssuanceService } from './issuance.service'; import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto'; @@ -82,7 +82,7 @@ export class IssuanceController { @MessagePattern({ cmd: 'issue-bulk-credentials' }) - async issueBulkCredentials(payload: { requestId: string, orgId: string, clientDetails: ClientDetails, reqPayload: ImportFileDetails }): Promise { + async issueBulkCredentials(payload: { requestId: string, orgId: string, clientDetails: IClientDetails, reqPayload: ImportFileDetails }): Promise { return this.issuanceService.issueBulkCredential(payload.requestId, payload.orgId, payload.clientDetails, payload.reqPayload); } diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 9c2825715..678a2e6b8 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -9,7 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs'; // import { ClientDetails, FileUploadData, ICredentialAttributesInterface, ImportFileDetails, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; -import { ClientDetails, FileUploadData, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; +import { FileUploadData, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; import { OrgAgentType } from '@credebl/enum/enum'; // import { platform_config } from '@prisma/client'; import * as QRCode from 'qrcode'; @@ -807,7 +807,7 @@ export class IssuanceService { return new Promise(resolve => setTimeout(resolve, ms)); } - async issueBulkCredential(requestId: string, orgId: string, clientDetails: ClientDetails, reqPayload: ImportFileDetails): Promise { + async issueBulkCredential(requestId: string, orgId: string, clientDetails: IClientDetails, reqPayload: ImportFileDetails): Promise { const fileUpload: { lastChangedDateTime: Date; name?: string; @@ -835,7 +835,7 @@ export class IssuanceService { throw new BadRequestException(ResponseMessages.issuance.error.cacheTimeOut); } - if (cachedData) { + if (cachedData && clientDetails?.isSelectiveIssuance) { await this.cacheManager.del(requestId); await this.importAndPreviewDataForIssuance(reqPayload, requestId); // await this.cacheManager.set(requestId, reqPayload); From d6345e3a2ae90338d565fde46ed9caef29e9c3a8 Mon Sep 17 00:00:00 2001 From: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Date: Thu, 15 Feb 2024 00:44:56 +0530 Subject: [PATCH 007/231] Feat/oob credential issuance verification (#512) * fix: removed the unnecessary logger from the agent-service module (#419) Signed-off-by: KulkarniShashank * WIP:OOB Proof Request Signed-off-by: ankita_patidar * WIP:OOB Proof Request Signed-off-by: ankita_patidar * fix:OOB Credential Offer restore changes Signed-off-by: ankita_patidar * fix:add email as optional Signed-off-by: ankita_patidar * fix:take response from presentation request payload Signed-off-by: ankita_patidar * fix: resolved sonar lint checks Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * expose agent format of proof request to API endpoint, disabled send offer by email Signed-off-by: ankita_patidar --------- Signed-off-by: KulkarniShashank Signed-off-by: ankita_patidar Signed-off-by: bhavanakarwade Signed-off-by: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Co-authored-by: Nishad Shirsat <103021375+nishad-ayanworks@users.noreply.github.com> Co-authored-by: Nishad Co-authored-by: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Co-authored-by: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Co-authored-by: bhavanakarwade --- .../src/verification/dto/request-proof.dto.ts | 44 +++++++++++++ .../verification/verification.controller.ts | 11 +--- .../src/verification/verification.service.ts | 4 +- .../src/interfaces/verification.interface.ts | 6 +- .../src/verification.controller.ts | 6 +- apps/verification/src/verification.service.ts | 62 ++++++++----------- 6 files changed, 81 insertions(+), 52 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 9ca102123..01789e009 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -134,3 +134,47 @@ export class OutOfBandRequestProof extends ProofPayload { }) autoAcceptProof: string; } + + +interface IProofFormats { + indy: IndyProof +} + +interface IndyProof { + name: string; + version: string; + requested_attributes: IRequestedAttributes; + requested_predicates: IRequestedPredicates; +} + +interface IRequestedAttributes { + [key: string]: IRequestedAttributesName; +} + +interface IRequestedAttributesName { + name: string; + restrictions: IRequestedRestriction[] +} + +interface IRequestedPredicates { + [key: string]: IRequestedPredicatesName; +} + +interface IRequestedPredicatesName { + name: string; + restrictions: IRequestedRestriction[] +} + +interface IRequestedRestriction { + cred_def_id?: string; + schema_id?: string; +} + +export interface ISendProofRequestPayload { + protocolVersion?: string; + comment?: string; + connectionId?: string; + proofFormats: IProofFormats; + autoAcceptProof?: string; + label?: string; +} diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index 4f55727d4..e35baaba0 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -16,7 +16,7 @@ import { Controller, Logger, Post, Body, Get, Query, HttpStatus, Res, UseGuards, import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; -import { OutOfBandRequestProof, RequestProofDto } from './dto/request-proof.dto'; +import { ISendProofRequestPayload, OutOfBandRequestProof, RequestProofDto } from './dto/request-proof.dto'; import { VerificationService } from './verification.service'; import IResponseType, { IResponse } from '@credebl/common/interfaces/response.interface'; import { Response } from 'express'; @@ -254,15 +254,10 @@ export class VerificationController { async sendOutOfBandPresentationRequest( @Res() res: Response, @User() user: IUserRequest, - @Body() outOfBandRequestProof: OutOfBandRequestProof, + @Body() outOfBandRequestProof: ISendProofRequestPayload, @Param('orgId') orgId: string ): Promise { - - for (const attrData of outOfBandRequestProof.attributes) { - await this.validateAttribute(attrData); - } - - outOfBandRequestProof.orgId = orgId; + user.orgId = orgId; const result = await this.verificationService.sendOutOfBandPresentationRequest(outOfBandRequestProof, user); const finalResponse: IResponseType = { statusCode: HttpStatus.CREATED, diff --git a/apps/api-gateway/src/verification/verification.service.ts b/apps/api-gateway/src/verification/verification.service.ts index 9faea8c2b..44dffc4b4 100644 --- a/apps/api-gateway/src/verification/verification.service.ts +++ b/apps/api-gateway/src/verification/verification.service.ts @@ -1,7 +1,7 @@ import { Injectable, Inject} from '@nestjs/common'; import { ClientProxy} from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; -import { OutOfBandRequestProof, RequestProofDto } from './dto/request-proof.dto'; +import { ISendProofRequestPayload, RequestProofDto } from './dto/request-proof.dto'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { WebhookPresentationProofDto } from './dto/webhook-proof.dto'; import { IProofPresentationDetails, IProofPresentationList } from '@credebl/common/interfaces/verification.interface'; @@ -70,7 +70,7 @@ export class VerificationService extends BaseService { * @param outOfBandRequestProof * @returns Get out-of-band requested proof presentation details */ - sendOutOfBandPresentationRequest(outOfBandRequestProof: OutOfBandRequestProof, user: IUserRequest): Promise { + sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { const payload = { outOfBandRequestProof, user }; return this.sendNatsMessage(this.verificationServiceProxy, 'send-out-of-band-proof-request', payload); } diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index d35a2daaf..8e2da7579 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -84,11 +84,11 @@ interface IRequestedRestriction { } export interface ISendProofRequestPayload { - protocolVersion: string; - comment: string; + protocolVersion?: string; + comment?: string; connectionId?: string; proofFormats: IProofFormats; - autoAcceptProof: string; + autoAcceptProof?: string; label?: string; goalCode?: string; parentThreadId?: string; diff --git a/apps/verification/src/verification.controller.ts b/apps/verification/src/verification.controller.ts index 2ca29829d..34e8bd9db 100644 --- a/apps/verification/src/verification.controller.ts +++ b/apps/verification/src/verification.controller.ts @@ -1,7 +1,7 @@ import { Controller } from '@nestjs/common'; import { VerificationService } from './verification.service'; import { MessagePattern } from '@nestjs/microservices'; -import { IProofPresentation, IProofPresentationData, IProofRequests, IRequestProof } from './interfaces/verification.interface'; +import { IProofPresentation, IProofPresentationData, IProofRequests, IRequestProof, ISendProofRequestPayload } from './interfaces/verification.interface'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { presentations } from '@prisma/client'; import { IProofPresentationDetails, IProofPresentationList } from '@credebl/common/interfaces/verification.interface'; @@ -63,8 +63,8 @@ export class VerificationController { } @MessagePattern({ cmd: 'send-out-of-band-proof-request' }) - async sendOutOfBandPresentationRequest(payload: { outOfBandRequestProof: IRequestProof, user: IUserRequest }): Promise { - return this.verificationService.sendOutOfBandPresentationRequest(payload.outOfBandRequestProof); + async sendOutOfBandPresentationRequest(payload: { outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest }): Promise { + return this.verificationService.sendOutOfBandPresentationRequest(payload.outOfBandRequestProof, payload.user); } @MessagePattern({ cmd: 'get-verified-proof-details' }) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index a63fcf1b4..c85fae1bc 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -333,20 +333,18 @@ export class VerificationService { * @param outOfBandRequestProof * @returns Get requested proof presentation details */ - async sendOutOfBandPresentationRequest(outOfBandRequestProof: IRequestProof): Promise { + async sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { try { this.logger.log(`-------outOfBandRequestProof------${JSON.stringify(outOfBandRequestProof)}`); - const comment = outOfBandRequestProof.comment || ''; - const protocolVersion = outOfBandRequestProof.protocolVersion || 'v1'; - const autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'never'; + outOfBandRequestProof.protocolVersion = outOfBandRequestProof.protocolVersion || 'v1'; + outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; - const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); + // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); - - const [getAgentDetails, organizationDetails] = await Promise.all([ - this.verificationRepository.getAgentEndPoint(outOfBandRequestProof.orgId), - this.verificationRepository.getOrganization(outOfBandRequestProof.orgId) + const [getAgentDetails] = await Promise.all([ + this.verificationRepository.getAgentEndPoint(user.orgId), + this.verificationRepository.getOrganization(user.orgId) ]); const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); @@ -355,39 +353,31 @@ export class VerificationService { const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); this.logger.log(`cachedApiKey----${apiKey}`); if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(outOfBandRequestProof.orgId); + apiKey = await this._getOrgAgentApiKey(user.orgId); } const payload: IProofRequestPayload = { apiKey, url, - proofRequestPayload: { - protocolVersion, - comment, - label: organizationDetails?.name, - proofFormats: { - indy: { - name: 'Proof Request', - version: '1.0', - requested_attributes: requestedAttributes, - requested_predicates: requestedPredicates - } - }, - autoAcceptProof, - goalCode: outOfBandRequestProof.goalCode || undefined, - parentThreadId: outOfBandRequestProof.parentThreadId || undefined, - willConfirm: outOfBandRequestProof.willConfirm || undefined - } + proofRequestPayload: outOfBandRequestProof }; - - if (outOfBandRequestProof.emailId) { - const batchSize = 100; // Define the batch size according to your needs - const { emailId } = outOfBandRequestProof; // Assuming it's an array - await this.sendEmailInBatches(payload, emailId, getAgentDetails, organizationDetails, batchSize); - return true; - } else { - return this.generateOOBProofReq(payload, getAgentDetails); - } + + const getProofPresentation = await this._sendOutOfBandProofRequest(payload); + this.logger.log(`-----getProofPresentation---${JSON.stringify(getProofPresentation)}`); + if (!getProofPresentation) { + throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); + } + return getProofPresentation.response; + + // Unused code : to be segregated + // if (outOfBandRequestProof.emailId) { + // const batchSize = 100; // Define the batch size according to your needs + // const { emailId } = outOfBandRequestProof; // Assuming it's an array + // await this.sendEmailInBatches(payload, emailId, getAgentDetails, organizationDetails, batchSize); + // return true; + // } else { + // return this.generateOOBProofReq(payload, getAgentDetails); + // } } catch (error) { this.logger.error(`[sendOutOfBandPresentationRequest] - error in out of band proof request : ${error.message}`); this.verificationErrorHandling(error); From effe2c26c926b61d07f3d951e7dc555fcf49354d Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Fri, 16 Feb 2024 13:18:52 +0530 Subject: [PATCH 008/231] fix: solved the edorsement credDef decline issue Signed-off-by: KulkarniShashank --- apps/ecosystem/src/ecosystem.service.ts | 420 +++++++++++++++--------- 1 file changed, 259 insertions(+), 161 deletions(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 10236182f..c4662bb06 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -30,16 +30,46 @@ import { } from '../enums/ecosystem.enum'; import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; import { EcosystemMembersPayload } from '../interfaces/ecosystemMembers.interface'; -import { CreateEcosystem, CredDefMessage, IEcosystemDashboard, LedgerDetails, OrganizationData, RequestCredDeffEndorsement, RequestSchemaEndorsement, SaveSchema, SchemaMessage, SignedTransactionMessage, TransactionPayload, saveCredDef, submitTransactionPayload, IEcosystem, EcosystemDetailsResult, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction } from '../interfaces/ecosystem.interfaces'; +import { + CreateEcosystem, + CredDefMessage, + IEcosystemDashboard, + LedgerDetails, + OrganizationData, + RequestCredDeffEndorsement, + RequestSchemaEndorsement, + SaveSchema, + SchemaMessage, + SignedTransactionMessage, + TransactionPayload, + saveCredDef, + submitTransactionPayload, + IEcosystem, + EcosystemDetailsResult, + IEcosystemInvitation, + IEcosystemInvitations, + IEditEcosystem, + IEndorsementTransaction +} from '../interfaces/ecosystem.interfaces'; import { GetAllSchemaList, GetEndorsementsPayload } from '../interfaces/endorsements.interface'; import { CommonConstants } from '@credebl/common/common.constant'; // eslint-disable-next-line camelcase -import { credential_definition, endorsement_transaction, org_agents, platform_config, schema, user } from '@prisma/client'; +import { +// eslint-disable-next-line camelcase + credential_definition, +// eslint-disable-next-line camelcase + endorsement_transaction, +// eslint-disable-next-line camelcase + org_agents, +// eslint-disable-next-line camelcase + platform_config, + schema, + user +} from '@prisma/client'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { updateEcosystemOrgsDto } from '../dtos/update-ecosystemOrgs.dto'; - @Injectable() export class EcosystemService { constructor( @@ -48,7 +78,7 @@ export class EcosystemService { private readonly logger: Logger, private readonly prisma: PrismaService, @Inject(CACHE_MANAGER) private cacheService: Cache - ) { } + ) {} /** * @@ -59,50 +89,52 @@ export class EcosystemService { // eslint-disable-next-line camelcase async createEcosystem(createEcosystemDto: CreateEcosystem): Promise { try { + const ecosystemExist = await this.ecosystemRepository.checkEcosystemNameExist(createEcosystemDto.name); - const ecosystemExist = await this.ecosystemRepository.checkEcosystemNameExist(createEcosystemDto.name); - - if (ecosystemExist) { - throw new ConflictException(ResponseMessages.ecosystem.error.exists); - } + if (ecosystemExist) { + throw new ConflictException(ResponseMessages.ecosystem.error.exists); + } - const isMultiEcosystemEnabled = await this.ecosystemRepository.getSpecificEcosystemConfig( - EcosystemConfigSettings.MULTI_ECOSYSTEM - ); + const isMultiEcosystemEnabled = await this.ecosystemRepository.getSpecificEcosystemConfig( + EcosystemConfigSettings.MULTI_ECOSYSTEM + ); - if (isMultiEcosystemEnabled && 'false' === isMultiEcosystemEnabled.value) { - const ecoOrganizationList = await this.ecosystemRepository.checkEcosystemOrgs(createEcosystemDto.orgId); + if (isMultiEcosystemEnabled && 'false' === isMultiEcosystemEnabled.value) { + const ecoOrganizationList = await this.ecosystemRepository.checkEcosystemOrgs(createEcosystemDto.orgId); - for (const organization of ecoOrganizationList) { - if (organization['ecosystemRole']['name'] === EcosystemRoles.ECOSYSTEM_MEMBER) { - throw new ConflictException(ResponseMessages.ecosystem.error.ecosystemOrgAlready); + for (const organization of ecoOrganizationList) { + if (organization['ecosystemRole']['name'] === EcosystemRoles.ECOSYSTEM_MEMBER) { + throw new ConflictException(ResponseMessages.ecosystem.error.ecosystemOrgAlready); + } } } - } - const orgDetails: OrganizationData = await this.getOrganizationDetails(createEcosystemDto.orgId, createEcosystemDto.userId); + const orgDetails: OrganizationData = await this.getOrganizationDetails( + createEcosystemDto.orgId, + createEcosystemDto.userId + ); - if (!orgDetails) { - throw new NotFoundException(ResponseMessages.ecosystem.error.orgNotExist); - } + if (!orgDetails) { + throw new NotFoundException(ResponseMessages.ecosystem.error.orgNotExist); + } - if (0 === orgDetails.org_agents.length) { - throw new NotFoundException(ResponseMessages.ecosystem.error.orgDidNotExist); - } + if (0 === orgDetails.org_agents.length) { + throw new NotFoundException(ResponseMessages.ecosystem.error.orgDidNotExist); + } - const ecosystemLedgers = orgDetails.org_agents.map((agent) => agent.ledgers.id); + const ecosystemLedgers = orgDetails.org_agents.map((agent) => agent.ledgers.id); - const createEcosystem = await this.ecosystemRepository.createNewEcosystem(createEcosystemDto, ecosystemLedgers); - if (!createEcosystem) { - throw new NotFoundException(ResponseMessages.ecosystem.error.notCreated); - } - - return createEcosystem; - } catch (error) { - this.logger.error(`createEcosystem: ${error}`); + const createEcosystem = await this.ecosystemRepository.createNewEcosystem(createEcosystemDto, ecosystemLedgers); + if (!createEcosystem) { + throw new NotFoundException(ResponseMessages.ecosystem.error.notCreated); + } + + return createEcosystem; + } catch (error) { + this.logger.error(`createEcosystem: ${error}`); throw new RpcException(error.response ? error.response : error); + } } -} async getOrganizationDetails(orgId: string, userId: string): Promise { const pattern = { cmd: 'get-organization-by-id' }; @@ -125,7 +157,6 @@ export class EcosystemService { return orgData; } - /** * * @param editEcosystemDto @@ -135,46 +166,55 @@ export class EcosystemService { // eslint-disable-next-line camelcase async editEcosystem(editEcosystemDto: CreateEcosystem, ecosystemId: string): Promise { try { - const { name, description, tags, logo, autoEndorsement, userId } = editEcosystemDto; + const { name, description, tags, logo, autoEndorsement, userId } = editEcosystemDto; - const updateData: CreateEcosystem = { - lastChangedBy: userId - }; + const updateData: CreateEcosystem = { + lastChangedBy: userId + }; - if (name) { updateData.name = name; } + if (name) { + updateData.name = name; + } - if (description) { updateData.description = description; } + if (description) { + updateData.description = description; + } - if (tags) { updateData.tags = tags; } + if (tags) { + updateData.tags = tags; + } - if (logo) { updateData.logoUrl = logo; } + if (logo) { + updateData.logoUrl = logo; + } - if ('' !== autoEndorsement.toString()) { updateData.autoEndorsement = autoEndorsement; } + if ('' !== autoEndorsement.toString()) { + updateData.autoEndorsement = autoEndorsement; + } - const ecosystemExist = await this.ecosystemRepository.checkEcosystemExist(editEcosystemDto.name, ecosystemId); + const ecosystemExist = await this.ecosystemRepository.checkEcosystemExist(editEcosystemDto.name, ecosystemId); - if (0 === ecosystemExist.length) { - const ecosystemExist = await this.ecosystemRepository.checkEcosystemNameExist(editEcosystemDto.name); - if (ecosystemExist) { - throw new ConflictException(ResponseMessages.ecosystem.error.exists); + if (0 === ecosystemExist.length) { + const ecosystemExist = await this.ecosystemRepository.checkEcosystemNameExist(editEcosystemDto.name); + if (ecosystemExist) { + throw new ConflictException(ResponseMessages.ecosystem.error.exists); + } } - } - const editEcosystem = await this.ecosystemRepository.updateEcosystemById(updateData, ecosystemId); - if (!editEcosystem) { - throw new NotFoundException(ResponseMessages.ecosystem.error.update); - } + const editEcosystem = await this.ecosystemRepository.updateEcosystemById(updateData, ecosystemId); + if (!editEcosystem) { + throw new NotFoundException(ResponseMessages.ecosystem.error.update); + } - // Removed unnecessary key from object - delete editEcosystem.deletedAt; + // Removed unnecessary key from object + delete editEcosystem.deletedAt; - return editEcosystem; - } catch (error) { + return editEcosystem; + } catch (error) { this.logger.error(`In update ecosystem : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } } - /** * @@ -189,7 +229,7 @@ export class EcosystemService { if (!getAllEcosystemDetails) { throw new NotFoundException(ResponseMessages.ecosystem.error.update); } - + return getAllEcosystemDetails; } @@ -221,7 +261,7 @@ export class EcosystemService { config: endorseMemberCount.ecosystemConfigData } }; - + return dashboardDetails; } catch (error) { this.logger.error(`In ecosystem dashboard details : ${JSON.stringify(error)}`); @@ -248,7 +288,6 @@ export class EcosystemService { }); } - /** * Description: get an ecosystem invitation * @returns Get sent ecosystem invitation details @@ -267,10 +306,13 @@ export class EcosystemService { AND: [{ email: userEmail }, { status: { contains: search, mode: 'insensitive' } }] }; - const ecosystemInvitations = await this.ecosystemRepository.getEcosystemInvitationsPagination(query, pageNumber, pageSize); + const ecosystemInvitations = await this.ecosystemRepository.getEcosystemInvitationsPagination( + query, + pageNumber, + pageSize + ); for (const invitation of ecosystemInvitations.invitations) { - const ledgerNetworks = invitation.ecosystem.ledgers; const ledgerData = []; @@ -282,11 +324,9 @@ export class EcosystemService { } invitation.ecosystem.networkDetails = ledgerData; } - } return ecosystemInvitations; - } catch (error) { this.logger.error(`In error getEcosystemInvitations: ${JSON.stringify(error)}`); throw new InternalServerErrorException(error); @@ -299,16 +339,21 @@ export class EcosystemService { * @param userId * @returns */ - async createInvitation(bulkInvitationDto: BulkSendInvitationDto, userId: string, userEmail: string, orgId: string): Promise { + async createInvitation( + bulkInvitationDto: BulkSendInvitationDto, + userId: string, + userEmail: string, + orgId: string + ): Promise { const { invitations, ecosystemId } = bulkInvitationDto; const invitationResponse = []; try { const ecosystemDetails = await this.ecosystemRepository.getEcosystemDetails(ecosystemId); - if (!ecosystemDetails.ledgers - || (Array.isArray(ecosystemDetails.ledgers) - && 0 === ecosystemDetails.ledgers.length)) { - + if ( + !ecosystemDetails.ledgers || + (Array.isArray(ecosystemDetails.ledgers) && 0 === ecosystemDetails.ledgers.length) + ) { const ecosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); const ecosystemAgents = await this.ecosystemRepository.getAllAgentDetails(ecosystemLeadDetails.orgId); @@ -403,7 +448,10 @@ export class EcosystemService { throw new NotFoundException(ResponseMessages.ecosystem.error.invitationNotFound); } - const orgDetails: OrganizationData = await this.getOrganizationDetails(acceptRejectInvitation.orgId, acceptRejectInvitation.userId); + const orgDetails: OrganizationData = await this.getOrganizationDetails( + acceptRejectInvitation.orgId, + acceptRejectInvitation.userId + ); if (!orgDetails) { throw new NotFoundException(ResponseMessages.ecosystem.error.orgNotExist); @@ -533,7 +581,13 @@ export class EcosystemService { * @param ecosystemName * @returns Send invitation mail */ - async sendInviteEmailTemplate(email: string, ecosystemName: string, firstName:string, orgName:string, isUserExist: boolean): Promise { + async sendInviteEmailTemplate( + email: string, + ecosystemName: string, + firstName: string, + orgName: string, + isUserExist: boolean + ): Promise { const platformConfigData = await this.prisma.platform_config.findMany(); const urlEmailTemplate = new EcosystemInviteTemplate(); @@ -542,7 +596,13 @@ export class EcosystemService { emailData.emailTo = email; emailData.emailSubject = `Invitation to join an Ecosystem “${ecosystemName}” on CREDEBL`; - emailData.emailHtml = await urlEmailTemplate.sendInviteEmailTemplate(email, ecosystemName, firstName, orgName, isUserExist); + emailData.emailHtml = await urlEmailTemplate.sendInviteEmailTemplate( + email, + ecosystemName, + firstName, + orgName, + isUserExist + ); //Email is sent to user for the verification through emailData const isEmailSent = await sendEmail(emailData); @@ -574,12 +634,11 @@ export class EcosystemService { return false; } - async getEcoUserName(userEmail: string): Promise { const pattern = { cmd: 'get-user-by-mail' }; const payload = { email: userEmail }; - const userData = await this.ecosystemServiceProxy + const userData = await this.ecosystemServiceProxy .send(pattern, payload) .toPromise() .catch((error) => { @@ -591,9 +650,9 @@ export class EcosystemService { }, error.status ); - }); - return userData; - } + }); + return userData; + } // eslint-disable-next-line camelcase async removeEndorsementTransactionFields(transactionObject: endorsement_transaction): Promise { @@ -634,17 +693,17 @@ export class EcosystemService { const schemaVersionIndexOf = -1; - if ( - isNaN(parseFloat(version)) || - version.toString().indexOf('.') === - schemaVersionIndexOf - ) { - throw new NotAcceptableException( - ResponseMessages.schema.error.invalidVersion - ); + if (isNaN(parseFloat(version)) || version.toString().indexOf('.') === schemaVersionIndexOf) { + throw new NotAcceptableException(ResponseMessages.schema.error.invalidVersion); } - const [schemaRequestExist, ecosystemMemberDetails, platformConfig, ecosystemLeadAgentDetails, getEcosystemOrgDetailsByOrgId] = await Promise.all([ + const [ + schemaRequestExist, + ecosystemMemberDetails, + platformConfig, + ecosystemLeadAgentDetails, + getEcosystemOrgDetailsByOrgId + ] = await Promise.all([ this.ecosystemRepository.findRecordsByNameAndVersion(requestSchemaPayload?.name, requestSchemaPayload?.version), this.ecosystemRepository.getAgentDetails(orgId), this.ecosystemRepository.getPlatformConfigDetails(), @@ -652,10 +711,11 @@ export class EcosystemService { this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); - const existSchema = schemaRequestExist?.filter(schema => schema.status === endorsementTransactionStatus.REQUESTED || - schema.status === endorsementTransactionStatus.SIGNED || - schema.status === endorsementTransactionStatus.SUBMITED -) ?? []; + const existSchema = schemaRequestExist?.filter( + (schema) => schema.status === endorsementTransactionStatus.REQUESTED || + schema.status === endorsementTransactionStatus.SIGNED || + schema.status === endorsementTransactionStatus.SUBMITED + ) ?? []; if (0 < existSchema.length) { throw new ConflictException(ResponseMessages.ecosystem.error.schemaAlreadyExist); @@ -734,9 +794,7 @@ export class EcosystemService { const errorObj = error?.status?.message?.error; if (errorObj) { throw new RpcException({ - message: errorObj?.reason - ? errorObj?.reason - : errorObj, + message: errorObj?.reason ? errorObj?.reason : errorObj, statusCode: error?.status?.code }); } else { @@ -767,7 +825,13 @@ export class EcosystemService { this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); - if (0 !== credDefRequestExist.length) { + const existsCredDef = credDefRequestExist?.filter( + (tag) => tag.status === endorsementTransactionStatus.REQUESTED || + tag.status === endorsementTransactionStatus.SIGNED || + tag.status === endorsementTransactionStatus.SUBMITED + ) ?? []; + + if (0 < existsCredDef.length) { throw new ConflictException(ResponseMessages.ecosystem.error.credDefAlreadyExist); } @@ -791,66 +855,92 @@ export class EcosystemService { throw new NotFoundException(ResponseMessages.ecosystem.error.ecosystemOrgNotFound); } - const orgAgentType = await this.ecosystemRepository.getOrgAgentType(ecosystemMemberDetails.orgAgentTypeId); - const url = await this.getAgentUrl( - orgAgentType, - ecosystemMemberDetails.agentEndPoint, - endorsementTransactionType.CREDENTIAL_DEFINITION, - ecosystemMemberDetails.tenantId - ); - const apiKey = await this._getOrgAgentApiKey(orgId); - const credDefTransactionPayload = { - endorserDid: ecosystemLeadAgentDetails.orgDid, - endorse: requestCredDefPayload.endorse, - tag: requestCredDefPayload.tag, - schemaId: requestCredDefPayload.schemaId, - issuerId: ecosystemMemberDetails.orgDid - }; + let requestCredDefBody; + const credDefData = credDefRequestExist?.filter((tag) => tag.status === endorsementTransactionStatus.DECLINED); + if (0 < credDefData.length) { + let schemaTransactionResponse; + credDefRequestExist.forEach((tag) => { + requestCredDefBody = tag.requestBody; + schemaTransactionResponse = { + endorserDid: ecosystemLeadAgentDetails.orgDid, + authorDid: ecosystemMemberDetails.orgDid, + requestPayload: tag.requestPayload, + status: endorsementTransactionStatus.REQUESTED, + ecosystemOrgId: getEcosystemOrgDetailsByOrgId.id, + userId: requestCredDefPayload.userId + }; + }); + const storeTransaction = await this.ecosystemRepository.storeTransactionRequest( + schemaTransactionResponse, + requestCredDefBody, + endorsementTransactionType.CREDENTIAL_DEFINITION + ); - const credDefTransactionRequest: CredDefMessage = await this._requestCredDeffEndorsement( - credDefTransactionPayload, - url, - apiKey - ); + // To return selective response + await this.removeEndorsementTransactionFields(storeTransaction); - if ('failed' === credDefTransactionRequest.message.credentialDefinitionState.state) { - throw new InternalServerErrorException(ResponseMessages.ecosystem.error.requestCredDefTransaction); - } + await new Promise(resolve => setTimeout(resolve, 5000)); + return storeTransaction; + } else { + const orgAgentType = await this.ecosystemRepository.getOrgAgentType(ecosystemMemberDetails.orgAgentTypeId); + const url = await this.getAgentUrl( + orgAgentType, + ecosystemMemberDetails.agentEndPoint, + endorsementTransactionType.CREDENTIAL_DEFINITION, + ecosystemMemberDetails.tenantId + ); + const apiKey = await this._getOrgAgentApiKey(orgId); + const credDefTransactionPayload = { + endorserDid: ecosystemLeadAgentDetails.orgDid, + endorse: requestCredDefPayload.endorse, + tag: requestCredDefPayload.tag, + schemaId: requestCredDefPayload.schemaId, + issuerId: ecosystemMemberDetails.orgDid + }; - const requestBody = credDefTransactionRequest.message.credentialDefinitionState.credentialDefinition; + const credDefTransactionRequest: CredDefMessage = await this._requestCredDeffEndorsement( + credDefTransactionPayload, + url, + apiKey + ); - if (!requestBody) { - throw new NotFoundException(ResponseMessages.ecosystem.error.credentialDefinitionNotFound); - } + if ('failed' === credDefTransactionRequest.message.credentialDefinitionState.state) { + throw new InternalServerErrorException(ResponseMessages.ecosystem.error.requestCredDefTransaction); + } - requestCredDefPayload['credentialDefinition'] = requestBody; - const schemaTransactionResponse = { - endorserDid: ecosystemLeadAgentDetails.orgDid, - authorDid: ecosystemMemberDetails.orgDid, - requestPayload: credDefTransactionRequest.message.credentialDefinitionState.credentialDefinitionRequest, - status: endorsementTransactionStatus.REQUESTED, - ecosystemOrgId: getEcosystemOrgDetailsByOrgId.id, - userId: requestCredDefPayload.userId - }; + const requestBody = credDefTransactionRequest.message.credentialDefinitionState.credentialDefinition; - const storeTransaction = await this.ecosystemRepository.storeTransactionRequest( - schemaTransactionResponse, - requestCredDefPayload, - endorsementTransactionType.CREDENTIAL_DEFINITION - ); + if (!requestBody) { + throw new NotFoundException(ResponseMessages.ecosystem.error.credentialDefinitionNotFound); + } - // To return selective response - await this.removeEndorsementTransactionFields(storeTransaction); + requestCredDefPayload['credentialDefinition'] = requestBody; + const schemaTransactionResponse = { + endorserDid: ecosystemLeadAgentDetails.orgDid, + authorDid: ecosystemMemberDetails.orgDid, + requestPayload: credDefTransactionRequest.message.credentialDefinitionState.credentialDefinitionRequest, + status: endorsementTransactionStatus.REQUESTED, + ecosystemOrgId: getEcosystemOrgDetailsByOrgId.id, + userId: requestCredDefPayload.userId + }; - return storeTransaction; + const storeTransaction = await this.ecosystemRepository.storeTransactionRequest( + schemaTransactionResponse, + requestCredDefPayload, + endorsementTransactionType.CREDENTIAL_DEFINITION + ); + + // To return selective response + await this.removeEndorsementTransactionFields(storeTransaction); + + return storeTransaction; + } } catch (error) { this.logger.error(`In request cred-def endorsement: ${JSON.stringify(error)}`); const errorObj = error?.status?.message?.error; if (errorObj) { throw new RpcException({ - message: errorObj?.reason - ? errorObj?.reason - : errorObj, + message: errorObj?.reason ? errorObj?.reason : errorObj, statusCode: error?.status?.code }); } else { @@ -989,13 +1079,13 @@ export class EcosystemService { if (!submitTxn) { await this.ecosystemRepository.updateTransactionStatus(endorsementId, endorsementTransactionStatus.REQUESTED); throw new InternalServerErrorException(ResponseMessages.ecosystem.error.sumbitTransaction); - } + } return { - autoEndorsement:ecosystemDetails.autoEndorsement, + autoEndorsement: ecosystemDetails.autoEndorsement, submitTxn - }; + }; } - + // To return selective response await this.removeEndorsementTransactionFields(updateSignedTransaction); @@ -1022,13 +1112,18 @@ export class EcosystemService { async getEcoystemMembers(payload: EcosystemMembersPayload): Promise { try { - const { ecosystemId, pageNumber, pageSize, search, sortBy } = payload; - const getEcosystemMember = await this.ecosystemRepository.findEcosystemMembers(ecosystemId, pageNumber, pageSize, search, sortBy); - + const { ecosystemId, pageNumber, pageSize, search, sortBy } = payload; + const getEcosystemMember = await this.ecosystemRepository.findEcosystemMembers( + ecosystemId, + pageNumber, + pageSize, + search, + sortBy + ); + const ecosystemMemberResponse = { totalItems: getEcosystemMember[1], - hasNextPage: - payload.pageSize * payload.pageNumber < getEcosystemMember[1], + hasNextPage: payload.pageSize * payload.pageNumber < getEcosystemMember[1], hasPreviousPage: 1 < payload.pageNumber, nextPage: Number(payload.pageNumber) + 1, previousPage: payload.pageNumber - 1, @@ -1346,7 +1441,8 @@ export class EcosystemService { if (!resourceId) { throw new Error( - `${ResponseMessages.ecosystem.error.invalidTransactionMessage} Missing "${transactionType === endorsementTransactionType.SCHEMA ? 'schemaId' : 'credentialDefinitionId' + `${ResponseMessages.ecosystem.error.invalidTransactionMessage} Missing "${ + transactionType === endorsementTransactionType.SCHEMA ? 'schemaId' : 'credentialDefinitionId' }" property.` ); } @@ -1510,10 +1606,10 @@ export class EcosystemService { async declineEndorsementRequestByLead(ecosystemId: string, endorsementId: string): Promise { try { const declineResponse = await this.ecosystemRepository.updateEndorsementRequestStatus(ecosystemId, endorsementId); - + // To return selective response this.removeEndorsementTransactionFields(declineResponse); - + return declineResponse; } catch (error) { this.logger.error(`error in decline endorsement request: ${error}`); @@ -1521,7 +1617,6 @@ export class EcosystemService { } } - async _getOrgAgentApiKey(orgId: string): Promise { const pattern = { cmd: 'get-org-agent-api-key' }; const payload = { orgId }; @@ -1532,10 +1627,13 @@ export class EcosystemService { return message; } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException({ - status: error.status, - error: error.message - }, error.status); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); } } } From 3ee66b1d608f7aa92e9c14a89d2aaf40ec074556 Mon Sep 17 00:00:00 2001 From: pallavicoder Date: Fri, 16 Feb 2024 18:50:14 +0530 Subject: [PATCH 009/231] refactor: added proof data property inside proof webhook DTO Signed-off-by: pallavicoder --- apps/api-gateway/src/verification/dto/webhook-proof.dto.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/api-gateway/src/verification/dto/webhook-proof.dto.ts b/apps/api-gateway/src/verification/dto/webhook-proof.dto.ts index 45564ddfc..960fbbf13 100644 --- a/apps/api-gateway/src/verification/dto/webhook-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/webhook-proof.dto.ts @@ -64,4 +64,8 @@ export class WebhookPresentationProofDto { @ApiPropertyOptional() @IsOptional() type: string; + + @ApiPropertyOptional() + @IsOptional() + proofData: object; } \ No newline at end of file From 0766e9ef9be2c9b4cafb72f9a8369703286dcdd4 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Mon, 19 Feb 2024 16:59:16 +0530 Subject: [PATCH 010/231] feat:added required field in create schema and create endorsement api Signed-off-by: pranalidhanavade --- .../api-gateway/src/dtos/create-schema.dto.ts | 11 +++- .../src/ecosystem/dtos/request-schema.dto.ts | 37 ++++++++---- .../src/issuance/dtos/issuance.dto.ts | 31 +++++++--- .../interfaces/issuance.interfaces.ts | 2 + apps/issuance/src/issuance.service.ts | 59 ++++++++++++++++++- .../interfaces/schema-payload.interface.ts | 4 +- apps/ledger/src/schema/schema.service.ts | 15 ++++- libs/common/src/response-messages/index.ts | 3 +- 8 files changed, 135 insertions(+), 27 deletions(-) diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index 7b83fbd9f..fa246beec 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -1,4 +1,4 @@ -import { IsArray, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator'; +import { ArrayMinSize, IsArray, IsBoolean, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; @@ -23,6 +23,11 @@ class AttributeValue { @Transform(({ value }) => trim(value)) @IsNotEmpty({ message: 'displayName is required' }) displayName: string; + + @ApiProperty() + @IsBoolean() + @IsNotEmpty({ message: 'isRequired property is required' }) + isRequired: boolean; } export class CreateSchemaDto { @@ -44,12 +49,14 @@ export class CreateSchemaDto { { attributeName: 'name', schemaDataType: 'string', - displayName: 'Name' + displayName: 'Name', + isRequired: 'true' } ] }) @IsArray({ message: 'attributes must be an array' }) @IsNotEmpty({ message: 'attributes are required' }) + @ArrayMinSize(1) @ValidateNested({ each: true }) @Type(() => AttributeValue) attributes: AttributeValue[]; diff --git a/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts b/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts index f39e29f0c..194c12612 100644 --- a/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts @@ -1,24 +1,34 @@ -import { ApiExtraModels, ApiProperty } from '@nestjs/swagger'; +import { ApiProperty } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; -import { IsArray, IsBoolean, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator'; +import { ArrayMinSize, IsArray, IsBoolean, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator'; import { trim } from '@credebl/common/cast.helper'; -@ApiExtraModels() - -class AttributeValue { +class AttributeValues { + @ApiProperty() @IsString() - @IsNotEmpty({ message: 'attributeName is required.' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'attributeName is required' }) attributeName: string; + @ApiProperty() @IsString() - @IsNotEmpty({ message: 'schemaDataType is required.' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'schemaDataType is required' }) schemaDataType: string; + @ApiProperty() @IsString() - @IsNotEmpty({ message: 'displayName is required.' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'displayName is required' }) displayName: string; + + @ApiProperty() + @IsBoolean() + @IsNotEmpty({ message: 'isRequired property is required' }) + isRequired: boolean; + } @@ -37,17 +47,22 @@ export class RequestSchemaDto { version: string; @ApiProperty({ + type: [AttributeValues], 'example': [ { attributeName: 'name', schemaDataType: 'string', - displayName: 'Name' + displayName: 'Name', + isRequired: 'true' } ] }) @IsArray({ message: 'attributes must be an array' }) - @IsNotEmpty({ message: 'please provide valid attributes' }) - attributes: AttributeValue[]; + @IsNotEmpty({ message: 'attributes are required' }) + @ArrayMinSize(1) + @ValidateNested({ each: true }) + @Type(() => AttributeValues) + attributes: AttributeValues[]; @ApiProperty() @IsBoolean({ message: 'endorse must be a boolean.' }) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 9c4a68942..0d81ebe75 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -19,6 +19,12 @@ class Attribute { @IsDefined() @Transform(({ value }) => trim(value)) value: string; + + @ApiProperty() + @IsBoolean() + @IsNotEmpty({ message: 'isRequired property is required' }) + isRequired: boolean; + } class CredentialsIssuanceDto { @@ -77,17 +83,24 @@ class CredentialsIssuanceDto { } export class OOBIssueCredentialDto extends CredentialsIssuanceDto { - - @ApiProperty({ example: [{ 'value': 'string', 'name': 'string' }] }) - @IsArray() - @ValidateNested({ each: true }) - @ArrayMinSize(1) - @Type(() => Attribute) - attributes: Attribute[]; + @ApiProperty({ + example: [ + { + value: 'string', + name: 'string', + isRequired: 'boolean' + } + ] + }) + @IsArray() + @ValidateNested({ each: true }) + @ArrayMinSize(1) + @Type(() => Attribute) + attributes: Attribute[]; } class CredentialOffer { - @ApiProperty({ example: [{ 'value': 'string', 'name': 'string' }] }) + @ApiProperty({ example: [{ 'value': 'string', 'name': 'string', 'isRequired':'boolean' }] }) @IsNotEmpty({ message: 'Attribute name is required' }) @IsArray({ message: 'Attributes should be an array' }) @ValidateNested({ each: true }) @@ -199,7 +212,7 @@ export class CredentialAttributes { } export class OOBCredentialDtoWithEmail { - @ApiProperty({ example: [{ 'emailId': 'abc@example.com', 'attributes': [{ 'value': 'string', 'name': 'string' }] }] }) + @ApiProperty({ example: [{ 'emailId': 'abc@example.com', 'attributes': [{ 'value': 'string', 'name': 'string', 'isRequired':'boolean' }] }] }) @IsNotEmpty({ message: 'Please provide valid attributes' }) @IsArray({ message: 'attributes should be array' }) @ArrayMaxSize(Number(process.env.OOB_BATCH_SIZE), { message: `Limit reached (${process.env.OOB_BATCH_SIZE} credentials max). Easily handle larger batches via seamless CSV file uploads` }) diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index f75aa5fc9..829984113 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -4,6 +4,8 @@ import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-service.interface'; export interface IAttributes { + attributeName: string; + isRequired: boolean; name: string; value: string; } diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 678a2e6b8..6e56f96e5 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -53,6 +53,26 @@ export class IssuanceService { async sendCredentialCreateOffer(payload: IIssuance): Promise { try { const { orgId, credentialDefinitionId, comment, connectionId, attributes } = payload || {}; +// console.log("1234567890", attributes); + + const attrError = []; + if (0 < attributes?.length) { + attributes?.forEach((attribute, i) => { + + if (attribute.isRequired && !attribute.value) { + attrError.push(`attributes.${i}.Value of "${attribute.name}" is required`); + return true; + } + + return attribute.isRequired && !attribute.value; + }); + + if (0 < attrError.length) { + throw new BadRequestException(attrError); + } + } + + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); if (!agentDetails) { @@ -118,9 +138,26 @@ export class IssuanceService { async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object; }> { try { const { orgId, credentialDefinitionId, comment, attributes, protocolVersion } = payload; + + const attrError = []; + if (0 < attributes?.length) { + attributes?.forEach((attribute, i) => { + + if (attribute.isRequired && !attribute.value) { + attrError.push(`attributes.${i}.Value of "${attribute.name}" is required`); + return true; + } + + return attribute.isRequired && !attribute.value; + }); + + if (0 < attrError.length) { + throw new BadRequestException(attrError); + } + } + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); // eslint-disable-next-line camelcase - // const platformConfig: platform_config = await this.issuanceRepository.getPlatformConfigDetails(); const { agentEndPoint } = agentDetails; if (!agentDetails) { @@ -351,6 +388,26 @@ export class IssuanceService { emailId } = outOfBandCredential; + const attrError = []; +if (0 < credentialOffer?.length) { + credentialOffer?.forEach((credential, i) => { + credential.attributes.forEach((attribute, i2) => { + + if (attribute.isRequired && !attribute.value) { + attrError.push(`credentialOffer.${i}.attributes.${i2}.Value of "${attribute.name}" is required`); + return true; + } + + return attribute.isRequired && !attribute.value; + }); + + }); + + if (0 < attrError.length) { + throw new BadRequestException(attrError); + } + } + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index 1b0c2797e..68cf783f7 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -20,9 +20,11 @@ export interface ISchema { } export interface IAttributeValue { + isRequired: boolean; attributeName: string; schemaDataType: string; - displayName: string + displayName: string; + } export interface ISchemaPayload { diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 8c967b9da..c8c1cd943 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -36,7 +36,7 @@ export class SchemaService extends BaseService { schema: ISchemaPayload, user: IUserRequestInterface, orgId: string - ): Promise { + ): Promise { let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { @@ -82,7 +82,8 @@ export class SchemaService extends BaseService { const trimmedAttributes = schema.attributes.map(attribute => ({ attributeName: attribute.attributeName.trim(), schemaDataType: attribute.schemaDataType, - displayName: attribute.displayName.trim() + displayName: attribute.displayName.trim(), + isRequired: attribute.isRequired })); @@ -122,7 +123,17 @@ export class SchemaService extends BaseService { const did = schema.orgDid?.split(':').length >= 4 ? schema.orgDid : orgDid; const orgAgentType = await this.schemaRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); + const attributeArray = trimmedAttributes.map(item => item.attributeName); + + const isRequiredAttributeExists = trimmedAttributes.some(attribute => attribute.isRequired); + + if (!isRequiredAttributeExists) { + throw new BadRequestException( + ResponseMessages.schema.error.atLeastOneRequired + ); + } + let schemaResponseFromAgentService; if (OrgAgentType.DEDICATED === orgAgentType) { const issuerId = did; diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index ecea0f3a2..766d0cd33 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -156,7 +156,8 @@ export const ResponseMessages = { credentialDefinitionNotFound: 'No credential definition exist', notStoredCredential: 'User credential not stored', agentDetailsNotFound: 'Agent details not found', - failedFetchSchema: 'Failed to fetch schema data' + failedFetchSchema: 'Failed to fetch schema data', + atLeastOneRequired: 'At least one of the attributes should have isReuired as `true`' } }, credentialDefinition: { From b48719297836d937d2592dcec086dad9d52c3f7c Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Mon, 19 Feb 2024 17:11:26 +0530 Subject: [PATCH 011/231] removed console statement Signed-off-by: pranalidhanavade --- apps/issuance/src/issuance.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 6e56f96e5..68ca23842 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -53,7 +53,6 @@ export class IssuanceService { async sendCredentialCreateOffer(payload: IIssuance): Promise { try { const { orgId, credentialDefinitionId, comment, connectionId, attributes } = payload || {}; -// console.log("1234567890", attributes); const attrError = []; if (0 < attributes?.length) { From 1bb636e4e698ea8662c63d9affaa3dfecb6776ab Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Mon, 19 Feb 2024 17:24:16 +0530 Subject: [PATCH 012/231] fixed:sonarlint errors Signed-off-by: pranalidhanavade --- .../src/ecosystem/dtos/request-schema.dto.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts b/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts index 194c12612..d7d496859 100644 --- a/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts @@ -12,12 +12,6 @@ class AttributeValues { @IsNotEmpty({ message: 'attributeName is required' }) attributeName: string; - @ApiProperty() - @IsString() - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'schemaDataType is required' }) - schemaDataType: string; - @ApiProperty() @IsString() @Transform(({ value }) => trim(value)) @@ -29,6 +23,12 @@ class AttributeValues { @IsNotEmpty({ message: 'isRequired property is required' }) isRequired: boolean; + @ApiProperty() + @IsString() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'schemaDataType is required' }) + schemaDataType: string; + } From f2374d196c63228ee644f0948017eaab67275597 Mon Sep 17 00:00:00 2001 From: Nishad Date: Mon, 19 Feb 2024 19:26:01 +0530 Subject: [PATCH 013/231] Refactored org roles API to fetch roles from keycloak, worked on user registration as holder, worked on the create org invitation Signed-off-by: Nishad --- .../organization/organization.controller.ts | 18 +- .../src/organization/organization.service.ts | 14 +- .../src/organization.controller.ts | 14 +- apps/organization/src/organization.service.ts | 149 +++++++++++--- apps/user/interfaces/user.interface.ts | 1 + apps/user/repositories/user.repository.ts | 2 + apps/user/src/user.service.ts | 17 +- .../src/client-registration.service.ts | 194 ++++++++++++++++-- .../src/interfaces/client.interface.ts | 8 + libs/common/src/response-messages/index.ts | 2 +- libs/keycloak-url/src/keycloak-url.service.ts | 42 ++++ .../interfaces/org-roles.interface.ts | 8 +- 12 files changed, 401 insertions(+), 68 deletions(-) create mode 100644 libs/client-registration/src/interfaces/client.interface.ts diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 20635a1f9..e9f5b82fc 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -95,7 +95,7 @@ export class OrganizationController { * @returns get organization roles */ - @Get('/roles') + @Get('/:orgId/roles') @ApiOperation({ summary: 'Fetch org-roles details', description: 'Fetch org-roles details' @@ -103,9 +103,9 @@ export class OrganizationController { @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @UseGuards(AuthGuard('jwt')) @ApiBearerAuth() - async getOrgRoles(@Res() res: Response): Promise { + async getOrgRoles(@Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, @Res() res: Response): Promise { - const orgRoles = await this.organizationService.getOrgRoles(); + const orgRoles = await this.organizationService.getOrgRoles(orgId.trim()); const finalResponse: IResponse = { statusCode: HttpStatus.OK, @@ -327,7 +327,11 @@ export class OrganizationController { @UseGuards(AuthGuard('jwt')) @ApiBearerAuth() async createOrganization(@Body() createOrgDto: CreateOrganizationDto, @Res() res: Response, @User() reqUser: user): Promise { - const orgData = await this.organizationService.createOrganization(createOrgDto, reqUser.id); + + // eslint-disable-next-line prefer-destructuring + const keycloakUserId = reqUser.keycloakUserId; + + const orgData = await this.organizationService.createOrganization(createOrgDto, reqUser.id, keycloakUserId); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, message: ResponseMessages.organisation.success.create, @@ -350,7 +354,11 @@ export class OrganizationController { @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @ApiBearerAuth() async createOrgCredentials(@Param('orgId') orgId: string, @Res() res: Response, @User() reqUser: user): Promise { - const orgCredentials = await this.organizationService.createOrgCredentials(orgId, reqUser.id); + + // eslint-disable-next-line prefer-destructuring + const keycloakUserId = reqUser.keycloakUserId; + + const orgCredentials = await this.organizationService.createOrgCredentials(orgId, reqUser.id, keycloakUserId); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, message: ResponseMessages.organisation.success.orgCredentials, diff --git a/apps/api-gateway/src/organization/organization.service.ts b/apps/api-gateway/src/organization/organization.service.ts index 1e4d377d8..68277b7dd 100644 --- a/apps/api-gateway/src/organization/organization.service.ts +++ b/apps/api-gateway/src/organization/organization.service.ts @@ -6,7 +6,6 @@ import { CreateOrganizationDto } from './dtos/create-organization-dto'; import { BulkSendInvitationDto } from './dtos/send-invitation.dto'; import { UpdateUserRolesDto } from './dtos/update-user-roles.dto'; import { UpdateOrganizationDto } from './dtos/update-organization-dto'; -import { IOrgRoles } from 'libs/org-roles/interfaces/org-roles.interface'; import { organisation } from '@prisma/client'; import { IGetOrgById, IGetOrganization } from 'apps/organization/interfaces/organization.interface'; import { IOrgUsers } from 'apps/user/interfaces/user.interface'; @@ -14,6 +13,7 @@ import { IOrgCredentials, IOrganization, IOrganizationInvitations, IOrganization import { ClientCredentialsDto } from './dtos/client-credentials.dto'; import { IAccessTokenData } from '@credebl/common/interfaces/interface'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; +import { IClientRoles } from '@credebl/client-registration/interfaces/client.interface'; @Injectable() export class OrganizationService extends BaseService { @@ -26,8 +26,8 @@ export class OrganizationService extends BaseService { * @param createOrgDto * @returns Organization creation Success */ - async createOrganization(createOrgDto: CreateOrganizationDto, userId: string): Promise { - const payload = { createOrgDto, userId }; + async createOrganization(createOrgDto: CreateOrganizationDto, userId: string, keycloakUserId: string): Promise { + const payload = { createOrgDto, userId, keycloakUserId }; return this.sendNatsMessage(this.serviceProxy, 'create-organization', payload); } @@ -37,8 +37,8 @@ export class OrganizationService extends BaseService { * @param userId * @returns Orgnization client credentials */ - async createOrgCredentials(orgId: string, userId: string): Promise { - const payload = { orgId, userId }; + async createOrgCredentials(orgId: string, userId: string, keycloakUserId: string): Promise { + const payload = { orgId, userId, keycloakUserId }; return this.sendNatsMessage(this.serviceProxy, 'create-org-credentials', payload); } @@ -133,8 +133,8 @@ export class OrganizationService extends BaseService { * @returns get organization roles */ - async getOrgRoles(): Promise { - const payload = {}; + async getOrgRoles(orgId: string): Promise { + const payload = {orgId}; return this.sendNatsMessage(this.serviceProxy, 'get-org-roles', payload); } diff --git a/apps/organization/src/organization.controller.ts b/apps/organization/src/organization.controller.ts index 87b1c45ea..3e6ddc2c0 100644 --- a/apps/organization/src/organization.controller.ts +++ b/apps/organization/src/organization.controller.ts @@ -8,9 +8,9 @@ import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; import { IGetOrgById, IGetOrganization, IUpdateOrganization, Payload } from '../interfaces/organization.interface'; import { organisation } from '@prisma/client'; -import { IOrgRoles } from 'libs/org-roles/interfaces/org-roles.interface'; import { IOrgCredentials, IOrganizationInvitations, IOrganization, IOrganizationDashboard } from '@credebl/common/interfaces/organization.interface'; import { IAccessTokenData } from '@credebl/common/interfaces/interface'; +import { IClientRoles } from '@credebl/client-registration/interfaces/client.interface'; @Controller() export class OrganizationController { @@ -24,8 +24,8 @@ export class OrganizationController { */ @MessagePattern({ cmd: 'create-organization' }) - async createOrganization(@Body() payload: { createOrgDto: CreateOrganizationDto; userId: string }): Promise { - return this.organizationService.createOrganization(payload.createOrgDto, payload.userId); + async createOrganization(@Body() payload: { createOrgDto: CreateOrganizationDto; userId: string, keycloakUserId: string }): Promise { + return this.organizationService.createOrganization(payload.createOrgDto, payload.userId, payload.keycloakUserId); } /** @@ -34,8 +34,8 @@ export class OrganizationController { * @returns organization client credentials */ @MessagePattern({ cmd: 'create-org-credentials' }) - async createOrgCredentials(@Body() payload: { orgId: string; userId: string }): Promise { - return this.organizationService.createOrgCredentials(payload.orgId); + async createOrgCredentials(@Body() payload: { orgId: string; userId: string, keycloakUserId: string }): Promise { + return this.organizationService.createOrgCredentials(payload.orgId, payload.userId, payload.keycloakUserId); } /** @@ -113,8 +113,8 @@ export class OrganizationController { */ @MessagePattern({ cmd: 'get-org-roles' }) - async getOrgRoles(): Promise { - return this.organizationService.getOrgRoles(); + async getOrgRoles(payload: {orgId: string}): Promise { + return this.organizationService.getOrgRoles(payload.orgId); } /** diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index aa2d580cf..a52164450 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -25,11 +25,11 @@ import { map } from 'rxjs/operators'; import { Cache } from 'cache-manager'; import { AwsService } from '@credebl/aws'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { IOrgRoles } from 'libs/org-roles/interfaces/org-roles.interface'; import { IOrgCredentials, IOrganization, IOrganizationInvitations, IOrganizationDashboard } from '@credebl/common/interfaces/organization.interface'; import { ClientCredentialTokenPayloadDto } from '@credebl/client-registration/dtos/client-credential-token-payload.dto'; import { IAccessTokenData } from '@credebl/common/interfaces/interface'; +import { IClientRoles } from '@credebl/client-registration/interfaces/client.interface'; @Injectable() export class OrganizationService { constructor( @@ -53,7 +53,7 @@ export class OrganizationService { */ // eslint-disable-next-line camelcase - async createOrganization(createOrgDto: CreateOrganizationDto, userId: string): Promise { + async createOrganization(createOrgDto: CreateOrganizationDto, userId: string, keycloakUserId: string): Promise { try { const organizationExist = await this.organizationRepository.checkOrganizationNameExist(createOrgDto.name); @@ -82,9 +82,20 @@ export class OrganizationService { delete organizationDetails.orgSlug; delete organizationDetails.website; - const ownerRoleData = await this.orgRoleService.getRole(OrgRoles.OWNER); + const orgCredentials = await this.registerToKeycloak(organizationDetails.name, organizationDetails.id, keycloakUserId); + + const {clientId, idpId} = orgCredentials; + + const updateOrgData = { + clientId, + idpId + }; - await this.userOrgRoleService.createUserOrgRole(userId, ownerRoleData.id, organizationDetails.id); + const updatedOrg = await this.organizationRepository.updateOrganizationById(updateOrgData, organizationDetails.id); + + if (!updatedOrg) { + throw new InternalServerErrorException(ResponseMessages.organisation.error.credentialsNotUpdate); + } await this.userActivityService.createActivity(userId, organizationDetails.id, `${organizationDetails.name} organization created`, 'Get started with inviting users to join organization'); @@ -101,7 +112,7 @@ export class OrganizationService { * @param orgId * @returns organization client credentials */ - async createOrgCredentials(orgId: string): Promise { + async createOrgCredentials(orgId: string, userId: string, keycloakUserId: string): Promise { try { const organizationDetails = await this.organizationRepository.getOrganizationDetails(orgId); @@ -109,16 +120,34 @@ export class OrganizationService { if (!organizationDetails) { throw new ConflictException(ResponseMessages.organisation.error.orgNotFound); } - - const orgCredentials = await this.registerToKeycloak(organizationDetails.name, organizationDetails.id); - const {clientId, clientSecret, idpId} = orgCredentials; + let updateOrgData = {}; + let generatedClientSecret = ''; - const updateOrgData = { - clientId, - clientSecret: this.maskString(clientSecret), - idpId - }; + if (organizationDetails.idpId) { + + const token = await this.clientRegistrationService.getManagementToken(); + + generatedClientSecret = await this.clientRegistrationService.generateClientSecret(organizationDetails.idpId, token); + + updateOrgData = { + clientSecret: this.maskString(generatedClientSecret) + }; + + } else { + + const orgCredentials = await this.registerToKeycloak(organizationDetails.name, organizationDetails.id, keycloakUserId); + + const { clientId, idpId, clientSecret } = orgCredentials; + + generatedClientSecret = clientSecret; + + updateOrgData = { + clientId, + clientSecret: this.maskString(clientSecret), + idpId + }; + } const updatedOrg = await this.organizationRepository.updateOrganizationById(updateOrgData, orgId); @@ -126,7 +155,11 @@ export class OrganizationService { throw new InternalServerErrorException(ResponseMessages.organisation.error.credentialsNotUpdate); } - return orgCredentials; + return { + idpId: updatedOrg.idpId, + clientId: updatedOrg.clientId, + clientSecret: generatedClientSecret + }; } catch (error) { this.logger.error(`In createOrgCredentials : ${JSON.stringify(error)}`); @@ -140,9 +173,44 @@ export class OrganizationService { * @param orgId * @returns client credentials */ - async registerToKeycloak(orgName: string, orgId: string): Promise { + async registerToKeycloak(orgName: string, orgId: string, userId: string): Promise { const token = await this.clientRegistrationService.getManagementToken(); - return this.clientRegistrationService.createClient(orgName, orgId, token); + const orgDetails = await this.clientRegistrationService.createClient(orgName, orgId, token); + + const orgRolesList = [ + OrgRoles.OWNER, + OrgRoles.ADMIN, + OrgRoles.ISSUER, + OrgRoles.VERIFIER, + OrgRoles.MEMBER + ]; + + try { + for (const role of orgRolesList) { + await this.clientRegistrationService.createClientRole( + orgDetails.idpId, + token, + role, + role + ); + } + } catch (error) { + this.logger.error(`Error In creating client roles : ${JSON.stringify(error)}`); + throw new InternalServerErrorException('Unable to create client roles'); + } + + const ownerRole = await this.clientRegistrationService.getClientSpecificRoles(orgDetails.idpId, token, OrgRoles.OWNER); + + const payload = [ + { + id: ownerRole.id, + name: ownerRole.name + } + ]; + + await this.clientRegistrationService.createUserClientRole(orgDetails.idpId, token, userId, payload); + + return orgDetails; } @@ -443,9 +511,22 @@ export class OrganizationService { */ - async getOrgRoles(): Promise { + async getOrgRoles(orgId: string): Promise { try { - return this.orgRoleService.getOrgRoles(); + + if (!orgId) { + throw new BadRequestException(ResponseMessages.organisation.error.orgIdIsRequired); + } + + const organizationDetails = await this.organizationRepository.getOrganizationDetails(orgId); + + if (!organizationDetails) { + throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); + } + + const token = await this.clientRegistrationService.getManagementToken(); + + return this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); } catch (error) { this.logger.error(`In getOrgRoles : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -514,9 +595,13 @@ export class OrganizationService { const userData = await this.getUserFirstName(userEmail); const {firstName} = userData; - const orgRolesDetails = await this.orgRoleService.getOrgRolesByIds(orgRoleId); - - if (0 === orgRolesDetails.length) { + + const token = await this.clientRegistrationService.getManagementToken(); + const clientRolesList = await this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); + + const matchedRoles = clientRolesList.filter(role => orgRoleId.includes(role.id.trim())); + + if (orgRoleId.length !== matchedRoles.length) { throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); } @@ -524,10 +609,10 @@ export class OrganizationService { if (!isInvitationExist && userEmail !== invitation.email) { - await this.organizationRepository.createSendInvitation(email, String(orgId), String(userId), orgRoleId); + await this.organizationRepository.createSendInvitation(email, String(orgId), String(userId), matchedRoles.map(role => role.id)); try { - await this.sendInviteEmailTemplate(email, organizationDetails.name, orgRolesDetails, firstName, isUserExist); + await this.sendInviteEmailTemplate(email, organizationDetails.name, matchedRoles, firstName, isUserExist); } catch (error) { throw new InternalServerErrorException(ResponseMessages.user.error.emailSend); } @@ -644,6 +729,12 @@ export class OrganizationService { throw new NotFoundException(ResponseMessages.user.error.invalidOrgId); } + const organizationDetails = await this.organizationRepository.getOrganizationDetails(orgId); + + if (!organizationDetails) { + throw new ConflictException(ResponseMessages.organisation.error.orgNotFound); + } + const invitationStatus = invitation.status as Invitation; if (!transition(invitationStatus, payload.status)) { throw new BadRequestException(`${ResponseMessages.user.error.invitationStatusUpdateInvalid} ${invitation.status}`); @@ -658,9 +749,15 @@ export class OrganizationService { if (status === Invitation.REJECTED) { return ResponseMessages.user.success.invitationReject; } - for (const roleId of invitation.orgRoles) { - await this.userOrgRoleService.createUserOrgRole(userId, roleId, orgId); - } + + const token = await this.clientRegistrationService.getManagementToken(); + + const clientRoles = await this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); + + const rolesPayload: { id: string; name: string}[] = clientRoles.filter(orgRole => (invitation.orgRoles.includes(orgRole.id.trim()) + && {id: orgRole.id, name: orgRole.name})); + + await this.clientRegistrationService.createUserClientRole(organizationDetails.idpId, token, userId, rolesPayload); return ResponseMessages.user.success.invitationAccept; diff --git a/apps/user/interfaces/user.interface.ts b/apps/user/interfaces/user.interface.ts index 7eaa65d52..503370357 100644 --- a/apps/user/interfaces/user.interface.ts +++ b/apps/user/interfaces/user.interface.ts @@ -5,6 +5,7 @@ export interface IUsersProfile { firstName?: string; lastName?: string; supabaseUserId?: string; + keycloakUserId?: string; userOrgRoles?: IUserOrgRole[]; } diff --git a/apps/user/repositories/user.repository.ts b/apps/user/repositories/user.repository.ts index f49d1cc79..95337baab 100644 --- a/apps/user/repositories/user.repository.ts +++ b/apps/user/repositories/user.repository.ts @@ -223,6 +223,7 @@ export class UserRepository { clientId: true, clientSecret: true, supabaseUserId: true, + keycloakUserId: true, userOrgRoles: { include: { orgRole: true, @@ -271,6 +272,7 @@ export class UserRepository { profileImg: true, publicProfile: true, supabaseUserId: true, + keycloakUserId: true, isEmailVerified: true, userOrgRoles: { select:{ diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index 37e6de446..e47b569c0 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -271,8 +271,19 @@ export class UserService { keycloakDetails.keycloakUserId.toString() ); - const holderRoleData = await this.orgRoleService.getRole(OrgRoles.HOLDER); - await this.userOrgRoleService.createUserOrgRole(userDetails.id, holderRoleData.id); + const realmRoles = await this.clientRegistrationService.getAllRealmRoles(token); + + const holderRole = realmRoles.filter(role => role.name === OrgRoles.HOLDER); + const holderRoleData = 0 < holderRole.length && holderRole[0]; + + const payload = [ + { + id: holderRoleData.id, + name: holderRoleData.name + } + ]; + + await this.clientRegistrationService.createUserHolderRole(token, keycloakDetails.keycloakUserId.toString(), payload); return ResponseMessages.user.success.signUpUser; } catch (error) { @@ -778,7 +789,7 @@ export class UserService { async acceptRejectInvitations(acceptRejectInvitation: AcceptRejectInvitationDto, userId: string): Promise { try { const userData = await this.userRepository.getUserById(userId); - return this.fetchInvitationsStatus(acceptRejectInvitation, userId, userData.email); + return this.fetchInvitationsStatus(acceptRejectInvitation, userData.keycloakUserId, userData.email); } catch (error) { this.logger.error(`acceptRejectInvitations: ${error}`); throw new RpcException(error.response ? error.response : error); diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 7766e0926..4ce1b7621 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -18,6 +18,7 @@ import { userTokenPayloadDto } from './dtos/userTokenPayloadDto'; import { KeycloakUserRegistrationDto } from 'apps/user/dtos/keycloak-register.dto'; import { ResponseMessages } from '@credebl/common/response-messages'; import { ResponseService } from '@credebl/response'; +import { IClientRoles } from './interfaces/client.interface'; @Injectable() export class ClientRegistrationService { @@ -186,7 +187,6 @@ export class ClientRegistrationService { } } - async getManagementTokenForMobile() { try { const payload = new ClientCredentialTokenPayloadDto(); @@ -277,6 +277,183 @@ export class ClientRegistrationService { } + async createUserClientRole( + idpId: string, + token: string, + userId: string, + payload: object[] + ): Promise { + + const realmName = process.env.KEYCLOAK_REALM; + + const createClientRolesResponse = await this.commonService.httpPost( + await this.keycloakUrlService.GetClientUserRoleURL(realmName, userId, idpId), + payload, + this.getAuthHeader(token) + ); + + this.logger.debug( + `createUserClientRolesResponse ${JSON.stringify( + createClientRolesResponse + )}` + ); + + return 'User client role is assigned'; + } + + async createUserHolderRole( + token: string, + userId: string, + payload: object[] + ): Promise { + + const realmName = process.env.KEYCLOAK_REALM; + + const createClientRolesResponse = await this.commonService.httpPost( + await this.keycloakUrlService.GetClientUserRoleURL(realmName, userId), + payload, + this.getAuthHeader(token) + ); + + this.logger.debug( + `createUserHolderRole ${JSON.stringify( + createClientRolesResponse + )}` + ); + + return 'User holder role is assigned'; + } + + async getAllClientRoles( + idpId: string, + token: string + ): Promise { + + const realmName = process.env.KEYCLOAK_REALM; + + const clientRolesResponse = await this.commonService.httpGet( + await this.keycloakUrlService.GetClientRoleURL(realmName, idpId), + this.getAuthHeader(token) + ); + + this.logger.debug( + `getAllClientRoles ${JSON.stringify( + clientRolesResponse + )}` + ); + + return clientRolesResponse; + } + + async getClientSpecificRoles( + idpId: string, + token: string, + roleName: string + ): Promise { + + const realmName = process.env.KEYCLOAK_REALM; + + const clientRolesResponse = await this.commonService.httpGet( + await this.keycloakUrlService.GetClientRoleURL(realmName, idpId, roleName), + this.getAuthHeader(token) + ); + + this.logger.debug( + `getClientSpecificRoles ${JSON.stringify( + clientRolesResponse + )}` + ); + + return clientRolesResponse; + } + + async getAllRealmRoles( + token: string + ): Promise { + + const realmName = process.env.KEYCLOAK_REALM; + + const realmRolesResponse = await this.commonService.httpGet( + await this.keycloakUrlService.GetRealmRoleURL(realmName), + this.getAuthHeader(token) + ); + + this.logger.debug( + `getAllRealmRoles ${JSON.stringify( + realmRolesResponse + )}` + ); + + return realmRolesResponse; + } + + + async createClientRole( + idpId: string, + token: string, + name: string, + description: string + ): Promise { + + const payload = { + clientRole: true, + name, + description + }; + + const realmName = process.env.KEYCLOAK_REALM; + + const createClientRolesResponse = await this.commonService.httpPost( + await this.keycloakUrlService.GetClientRoleURL(realmName, idpId), + payload, + this.getAuthHeader(token) + ); + + this.logger.debug( + `createClientRolesResponse ${JSON.stringify( + createClientRolesResponse + )}` + ); + + return 'Client role is created'; + + } + + async generateClientSecret( + idpId: string, + token: string + ): Promise { + + const realmName = process.env.KEYCLOAK_REALM; + + const createClientSercretResponse = await this.commonService.httpPost( + await this.keycloakUrlService.GetClientSecretURL(realmName, idpId), + {}, + this.getAuthHeader(token) + ); + + this.logger.debug( + `ClientRegistrationService create realm client secret ${JSON.stringify( + createClientSercretResponse + )}` + ); + + const getClientSercretResponse = await this.commonService.httpGet( + await this.keycloakUrlService.GetClientSecretURL(realmName, idpId), + this.getAuthHeader(token) + ); + this.logger.debug( + `ClientRegistrationService get client secret ${JSON.stringify( + getClientSercretResponse + )}` + ); + this.logger.log(`${getClientSercretResponse.value}`); + const clientSecret = getClientSercretResponse.value; + + return clientSecret; + + } + async createClient( orgName: string, orgId: string, @@ -324,20 +501,7 @@ export class ClientRegistrationService { publicClient: false, frontchannelLogout: false, fullScopeAllowed: false, - nodeReRegistrationTimeout: 0, - defaultClientScopes: [ - 'web-origins', - 'role_list', - 'profile', - 'roles', - 'email' - ], - optionalClientScopes: [ - 'address', - 'phone', - 'offline_access', - 'microprofile-jwt' - ] + nodeReRegistrationTimeout: 0 }; const createClientResponse = await this.commonService.httpPost( diff --git a/libs/client-registration/src/interfaces/client.interface.ts b/libs/client-registration/src/interfaces/client.interface.ts new file mode 100644 index 000000000..59e78a52c --- /dev/null +++ b/libs/client-registration/src/interfaces/client.interface.ts @@ -0,0 +1,8 @@ +export interface IClientRoles { + id: string + name: string + description?: string + composite?: boolean + clientRole?: boolean + containerId?: string +} \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index ecea0f3a2..e57e8358b 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -91,7 +91,7 @@ export const ResponseMessages = { rolesNotExist: 'Provided roles not exists in the platform', orgProfile: 'Organization profile not found', userNotFound: 'User not found for the given organization', - orgRoleIdNotFound:'Provided roles not exists in the platform', + orgRoleIdNotFound:'Provided roles not exists for this organization', updateUserRoles: 'Unable to update user roles', deleteOrg: 'Organization not found', deleteOrgInvitation: 'Organization does not have access to delete this invitation', diff --git a/libs/keycloak-url/src/keycloak-url.service.ts b/libs/keycloak-url/src/keycloak-url.service.ts index cb766e604..d10c9f5f8 100644 --- a/libs/keycloak-url/src/keycloak-url.service.ts +++ b/libs/keycloak-url/src/keycloak-url.service.ts @@ -71,6 +71,48 @@ export class KeycloakUrlService { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients/${clientid}/client-secret`; } + async GetClientRoleURL( + realm: string, + clientid: string, + roleName = '' + ):Promise { + + if ('' === roleName) { + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients/${clientid}/roles`; + } + + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients/${clientid}/roles/${roleName}`; + + } + + async GetRealmRoleURL( + realm: string, + roleName = '' + ):Promise { + + if ('' === roleName) { + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/roles`; + } + + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/roles/${roleName}`; + + } + + async GetClientUserRoleURL( + realm: string, + userId: string, + clientId?: string + ):Promise { + + if (clientId) { + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users/${userId}/role-mappings/clients/${clientId}`; + } + + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users/${userId}/role-mappings/realm`; + + } + + async GetClientIdpURL( realm: string, idp: string diff --git a/libs/org-roles/interfaces/org-roles.interface.ts b/libs/org-roles/interfaces/org-roles.interface.ts index b170d93af..ec63e63c1 100644 --- a/libs/org-roles/interfaces/org-roles.interface.ts +++ b/libs/org-roles/interfaces/org-roles.interface.ts @@ -2,8 +2,8 @@ export interface IOrgRoles { id: string; name: string; description: string; - createDateTime: Date; - createdBy: string; - lastChangedDateTime: Date; - lastChangedBy: string; + createDateTime?: Date; + createdBy?: string; + lastChangedDateTime?: Date; + lastChangedBy?: string; } \ No newline at end of file From 1204bf428c106a8054c3e464c0c980e80aedec47 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 19 Feb 2024 19:57:09 +0530 Subject: [PATCH 014/231] feat: implement push notification module and microservice Signed-off-by: KulkarniShashank --- apps/api-gateway/src/app.module.ts | 2 + .../src/notification/dtos/notification.dto.ts | 85 +++++++++++++++++++ .../interfaces/notification.interfaces.ts | 5 ++ .../notification/notification.controller.ts | 82 ++++++++++++++++++ .../src/notification/notification.module.ts | 28 ++++++ .../src/notification/notification.service.ts | 30 +++++++ .../dtos/create-organization-dto.ts | 16 +++- .../interfaces/notification.interfaces.ts | 23 +++++ apps/notification/src/main.ts | 23 +++++ .../src/notification.controller.ts | 29 +++++++ apps/notification/src/notification.module.ts | 28 ++++++ .../src/notification.repository.ts | 65 ++++++++++++++ apps/notification/src/notification.service.ts | 83 ++++++++++++++++++ apps/notification/tsconfig.app.json | 9 ++ .../dtos/create-organization.dto.ts | 1 + apps/organization/src/organization.service.ts | 24 ++++++ libs/common/src/response-messages/index.ts | 12 ++- .../20240219142237_notification/migration.sql | 16 ++++ libs/prisma-service/prisma/schema.prisma | 11 +++ nest-cli.json | 9 ++ 20 files changed, 579 insertions(+), 2 deletions(-) create mode 100644 apps/api-gateway/src/notification/dtos/notification.dto.ts create mode 100644 apps/api-gateway/src/notification/interfaces/notification.interfaces.ts create mode 100644 apps/api-gateway/src/notification/notification.controller.ts create mode 100644 apps/api-gateway/src/notification/notification.module.ts create mode 100644 apps/api-gateway/src/notification/notification.service.ts create mode 100644 apps/notification/interfaces/notification.interfaces.ts create mode 100644 apps/notification/src/main.ts create mode 100644 apps/notification/src/notification.controller.ts create mode 100644 apps/notification/src/notification.module.ts create mode 100644 apps/notification/src/notification.repository.ts create mode 100644 apps/notification/src/notification.service.ts create mode 100644 apps/notification/tsconfig.app.json create mode 100644 libs/prisma-service/prisma/migrations/20240219142237_notification/migration.sql diff --git a/apps/api-gateway/src/app.module.ts b/apps/api-gateway/src/app.module.ts index b4b297e94..8b853a00e 100644 --- a/apps/api-gateway/src/app.module.ts +++ b/apps/api-gateway/src/app.module.ts @@ -26,6 +26,7 @@ import { CacheModule } from '@nestjs/cache-manager'; import * as redisStore from 'cache-manager-redis-store'; import { WebhookModule } from './webhook/webhook.module'; import { UtilitiesModule } from './utilities/utilities.module'; +import { NotificationModule } from './notification/notification.module'; @Module({ imports: [ @@ -52,6 +53,7 @@ import { UtilitiesModule } from './utilities/utilities.module'; EcosystemModule, UtilitiesModule, WebhookModule, + NotificationModule, CacheModule.register({ store: redisStore, host: process.env.REDIS_HOST, port: process.env.REDIS_PORT }), BullModule.forRoot({ redis: { diff --git a/apps/api-gateway/src/notification/dtos/notification.dto.ts b/apps/api-gateway/src/notification/dtos/notification.dto.ts new file mode 100644 index 000000000..8ddef46d3 --- /dev/null +++ b/apps/api-gateway/src/notification/dtos/notification.dto.ts @@ -0,0 +1,85 @@ +import { ApiExtraModels, ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString, IsUrl } from 'class-validator'; + +import { Transform } from 'class-transformer'; +import { trim } from '@credebl/common/cast.helper'; + +@ApiExtraModels() +export class RegisterHolderCredentalsDto { + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'fcmToken is required.' }) + @IsString({ message: 'fcmToken must be in string format.' }) + fcmToken: string; + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'orgId is required.' }) + @IsString({ message: 'orgId must be in string format.' }) + orgId: string; + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'userKey is required.' }) + @IsString({ message: 'userKey must be in string format.' }) + userKey: string; +} + +export class RegisterOrgWebhhookEndpointDto { + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'orgId is required.' }) + @IsString({ message: 'orgId must be in string format.' }) + orgId: string; + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'notificationWebhook is required.' }) + @IsString({ message: 'notificationWebhook must be in string format.' }) + @IsUrl({ + // eslint-disable-next-line camelcase + require_protocol: true, // require URL protocol (e.g., http:// or https://) + // eslint-disable-next-line camelcase + require_tld: true // require top-level domain (e.g., .com, .net) + + }) + notificationWebhook: string; +} + +export class SendNotificationDto { + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'fcmToken is required.' }) + @IsString({ message: 'fcmToken must be in string format.' }) + fcmToken: string; + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'messageType is required.' }) + @IsString({ message: 'messageType must be in string format.' }) + messageType: string; + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'clientCode is required.' }) + @IsString({ message: 'clientCode must be in string format.' }) + clientCode: string; +} + +export class GetNotificationDto { + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'fcmToken is required.' }) + @IsString({ message: 'fcmToken must be in string format.' }) + fcmToken: string; + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'messageType is required.' }) + @IsString({ message: 'messageType must be in string format.' }) + messageType: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/notification/interfaces/notification.interfaces.ts b/apps/api-gateway/src/notification/interfaces/notification.interfaces.ts new file mode 100644 index 000000000..a2b4aa1a3 --- /dev/null +++ b/apps/api-gateway/src/notification/interfaces/notification.interfaces.ts @@ -0,0 +1,5 @@ +export interface INotification { + id: string; + orgId: string; + webhookEndpoint: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/notification/notification.controller.ts b/apps/api-gateway/src/notification/notification.controller.ts new file mode 100644 index 000000000..22c82fc24 --- /dev/null +++ b/apps/api-gateway/src/notification/notification.controller.ts @@ -0,0 +1,82 @@ +import { CustomExceptionFilter } from '@credebl/common/exception-handler'; +import { Body, Controller, HttpStatus, Logger, Post, Res, UseFilters } from '@nestjs/common'; +import { ApiForbiddenResponse, ApiOperation, ApiResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; +import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; +import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; +import { ApiResponseDto } from '../dtos/apiResponse.dto'; +import { RegisterOrgWebhhookEndpointDto, SendNotificationDto } from './dtos/notification.dto'; +import { IResponse } from '@credebl/common/interfaces/response.interface'; +import { Response } from 'express'; +import { ResponseMessages } from '@credebl/common/response-messages'; +import { NotificationService } from './notification.service'; + + +@Controller('notification') +@UseFilters(CustomExceptionFilter) +@ApiTags('notification') +@ApiUnauthorizedResponse({ status: 401, description: 'Unauthorized', type: UnauthorizedErrorDto }) +@ApiForbiddenResponse({ status: 403, description: 'Forbidden', type: ForbiddenErrorDto }) +export class NotificationController { + constructor( + private readonly notificationService: NotificationService + ) { } + private readonly logger = new Logger('NotificationController'); + + /** + * Register organization webhook endpoint + * @param registerOrgWebhhookEndpointDto + * @param res + * @returns Stored notification data + */ + @Post('/register/webhook-endpoint') + @ApiOperation({ + summary: `Register organization webhook endpoint for notification`, + description: `Register organization webhook endpoint for notification` + }) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async registerOrgWebhookEndpoint( + @Body() registerOrgWebhhookEndpointDto: RegisterOrgWebhhookEndpointDto, + @Res() res: Response + ): Promise { + + const registerUserEndpoint = await this.notificationService.registerOrgWebhookEndpoint( + registerOrgWebhhookEndpointDto + ); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.notification.success.register, + data: registerUserEndpoint + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + /** + * Send notification for holder + * @param sendNotificationDto + * @param res + * @returns Get notification details + */ + @Post('/') + @ApiOperation({ + summary: `Send notification for holder`, + description: `Send notification for holder` + }) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async sendNotification( + @Body() notificationRequestBody: SendNotificationDto, + @Res() res: Response + ): Promise { + + const sendNotification = await this.notificationService.sendNotification( + notificationRequestBody + ); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.notification.success.sendNotification, + data: sendNotification + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } +} \ No newline at end of file diff --git a/apps/api-gateway/src/notification/notification.module.ts b/apps/api-gateway/src/notification/notification.module.ts new file mode 100644 index 000000000..bc3bc7706 --- /dev/null +++ b/apps/api-gateway/src/notification/notification.module.ts @@ -0,0 +1,28 @@ + +import { CommonModule, CommonService } from '@credebl/common'; +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { ConfigModule } from '@nestjs/config'; +import { HttpModule } from '@nestjs/axios'; +import { Module } from '@nestjs/common'; +import { getNatsOptions } from '@credebl/common/nats.config'; +import { NotificationController } from './notification.controller'; +import { NotificationService } from './notification.service'; + +@Module({ + imports: [ + HttpModule, + ConfigModule.forRoot(), + ClientsModule.register([ + { + name: 'NATS_CLIENT', + transport: Transport.NATS, + options: getNatsOptions(process.env.API_GATEWAY_NKEY_SEED) + + }, + CommonModule + ]) + ], + controllers: [NotificationController], + providers: [NotificationService, CommonService] +}) +export class NotificationModule { } diff --git a/apps/api-gateway/src/notification/notification.service.ts b/apps/api-gateway/src/notification/notification.service.ts new file mode 100644 index 000000000..01847e1d9 --- /dev/null +++ b/apps/api-gateway/src/notification/notification.service.ts @@ -0,0 +1,30 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { ClientProxy } from '@nestjs/microservices'; +import { BaseService } from 'libs/service/base.service'; +import { RegisterOrgWebhhookEndpointDto, SendNotificationDto } from './dtos/notification.dto'; +import { INotification } from './interfaces/notification.interfaces'; + +@Injectable() +export class NotificationService extends BaseService { + constructor(@Inject('NATS_CLIENT') private readonly serviceProxy: ClientProxy) { + super('NotificationService'); + } + + /** + * Register organization webhook endpoint + * @param registerOrgWebhhookEndpointDto + * @returns Stored notification data + */ + async registerOrgWebhookEndpoint(registerOrgWebhhookEndpointDto: RegisterOrgWebhhookEndpointDto): Promise { + return this.sendNatsMessage(this.serviceProxy, 'register-org-webhook-endpoint-for-notification', registerOrgWebhhookEndpointDto); + } + + /** + * Send notification for holder + * @param sendNotificationDto + * @returns Get notification details + */ + async sendNotification(notificationRequestBody: SendNotificationDto): Promise { + return this.sendNatsMessage(this.serviceProxy, 'send-notification', notificationRequestBody); + } +} \ No newline at end of file diff --git a/apps/api-gateway/src/organization/dtos/create-organization-dto.ts b/apps/api-gateway/src/organization/dtos/create-organization-dto.ts index 6847b57ba..61e2c0f73 100644 --- a/apps/api-gateway/src/organization/dtos/create-organization-dto.ts +++ b/apps/api-gateway/src/organization/dtos/create-organization-dto.ts @@ -1,5 +1,5 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; +import { IsNotEmpty, IsOptional, IsString, IsUrl, MaxLength, MinLength } from 'class-validator'; import { Transform } from 'class-transformer'; import { trim } from '@credebl/common/cast.helper'; @@ -28,6 +28,20 @@ export class CreateOrganizationDto { @Transform(({ value }) => trim(value)) website?: string; + @ApiPropertyOptional() + @IsOptional() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'notificationWebhook is required.' }) + @IsString({ message: 'notificationWebhook must be in string format.' }) + @IsUrl({ + // eslint-disable-next-line camelcase + require_protocol: true, // require URL protocol (e.g., http:// or https://) + // eslint-disable-next-line camelcase + require_tld: true // require top-level domain (e.g., .com, .net) + + }) + notificationWebhook?: string; + @ApiPropertyOptional() @IsOptional() @Transform(({ value }) => trim(value)) diff --git a/apps/notification/interfaces/notification.interfaces.ts b/apps/notification/interfaces/notification.interfaces.ts new file mode 100644 index 000000000..57edecd46 --- /dev/null +++ b/apps/notification/interfaces/notification.interfaces.ts @@ -0,0 +1,23 @@ + + +export interface INotification { + id: string; + orgId: string; + notificationWebhook: string; +} + +export interface IWebhookEndpoint { + orgId: string; + notificationWebhook: string; +} + +export interface ISendNotification { + fcmToken: string; + messageType: string; + clientCode: string; +} + +export interface IGetNotification { + fcmToken: string; + messageType: string; +} \ No newline at end of file diff --git a/apps/notification/src/main.ts b/apps/notification/src/main.ts new file mode 100644 index 000000000..c41d3ac60 --- /dev/null +++ b/apps/notification/src/main.ts @@ -0,0 +1,23 @@ +import { NestFactory } from '@nestjs/core'; +import { HttpExceptionFilter } from 'libs/http-exception.filter'; +import { Logger } from '@nestjs/common'; +import { MicroserviceOptions, Transport } from '@nestjs/microservices'; +import { NotificationModule } from '../src/notification.module'; +import { getNatsOptions } from '@credebl/common/nats.config'; + +const logger = new Logger(); + +async function bootstrap(): Promise { + + const app = await NestFactory.createMicroservice(NotificationModule, { + transport: Transport.NATS, + options: getNatsOptions(process.env.NOTIFICATION_NKEY_SEED) + + }); + + app.useGlobalFilters(new HttpExceptionFilter()); + + await app.listen(); + logger.log('Issuance-Service Microservice is listening to NATS '); +} +bootstrap(); \ No newline at end of file diff --git a/apps/notification/src/notification.controller.ts b/apps/notification/src/notification.controller.ts new file mode 100644 index 000000000..40c9d33e8 --- /dev/null +++ b/apps/notification/src/notification.controller.ts @@ -0,0 +1,29 @@ +import { Controller } from '@nestjs/common'; +import { NotificationService } from './notification.service'; +import { MessagePattern } from '@nestjs/microservices'; +import { INotification, IWebhookEndpoint, ISendNotification } from '../interfaces/notification.interfaces'; + +@Controller() +export class NotificationController { + constructor(private readonly notificationService: NotificationService) {} + + /** + * Register organization webhook endpoint + * @param payload + * @returns Stored notification data + */ + @MessagePattern({ cmd: 'register-org-webhook-endpoint-for-notification' }) + async registerOrgWebhookEndpoint(payload: IWebhookEndpoint): Promise { + return this.notificationService.registerOrgWebhookEndpoint(payload); + } + + /** + * Send notification for holder + * @param payload + * @returns Get notification details + */ + @MessagePattern({ cmd: 'send-notification' }) + async sendNotification(payload: ISendNotification): Promise { + return this.notificationService.sendNotification(payload); + } +} \ No newline at end of file diff --git a/apps/notification/src/notification.module.ts b/apps/notification/src/notification.module.ts new file mode 100644 index 000000000..2383d7779 --- /dev/null +++ b/apps/notification/src/notification.module.ts @@ -0,0 +1,28 @@ +import { CommonModule } from '@credebl/common'; +import { getNatsOptions } from '@credebl/common/nats.config'; +import { CacheModule } from '@nestjs/cache-manager'; +import { Logger, Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { NotificationController } from './notification.controller'; +import { NotificationService } from './notification.service'; +import { PrismaService } from '@credebl/prisma-service'; +import { NotificationRepository } from './notification.repository'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + ClientsModule.register([ + { + name: 'NATS_CLIENT', + transport: Transport.NATS, + options: getNatsOptions(process.env.ISSUANCE_NKEY_SEED) + } + ]), + CommonModule, + CacheModule.register({ host: process.env.REDIS_HOST, port: process.env.REDIS_PORT }) + ], + controllers: [NotificationController], + providers: [NotificationService, NotificationRepository, PrismaService, Logger] +}) +export class NotificationModule { } \ No newline at end of file diff --git a/apps/notification/src/notification.repository.ts b/apps/notification/src/notification.repository.ts new file mode 100644 index 000000000..e543899b7 --- /dev/null +++ b/apps/notification/src/notification.repository.ts @@ -0,0 +1,65 @@ +import { ResponseMessages } from '@credebl/common/response-messages'; +import { PrismaService } from '@credebl/prisma-service'; +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { INotification, IWebhookEndpoint } from '../interfaces/notification.interfaces'; + +@Injectable() +export class NotificationRepository { + constructor( + private readonly prisma: PrismaService, + private readonly logger: Logger + ) { } + + + /** + * Register organization webhook endpoint + * @param payload + * @returns Stored notification data + */ + async storeOrgWebhookEndpoint(payload: IWebhookEndpoint): Promise { + try { + + const { orgId, notificationWebhook } = payload; + const updateNotification = await this.prisma.notification.create({ + data: { + orgId, + notificationWebhook + } + }); + + if (!updateNotification) { + throw new NotFoundException(ResponseMessages.notification.error.notFound); + } + + return updateNotification; + } catch (error) { + this.logger.error(`Error in storeOrgWebhookEndpoint: ${error.message} `); + throw error; + } + } + + /** + * Get webhook endpoint + * @param orgId + * @returns Get notification details + */ + async getOrgWebhookEndpoint(orgId: string): Promise { + try { + + const updateNotification = await this.prisma.notification.findUnique({ + where: { + orgId + } + }); + + if (!updateNotification) { + throw new NotFoundException(ResponseMessages.notification.error.notFound); + } + + return updateNotification; + } catch (error) { + this.logger.error(`Error in getOrgWebhookEndpoint: ${error.message} `); + throw error; + } + } +} \ No newline at end of file diff --git a/apps/notification/src/notification.service.ts b/apps/notification/src/notification.service.ts new file mode 100644 index 000000000..01bb467d2 --- /dev/null +++ b/apps/notification/src/notification.service.ts @@ -0,0 +1,83 @@ +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { + INotification, + IWebhookEndpoint, + ISendNotification +} from '../interfaces/notification.interfaces'; +import { RpcException } from '@nestjs/microservices'; +import { NotificationRepository } from './notification.repository'; +import { ResponseMessages } from '@credebl/common/response-messages'; +import { CommonService } from '@credebl/common'; + +@Injectable() +export class NotificationService { + private readonly logger = new Logger('NotificationService'); + constructor( + private readonly commonService: CommonService, + private readonly notificationRepository: NotificationRepository + ) {} + + /** + * Register organization webhook endpoint + * @param payload + * @returns Stored notification data + */ + async registerOrgWebhookEndpoint(payload: IWebhookEndpoint): Promise { + try { + /** + * Call the function for store the org webhook endpoint on notification table + */ + const storeOrgWebhookEndpoint = await this.notificationRepository.storeOrgWebhookEndpoint(payload); + return storeOrgWebhookEndpoint; + } catch (error) { + this.logger.error(`[registerEndpoint] - error in register org webhook endpoint: ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } + + /** + * Send notification for holder + * @param payload + * @returns Get notification details + */ + async sendNotification(payload: ISendNotification): Promise { + try { + const orgId = payload.clientCode; + + /** + * Fetch the webhook endpoint by orgId + */ + const getWebhookUrl = await this.notificationRepository.getOrgWebhookEndpoint(orgId); + + const webhookPayload = { + fcmToken: payload.fcmToken, + messageType: payload.messageType + }; + + /** + * Send notification details with webhook endpoint + */ + const webhookResponse = await this.commonService + .httpPost(getWebhookUrl?.notificationWebhook, webhookPayload) + .then(async (response) => response) + .catch((error) => { + this.logger.error(`Error in sendNotification : ${JSON.stringify(error)}`); + throw error; + }); + + if (!this.isValidUrl(getWebhookUrl?.notificationWebhook)) { + throw new BadRequestException(ResponseMessages.notification.error.invalidUrl); + } + + return webhookResponse; + } catch (error) { + this.logger.error(`[registerEndpoint] - error in send notification: ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } + + private isValidUrl(url: string): boolean { + const urlRegex = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/; + return urlRegex.test(url); + } +} diff --git a/apps/notification/tsconfig.app.json b/apps/notification/tsconfig.app.json new file mode 100644 index 000000000..87277fc96 --- /dev/null +++ b/apps/notification/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": false, + "outDir": "../../dist/apps/notification" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "test", "**/*spec.ts"] +} diff --git a/apps/organization/dtos/create-organization.dto.ts b/apps/organization/dtos/create-organization.dto.ts index 0897f9a30..9fbdd8d15 100644 --- a/apps/organization/dtos/create-organization.dto.ts +++ b/apps/organization/dtos/create-organization.dto.ts @@ -10,6 +10,7 @@ export class CreateOrganizationDto { createdBy?:string; updatedBy?:string; lastChangedBy?:string; + notificationWebhook?: string; } export class CreateUserRoleOrgDto { diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index aa2d580cf..973c14c2f 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -84,6 +84,10 @@ export class OrganizationService { const ownerRoleData = await this.orgRoleService.getRole(OrgRoles.OWNER); + if (createOrgDto.notificationWebhook) { + await this.storeOrgWebhookEndpoint(organizationDetails.id, createOrgDto.notificationWebhook); + } + await this.userOrgRoleService.createUserOrgRole(userId, ownerRoleData.id, organizationDetails.id); await this.userActivityService.createActivity(userId, organizationDetails.id, `${organizationDetails.name} organization created`, 'Get started with inviting users to join organization'); @@ -869,4 +873,24 @@ export class OrganizationService { throw new RpcException(error.response ? error.response : error); } } + + async storeOrgWebhookEndpoint(orgId: string, notificationWebhook: string): Promise { + const pattern = { cmd: 'register-org-webhook-endpoint-for-notification' }; + const payload = { + orgId, + notificationWebhook + }; + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const message = await this.organizationServiceProxy.send(pattern, payload).toPromise(); + return message; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException({ + status: error.status, + error: error.message + }, error.status); + } + } } \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index ecea0f3a2..4212d973e 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -391,5 +391,15 @@ export const ResponseMessages = { getshorteningUrl:'Shortening Url fetched successfully', createShorteningUrl: 'Shortening Url created successfully' } - } + }, + notification: { + success: { + register: 'Notification webhook registration process completed successfully', + sendNotification: 'Notification send successfully' + }, + error: { + notFound: 'Notification record not found.', + invalidUrl: 'Invalid URL' + } + }, }; \ No newline at end of file diff --git a/libs/prisma-service/prisma/migrations/20240219142237_notification/migration.sql b/libs/prisma-service/prisma/migrations/20240219142237_notification/migration.sql new file mode 100644 index 000000000..3cbdcc3a5 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240219142237_notification/migration.sql @@ -0,0 +1,16 @@ +-- CreateTable +CREATE TABLE "notification" ( + "id" UUID NOT NULL, + "orgId" UUID, + "notificationWebhook" TEXT, + "createDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdBy" TEXT NOT NULL DEFAULT '1', + "lastChangedDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "lastChangedBy" TEXT NOT NULL DEFAULT '1', + "deletedAt" TIMESTAMP(6), + + CONSTRAINT "notification_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "notification_orgId_key" ON "notification"("orgId"); diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 8353b7b46..714d724d6 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -474,3 +474,14 @@ model file_data { status Boolean @default(false) credential_data Json? } + +model notification { + id String @id @default(uuid()) @db.Uuid + orgId String? @unique @db.Uuid + notificationWebhook String? + createDateTime DateTime @default(now()) @db.Timestamptz(6) + createdBy String @default("1") + lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) + lastChangedBy String @default("1") + deletedAt DateTime? @db.Timestamp(6) +} \ No newline at end of file diff --git a/nest-cli.json b/nest-cli.json index 18ed21e79..76d477731 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -277,6 +277,15 @@ "compilerOptions": { "tsConfigPath": "apps/utility/tsconfig.app.json" } + }, + "notification": { + "type": "application", + "root": "apps/notification", + "entryFile": "main", + "sourceRoot": "apps/notification/src", + "compilerOptions": { + "tsConfigPath": "apps/notification/tsconfig.app.json" + } } } } \ No newline at end of file From 7ee50c4f0b3c5c1c585530ae14cecbae27640a20 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 20 Feb 2024 11:59:30 +0530 Subject: [PATCH 015/231] Notification success message refactor Signed-off-by: KulkarniShashank --- libs/common/src/response-messages/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 4212d973e..92271e9cf 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -395,7 +395,7 @@ export const ResponseMessages = { notification: { success: { register: 'Notification webhook registration process completed successfully', - sendNotification: 'Notification send successfully' + sendNotification: 'Notification sent successfully' }, error: { notFound: 'Notification record not found.', From 79a57ca423cf3b35a1d7aa22f6adb59c41f182ef Mon Sep 17 00:00:00 2001 From: Nishad Date: Tue, 20 Feb 2024 17:28:11 +0530 Subject: [PATCH 016/231] Updated org invitation Signed-off-by: Nishad --- apps/organization/src/organization.service.ts | 4 ++-- libs/common/src/response-messages/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index a52164450..584e27d29 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -718,10 +718,10 @@ export class OrganizationService { */ async updateOrgInvitation(payload: UpdateInvitationDto): Promise { try { - const { orgId, status, invitationId, userId } = payload; + const { orgId, status, invitationId, userId, email } = payload; const invitation = await this.organizationRepository.getInvitationById(String(invitationId)); - if (!invitation) { + if (!invitation || (invitation && invitation.email !== email)) { throw new NotFoundException(ResponseMessages.user.error.invitationNotFound); } diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index e57e8358b..4116f97eb 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -40,7 +40,7 @@ export const ResponseMessages = { verifyMail: 'Please verify your email', invalidCredentials: 'Invalid Credentials', registerFido: 'Please complete your fido registration', - invitationNotFound: 'Invitation not found', + invitationNotFound: 'Invitation not found for this user', invitationAlreadyAccepted:'Organization invitation already accepted', invitationAlreadyRejected:'Organization invitation already rejected', invalidInvitationStatus: 'Invalid invitation status', From b6e3183dd73bad4f1ad8e6c193ee0b7a324240f0 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:43:02 +0530 Subject: [PATCH 017/231] feat: Create new connection invitation (#520) * feat: modify connection invitation url Signed-off-by: bhavanakarwade * changed message pattern Signed-off-by: bhavanakarwade * fix: optimized conditions Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- apps/connection/src/connection.service.ts | 4 -- apps/issuance/src/issuance.repository.ts | 30 ----------- .../interfaces/organization.interface.ts | 12 +++++ .../repositories/organization.repository.ts | 39 ++++++++++++-- apps/organization/src/organization.service.ts | 52 ++++++++++++++++--- 5 files changed, 90 insertions(+), 47 deletions(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index b064518ed..39b369c80 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -42,10 +42,6 @@ export class ConnectionService { const { orgId, multiUseInvitation, autoAcceptConnection, alias, imageUrl, goal, goalCode, handshake, handshakeProtocols } = payload; try { - const connectionInvitationExist = await this.connectionRepository.getConnectionInvitationByOrgId(orgId); - if (connectionInvitationExist) { - return connectionInvitationExist; - } const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index c6078c128..6f6efc02b 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -3,7 +3,6 @@ import { Injectable, InternalServerErrorException, Logger, NotFoundException } f import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase import { - agent_invitations, credentials, file_data, file_upload, @@ -184,35 +183,6 @@ export class IssuanceRepository { } } - /** - * Description: Save connection details - * @param connectionInvitation - * @param agentId - * @param orgId - * @returns Get connection details - */ - // eslint-disable-next-line camelcase - async saveAgentConnectionInvitations( - connectionInvitation: string, - agentId: string, - orgId: string - ): Promise { - try { - const agentInvitationData = await this.prisma.agent_invitations.create({ - data: { - orgId, - agentId, - connectionInvitation, - multiUse: true - } - }); - return agentInvitationData; - } catch (error) { - this.logger.error(`Error in saveAgentConnectionInvitations: ${error.message} `); - throw error; - } - } - /** * Get platform config details * @returns diff --git a/apps/organization/interfaces/organization.interface.ts b/apps/organization/interfaces/organization.interface.ts index adbd4f8f2..032dd76ac 100644 --- a/apps/organization/interfaces/organization.interface.ts +++ b/apps/organization/interfaces/organization.interface.ts @@ -23,6 +23,18 @@ export interface IUpdateOrganization { } +export interface ICreateConnectionUrl { + id: string; + orgId: string; + agentId: string; + connectionInvitation: string; + multiUse: boolean; + createDateTime: Date; + createdBy: number; + lastChangedDateTime: Date; + lastChangedBy: number; +} + export interface IOrgAgent { url: string; apiKey: string; diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index 2c37e07c7..930fe04d7 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -3,7 +3,7 @@ import { Injectable, Logger, NotFoundException } from '@nestjs/common'; // eslint-disable-next-line camelcase -import { org_agents, org_invitations, user_org_roles } from '@prisma/client'; +import { Prisma, agent_invitations, org_agents, org_invitations, user_org_roles } from '@prisma/client'; import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { IGetOrgById, IGetOrganization, IUpdateOrganization } from '../interfaces/organization.interface'; @@ -38,7 +38,7 @@ export class OrganizationRepository { }); } catch (error) { this.logger.error(`error: ${JSON.stringify(error)}`); - throw new InternalServerErrorException(error); + throw error; } } @@ -97,6 +97,35 @@ export class OrganizationRepository { } } + async getAgentInvitationDetails(orgId: string): Promise { + try { + const response = await this.prisma.agent_invitations.findUnique({ + where: { + id: orgId + } + }); + return response; + } catch (error) { + this.logger.error(`error in getting agent invitation details: ${JSON.stringify(error)}`); + throw error; + } + } + + async updateConnectionInvitationDetails(orgId: string, connectionInvitation: string): Promise { + try { + const temp = await this.prisma.agent_invitations.updateMany({ + where: {orgId}, + data: { + connectionInvitation + } + }); + return temp; + + } catch (error) { + this.logger.error(`Error in updating connection invitation details: ${JSON.stringify(error)}`); + throw error; + } + } /** * @@ -538,9 +567,9 @@ export class OrganizationRepository { * @returns Organization exist details */ - async checkOrganizationExist(name: string, orgId: string): Promise { + async checkOrganizationExist(name: string, orgId: string): Promise { try { - return this.prisma.organisation.findMany({ + return this.prisma.organisation.findUnique({ where: { id: orgId, name @@ -548,7 +577,7 @@ export class OrganizationRepository { }); } catch (error) { this.logger.error(`error: ${JSON.stringify(error)}`); - throw new InternalServerErrorException(error); + throw error; } } diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 973c14c2f..a82df7f25 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -17,7 +17,7 @@ import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; import { Invitation, OrgAgentType, transition } from '@credebl/enum/enum'; -import { IGetOrgById, IGetOrganization, IUpdateOrganization, IOrgAgent, IClientCredentials } from '../interfaces/organization.interface'; +import { IGetOrgById, IGetOrganization, IUpdateOrganization, IOrgAgent, IClientCredentials, ICreateConnectionUrl } from '../interfaces/organization.interface'; import { UserActivityService } from '@credebl/user-activity'; import { CommonConstants } from '@credebl/common/common.constant'; import { ClientRegistrationService } from '@credebl/client-registration/client-registration.service'; @@ -252,13 +252,10 @@ export class OrganizationService { async updateOrganization(updateOrgDto: IUpdateOrganization, userId: string, orgId: string): Promise { try { - const organizationExist = await this.organizationRepository.checkOrganizationExist(updateOrgDto.name, orgId); + const organizationExist = await this.organizationRepository.checkOrganizationNameExist(updateOrgDto.name); - if (0 === organizationExist.length) { - const organizationExist = await this.organizationRepository.checkOrganizationNameExist(updateOrgDto.name); - if (organizationExist) { - throw new ConflictException(ResponseMessages.organisation.error.exists); - } + if (organizationExist && organizationExist.id !== orgId) { + throw new ConflictException(ResponseMessages.organisation.error.exists); } const orgSlug = await this.createOrgSlug(updateOrgDto.name); @@ -272,7 +269,16 @@ export class OrganizationService { delete updateOrgDto.logo; } - const organizationDetails = await this.organizationRepository.updateOrganization(updateOrgDto); + let organizationDetails; + const checkAgentIsExists = await this.organizationRepository.getAgentInvitationDetails(orgId); + + if (!checkAgentIsExists?.connectionInvitation && !checkAgentIsExists?.agentId) { + organizationDetails = await this.organizationRepository.updateOrganization(updateOrgDto); + } else if (organizationDetails?.logoUrl !== organizationExist?.logoUrl || organizationDetails?.name !== organizationExist?.name) { + const invitationData = await this._createConnection(updateOrgDto?.logo, updateOrgDto?.name, orgId); + await this.organizationRepository.updateConnectionInvitationDetails(orgId, invitationData?.connectionInvitation); + } + await this.userActivityService.createActivity(userId, organizationDetails.id, `${organizationDetails.name} organization updated`, 'Organization details updated successfully'); return organizationDetails; } catch (error) { @@ -281,6 +287,36 @@ export class OrganizationService { } } + + async _createConnection( + orgName: string, + logoUrl: string, + orgId: string + ): Promise { + const pattern = { cmd: 'create-connection' }; + + const payload = { + orgName, + logoUrl, + orgId + }; + const connectionInvitationData = await this.organizationServiceProxy + .send(pattern, payload) + .toPromise() + .catch((error) => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + }); + + return connectionInvitationData; + } + /** * @returns Get created organizations details */ From ed466484b1d97669db70cbab3ffda45550432654 Mon Sep 17 00:00:00 2001 From: sanjay-k1910 Date: Wed, 21 Feb 2024 17:43:23 +0530 Subject: [PATCH 018/231] fix: user friendly message of ecosystem roles if user does not have access Signed-off-by: sanjay-k1910 --- .../src/authz/guards/ecosystem-roles.guard.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts index a0f5542fc..9f011b92c 100644 --- a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts @@ -1,4 +1,4 @@ -import { BadRequestException, CanActivate, ExecutionContext, Logger } from '@nestjs/common'; +import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Logger } from '@nestjs/common'; import { HttpException } from '@nestjs/common'; import { HttpStatus } from '@nestjs/common'; @@ -69,7 +69,12 @@ export class EcosystemRolesGuard implements CanActivate { throw new HttpException('organization & ecosystem is required', HttpStatus.BAD_REQUEST); } - return requiredRoles.some((role) => user.ecosystemOrgRole === role); - + // Sending user friendly message if a user attempts to access an API that is inaccessible to their role + const roleAccess = requiredRoles.some((role) => user.ecosystemOrgRole === role); + if (!roleAccess) { + throw new ForbiddenException(ResponseMessages.organisation.error.roleNotMatch, { cause: new Error(), description: ResponseMessages.errorMessages.forbidden }); + } + + return roleAccess; } } \ No newline at end of file From 6cfb83dea31bfabe28922e6cc1ad1b2ce6294092 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Wed, 21 Feb 2024 19:33:15 +0530 Subject: [PATCH 019/231] Changes on the agent-provisioning dockerfile for the spinup the agent Signed-off-by: KulkarniShashank --- Dockerfiles/Dockerfile.agent-provisioning | 7 ++++++- Dockerfiles/Dockerfile.agent-service | 16 ++++++++++++++++ .../AFJ/scripts/docker_start_agent.sh | 12 ++++++------ docker-compose.yml | 15 ++++++++++++++- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Dockerfiles/Dockerfile.agent-provisioning b/Dockerfiles/Dockerfile.agent-provisioning index d23333fa0..72201a53f 100644 --- a/Dockerfiles/Dockerfile.agent-provisioning +++ b/Dockerfiles/Dockerfile.agent-provisioning @@ -16,6 +16,11 @@ RUN npm install -g pnpm --ignore-scripts \ RUN docker --version && \ docker-compose --version +ARG ROOT_PATH +ENV ROOT_PATH ${ROOT_PATH} + +RUN echo "ROOT_PATH is set to: $ROOT_PATH" + # Set the working directory WORKDIR /app @@ -76,7 +81,7 @@ RUN chmod 777 /app/agent-provisioning/AFJ/token COPY libs/ ./libs/ # Set the command to run the microservice -CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/agent-provisioning/main.js"] +CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/agent-provisioning/main.js $ROOT_PATH"] # docker build -t agent-provisioning-service -f Dockerfiles/Dockerfile.agent-provisioning . # docker run -d --env-file .env --name agent-provisioning-service docker.io/library/agent-provisioning-service \ No newline at end of file diff --git a/Dockerfiles/Dockerfile.agent-service b/Dockerfiles/Dockerfile.agent-service index 1089b8234..b226e0631 100644 --- a/Dockerfiles/Dockerfile.agent-service +++ b/Dockerfiles/Dockerfile.agent-service @@ -1,5 +1,13 @@ # Stage 1: Build the application FROM node:18-alpine as build + +RUN npm install -g pnpm --ignore-scripts \ + && apk update \ + && apk add openssh-client \ + && apk add aws-cli \ + && apk add docker \ + && apk add docker-compose + RUN npm install -g pnpm # Set the working directory WORKDIR /app @@ -19,6 +27,14 @@ RUN pnpm run build agent-service # Stage 2: Create the final image FROM node:18-alpine + +RUN npm install -g pnpm --ignore-scripts \ + && apk update \ + && apk add openssh-client \ + && apk add aws-cli \ + && apk add docker \ + && apk add docker-compose + RUN npm install -g pnpm # Set the working directory WORKDIR /app diff --git a/apps/agent-provisioning/AFJ/scripts/docker_start_agent.sh b/apps/agent-provisioning/AFJ/scripts/docker_start_agent.sh index 520a138ad..ab3673453 100644 --- a/apps/agent-provisioning/AFJ/scripts/docker_start_agent.sh +++ b/apps/agent-provisioning/AFJ/scripts/docker_start_agent.sh @@ -106,8 +106,6 @@ AGENT_ENDPOINT="${PROTOCOL}://${EXTERNAL_IP}:${INBOUND_PORT}" echo "-----$AGENT_ENDPOINT----" CONFIG_FILE="${PWD}/agent-provisioning/AFJ/agent-config/${AGENCY}_${CONTAINER_NAME}.json" -echo "--CONFIG_FILE----${CONFIG_FILE}" - # Check if the file exists if [ -f "$CONFIG_FILE" ]; then # If it exists, remove the file @@ -159,25 +157,27 @@ if [ -f "$DOCKER_COMPOSE" ]; then # If it exists, remove the file rm "$DOCKER_COMPOSE" fi +echo ${PWD} cat <${DOCKER_COMPOSE} version: '3' services: - agent: + agent: image: $AFJ_VERSION container_name: ${AGENCY}_${CONTAINER_NAME} restart: always environment: AFJ_REST_LOG_LEVEL: 1 + ROOT_PATH: ${ROOT_PATH} ports: - ${INBOUND_PORT}:${INBOUND_PORT} - ${ADMIN_PORT}:${ADMIN_PORT} volumes: - - ./agent-config/${AGENCY}_${CONTAINER_NAME}.json:/config.json + - ${ROOT_PATH}:/agent-config - command: --auto-accept-connections --config /config.json + command: --auto-accept-connections --config /agent-config/${AGENCY}_${CONTAINER_NAME}.json volumes: pgdata: @@ -194,7 +194,7 @@ if [ $? -eq 0 ]; then echo "container-name::::::${CONTAINER_NAME}" echo "file-name::::::$FILE_NAME" - docker-compose -f $FILE_NAME up -d + docker compose -f $FILE_NAME up -d if [ $? -eq 0 ]; then n=0 diff --git a/docker-compose.yml b/docker-compose.yml index b05c9ab25..07d9fd9c8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -111,9 +111,17 @@ services: - verification build: context: ./ # Adjust the context path as needed - dockerfile: Dockerfiles/Dockerfile.agnet-provisioning + dockerfile: Dockerfiles/Dockerfile.agent-provisioning + args: + - ROOT_PATH=$PWD/apps/agent-provisioning/AFJ/agent-config env_file: - ./.env + environment: + - ROOT_PATH=$PWD/apps/agent-provisioning/AFJ/agent-config + volumes: + - $PWD/apps/agent-provisioning/AFJ/agent-config:/app/agent-provisioning/AFJ/agent-config + - /var/run/docker.sock:/var/run/docker.sock + - /app/agent-provisioning/AFJ/token:/app/agent-provisioning/AFJ/token agent-service: depends_on: - nats # Use depends_on instead of needs @@ -125,11 +133,16 @@ services: - organization - verification - agent-provisioning + command: sh -c 'until (docker logs platform-agent-provisioning-1 | grep "Agent-Provisioning-Service Microservice is listening to NATS"); do sleep 1; done && node dist/apps/agent-service/main.js' build: context: ./ # Adjust the context path as needed dockerfile: Dockerfiles/Dockerfile.agent-service env_file: - ./.env + volumes: + - /var/run/docker.sock:/var/run/docker.sock + volumes_from: + - agent-provisioning From 169565599ea154fc995e85870a3b7599f3d0bc1b Mon Sep 17 00:00:00 2001 From: Nishad Date: Wed, 21 Feb 2024 20:51:31 +0530 Subject: [PATCH 020/231] created deleteUserCLient roles API, created migration file for idpRoleId, created userorgrole with idp role id Signed-off-by: Nishad --- .../organization/dtos/update-invitation.dt.ts | 1 + apps/organization/src/organization.service.ts | 49 +++++++++++++++++-- apps/user/src/user.controller.ts | 6 +++ apps/user/src/user.service.ts | 14 ++++-- .../src/client-registration.service.ts | 24 ++++++++- .../migration.sql | 2 + libs/prisma-service/prisma/schema.prisma | 3 +- libs/user-org-roles/repositories/index.ts | 7 ++- .../src/user-org-roles.service.ts | 8 +-- 9 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 libs/prisma-service/prisma/migrations/20240220121359_user_org_idproleid/migration.sql diff --git a/apps/organization/dtos/update-invitation.dt.ts b/apps/organization/dtos/update-invitation.dt.ts index 1d80b08d4..21e345858 100644 --- a/apps/organization/dtos/update-invitation.dt.ts +++ b/apps/organization/dtos/update-invitation.dt.ts @@ -5,5 +5,6 @@ export class UpdateInvitationDto { orgId: string; status: Invitation; userId: string; + keycloakUserId: string; email: string; } \ No newline at end of file diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index e006eb3d3..190293d9f 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -775,6 +775,27 @@ export class OrganizationService { return userData; } + async getUserUserId(userId: string): Promise { + const pattern = { cmd: 'get-user-by-user-id' }; + // const payload = { id: userId }; + + const userData = await this.organizationServiceProxy + .send(pattern, userId) + .toPromise() + .catch((error) => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.error, + message: error.message + }, + error.status + ); + }); + return userData; + } + async fetchUserInvitation( email: string, status: string, @@ -886,12 +907,34 @@ export class OrganizationService { throw new NotFoundException(ResponseMessages.organisation.error.userNotFound); } - const isRolesExist = await this.orgRoleService.getOrgRolesByIds(roleIds); + const organizationDetails = await this.organizationRepository.getOrganizationDetails(orgId); + + if (!organizationDetails) { + throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); + } + + // const roleIdsList = []; + + const token = await this.clientRegistrationService.getManagementToken(); + const clientRolesList = await this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); + // const orgRoles = await this.orgRoleService.getOrgRoles(); - if (isRolesExist && 0 === isRolesExist.length) { - throw new NotFoundException(ResponseMessages.organisation.error.rolesNotExist); + const matchedRoles = clientRolesList.filter((role) => roleIds.includes(role.id.trim())).map((role) => role.name); + + if (roleIds.length !== matchedRoles.length) { + throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); } + const userData = await this.getUserUserId(userId); + + // const isRolesExist = await this.orgRoleService.getOrgRolesByIds(roleIds); + + // if (isRolesExist && 0 === isRolesExist.length) { + // throw new NotFoundException(ResponseMessages.organisation.error.rolesNotExist); + // } + + await this.clientRegistrationService.deleteUserClientRoles(organizationDetails.idpId, token, userData.keycloakUserId); + const deleteUserRecords = await this.userOrgRoleService.deleteOrgRoles(userId, orgId); if (0 === deleteUserRecords['count']) { diff --git a/apps/user/src/user.controller.ts b/apps/user/src/user.controller.ts index 35574e26e..802b7b150 100644 --- a/apps/user/src/user.controller.ts +++ b/apps/user/src/user.controller.ts @@ -90,6 +90,12 @@ export class UserController { async findUserByEmail(payload: { email }): Promise { return this.userService.findUserByEmail(payload); } + + @MessagePattern({ cmd: 'get-user-by-user-id' }) + async findUserByUserId(id: string): Promise { + return this.userService.findUserByUserId(id); + } + /** * @param credentialId * @returns User credentials diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index e47b569c0..4a3f427e1 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -504,6 +504,11 @@ export class UserService { } } + findUserByUserId(id: string): Promise { + return this.userRepository.getUserById(id); + + } + async resetPassword(resetPasswordDto: IUserResetPassword): Promise { const { email, oldPassword, newPassword } = resetPasswordDto; @@ -789,7 +794,7 @@ export class UserService { async acceptRejectInvitations(acceptRejectInvitation: AcceptRejectInvitationDto, userId: string): Promise { try { const userData = await this.userRepository.getUserById(userId); - return this.fetchInvitationsStatus(acceptRejectInvitation, userData.keycloakUserId, userData.email); + return this.fetchInvitationsStatus(acceptRejectInvitation, userData.keycloakUserId, userData.email, userId); } catch (error) { this.logger.error(`acceptRejectInvitations: ${error}`); throw new RpcException(error.response ? error.response : error); @@ -894,15 +899,16 @@ export class UserService { */ async fetchInvitationsStatus( acceptRejectInvitation: AcceptRejectInvitationDto, - userId: string, - email: string + keycloakUserId: string, + email: string, + userId: string ): Promise { try { const pattern = { cmd: 'update-invitation-status' }; const { orgId, invitationId, status } = acceptRejectInvitation; - const payload = { userId, orgId, invitationId, status, email }; + const payload = { userId, keycloakUserId, orgId, invitationId, status, email }; const invitationsData = await this.userServiceProxy .send(pattern, payload) diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 4ce1b7621..7c2d2b7bb 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -301,6 +301,28 @@ export class ClientRegistrationService { return 'User client role is assigned'; } + async deleteUserClientRoles( + idpId: string, + token: string, + userId: string + ): Promise { + + const realmName = process.env.KEYCLOAK_REALM; + + const createClientRolesResponse = await this.commonService.httpDelete( + await this.keycloakUrlService.GetClientUserRoleURL(realmName, userId, idpId), + this.getAuthHeader(token) + ); + + this.logger.debug( + `deleteUserClientRoles ${JSON.stringify( + createClientRolesResponse + )}` + ); + + return 'User client role is deleted'; + } + async createUserHolderRole( token: string, userId: string, @@ -730,7 +752,7 @@ export class ClientRegistrationService { payload.username = email; payload.password = password; - this.logger.log(`User Token Payload: ${JSON.stringify(payload)}`); + // this.logger.log(`User Token Payload: ${JSON.stringify(payload)}`); if ( diff --git a/libs/prisma-service/prisma/migrations/20240220121359_user_org_idproleid/migration.sql b/libs/prisma-service/prisma/migrations/20240220121359_user_org_idproleid/migration.sql new file mode 100644 index 000000000..4d6fed26f --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240220121359_user_org_idproleid/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "user_org_roles" ADD COLUMN "idpRoleId" UUID; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 714d724d6..28dcfbf44 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -84,6 +84,7 @@ model user_org_roles { userId String @db.Uuid orgRoleId String @db.Uuid orgId String? @db.Uuid + idpRoleId String? @db.Uuid organisation organisation? @relation(fields: [orgId], references: [id]) orgRole org_roles @relation(fields: [orgRoleId], references: [id]) user user @relation(fields: [userId], references: [id]) @@ -484,4 +485,4 @@ model notification { lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) lastChangedBy String @default("1") deletedAt DateTime? @db.Timestamp(6) -} \ No newline at end of file +} diff --git a/libs/user-org-roles/repositories/index.ts b/libs/user-org-roles/repositories/index.ts index 618c6d50d..5458796cd 100644 --- a/libs/user-org-roles/repositories/index.ts +++ b/libs/user-org-roles/repositories/index.ts @@ -18,13 +18,14 @@ export class UserOrgRolesRepository { * @returns user details */ // eslint-disable-next-line camelcase - async createUserOrgRole(userId: string, roleId: string, orgId?: string): Promise { + async createUserOrgRole(userId: string, roleId: string, orgId?: string, idpRoleId?: string): Promise { try { const data: { orgRole: { connect: { id: string } }; user: { connect: { id: string } }; organisation?: { connect: { id: string } }; + idpRoleId?: string } = { orgRole: { connect: { id: roleId } }, user: { connect: { id: userId } } @@ -34,6 +35,10 @@ export class UserOrgRolesRepository { data.organisation = { connect: { id: orgId } }; } + if (idpRoleId) { + data.idpRoleId = idpRoleId; + } + const saveResponse = await this.prisma.user_org_roles.create({ data }); diff --git a/libs/user-org-roles/src/user-org-roles.service.ts b/libs/user-org-roles/src/user-org-roles.service.ts index e5677dc19..ecad069db 100644 --- a/libs/user-org-roles/src/user-org-roles.service.ts +++ b/libs/user-org-roles/src/user-org-roles.service.ts @@ -13,8 +13,8 @@ export class UserOrgRolesService { * @returns user details */ // eslint-disable-next-line camelcase - async createUserOrgRole(userId: string, roleId: string, orgId?: string): Promise { - return this.userOrgRoleRepository.createUserOrgRole(userId, roleId, orgId); + async createUserOrgRole(userId: string, roleId: string, orgId?: string, idpRoleId?: string): Promise { + return this.userOrgRoleRepository.createUserOrgRole(userId, roleId, orgId, idpRoleId); } @@ -46,10 +46,10 @@ export class UserOrgRolesService { * @param roleIds * @returns */ - async updateUserOrgRole(userId: string, orgId: string, roleIds: string[]): Promise { + async updateUserOrgRole(userId: string, orgId: string, roleIds: string[], idpRoleId?: string): Promise { for (const role of roleIds) { - this.userOrgRoleRepository.createUserOrgRole(userId, role, orgId); + this.userOrgRoleRepository.createUserOrgRole(userId, role, orgId, idpRoleId); } return true; From eb690e81cccadaffa70d2b70a03754d06acc98d3 Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 21 Feb 2024 21:07:17 +0530 Subject: [PATCH 021/231] Added Additional object storing endpoint in utility Signed-off-by: Krishna --- .../src/utilities/utilities.controller.ts | 19 ++++++++-- .../src/utilities/utilities.service.ts | 5 +++ apps/connection/src/connection.module.ts | 3 +- .../interfaces/shortening-url.interface.ts | 9 +++++ apps/utility/src/utilities.controller.ts | 6 ++++ apps/utility/src/utilities.module.ts | 3 +- apps/utility/src/utilities.service.ts | 17 ++++++++- libs/aws/src/aws.service.ts | 35 ++++++++++++++++--- libs/common/src/response-messages/index.ts | 7 +++- 9 files changed, 93 insertions(+), 11 deletions(-) diff --git a/apps/api-gateway/src/utilities/utilities.controller.ts b/apps/api-gateway/src/utilities/utilities.controller.ts index 35984c216..b7f5b3e7b 100644 --- a/apps/api-gateway/src/utilities/utilities.controller.ts +++ b/apps/api-gateway/src/utilities/utilities.controller.ts @@ -1,5 +1,5 @@ import { ApiForbiddenResponse, ApiOperation, ApiResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; -import { Controller, UseFilters, Post, Body, Res, HttpStatus } from '@nestjs/common'; +import { Controller, UseFilters, Post, Body, Res, HttpStatus, Param } from '@nestjs/common'; import IResponse from '@credebl/common/interfaces/response.interface'; import { Response } from 'express'; import { ApiResponseDto } from '../dtos/apiResponse.dto'; @@ -20,7 +20,7 @@ export class UtilitiesController { constructor( private readonly utilitiesService: UtilitiesService ) { } - + @Post('/') @ApiOperation({ summary: 'Create a shorteningurl', description: 'Create a shortening url' }) @@ -35,5 +35,20 @@ export class UtilitiesController { return res.status(HttpStatus.CREATED).json(finalResponse); } + @Post('/store-object/:persistent') + @ApiOperation({ summary: 'Store an object and return a short url to it', description: 'Create a short url representing the object' }) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async storeObject(@Body() storeObjectDto: UtilitiesDto, @Param('persistent') persistent: boolean, @Res() res: Response): Promise { + // eslint-disable-next-line no-console + console.log(storeObjectDto); + const shorteningUrl = await this.utilitiesService.storeObject(persistent.valueOf(), storeObjectDto); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.storeObject.success.storeObject, + data: shorteningUrl + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + } diff --git a/apps/api-gateway/src/utilities/utilities.service.ts b/apps/api-gateway/src/utilities/utilities.service.ts index fae204616..4d3f5900a 100644 --- a/apps/api-gateway/src/utilities/utilities.service.ts +++ b/apps/api-gateway/src/utilities/utilities.service.ts @@ -12,4 +12,9 @@ export class UtilitiesService extends BaseService { async createShorteningUrl(shorteningUrlDto: UtilitiesDto): Promise { return this.sendNatsMessage(this.serviceProxy, 'create-shortening-url', shorteningUrlDto); } + + async storeObject(persistent: boolean, storeObj: UtilitiesDto): Promise { + const payload = {persistent, storeObj}; + return this.sendNatsMessage(this.serviceProxy, 'store-object', payload); + } } diff --git a/apps/connection/src/connection.module.ts b/apps/connection/src/connection.module.ts index 5182aa12d..19288cfe9 100644 --- a/apps/connection/src/connection.module.ts +++ b/apps/connection/src/connection.module.ts @@ -8,6 +8,7 @@ import { ConnectionRepository } from './connection.repository'; import { PrismaService } from '@credebl/prisma-service'; import { CacheModule } from '@nestjs/cache-manager'; import { getNatsOptions } from '@credebl/common/nats.config'; +import { UtilitiesService } from 'apps/utility/src/utilities.service'; // import { nkeyAuthenticator } from 'nats'; @Module({ @@ -24,6 +25,6 @@ import { getNatsOptions } from '@credebl/common/nats.config'; CacheModule.register() ], controllers: [ConnectionController], - providers: [ConnectionService, ConnectionRepository, PrismaService, Logger] + providers: [ConnectionService, ConnectionRepository, PrismaService, Logger, UtilitiesService] }) export class ConnectionModule { } diff --git a/apps/utility/interfaces/shortening-url.interface.ts b/apps/utility/interfaces/shortening-url.interface.ts index 4a18a4078..00d2e3a29 100644 --- a/apps/utility/interfaces/shortening-url.interface.ts +++ b/apps/utility/interfaces/shortening-url.interface.ts @@ -8,3 +8,12 @@ export interface IShorteningUrlData { export interface IAttributes { [key: string]: string } + +export interface IUtilities { + credentialId: string; + schemaId: string; + credDefId: string; + invitationUrl: string; + attributes: IAttributes[]; +} + diff --git a/apps/utility/src/utilities.controller.ts b/apps/utility/src/utilities.controller.ts index cf08cbe18..3f2b69df7 100644 --- a/apps/utility/src/utilities.controller.ts +++ b/apps/utility/src/utilities.controller.ts @@ -2,6 +2,7 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { UtilitiesService } from './utilities.service'; import { IShorteningUrlData } from '../interfaces/shortening-url.interface'; +import { IUtilities } from '../interfaces/shortening-url.interface'; @Controller() export class UtilitiesController { @@ -16,4 +17,9 @@ export class UtilitiesController { async getShorteningUrl(referenceId: string): Promise { return this.utilitiesService.getShorteningUrl(referenceId); } + + @MessagePattern({ cmd: 'store-object' }) + async storeObject(payload: {persistent: boolean, storeObj: IUtilities}): Promise { + return this.utilitiesService.storeObject(payload); + } } \ No newline at end of file diff --git a/apps/utility/src/utilities.module.ts b/apps/utility/src/utilities.module.ts index 5045ac127..5048add05 100644 --- a/apps/utility/src/utilities.module.ts +++ b/apps/utility/src/utilities.module.ts @@ -7,6 +7,7 @@ import { PrismaService } from '@credebl/prisma-service'; import { UtilitiesController } from './utilities.controller'; import { UtilitiesService } from './utilities.service'; import { UtilitiesRepository } from './utilities.repository'; +import { AwsService } from '@credebl/aws'; @Module({ imports: [ @@ -21,6 +22,6 @@ import { UtilitiesRepository } from './utilities.repository'; CacheModule.register() ], controllers: [UtilitiesController], - providers: [UtilitiesService, Logger, PrismaService, UtilitiesRepository] + providers: [UtilitiesService, Logger, PrismaService, UtilitiesRepository, AwsService] }) export class UtilitiesModule { } diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index fbd300063..a5be3a700 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -1,12 +1,16 @@ import { Injectable, Logger } from '@nestjs/common'; import { RpcException } from '@nestjs/microservices'; import { UtilitiesRepository } from './utilities.repository'; +import { AwsService } from '@credebl/aws'; +import { IUtilities } from '../interfaces/shortening-url.interface'; +import { S3 } from 'aws-sdk'; @Injectable() export class UtilitiesService { constructor( private readonly logger: Logger, - private readonly utilitiesRepository: UtilitiesRepository + private readonly utilitiesRepository: UtilitiesRepository, + private readonly awsService: AwsService ) { } async createAndStoreShorteningUrl(payload): Promise { @@ -44,4 +48,15 @@ export class UtilitiesService { throw new RpcException(error); } } + + async storeObject(payload: {persistent: boolean, storeObj: IUtilities}): Promise { + try { + const timestamp = Date.now(); + const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, timestamp, payload.storeObj); + const url: string = `https://${uploadResult.Bucket}.s3.${process.env.AWS_S3_STOREOBJECT_REGION}.amazonaws.com/${uploadResult.Key}`; + return url; + } catch (error) { + throw new Error('An error occurred while uploading data to S3.'); + } + } } diff --git a/libs/aws/src/aws.service.ts b/libs/aws/src/aws.service.ts index 29b067cdf..9c14ab513 100644 --- a/libs/aws/src/aws.service.ts +++ b/libs/aws/src/aws.service.ts @@ -7,6 +7,7 @@ import { promisify } from 'util'; export class AwsService { private s3: S3; private s4: S3; + private s3StoreObject: S3; constructor() { this.s3 = new S3({ @@ -16,19 +17,24 @@ export class AwsService { }); this.s4 = new S3({ - accessKeyId: process.env.AWS_PUBLIC_ACCESS_KEY, secretAccessKey: process.env.AWS_PUBLIC_SECRET_KEY, region: process.env.AWS_PUBLIC_REGION }); + + this.s3StoreObject = new S3({ + accessKeyId: process.env.AWS_S3_STOREOBJECT_ACCESS_KEY, + secretAccessKey: process.env.AWS_S3_STOREOBJECT_SECRET_KEY, + region: process.env.AWS_S3_STOREOBJECT_REGION + }); } - + async uploadUserCertificate( fileBuffer: Buffer, ext: string, filename: string, bucketName: string, - encoding : string, + encoding: string, pathAWS: string = '' ): Promise { const timestamp = Date.now(); @@ -43,8 +49,8 @@ export class AwsService { ContentType: `image/png` }); - const imageUrl = `https://${process.env.AWS_ORG_LOGO_BUCKET_NAME}.s3.${process.env.AWS_PUBLIC_REGION}.amazonaws.com/${pathAWS}/${encodeURIComponent(filename)}-${timestamp}.${ext}`; - return imageUrl; + const imageUrl = `https://${process.env.AWS_ORG_LOGO_BUCKET_NAME}.s3.${process.env.AWS_PUBLIC_REGION}.amazonaws.com/${pathAWS}/${encodeURIComponent(filename)}-${timestamp}.${ext}`; + return imageUrl; } catch (error) { throw new HttpException(error, HttpStatus.SERVICE_UNAVAILABLE); } @@ -87,4 +93,23 @@ export class AwsService { throw new RpcException(error.response ? error.response : error); } } + + async storeObject(persistent: boolean, key: number, body: unknown): Promise { + const objKey: string = persistent ? `persistent/${key}` : `default/${key}`; + const buf = Buffer.from(JSON.stringify(body)); + const params: AWS.S3.PutObjectRequest = { + Bucket: process.env.AWS_S3_STOREOBJECT_BUCKET, + Body: buf, + Key: objKey, + ContentEncoding: 'base64', + ContentType: 'application/json' + }; + + try { + const receivedData = await this.s3StoreObject.upload(params).promise(); + return receivedData; + } catch (error) { + throw new RpcException(error.response ? error.response : error); + } + } } diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index ecea0f3a2..3121a319b 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -391,5 +391,10 @@ export const ResponseMessages = { getshorteningUrl:'Shortening Url fetched successfully', createShorteningUrl: 'Shortening Url created successfully' } - } + }, + storeObject: { + success: { + storeObject: 'Object stored successfully' + } + } }; \ No newline at end of file From 7421fba6494f2d339b160760973d2752a19c1a5e Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:36:26 +0530 Subject: [PATCH 022/231] fix: issuance API with required attribute (#528) * fix:issuance api Signed-off-by: pranalidhanavade * fix:issuance api issue Signed-off-by: pranalidhanavade * fix:sonarlint issues Signed-off-by: pranalidhanavade * fix:requried attribute in issuance api Signed-off-by: pranalidhanavade * fix:requried attributes in csv file Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- .../src/issuance/dtos/issuance.dto.ts | 22 +- apps/issuance/src/issuance.service.ts | 261 +++++++++++------- 2 files changed, 168 insertions(+), 115 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 0d81ebe75..5b7957137 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -20,10 +20,11 @@ class Attribute { @Transform(({ value }) => trim(value)) value: string; - @ApiProperty() + @ApiProperty({ default: false }) @IsBoolean() + @IsOptional() @IsNotEmpty({ message: 'isRequired property is required' }) - isRequired: boolean; + isRequired?: boolean = false; } @@ -87,8 +88,7 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { example: [ { value: 'string', - name: 'string', - isRequired: 'boolean' + name: 'string' } ] }) @@ -100,14 +100,14 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { } class CredentialOffer { - @ApiProperty({ example: [{ 'value': 'string', 'name': 'string', 'isRequired':'boolean' }] }) + @ApiProperty({ example: [{ 'value': 'string', 'name': 'string' }] }) @IsNotEmpty({ message: 'Attribute name is required' }) @IsArray({ message: 'Attributes should be an array' }) @ValidateNested({ each: true }) @Type(() => Attribute) attributes: Attribute[]; - @ApiProperty({ example: 'testmail@mailinator.com' }) + @ApiProperty({ example: 'testmail@xyz.com' }) @IsEmail({}, { message: 'Please provide a valid email' }) @IsNotEmpty({ message: 'Email is required' }) @IsString({ message: 'Email should be a string' }) @@ -212,7 +212,7 @@ export class CredentialAttributes { } export class OOBCredentialDtoWithEmail { - @ApiProperty({ example: [{ 'emailId': 'abc@example.com', 'attributes': [{ 'value': 'string', 'name': 'string', 'isRequired':'boolean' }] }] }) + @ApiProperty({ example: [{ 'emailId': 'abc@example.com', 'attributes': [{ 'value': 'string', 'name': 'string' }] }] }) @IsNotEmpty({ message: 'Please provide valid attributes' }) @IsArray({ message: 'attributes should be array' }) @ArrayMaxSize(Number(process.env.OOB_BATCH_SIZE), { message: `Limit reached (${process.env.OOB_BATCH_SIZE} credentials max). Easily handle larger batches via seamless CSV file uploads` }) @@ -220,14 +220,6 @@ export class OOBCredentialDtoWithEmail { @Type(() => CredentialOffer) credentialOffer: CredentialOffer[]; - @ApiProperty({ example: 'awqx@getnada.com' }) - @IsEmail({}, { message: 'Please provide a valid email' }) - @IsNotEmpty({ message: 'Please provide valid email' }) - @IsString({ message: 'email should be string' }) - @Transform(({ value }) => value.trim().toLowerCase()) - @IsOptional() - emailId: string; - @ApiProperty({ example: 'string' }) @IsNotEmpty({ message: 'Please provide valid credential definition id' }) @IsString({ message: 'credential definition id should be string' }) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 68ca23842..f44d2c216 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -9,7 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs'; // import { ClientDetails, FileUploadData, ICredentialAttributesInterface, ImportFileDetails, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; -import { FileUploadData, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; +import { FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; import { OrgAgentType } from '@credebl/enum/enum'; // import { platform_config } from '@prisma/client'; import * as QRCode from 'qrcode'; @@ -51,26 +51,37 @@ export class IssuanceService { async sendCredentialCreateOffer(payload: IIssuance): Promise { + try { const { orgId, credentialDefinitionId, comment, connectionId, attributes } = payload || {}; - const attrError = []; - if (0 < attributes?.length) { - attributes?.forEach((attribute, i) => { - - if (attribute.isRequired && !attribute.value) { - attrError.push(`attributes.${i}.Value of "${attribute.name}" is required`); - return true; - } + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); + + if (schemaResponse?.attributes) { + const schemaResponseError = []; + const attributesArray: IAttributes[] = JSON.parse(schemaResponse.attributes); + + attributesArray.forEach((attribute) => { + if (attribute.attributeName && attribute.isRequired) { - return attribute.isRequired && !attribute.value; - }); - - if (0 < attrError.length) { - throw new BadRequestException(attrError); - } + payload.attributes.map((attr) => { + if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { + schemaResponseError.push( + `Attribute ${attribute.attributeName} is required` + ); + } + return true; + }); + } + }); + if (0 < schemaResponseError.length) { + throw new BadRequestException(schemaResponseError); + } - + + } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); @@ -133,28 +144,37 @@ export class IssuanceService { } } - - async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object; }> { + async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { const { orgId, credentialDefinitionId, comment, attributes, protocolVersion } = payload; - const attrError = []; - if (0 < attributes?.length) { - attributes?.forEach((attribute, i) => { - - if (attribute.isRequired && !attribute.value) { - attrError.push(`attributes.${i}.Value of "${attribute.name}" is required`); - return true; - } + const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); + + if (schemadetailsResponse?.attributes) { + const schemadetailsResponseError = []; + const attributesArray: IAttributes[] = JSON.parse(schemadetailsResponse.attributes); + + attributesArray.forEach((attribute) => { + if (attribute.attributeName && attribute.isRequired) { - return attribute.isRequired && !attribute.value; - }); - - if (0 < attrError.length) { - throw new BadRequestException(attrError); - } + payload.attributes.map((attr) => { + if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { + schemadetailsResponseError.push( + `Attribute '${attribute.attributeName}' is required but has an empty value.` + ); + } + return true; + }); + } + }); + if (0 < schemadetailsResponseError.length) { + throw new BadRequestException(schemadetailsResponseError); } + } + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); // eslint-disable-next-line camelcase @@ -239,8 +259,8 @@ export class IssuanceService { .pipe( map((response) => ( { - response - })) + response + })) ).toPromise() .catch(error => { this.logger.error(`catch: ${JSON.stringify(error)}`); @@ -387,26 +407,58 @@ export class IssuanceService { emailId } = outOfBandCredential; - const attrError = []; -if (0 < credentialOffer?.length) { - credentialOffer?.forEach((credential, i) => { - credential.attributes.forEach((attribute, i2) => { + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); - if (attribute.isRequired && !attribute.value) { - attrError.push(`credentialOffer.${i}.attributes.${i2}.Value of "${attribute.name}" is required`); - return true; - } - - return attribute.isRequired && !attribute.value; - }); + let attributesArray:IAttributes[] = []; + if (schemaResponse?.attributes) { - }); + attributesArray = JSON.parse(schemaResponse.attributes); + } - if (0 < attrError.length) { - throw new BadRequestException(attrError); - } - } - + if (0 < attributes?.length) { +const attrError = []; + attributesArray.forEach((schemaAttribute, i) => { + if (schemaAttribute.isRequired) { + + const attribute = attributes.find(attribute => attribute.name === schemaAttribute.attributeName); + if (!attribute?.value) { + attrError.push( + `attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` + ); + } + + } + + }); + if (0 < attrError.length) { + throw new BadRequestException(attrError); + } + } + if (0 < credentialOffer?.length) { +const credefError = []; + credentialOffer.forEach((credentialAttribute, index) => { + + attributesArray.forEach((schemaAttribute, i) => { + + const attribute = credentialAttribute.attributes.find(attribute => attribute.name === schemaAttribute.attributeName); + + if (schemaAttribute.isRequired && !attribute?.value) { + credefError.push( + `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` + ); + } + // + + }); + }); + if (0 < credefError.length) { + throw new BadRequestException(credefError); + } + } + + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); @@ -422,9 +474,6 @@ if (0 < credentialOffer?.length) { throw new NotFoundException(ResponseMessages.issuance.error.organizationNotFound); } - // if (!(credentialOffer && 0 < credentialOffer.length)) { - // throw new NotFoundException(ResponseMessages.issuance.error.credentialOfferNotFound); - // } let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { @@ -494,10 +543,10 @@ if (0 < credentialOffer?.length) { disposition: 'attachment' } ]; - + const isEmailSent = await sendEmail(this.emailData); this.logger.log(`isEmailSent ::: ${JSON.stringify(isEmailSent)}`); - + if (!isEmailSent) { errors.push(new InternalServerErrorException(ResponseMessages.issuance.error.emailSend)); return false; @@ -568,7 +617,7 @@ if (0 < credentialOffer?.length) { } } - + async _outOfBandCredentialOffer(outOfBandIssuancePayload: object, url: string, apiKey: string): Promise<{ response; }> { @@ -583,10 +632,10 @@ if (0 < credentialOffer?.length) { } /** - * Description: Fetch agent url - * @param referenceId - * @returns agent URL - */ + * Description: Fetch agent url + * @param referenceId + * @returns agent URL + */ async getAgentUrl( issuanceMethodLabel: string, orgAgentType: string, @@ -600,8 +649,8 @@ if (0 < credentialOffer?.length) { switch (issuanceMethodLabel) { case 'create-offer': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_CREATE_CRED_OFFER_AFJ}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_CREATE_CRED_OFFER_AFJ}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_OFFER}`.replace('#', tenantId) : null; break; @@ -609,8 +658,8 @@ if (0 < credentialOffer?.length) { case 'create-offer-oob': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_OUT_OF_BAND_CREDENTIAL_OFFER}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_OUT_OF_BAND_CREDENTIAL_OFFER}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_OFFER_OUT_OF_BAND}`.replace('#', tenantId) : null; break; @@ -618,18 +667,18 @@ if (0 < credentialOffer?.length) { case 'get-issue-credentials': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREDENTIALS}`.replace('#', tenantId) : null; break; } case 'get-issue-credential-by-credential-id': { - + url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ_BY_CRED_REC_ID}/${credentialRecordId}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ_BY_CRED_REC_ID}/${credentialRecordId}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREDENTIALS_BY_CREDENTIAL_ID}`.replace('#', credentialRecordId).replace('@', tenantId) : null; break; @@ -697,7 +746,7 @@ if (0 < credentialOffer?.length) { async importAndPreviewDataForIssuance(importFileDetails: ImportFileDetails, requestId?: string): Promise { try { - + const credDefResponse = await this.issuanceRepository.getCredentialDefinitionDetails(importFileDetails.credDefId); @@ -734,7 +783,7 @@ if (0 < credentialOffer?.length) { throw new BadRequestException(`Invalid emails found in the chosen file`); } - const fileData: string[] = parsedData.data.map(Object.values); + const fileData: string[][] = parsedData.data.map(Object.values); const fileHeader: string[] = parsedData.meta.fields; const attributesArray = JSON.parse(credDefResponse.attributes); @@ -749,7 +798,7 @@ if (0 < credentialOffer?.length) { } await this.validateFileHeaders(fileHeader, attributeNameArray); - await this.validateFileData(fileData); + await this.validateFileData(fileData, attributesArray, fileHeader); const resData = { schemaLedgerId: credDefResponse.schemaLedgerId, @@ -765,8 +814,8 @@ if (0 < credentialOffer?.length) { return newCacheKey; } catch (error) { - this.logger.error(`error in validating credentials : ${error}`); - throw new RpcException(error.response ? error.response : error); + this.logger.error(`error in validating credentials : ${error.response}`); + throw new RpcException(error.response ? error.response : error); } finally { // await this.awsService.deleteFile(importFileDetails.fileKey); // this.logger.error(`Deleted uploaded file after processing.`); @@ -892,11 +941,11 @@ if (0 < credentialOffer?.length) { } if (cachedData && clientDetails?.isSelectiveIssuance) { - await this.cacheManager.del(requestId); - await this.importAndPreviewDataForIssuance(reqPayload, requestId); + await this.cacheManager.del(requestId); + await this.importAndPreviewDataForIssuance(reqPayload, requestId); // await this.cacheManager.set(requestId, reqPayload); cachedData = await this.cacheManager.get(requestId); - } + } const parsedData = JSON.parse(cachedData as string).fileData.data; const parsedPrimeDetails = JSON.parse(cachedData as string); @@ -1117,7 +1166,6 @@ if (0 < credentialOffer?.length) { ): Promise { try { const fileSchemaHeader: string[] = fileHeader.slice(); - if ('email' === fileHeader[0]) { fileSchemaHeader.splice(0, 1); } else { @@ -1141,25 +1189,38 @@ if (0 < credentialOffer?.length) { } } - async validateFileData(fileData: string[]): Promise { - let rowIndex: number = 0; - let columnIndex: number = 0; - const isNullish = Object.values(fileData).some((value) => { - columnIndex = 0; - rowIndex++; - const isFalsyForColumnValue = Object.values(value).some((colvalue) => { - columnIndex++; - if (null === colvalue || '' == colvalue) { - return true; - } - return false; + async validateFileData(fileData: string[][], attributesArray: { attributeName: string, schemaDataType: string, displayName: string, isRequired: boolean }[], fileHeader: string[]): Promise { + try { + const filedata = fileData.map((item: string[]) => { + const fileHeaderData = item?.map((element, j) => ({ + value: element, + header: fileHeader[j] + })); + return fileHeaderData; }); - return isFalsyForColumnValue; - }); - if (isNullish) { - throw new BadRequestException( - `Empty data found at row ${rowIndex} and column ${columnIndex}` - ); + + const errorFileData = []; + + filedata.forEach((attr, i) => { + attr.forEach((eachElement) => { + + attributesArray.forEach((eachItem) => { + if (eachItem.attributeName === eachElement.header) { + if (eachItem.isRequired && !eachElement.value) { + errorFileData.push(`Attribute ${eachItem.attributeName} is required at row ${i + 1}`); + } + } + }); + return eachElement; + }); + return attr; + }); + + if (0 < errorFileData.length) { + throw new BadRequestException(errorFileData); + } + } catch (error) { + throw error; } } @@ -1174,9 +1235,9 @@ if (0 < credentialOffer?.length) { } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException({ - status: error.status, - error: error.message - }, error.status); + status: error.status, + error: error.message + }, error.status); } } From cb84abecf67cfd0e51d7fe5181e178a4d2d8faf1 Mon Sep 17 00:00:00 2001 From: sanjay-k1910 Date: Thu, 22 Feb 2024 18:24:58 +0530 Subject: [PATCH 023/231] fix: search and sorting issue in ecosystem member list API Signed-off-by: sanjay-k1910 --- .../src/authz/guards/ecosystem-roles.guard.ts | 16 +-- .../src/ecosystem/dtos/get-members.dto.ts | 29 +--- .../src/ecosystem/ecosystem.controller.ts | 13 +- .../src/ecosystem/ecosystem.service.ts | 9 +- .../interfaces/ecosystemMembers.interface.ts | 1 + apps/ecosystem/src/ecosystem.controller.ts | 2 +- apps/ecosystem/src/ecosystem.repository.ts | 134 +++++++++--------- apps/ecosystem/src/ecosystem.service.ts | 7 +- libs/enum/src/enum.ts | 7 + libs/http-exception.filter.ts | 4 + 10 files changed, 110 insertions(+), 112 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts index 9f011b92c..099f1146a 100644 --- a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts @@ -1,8 +1,4 @@ -import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Logger } from '@nestjs/common'; - -import { HttpException } from '@nestjs/common'; -import { HttpStatus } from '@nestjs/common'; -import { Injectable } from '@nestjs/common'; +import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Logger, Injectable } from '@nestjs/common'; import { ECOSYSTEM_ROLES_KEY } from '../decorators/roles.decorator'; import { Reflector } from '@nestjs/core'; import { EcosystemService } from '../../ecosystem/ecosystem.service'; @@ -54,19 +50,17 @@ export class EcosystemRolesGuard implements CanActivate { const ecosystemOrgData = await this.ecosystemService.fetchEcosystemOrg(ecosystemId, orgId); if (!ecosystemOrgData) { - throw new HttpException('Organization does not match', HttpStatus.FORBIDDEN); + throw new ForbiddenException('Organization does not match'); } - const {response} = ecosystemOrgData; - - user.ecosystemOrgRole = response['ecosystemRole']['name']; + user.ecosystemOrgRole = ecosystemOrgData['ecosystemRole']['name']; if (!user.ecosystemOrgRole) { - throw new HttpException('Ecosystem role not match', HttpStatus.FORBIDDEN); + throw new ForbiddenException('Ecosystem role not match'); } } else { - throw new HttpException('organization & ecosystem is required', HttpStatus.BAD_REQUEST); + throw new BadRequestException('organization & ecosystem is required'); } // Sending user friendly message if a user attempts to access an API that is inaccessible to their role diff --git a/apps/api-gateway/src/ecosystem/dtos/get-members.dto.ts b/apps/api-gateway/src/ecosystem/dtos/get-members.dto.ts index 3a7e78f78..f12af3ea4 100644 --- a/apps/api-gateway/src/ecosystem/dtos/get-members.dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/get-members.dto.ts @@ -1,34 +1,20 @@ -import { Transform, Type } from 'class-transformer'; +import { Transform } from 'class-transformer'; import { trim } from '@credebl/common/cast.helper'; import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsOptional } from 'class-validator'; -import { SortFields } from 'apps/connection/src/enum/connection.enum'; -import { SortValue } from '@credebl/enum/enum'; - -export class GetAllEcosystemMembersDto { - - @ApiProperty({ required: false, example: '1' }) - @IsOptional() - pageNumber: number; - - @ApiProperty({ required: false, example: '10' }) - @IsOptional() - pageSize: number; - - @ApiProperty({ required: false }) - @IsOptional() - @Transform(({ value }) => trim(value)) - @Type(() => String) - search: string = ''; +import { SortMembers, SortValue } from '@credebl/enum/enum'; +import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; +export class GetAllEcosystemMembersDto extends PaginationDto { @ApiProperty({ + enum: [SortMembers.CREATED_DATE_TIME, SortMembers.ID, SortMembers.ORGANIZATION, SortMembers.STATUS], required: false }) @Transform(({ value }) => trim(value)) @IsOptional() - @IsEnum(SortFields) - sortField: string = SortFields.CREATED_DATE_TIME; + @IsEnum(SortMembers) + sortField: string = SortMembers.CREATED_DATE_TIME; @ApiProperty({ enum: [SortValue.DESC, SortValue.ASC], @@ -38,5 +24,4 @@ export class GetAllEcosystemMembersDto { @IsOptional() @IsEnum(SortValue) sortBy: string = SortValue.DESC; - } diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index bfc3371c8..05a0ba0b6 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -1,6 +1,6 @@ import { ApiBearerAuth, ApiExcludeEndpoint, ApiForbiddenResponse, ApiOperation, ApiQuery, ApiResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; import { EcosystemService } from './ecosystem.service'; -import { Controller, UseFilters, Put, Post, Get, Body, Param, UseGuards, Query, BadRequestException, Delete, HttpStatus, Res } from '@nestjs/common'; +import { Controller, UseFilters, Put, Post, Get, Body, Param, UseGuards, Query, BadRequestException, Delete, HttpStatus, Res, ParseUUIDPipe } from '@nestjs/common'; import { RequestCredDefDto, RequestSchemaDto } from './dtos/request-schema.dto'; import IResponse from '@credebl/common/interfaces/response.interface'; import { Response } from 'express'; @@ -256,7 +256,7 @@ export class EcosystemController { @EcosystemsRoles(EcosystemRoles.ECOSYSTEM_OWNER, EcosystemRoles.ECOSYSTEM_LEAD, EcosystemRoles.ECOSYSTEM_MEMBER) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard, OrgRolesGuard) - @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiOperation({ summary: 'Get ecosystem members list', description: 'Get ecosystem members list.' }) @ApiQuery({ name: 'pageNumber', @@ -274,18 +274,19 @@ export class EcosystemController { required: false }) async getEcosystemMembers( - @Param('ecosystemId') ecosystemId: string, + @Param('ecosystemId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for ecosystemId`); }})) ecosystemId: string, @Param('orgId') orgId: string, @Query() getEcosystemMembers: GetAllEcosystemMembersDto, @Res() res: Response): Promise { + const members = await this.ecosystemService.getEcosystemMembers(ecosystemId, getEcosystemMembers); const finalResponse: IResponse = { - statusCode: 200, + statusCode: HttpStatus.OK, message: ResponseMessages.ecosystem.success.fetchMembers, - data: members?.response + data: members }; - return res.status(200).json(finalResponse); + return res.status(HttpStatus.OK).json(finalResponse); } @Post('/:ecosystemId/:orgId/transaction/schema') diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index 2494fee1b..92822bf1d 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -93,11 +93,10 @@ export class EcosystemService extends BaseService { */ async getEcosystemMembers( ecosystemId: string, - getEcosystemMembers: GetAllEcosystemMembersDto + payload: GetAllEcosystemMembersDto ): Promise<{ response: object }> { - const { pageNumber, pageSize, search, sortBy } = getEcosystemMembers; - const payload = { ecosystemId, pageNumber, pageSize, search, sortBy }; - return this.sendNats(this.serviceProxy, 'fetch-ecosystem-members', payload); + payload['ecosystemId'] = ecosystemId; + return this.sendNatsMessage(this.serviceProxy, 'fetch-ecosystem-members', payload); } /** @@ -128,7 +127,7 @@ export class EcosystemService extends BaseService { async fetchEcosystemOrg(ecosystemId: string, orgId: string): Promise<{ response: object }> { const payload = { ecosystemId, orgId }; - return this.sendNats(this.serviceProxy, 'fetch-ecosystem-org-data', payload); + return this.sendNatsMessage(this.serviceProxy, 'fetch-ecosystem-org-data', payload); } async getEndorsementTranasactions( diff --git a/apps/ecosystem/interfaces/ecosystemMembers.interface.ts b/apps/ecosystem/interfaces/ecosystemMembers.interface.ts index 8ea39f7cf..58b12fd4b 100644 --- a/apps/ecosystem/interfaces/ecosystemMembers.interface.ts +++ b/apps/ecosystem/interfaces/ecosystemMembers.interface.ts @@ -5,4 +5,5 @@ export interface EcosystemMembersPayload { pageSize: number; search: string; sortBy: string; + sortField: string; } \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index db3ab3281..4797821ed 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -79,7 +79,7 @@ export class EcosystemController { */ @MessagePattern({ cmd: 'fetch-ecosystem-members' }) async getEcosystemMembers(@Body() payload: EcosystemMembersPayload): Promise { - return this.ecosystemService.getEcoystemMembers(payload); + return this.ecosystemService.getEcosystemMembers(payload); } /** diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index da17cd2f0..98be181dc 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -9,6 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { NotFoundException } from '@nestjs/common'; import { CommonConstants } from '@credebl/common/common.constant'; import { GetAllSchemaList } from '../interfaces/endorsements.interface'; +import { SortValue } from '@credebl/enum/enum'; // eslint-disable-next-line camelcase @Injectable() @@ -160,7 +161,7 @@ export class EcosystemRepository { } }) ]); - + return { ecosystemDetails, totalCount: ecosystemCount @@ -170,7 +171,7 @@ export class EcosystemRepository { throw error; } } - + /** * @@ -486,62 +487,67 @@ export class EcosystemRepository { * @returns users list */ -async findEcosystemMembers( - ecosystemId: string, - pageNumber: number, - pageSize: number, - search: string, - sortBy: string -): Promise { - try { - const result = await this.prisma.$transaction([ - this.prisma.ecosystem_orgs.findMany({ - where: { - ecosystemId, - OR: [ - { - organisation: { - name: { contains: search, mode: 'insensitive' }, - // eslint-disable-next-line camelcase - org_agents: { - some: { - orgDid: { contains: search, mode: 'insensitive' } + async findEcosystemMembers( + ecosystemId: string, + pageNumber: number, + pageSize: number, + search: string, + sortBy: string, + sortField: string + ): Promise { + try { + const result = await this.prisma.$transaction([ + this.prisma.ecosystem_orgs.findMany({ + where: { + ecosystemId, + OR: [ + { + organisation: { + name: { contains: search, mode: 'insensitive' } + } + }, + { + organisation: { + // eslint-disable-next-line camelcase + org_agents: { + some: { + orgDid: { contains: search, mode: 'insensitive' } + } } } } + ] + }, + include: { + ecosystem: true, + ecosystemRole: true, + organisation: { + select: { + name: true, + orgSlug: true, + // eslint-disable-next-line camelcase + org_agents: true + } } - ] - }, - include: { - ecosystem: true, - ecosystemRole: true, - organisation: { - select: { - name: true, - orgSlug: true, - // eslint-disable-next-line camelcase - org_agents: true - } + }, + take: Number(pageSize), + skip: (pageNumber - 1) * pageSize, + orderBy: { + [sortField]: SortValue.ASC === sortBy ? 'asc' : 'desc' } - }, - take: Number(pageSize), - skip: (pageNumber - 1) * pageSize, - orderBy: { - createDateTime: 'asc' === sortBy ? 'asc' : 'desc' - } - }), - this.prisma.ecosystem_orgs.count({ - where: { - ecosystemId - } - }) - ]); - return result; - } catch (error) { - this.logger.error(`error: ${JSON.stringify(error)}`); - throw error; + }), + this.prisma.ecosystem_orgs.count({ + where: { + ecosystemId + } + }) + ]); + return result; + } catch (error) { + this.logger.error(`error: ${JSON.stringify(error)}`); + throw error; + } } -} async getEcosystemInvitationsPagination(queryObject: object, pageNumber: number, pageSize: number): Promise { try { @@ -590,18 +596,18 @@ async findEcosystemMembers( async fetchEcosystemOrg( payload: object ): Promise { - + return this.prisma.ecosystem_orgs.findFirst({ - where: { - ...payload - }, - select: { - ecosystem: true, - ecosystemRole: true, - organisation: true - } - }); - + where: { + ...payload + }, + select: { + ecosystem: true, + ecosystemRole: true, + organisation: true + } + }); + } @@ -833,7 +839,7 @@ async findEcosystemMembers( schemaTransactionResponse: SchemaTransactionResponse, requestBody: object, type: endorsementTransactionType - // eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase ): Promise { try { const { endorserDid, authorDid, requestPayload, status, ecosystemOrgId, userId } = schemaTransactionResponse; diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 40c540167..97fb35a47 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -1109,15 +1109,16 @@ export class EcosystemService { * @returns Ecosystem members list */ - async getEcoystemMembers(payload: EcosystemMembersPayload): Promise { + async getEcosystemMembers(payload: EcosystemMembersPayload): Promise { try { - const { ecosystemId, pageNumber, pageSize, search, sortBy } = payload; + const { ecosystemId, pageNumber, pageSize, search, sortBy, sortField } = payload; const getEcosystemMember = await this.ecosystemRepository.findEcosystemMembers( ecosystemId, pageNumber, pageSize, search, - sortBy + sortBy, + sortField ); const ecosystemMemberResponse = { diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 2dd5470ed..3536f0bbc 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -70,6 +70,13 @@ export enum AutoAccept { Never = "never" } +export enum SortMembers { + CREATED_DATE_TIME = 'createDateTime', + STATUS = 'status', + ID = 'id', + ORGANIZATION = 'organization' +} + const transitionMap: { [key in Invitation]: Invitation[] } = { [Invitation.PENDING]: [Invitation.ACCEPTED, Invitation.REJECTED], [Invitation.ACCEPTED]: [], diff --git a/libs/http-exception.filter.ts b/libs/http-exception.filter.ts index 645b20b3a..c7dc008fb 100644 --- a/libs/http-exception.filter.ts +++ b/libs/http-exception.filter.ts @@ -39,6 +39,10 @@ export class HttpExceptionFilter implements ExceptionFilter { httpStatus = HttpStatus.BAD_REQUEST; message = exception?.response?.message || exception?.message; break; + case 'P2023': // Inconsistent column data: {message} + httpStatus = HttpStatus.BAD_REQUEST; + message = exception?.meta?.message || exception?.message; + break; case 'P2018': // The required connected records were not found. {details} case 'P2025': // An operation failed because it depends on one or more records that were required but not found. {cause} case 'P2015': // A related record could not be found. {details} From ef57150c7abe0357e96fe66416613ba81e94fbec Mon Sep 17 00:00:00 2001 From: sanjay-k1910 Date: Thu, 22 Feb 2024 18:34:04 +0530 Subject: [PATCH 024/231] refactor: type Signed-off-by: sanjay-k1910 --- apps/api-gateway/src/ecosystem/ecosystem.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index 92822bf1d..a55c21d13 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -125,7 +125,7 @@ export class EcosystemService extends BaseService { return this.sendNats(this.serviceProxy, 'accept-reject-ecosystem-invitations', payload); } - async fetchEcosystemOrg(ecosystemId: string, orgId: string): Promise<{ response: object }> { + async fetchEcosystemOrg(ecosystemId: string, orgId: string): Promise { const payload = { ecosystemId, orgId }; return this.sendNatsMessage(this.serviceProxy, 'fetch-ecosystem-org-data', payload); } From b608fde5895170bbdeebcf8e061a5173273ad898 Mon Sep 17 00:00:00 2001 From: Nishad Date: Thu, 22 Feb 2024 19:12:44 +0530 Subject: [PATCH 025/231] Refactored the updateUserOrgRole function, Worked on the orgroleguard to get client details from user access token, worked on the update user roles for the client Signed-off-by: Nishad --- .../src/authz/guards/org-roles.guard.ts | 17 +++- apps/organization/src/organization.service.ts | 88 +++++++++++++------ libs/common/src/common.service.ts | 1 - .../src/user-org-roles.service.ts | 10 ++- 4 files changed, 84 insertions(+), 32 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/org-roles.guard.ts b/apps/api-gateway/src/authz/guards/org-roles.guard.ts index b4e71fb26..5888b1dc7 100644 --- a/apps/api-gateway/src/authz/guards/org-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/org-roles.guard.ts @@ -40,9 +40,22 @@ export class OrgRolesGuard implements CanActivate { if (!isValidUUID(orgId)) { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); - } + } - if (orgId) { + if (orgId) { + + const orgDetails = user.resource_access[orgId]; + + if (orgDetails) { + const orgRoles: string[] = orgDetails.roles; + const roleAccess = requiredRoles.some((role) => orgRoles.includes(role)); + + if (!roleAccess) { + throw new ForbiddenException(ResponseMessages.organisation.error.roleNotMatch, { cause: new Error(), description: ResponseMessages.errorMessages.forbidden }); + } + return roleAccess; + } + const specificOrg = user.userOrgRoles.find((orgDetails) => { if (!orgDetails.orgId) { return false; diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 190293d9f..6f9ed83d2 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -260,7 +260,14 @@ export class OrganizationService { const deleteUserRecords = await this.userOrgRoleService.deleteOrgRoles(userId, orgId); this.logger.log(`deleteUserRecords::`, deleteUserRecords); - await this.userOrgRoleService.updateUserOrgRole(userId, orgId, [ownerRoleData.id], ownerRoleClient.id); + const roleIdList = [ + { + roleId: ownerRoleData.id, + idpRoleId: ownerRoleClient.id + } + ]; + + await this.userOrgRoleService.updateUserOrgRole(userId, orgId, roleIdList); } return orgDetails; @@ -846,9 +853,8 @@ export class OrganizationService { status }; - await this.organizationRepository.updateOrgInvitation(invitationId, data); - if (status === Invitation.REJECTED) { + await this.organizationRepository.updateOrgInvitation(invitationId, data); return ResponseMessages.user.success.invitationReject; } @@ -857,15 +863,15 @@ export class OrganizationService { const orgRoles = await this.orgRoleService.getOrgRolesByIds(invitation.orgRoles); - const rolesPayload: { id: string; name: string; idpId: string }[] = orgRoles.map((orgRole: IOrgRole) => { - let roleObj: { id: string; name: string; idpId: string } = null; + const rolesPayload: { roleId: string; name: string; idpRoleId: string }[] = orgRoles.map((orgRole: IOrgRole) => { + let roleObj: { roleId: string; name: string; idpRoleId: string} = null; for (let index = 0; index < clientRolesList.length; index++) { if (clientRolesList[index].name === orgRole.name) { roleObj = { - id: orgRole.id, + roleId: orgRole.id, name: orgRole.name, - idpId: clientRolesList[index].id + idpRoleId: clientRolesList[index].id }; break; } @@ -874,16 +880,11 @@ export class OrganizationService { return roleObj; }); - await this.clientRegistrationService.createUserClientRole( - organizationDetails.idpId, - token, - keycloakUserId, - rolesPayload.map((role) => ({ id: role.idpId, name: role.name })) - ); - - for (const roleData of rolesPayload) { - await this.userOrgRoleService.createUserOrgRole(userId, roleData.id, orgId, roleData.idpId); - } + await Promise.all([ + this.organizationRepository.updateOrgInvitation(invitationId, data), + this.clientRegistrationService.createUserClientRole(organizationDetails.idpId, token, keycloakUserId, rolesPayload.map(role => ({id: role.idpRoleId, name: role.name}))), + this.userOrgRoleService.updateUserOrgRole(userId, orgId, rolesPayload) + ]); return ResponseMessages.user.success.invitationAccept; } catch (error) { @@ -917,14 +918,33 @@ export class OrganizationService { const token = await this.clientRegistrationService.getManagementToken(); const clientRolesList = await this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); - // const orgRoles = await this.orgRoleService.getOrgRoles(); + const orgRoles = await this.orgRoleService.getOrgRoles(); - const matchedRoles = clientRolesList.filter((role) => roleIds.includes(role.id.trim())).map((role) => role.name); + const matchedClientRoles = clientRolesList.filter((role) => roleIds.includes(role.id.trim())); + // .map((role) => role.name); + // const matchedOrgRoles = orgRoles.filter((role) => matchedClientRoles.some(clientRole => clientRole.name === role.name)); - if (roleIds.length !== matchedRoles.length) { + if (roleIds.length !== matchedClientRoles.length) { throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); } + const rolesPayload: { roleId: string; name: string; idpRoleId: string }[] = matchedClientRoles.map((clientRole: IClientRoles) => { + let roleObj: { roleId: string; name: string; idpRoleId: string} = null; + + for (let index = 0; index < orgRoles.length; index++) { + if (orgRoles[index].name === clientRole.name) { + roleObj = { + roleId: orgRoles[index].id, + name: orgRoles[index].name, + idpRoleId: clientRole.id + }; + break; + } + } + + return roleObj; + }); + const userData = await this.getUserUserId(userId); // const isRolesExist = await this.orgRoleService.getOrgRolesByIds(roleIds); @@ -933,15 +953,31 @@ export class OrganizationService { // throw new NotFoundException(ResponseMessages.organisation.error.rolesNotExist); // } - await this.clientRegistrationService.deleteUserClientRoles(organizationDetails.idpId, token, userData.keycloakUserId); + // await this.clientRegistrationService.deleteUserClientRoles(organizationDetails.idpId, token, userData.keycloakUserId); - const deleteUserRecords = await this.userOrgRoleService.deleteOrgRoles(userId, orgId); + // const deleteUserRecords = await this.userOrgRoleService.deleteOrgRoles(userId, orgId); - if (0 === deleteUserRecords['count']) { - throw new InternalServerErrorException(ResponseMessages.organisation.error.updateUserRoles); - } + const [ + , + deletedUserRoleRecords + ] = await Promise.all([ + this.clientRegistrationService.deleteUserClientRoles(organizationDetails.idpId, token, userData.keycloakUserId), + this.userOrgRoleService.deleteOrgRoles(userId, orgId) + ]); - return this.userOrgRoleService.updateUserOrgRole(userId, orgId, roleIds); + if (0 === deletedUserRoleRecords['count']) { + throw new InternalServerErrorException(ResponseMessages.organisation.error.updateUserRoles); + } + + const [ + , + isUserRoleUpdated + ] = await Promise.all([ + this.clientRegistrationService.createUserClientRole(organizationDetails.idpId, token, userData.keycloakUserId, rolesPayload.map(role => ({id: role.idpRoleId, name: role.name}))), + this.userOrgRoleService.updateUserOrgRole(userId, orgId, rolesPayload) + ]); + + return isUserRoleUpdated; } catch (error) { this.logger.error(`Error in updateUserRoles: ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index 5a34f2247..f6050f09b 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -36,7 +36,6 @@ export class CommonService { .post(url, payload, apiKey) .toPromise() .then((response: any) => { - this.logger.error(response.data); return response.data; }); } catch (error) { diff --git a/libs/user-org-roles/src/user-org-roles.service.ts b/libs/user-org-roles/src/user-org-roles.service.ts index ecad069db..0b87cd9cf 100644 --- a/libs/user-org-roles/src/user-org-roles.service.ts +++ b/libs/user-org-roles/src/user-org-roles.service.ts @@ -46,10 +46,14 @@ export class UserOrgRolesService { * @param roleIds * @returns */ - async updateUserOrgRole(userId: string, orgId: string, roleIds: string[], idpRoleId?: string): Promise { + async updateUserOrgRole( + userId: string, + orgId: string, + roleIdList: {roleId: string, idpRoleId: string}[] + ): Promise { - for (const role of roleIds) { - this.userOrgRoleRepository.createUserOrgRole(userId, role, orgId, idpRoleId); + for (const roleData of roleIdList) { + this.userOrgRoleRepository.createUserOrgRole(userId, roleData.roleId, orgId, roleData.idpRoleId); } return true; From 617b664edc9d9ac7b691c7ff4a739594ba1b4748 Mon Sep 17 00:00:00 2001 From: Nishad Date: Thu, 22 Feb 2024 19:27:02 +0530 Subject: [PATCH 026/231] Worked on the fix of user registration if alredy registered Signed-off-by: Nishad --- apps/user/src/user.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index 37e6de446..f9e112b41 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -221,7 +221,7 @@ export class UserService { if (!checkUserDetails) { throw new NotFoundException(ResponseMessages.user.error.emailIsNotVerified); } - if (checkUserDetails.keycloakUserId) { + if (checkUserDetails.keycloakUserId || (!checkUserDetails.keycloakUserId && checkUserDetails.supabaseUserId)) { throw new ConflictException(ResponseMessages.user.error.exists); } if (false === checkUserDetails.isEmailVerified) { @@ -974,6 +974,8 @@ export class UserService { throw new ConflictException(ResponseMessages.user.error.verificationAlreadySent); } else if (userDetails && userDetails.keycloakUserId) { throw new ConflictException(ResponseMessages.user.error.exists); + } else if (userDetails && !userDetails.keycloakUserId && userDetails.supabaseUserId) { + throw new ConflictException(ResponseMessages.user.error.exists); } else if (null === userDetails) { return { isRegistrationCompleted: false, From e07698a85b730a973344e49b73825117ebc3c721 Mon Sep 17 00:00:00 2001 From: sanjay-k1910 Date: Thu, 22 Feb 2024 19:42:59 +0530 Subject: [PATCH 027/231] refactor: error messages in common messages Signed-off-by: sanjay-k1910 --- apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts | 6 +++--- libs/common/src/response-messages/index.ts | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts index 099f1146a..af5b5b619 100644 --- a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts @@ -50,17 +50,17 @@ export class EcosystemRolesGuard implements CanActivate { const ecosystemOrgData = await this.ecosystemService.fetchEcosystemOrg(ecosystemId, orgId); if (!ecosystemOrgData) { - throw new ForbiddenException('Organization does not match'); + throw new ForbiddenException(ResponseMessages.organisation.error.orgDoesNotMatch); } user.ecosystemOrgRole = ecosystemOrgData['ecosystemRole']['name']; if (!user.ecosystemOrgRole) { - throw new ForbiddenException('Ecosystem role not match'); + throw new ForbiddenException(ResponseMessages.ecosystem.error.ecosystemRoleNotMatch); } } else { - throw new BadRequestException('organization & ecosystem is required'); + throw new BadRequestException(ResponseMessages.ecosystem.error.orgEcoIdRequired); } // Sending user friendly message if a user attempts to access an API that is inaccessible to their role diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 5f6cb2cb3..695d8210c 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -107,7 +107,8 @@ export const ResponseMessages = { invalidUserId:'Invalid format of userId', invalidInvitationId:'Invalid format for invitation id', ecosystemIdIsRequired:'ecosystemId is required', - roleNotMatch: 'User does not have access' + roleNotMatch: 'User does not have access', + orgDoesNotMatch: 'Organization does not match' } }, @@ -353,7 +354,9 @@ export const ResponseMessages = { updateSchemaId: 'Error while updating the schema id', updateCredDefId: 'Error while updating the credential-definition', invalidMessage: 'Invalid transaction details. Missing "message" property.', - invalidTransactionMessage: 'Invalid transaction details' + invalidTransactionMessage: 'Invalid transaction details', + ecosystemRoleNotMatch: 'Ecosystem role not match', + orgEcoIdRequired: 'OrgId & EcosystemId is required' } }, bulkIssuance: { From 7374c3b6522254789aab89c725e65273aa7a0c33 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Thu, 22 Feb 2024 19:51:20 +0530 Subject: [PATCH 028/231] fix:csv file error handling (#536) Signed-off-by: pranalidhanavade --- apps/issuance/src/issuance.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index f44d2c216..41cfaa105 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -815,7 +815,7 @@ const credefError = []; } catch (error) { this.logger.error(`error in validating credentials : ${error.response}`); - throw new RpcException(error.response ? error.response : error); + throw new Error(error.response.message ? error.response.message : error); } finally { // await this.awsService.deleteFile(importFileDetails.fileKey); // this.logger.error(`Deleted uploaded file after processing.`); From d88ab7657bfbc3b9fa1859364fff0ffeb8c6555e Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 22 Feb 2024 20:12:36 +0530 Subject: [PATCH 029/231] Added some changes Signed-off-by: Krishna --- .../AFJ/scripts/start_agent.sh | 6 +- apps/api-gateway/src/main.ts | 8 +- .../src/utilities/dtos/store-object.dto.ts | 11 +++ .../src/utilities/utilities.controller.ts | 3 +- .../src/utilities/utilities.service.ts | 8 +- apps/connection/src/connection.module.ts | 3 +- apps/connection/src/connection.service.ts | 35 ++++++++ apps/utility/src/utilities.controller.ts | 6 +- apps/utility/src/utilities.service.ts | 4 +- pnpm-lock.yaml | 89 ++++++++++++++++--- 10 files changed, 143 insertions(+), 30 deletions(-) mode change 100644 => 100755 apps/agent-provisioning/AFJ/scripts/start_agent.sh create mode 100644 apps/api-gateway/src/utilities/dtos/store-object.dto.ts diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh old mode 100644 new mode 100755 index 37047815c..3fdea6a2d --- a/apps/agent-provisioning/AFJ/scripts/start_agent.sh +++ b/apps/agent-provisioning/AFJ/scripts/start_agent.sh @@ -21,8 +21,8 @@ INBOUND_ENDPOINT=${16} ADMIN_PORT_FILE="$PWD/apps/agent-provisioning/AFJ/port-file/last-admin-port.txt" INBOUND_PORT_FILE="$PWD/apps/agent-provisioning/AFJ/port-file/last-inbound-port.txt" -ADMIN_PORT=8001 -INBOUND_PORT=9001 +ADMIN_PORT=4001 +INBOUND_PORT=5001 increment_port() { local port="$1" @@ -227,7 +227,7 @@ if [ $? -eq 0 ]; then container_logs=$(docker logs $(docker ps -q --filter "name=${AGENCY}_${CONTAINER_NAME}")) # Extract the token from the logs using sed - token=$(echo "$container_logs" | sed -nE 's/.*API Toekn: ([^ ]+).*/\1/p') + token=$(echo "$container_logs" | sed -nE 's/.*API Token: ([^ ]+).*/\1/p') # Print the extracted token echo "Token: $token" diff --git a/apps/api-gateway/src/main.ts b/apps/api-gateway/src/main.ts index 80d22c6ad..606427ac4 100644 --- a/apps/api-gateway/src/main.ts +++ b/apps/api-gateway/src/main.ts @@ -10,7 +10,7 @@ import { AllExceptionsFilter } from '@credebl/common/exception-handler'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { getNatsOptions } from '@credebl/common/nats.config'; -import helmet from 'helmet'; +// import helmet from 'helmet'; import { NodeEnvironment } from '@credebl/enum/enum'; dotenv.config(); @@ -61,9 +61,9 @@ async function bootstrap(): Promise { app.use(express.static('uploadedFiles/bulk-verification-templates')); app.use(express.static('uploadedFiles/import')); app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); - app.use(helmet({ - xssFilter:true - })); + // app.use(helmet({ + // xssFilter:true + // })); await app.listen(process.env.API_GATEWAY_PORT, `${process.env.API_GATEWAY_HOST}`); Logger.log(`API Gateway is listening on port ${process.env.API_GATEWAY_PORT}`); } diff --git a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts new file mode 100644 index 000000000..d52fd8f5e --- /dev/null +++ b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts @@ -0,0 +1,11 @@ +import { ApiExtraModels, ApiProperty } from '@nestjs/swagger'; + +@ApiExtraModels() + +export class StoreObjectDto { + + @ApiProperty({ + description: 'The data to be stored' + }) + data: object; +} \ No newline at end of file diff --git a/apps/api-gateway/src/utilities/utilities.controller.ts b/apps/api-gateway/src/utilities/utilities.controller.ts index b7f5b3e7b..e0b02fa4c 100644 --- a/apps/api-gateway/src/utilities/utilities.controller.ts +++ b/apps/api-gateway/src/utilities/utilities.controller.ts @@ -9,6 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { UtilitiesDto } from './dtos/shortening-url.dto'; import { UtilitiesService } from './utilities.service'; +import { StoreObjectDto } from './dtos/store-object.dto'; @UseFilters(CustomExceptionFilter) @Controller('utilities') @@ -38,7 +39,7 @@ export class UtilitiesController { @Post('/store-object/:persistent') @ApiOperation({ summary: 'Store an object and return a short url to it', description: 'Create a short url representing the object' }) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) - async storeObject(@Body() storeObjectDto: UtilitiesDto, @Param('persistent') persistent: boolean, @Res() res: Response): Promise { + async storeObject(@Body() storeObjectDto: StoreObjectDto, @Param('persistent') persistent: boolean, @Res() res: Response): Promise { // eslint-disable-next-line no-console console.log(storeObjectDto); const shorteningUrl = await this.utilitiesService.storeObject(persistent.valueOf(), storeObjectDto); diff --git a/apps/api-gateway/src/utilities/utilities.service.ts b/apps/api-gateway/src/utilities/utilities.service.ts index 4d3f5900a..4aa7aac5f 100644 --- a/apps/api-gateway/src/utilities/utilities.service.ts +++ b/apps/api-gateway/src/utilities/utilities.service.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { UtilitiesDto } from './dtos/shortening-url.dto'; +import { StoreObjectDto } from './dtos/store-object.dto'; @Injectable() export class UtilitiesService extends BaseService { @@ -13,8 +14,9 @@ export class UtilitiesService extends BaseService { return this.sendNatsMessage(this.serviceProxy, 'create-shortening-url', shorteningUrlDto); } - async storeObject(persistent: boolean, storeObj: UtilitiesDto): Promise { - const payload = {persistent, storeObj}; - return this.sendNatsMessage(this.serviceProxy, 'store-object', payload); + async storeObject(persistent: boolean, storeObj: StoreObjectDto): Promise { + // const payload = {persistent, storeObj}; + return `This is the storeObject:::::::: ${JSON.stringify(storeObj)}`; + // return this.sendNatsMessage(this.serviceProxy, 'store-object-return-url', payload); } } diff --git a/apps/connection/src/connection.module.ts b/apps/connection/src/connection.module.ts index 19288cfe9..5182aa12d 100644 --- a/apps/connection/src/connection.module.ts +++ b/apps/connection/src/connection.module.ts @@ -8,7 +8,6 @@ import { ConnectionRepository } from './connection.repository'; import { PrismaService } from '@credebl/prisma-service'; import { CacheModule } from '@nestjs/cache-manager'; import { getNatsOptions } from '@credebl/common/nats.config'; -import { UtilitiesService } from 'apps/utility/src/utilities.service'; // import { nkeyAuthenticator } from 'nats'; @Module({ @@ -25,6 +24,6 @@ import { UtilitiesService } from 'apps/utility/src/utilities.service'; CacheModule.register() ], controllers: [ConnectionController], - providers: [ConnectionService, ConnectionRepository, PrismaService, Logger, UtilitiesService] + providers: [ConnectionService, ConnectionRepository, PrismaService, Logger] }) export class ConnectionModule { } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index b064518ed..e4b87c31e 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -77,7 +77,16 @@ export class ConnectionService { } const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); const invitationObject = createConnectionInvitation?.message?.invitation['@id']; + // Need to implement shortening invitation logic + // Krishna start + // const connectionInvitaion = createConnectionInvitation?.message?.invitation; + // make call to function that will call + // const shortenedUrl = await this.storeObjectAndReturnUrl(connectionInvitaion, multiUseInvitation); + // Krishna end + // eslint-disable-next-line no-console + console.log("This is Invitation object::::::", createConnectionInvitation?.message?.invitation); let shortenedUrl; + if (agentDetails?.tenantId) { shortenedUrl = `${agentEndPoint}/multi-tenancy/url/${agentDetails?.tenantId}/${invitationObject}`; } else { @@ -513,5 +522,31 @@ export class ConnectionService { }, error.error); }); } + + // Krishna Start + async storeObjectAndReturnUrl(urlObjectReference: unknown, multiUseInvitation: boolean): Promise { + const persistent:boolean = multiUseInvitation; + const connectionInvitation: unknown = urlObjectReference; + + //nats call in agent-service to create an invitation url + const pattern = { cmd: 'store-object-return-url' }; + const payload = { persistent, connectionInvitation}; + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const message = await this.connectionServiceProxy.send(pattern, payload).toPromise(); + return message; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + // Krishna End } diff --git a/apps/utility/src/utilities.controller.ts b/apps/utility/src/utilities.controller.ts index 3f2b69df7..5ddb945f4 100644 --- a/apps/utility/src/utilities.controller.ts +++ b/apps/utility/src/utilities.controller.ts @@ -2,7 +2,7 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { UtilitiesService } from './utilities.service'; import { IShorteningUrlData } from '../interfaces/shortening-url.interface'; -import { IUtilities } from '../interfaces/shortening-url.interface'; +// import { IUtilities } from '../interfaces/shortening-url.interface'; @Controller() export class UtilitiesController { @@ -18,8 +18,8 @@ export class UtilitiesController { return this.utilitiesService.getShorteningUrl(referenceId); } - @MessagePattern({ cmd: 'store-object' }) - async storeObject(payload: {persistent: boolean, storeObj: IUtilities}): Promise { + @MessagePattern({ cmd: 'store-object-return-url' }) + async storeObject(payload: {persistent: boolean, storeObj: object}): Promise { return this.utilitiesService.storeObject(payload); } } \ No newline at end of file diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index a5be3a700..cc373f90a 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { RpcException } from '@nestjs/microservices'; import { UtilitiesRepository } from './utilities.repository'; import { AwsService } from '@credebl/aws'; -import { IUtilities } from '../interfaces/shortening-url.interface'; +// import { IUtilities } from '../interfaces/shortening-url.interface'; import { S3 } from 'aws-sdk'; @Injectable() @@ -49,7 +49,7 @@ export class UtilitiesService { } } - async storeObject(payload: {persistent: boolean, storeObj: IUtilities}): Promise { + async storeObject(payload: {persistent: boolean, storeObj: object}): Promise { try { const timestamp = Date.now(); const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, timestamp, payload.storeObj); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5fe5087c8..8edc999d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@nestjs/bull': specifier: ^10.0.1 version: 10.0.1(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(bull@4.11.4) + '@nestjs/cache-manager': + specifier: ^2.1.0 + version: 2.2.1(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(cache-manager@5.4.0)(rxjs@7.8.1) '@nestjs/common': specifier: ^10.2.7 version: 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) @@ -25,7 +28,7 @@ dependencies: version: 10.1.0(@nestjs/common@10.2.8) '@nestjs/microservices': specifier: ^10.1.3 - version: 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/websockets@10.1.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1) + version: 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/websockets@10.1.3)(cache-manager@5.4.0)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/passport': specifier: ^10.0.0 version: 10.0.0(@nestjs/common@10.2.8)(passport@0.6.0) @@ -75,8 +78,8 @@ dependencies: specifier: ^9.22.1 version: 9.22.1 aws-sdk: - specifier: ^2.1492.0 - version: 2.1499.0 + specifier: ^2.1510.0 + version: 2.1560.0 bcrypt: specifier: ^5.1.0 version: 5.1.0 @@ -92,6 +95,12 @@ dependencies: bull: specifier: ^4.11.4 version: 4.11.4 + cache-manager: + specifier: ^5.2.4 + version: 5.4.0 + cache-manager-redis-store: + specifier: ^2.0.0 + version: 2.0.0 class-transformer: specifier: ^0.5.1 version: 0.5.1 @@ -1208,6 +1217,20 @@ packages: tslib: 2.6.0 dev: false + /@nestjs/cache-manager@2.2.1(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(cache-manager@5.4.0)(rxjs@7.8.1): + resolution: {integrity: sha512-mXj0zenuyMPJICokwVud4Kjh0+pzBNBAgfpx3I48LozNkd8Qfv/MAhZsb15GihGpbFRxafUo3p6XvtAqRm8GRw==} + peerDependencies: + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + cache-manager: <=5 + rxjs: ^7.0.0 + dependencies: + '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.1.3(@nestjs/common@10.2.8)(@nestjs/microservices@10.1.3)(@nestjs/platform-express@10.1.3)(@nestjs/websockets@10.1.3)(reflect-metadata@0.1.13)(rxjs@7.8.1) + cache-manager: 5.4.0 + rxjs: 7.8.1 + dev: false + /@nestjs/cli@10.1.11: resolution: {integrity: sha512-ORkpVFQvcPYtvkLfa0I9dMSPIppkqTOyLqPvJV0wiZofp8iR1+VEVzJVi+PMj53gOkly8TV9+6iy/dBA5Ssrog==} engines: {node: '>= 16'} @@ -1303,7 +1326,7 @@ packages: optional: true dependencies: '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) - '@nestjs/microservices': 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/websockets@10.1.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/microservices': 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/websockets@10.1.3)(cache-manager@5.4.0)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/platform-express': 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3) '@nestjs/websockets': 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/platform-socket.io@10.1.3)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nuxtjs/opencollective': 0.3.2 @@ -1346,7 +1369,7 @@ packages: reflect-metadata: 0.1.13 dev: false - /@nestjs/microservices@10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/websockets@10.1.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1): + /@nestjs/microservices@10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/websockets@10.1.3)(cache-manager@5.4.0)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1): resolution: {integrity: sha512-IBKefw+DR6v2SaXjPJ8tRT+gQTJUSGN83gxuaA32uCQNW2rK+CyVapgX3fDeM/zJsLfBkdveSMX+R74w5wuk+Q==} peerDependencies: '@grpc/grpc-js': '*' @@ -1385,6 +1408,7 @@ packages: '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/core': 10.1.3(@nestjs/common@10.2.8)(@nestjs/microservices@10.1.3)(@nestjs/platform-express@10.1.3)(@nestjs/websockets@10.1.3)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/websockets': 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/platform-socket.io@10.1.3)(reflect-metadata@0.1.13)(rxjs@7.8.1) + cache-manager: 5.4.0 iterare: 1.2.1 nats: 2.15.1 reflect-metadata: 0.1.13 @@ -1507,7 +1531,7 @@ packages: dependencies: '@nestjs/common': 10.2.8(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/core': 10.1.3(@nestjs/common@10.2.8)(@nestjs/microservices@10.1.3)(@nestjs/platform-express@10.1.3)(@nestjs/websockets@10.1.3)(reflect-metadata@0.1.13)(rxjs@7.8.1) - '@nestjs/microservices': 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/websockets@10.1.3)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/microservices': 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3)(@nestjs/websockets@10.1.3)(cache-manager@5.4.0)(nats@2.15.1)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/platform-express': 10.1.3(@nestjs/common@10.2.8)(@nestjs/core@10.1.3) tslib: 2.6.1 dev: true @@ -2672,8 +2696,8 @@ packages: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} - /aws-sdk@2.1499.0: - resolution: {integrity: sha512-kh89lcXx7lP83uVjzRPkOueRoM8gQlep86W9+l3qCTHSLiVJuc0MiPmqCLMPlOAZil+35roFkwWIP2FJ1WcdXg==} + /aws-sdk@2.1560.0: + resolution: {integrity: sha512-nakTZHytnhKWZpwu9d1crqjoegBRG+j1/rflsVnckXxoIwlKM0D/v/NIe+BJmRnCA2aCdwuMx3dtkgLz/AB6VA==} engines: {node: '>= 10.0.0'} dependencies: buffer: 4.9.2 @@ -2685,7 +2709,7 @@ packages: url: 0.10.3 util: 0.12.5 uuid: 8.0.0 - xml2js: 0.5.0 + xml2js: 0.6.2 dev: false /aws-sign2@0.7.0: @@ -3051,6 +3075,20 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + /cache-manager-redis-store@2.0.0: + resolution: {integrity: sha512-bWLWlUg6nCYHiJLCCYxY2MgvwvKnvlWwrbuynrzpjEIhfArD2GC9LtutIHFEPeyGVQN6C+WEw+P3r+BFBwhswg==} + engines: {node: '>= 8.3'} + dependencies: + redis: 3.1.2 + dev: false + + /cache-manager@5.4.0: + resolution: {integrity: sha512-FS7o8vqJosnLpu9rh2gQTo8EOzCRJLF1BJ4XDEUDMqcfvs7SJZs5iuoFTXLauzQ3S5v8sBAST1pCwMaurpyi1A==} + dependencies: + lodash.clonedeep: 4.5.0 + lru-cache: 10.2.0 + promise-coalesce: 1.1.2 + /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -3643,6 +3681,11 @@ packages: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} dev: false + /denque@1.5.1: + resolution: {integrity: sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==} + engines: {node: '>=0.10'} + dev: false + /denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} @@ -6286,7 +6329,6 @@ packages: /lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - dev: false /lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} @@ -6332,6 +6374,11 @@ packages: /lru-cache@10.0.1: resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} engines: {node: 14 || >=16.14} + dev: true + + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} /lru-cache@4.0.2: resolution: {integrity: sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==} @@ -7320,6 +7367,10 @@ packages: engines: {node: '>=0.4.0'} dev: false + /promise-coalesce@1.1.2: + resolution: {integrity: sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg==} + engines: {node: '>=16'} + /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -7584,6 +7635,10 @@ packages: resolve: 1.22.2 dev: true + /redis-commands@1.7.0: + resolution: {integrity: sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==} + dev: false + /redis-errors@1.2.0: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} engines: {node: '>=4'} @@ -7596,6 +7651,16 @@ packages: redis-errors: 1.2.0 dev: false + /redis@3.1.2: + resolution: {integrity: sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==} + engines: {node: '>=10'} + dependencies: + denque: 1.5.1 + redis-commands: 1.7.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + dev: false + /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} @@ -9311,8 +9376,8 @@ packages: xmlbuilder: 11.0.1 dev: false - /xml2js@0.5.0: - resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + /xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} engines: {node: '>=4.0.0'} dependencies: sax: 1.2.4 From 1f80e068202fc5051e60df17efa967cee7a79741 Mon Sep 17 00:00:00 2001 From: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Date: Thu, 22 Feb 2024 20:15:27 +0530 Subject: [PATCH 030/231] Updated OOB Proof Verification endpoint (#533) * fix: removed the unnecessary logger from the agent-service module (#419) Signed-off-by: KulkarniShashank * WIP:OOB Proof Request Signed-off-by: ankita_patidar * WIP:OOB Proof Request Signed-off-by: ankita_patidar * fix:OOB Credential Offer restore changes Signed-off-by: ankita_patidar * fix:add email as optional Signed-off-by: ankita_patidar * fix:take response from presentation request payload Signed-off-by: ankita_patidar * fix: resolved sonar lint checks Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * expose agent format of proof request to API endpoint, disabled send offer by email Signed-off-by: ankita_patidar * update OOB verification input Signed-off-by: ankita_patidar * added few more allowed restrictions, corrected API description Signed-off-by: ankita_patidar * added few more allowed restrictions Signed-off-by: ankita_patidar --------- Signed-off-by: KulkarniShashank Signed-off-by: ankita_patidar Signed-off-by: bhavanakarwade Signed-off-by: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Co-authored-by: Nishad Shirsat <103021375+nishad-ayanworks@users.noreply.github.com> Co-authored-by: Nishad Co-authored-by: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Co-authored-by: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Co-authored-by: bhavanakarwade --- .../src/interface/agent-service.interface.ts | 3 + .../src/verification/dto/request-proof.dto.ts | 87 +++++++++++-------- .../interfaces/verification.interface.ts | 39 +++++++++ .../verification/verification.controller.ts | 6 +- .../src/verification/verification.service.ts | 4 +- .../src/interfaces/verification.interface.ts | 7 +- 6 files changed, 105 insertions(+), 41 deletions(-) diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 685122c49..663a32e27 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -277,6 +277,9 @@ interface IRequestedPredicatesName { interface IRequestedRestriction { cred_def_id?: string; schema_id?: string; + schema_issuer_did?: string; + schema_name?: string; + issuer_did?: string; } export interface IAgentSpinUpSatus { diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 01789e009..dc765e008 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,8 +1,9 @@ -import { IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString } from 'class-validator'; +import { IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, IsUUID } from 'class-validator'; import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { AutoAccept } from '@credebl/enum/enum'; +import { IProofFormats } from '../interfaces/verification.interface'; export class ProofRequestAttribute { @IsString() @@ -135,46 +136,62 @@ export class OutOfBandRequestProof extends ProofPayload { autoAcceptProof: string; } +export class SendProofRequestPayload { -interface IProofFormats { - indy: IndyProof -} + @ApiPropertyOptional() + @IsString({ message: 'protocolVersion must be in string' }) + @IsNotEmpty({ message: 'please provide valid protocol version' }) + @IsOptional() + protocolVersion: string; -interface IndyProof { - name: string; - version: string; - requested_attributes: IRequestedAttributes; - requested_predicates: IRequestedPredicates; -} + @ApiPropertyOptional() + @IsOptional() + @IsString({ message: 'comment must be in string' }) + comment: string; -interface IRequestedAttributes { - [key: string]: IRequestedAttributesName; -} + @ApiProperty() + @IsString() + @Transform(({ value }) => trim(value)) + @Transform(({ value }) => toLowerCase(value)) + @IsNotEmpty({ message: 'connectionId is required.' }) + connectionId: string; -interface IRequestedAttributesName { - name: string; - restrictions: IRequestedRestriction[] -} + @ApiProperty({ + 'example': [ + { + indy: { + name: 'Verify national identity', + version: '1.0', + // eslint-disable-next-line camelcase + requested_attributes: {}, + // eslint-disable-next-line camelcase + requested_predicates: {} + } + } + ] + }) + @IsObject({ each: true }) + @IsNotEmpty({ message: 'please provide valid proofFormat' }) + proofFormats: IProofFormats; -interface IRequestedPredicates { - [key: string]: IRequestedPredicatesName; -} + @ApiPropertyOptional() + @IsString({ message: 'auto accept proof must be in string' }) + @IsNotEmpty({ message: 'please provide from valid auto accept proof options' }) + @IsOptional() + @IsEnum(AutoAccept, { + message: `Invalid auto accept proof. It should be one of: ${Object.values(AutoAccept).join(', ')}` + }) + autoAcceptProof: AutoAccept; -interface IRequestedPredicatesName { - name: string; - restrictions: IRequestedRestriction[] -} + @ApiPropertyOptional() + @IsOptional() + @IsString({ message: 'label must be in string' }) + label: string; -interface IRequestedRestriction { - cred_def_id?: string; - schema_id?: string; + @ApiPropertyOptional() + @IsOptional() + @IsUUID() + @IsNotEmpty({ message: 'please provide valid parentThreadId' }) + parentThreadId: string; } -export interface ISendProofRequestPayload { - protocolVersion?: string; - comment?: string; - connectionId?: string; - proofFormats: IProofFormats; - autoAcceptProof?: string; - label?: string; -} diff --git a/apps/api-gateway/src/verification/interfaces/verification.interface.ts b/apps/api-gateway/src/verification/interfaces/verification.interface.ts index 7937c6aee..a722da72a 100644 --- a/apps/api-gateway/src/verification/interfaces/verification.interface.ts +++ b/apps/api-gateway/src/verification/interfaces/verification.interface.ts @@ -51,3 +51,42 @@ interface ITags { state: string; threadId: string; } + +export interface IProofFormats { + indy: IndyProof +} + +interface IndyProof { + name: string; + version: string; + requested_attributes: IRequestedAttributes; + requested_predicates: IRequestedPredicates; +} + +interface IRequestedAttributes { + [key: string]: IRequestedAttributesName; +} + +interface IRequestedAttributesName { + name?: string; + names?: string; + restrictions: IRequestedRestriction[] +} + +interface IRequestedPredicates { + [key: string]: IRequestedPredicatesName; +} + +interface IRequestedPredicatesName { + name: string; + restrictions: IRequestedRestriction[] +} + +interface IRequestedRestriction { + cred_def_id?: string; + schema_id?: string; + schema_issuer_did?: string; + schema_name?: string; + issuer_did?: string; + schema_version?: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index e35baaba0..7f9bb9b45 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -16,7 +16,7 @@ import { Controller, Logger, Post, Body, Get, Query, HttpStatus, Res, UseGuards, import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; -import { ISendProofRequestPayload, OutOfBandRequestProof, RequestProofDto } from './dto/request-proof.dto'; +import { SendProofRequestPayload, RequestProofDto } from './dto/request-proof.dto'; import { VerificationService } from './verification.service'; import IResponseType, { IResponse } from '@credebl/common/interfaces/response.interface'; import { Response } from 'express'; @@ -247,14 +247,14 @@ export class VerificationController { @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) @ApiUnauthorizedResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Unauthorized', type: UnauthorizedErrorDto }) @ApiForbiddenResponse({ status: HttpStatus.FORBIDDEN, description: 'Forbidden', type: ForbiddenErrorDto }) - @ApiBody({ type: OutOfBandRequestProof }) + @ApiBody({ type: SendProofRequestPayload }) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.VERIFIER) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), OrgRolesGuard) async sendOutOfBandPresentationRequest( @Res() res: Response, @User() user: IUserRequest, - @Body() outOfBandRequestProof: ISendProofRequestPayload, + @Body() outOfBandRequestProof: SendProofRequestPayload, @Param('orgId') orgId: string ): Promise { user.orgId = orgId; diff --git a/apps/api-gateway/src/verification/verification.service.ts b/apps/api-gateway/src/verification/verification.service.ts index 44dffc4b4..8a51651b4 100644 --- a/apps/api-gateway/src/verification/verification.service.ts +++ b/apps/api-gateway/src/verification/verification.service.ts @@ -1,7 +1,7 @@ import { Injectable, Inject} from '@nestjs/common'; import { ClientProxy} from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; -import { ISendProofRequestPayload, RequestProofDto } from './dto/request-proof.dto'; +import { SendProofRequestPayload, RequestProofDto } from './dto/request-proof.dto'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { WebhookPresentationProofDto } from './dto/webhook-proof.dto'; import { IProofPresentationDetails, IProofPresentationList } from '@credebl/common/interfaces/verification.interface'; @@ -70,7 +70,7 @@ export class VerificationService extends BaseService { * @param outOfBandRequestProof * @returns Get out-of-band requested proof presentation details */ - sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { + sendOutOfBandPresentationRequest(outOfBandRequestProof: SendProofRequestPayload, user: IUserRequest): Promise { const payload = { outOfBandRequestProof, user }; return this.sendNatsMessage(this.verificationServiceProxy, 'send-out-of-band-proof-request', payload); } diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 8e2da7579..082fe897f 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -65,7 +65,8 @@ interface IRequestedAttributes { } interface IRequestedAttributesName { - name: string; + name?: string; + names?: string; restrictions: IRequestedRestriction[] } @@ -81,6 +82,10 @@ interface IRequestedPredicatesName { interface IRequestedRestriction { cred_def_id?: string; schema_id?: string; + schema_issuer_did?: string; + schema_name?: string; + issuer_did?: string; + schema_version?: string; } export interface ISendProofRequestPayload { From e10fddadfcb0c2a61798a42f473435784d19e64f Mon Sep 17 00:00:00 2001 From: KambleSahil3 Date: Thu, 22 Feb 2024 21:09:22 +0530 Subject: [PATCH 031/231] fixed Puppeteer issue --- Dockerfiles/Dockerfile.user | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfiles/Dockerfile.user b/Dockerfiles/Dockerfile.user index 4b59f0a25..775c12779 100644 --- a/Dockerfiles/Dockerfile.user +++ b/Dockerfiles/Dockerfile.user @@ -4,6 +4,7 @@ RUN npm install -g pnpm # We don't need the standalone Chromium ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true +ENV PUPPETEER_SKIP_DOWNLOAD true # Install Google Chrome Stable and fonts # Note: this installs the necessary libs to make the browser work with Puppeteer. @@ -36,6 +37,7 @@ FROM node:18-slim # We don't need the standalone Chromium ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true +ENV PUPPETEER_SKIP_DOWNLOAD true # Install Google Chrome Stable and fonts # Note: this installs the necessary libs to make the browser work with Puppeteer. @@ -64,4 +66,4 @@ CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx pri # docker build -t user -f Dockerfiles/Dockerfile.user . # docker run -d --env-file .env --name user docker.io/library/user -# docker logs -f user \ No newline at end of file +# docker logs -f user From f9df6c4365375aeb975bdb6bd0b74bc802bc2bda Mon Sep 17 00:00:00 2001 From: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Date: Fri, 23 Feb 2024 10:19:02 +0530 Subject: [PATCH 032/231] Remove unwanted attributes, Added support for more restrictions (#538) * fix: removed the unnecessary logger from the agent-service module (#419) Signed-off-by: KulkarniShashank * WIP:OOB Proof Request Signed-off-by: ankita_patidar * WIP:OOB Proof Request Signed-off-by: ankita_patidar * fix:OOB Credential Offer restore changes Signed-off-by: ankita_patidar * fix:add email as optional Signed-off-by: ankita_patidar * fix:take response from presentation request payload Signed-off-by: ankita_patidar * fix: resolved sonar lint checks Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * expose agent format of proof request to API endpoint, disabled send offer by email Signed-off-by: ankita_patidar * update OOB verification input Signed-off-by: ankita_patidar * added few more allowed restrictions, corrected API description Signed-off-by: ankita_patidar * added few more allowed restrictions Signed-off-by: ankita_patidar * removed unnecessary attribute,updated example Signed-off-by: ankita_patidar --------- Signed-off-by: KulkarniShashank Signed-off-by: ankita_patidar Signed-off-by: bhavanakarwade Signed-off-by: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Co-authored-by: Nishad Shirsat <103021375+nishad-ayanworks@users.noreply.github.com> Co-authored-by: Nishad Co-authored-by: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Co-authored-by: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Co-authored-by: bhavanakarwade --- .../src/verification/dto/request-proof.dto.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index dc765e008..3ab0d48ee 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -149,13 +149,6 @@ export class SendProofRequestPayload { @IsString({ message: 'comment must be in string' }) comment: string; - @ApiProperty() - @IsString() - @Transform(({ value }) => trim(value)) - @Transform(({ value }) => toLowerCase(value)) - @IsNotEmpty({ message: 'connectionId is required.' }) - connectionId: string; - @ApiProperty({ 'example': [ { @@ -163,7 +156,16 @@ export class SendProofRequestPayload { name: 'Verify national identity', version: '1.0', // eslint-disable-next-line camelcase - requested_attributes: {}, + requested_attributes: { + verifynameAddress: { + names: ['name', 'address'], + restrictions: [{'schema_id': 'KU583UbI4yAKfaBTSz1rqG:2:National ID:1.0.0'}] + }, + verifyBirthPlace: { + name: 'Place', + restrictions: [{'schema_id': 'KU583UbI4yAKfaBTSz1rqG:2:Birth Certificate:1.0.0'}] + } + }, // eslint-disable-next-line camelcase requested_predicates: {} } From e9b54987f91f9bb5c8fcd294c3ded45f375885ea Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Fri, 23 Feb 2024 18:09:32 +0530 Subject: [PATCH 033/231] Add multiple attributes in single proof request (#531) * feat:add multiple attributes in single proof request Signed-off-by: pranalidhanavade * feat:add multiple attributes in single proof request Signed-off-by: pranalidhanavade * fix: verification issue Signed-off-by: pranalidhanavade * fix: verification issue while verifying credebtials Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade --- .../src/verification/dto/request-proof.dto.ts | 34 +++++++++--- .../verification/verification.controller.ts | 26 +-------- apps/verification/src/verification.service.ts | 53 +++++++++++++++---- 3 files changed, 71 insertions(+), 42 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 3ab0d48ee..7d04d5960 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,29 +1,43 @@ -import { IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, IsUUID } from 'class-validator'; +import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID } from 'class-validator'; import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; +import { Transform, Type } from 'class-transformer'; import { AutoAccept } from '@credebl/enum/enum'; import { IProofFormats } from '../interfaces/verification.interface'; + export class ProofRequestAttribute { - @IsString() - @IsNotEmpty({ message: 'attributeName is required.' }) - attributeName: string; + @ValidateIf((obj) => obj.attributeNames === undefined) + @IsNotEmpty() + @IsString({each:true}) + attributeName?: string; + + @ValidateIf((obj) => obj.attributeName === undefined) + @IsArray({ message: 'attributeNames must be an array.' }) + @ArrayNotEmpty({ message: 'array can not be empty' }) + @IsString({ each: true}) + @IsNotEmpty({ each: true, message: 'each element cannot be empty' }) + attributeNames?: string[]; + + @ApiPropertyOptional() @IsString() @IsOptional() schemaId?: string; + @ApiPropertyOptional() @IsString() @IsOptional() @IsNotEmpty({ message: 'condition is required.' }) condition?: string; + @ApiPropertyOptional() @IsOptional() @IsNotEmpty({ message: 'value is required.' }) @IsNumberString({}, { message: 'Value must be a number' }) value?: string; + @ApiPropertyOptional() @IsString() @IsOptional() credDefId?: string; @@ -72,11 +86,14 @@ export class RequestProofDto extends ProofPayload { credDefId: 'string', schemaId: 'string' } - ] + ], + type: () => [ProofRequestAttribute] }) @IsArray({ message: 'attributes must be in array' }) + @ValidateNested() @IsObject({ each: true }) @IsNotEmpty({ message: 'please provide valid attributes' }) + @Type(() => ProofRequestAttribute) attributes: ProofRequestAttribute[]; @ApiPropertyOptional() @@ -106,11 +123,14 @@ export class OutOfBandRequestProof extends ProofPayload { credDefId: '', schemaId: '' } - ] + ], + type: () => [ProofRequestAttribute] }) @IsArray({ message: 'attributes must be in array' }) + @ValidateNested({each: true}) @IsObject({ each: true }) @IsNotEmpty({ message: 'please provide valid attributes' }) + @Type(() => ProofRequestAttribute) attributes: ProofRequestAttribute[]; @ApiProperty() diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index 7f9bb9b45..efb8570a7 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -188,8 +188,7 @@ export class VerificationController { } else { throw new BadRequestException('Please provide unique attribute names'); } - - await this.validateAttribute(attrData); + } requestProof.orgId = orgId; @@ -313,27 +312,4 @@ export class VerificationController { return res.status(HttpStatus.CREATED).json(finalResponse); } - - async validateAttribute( - attrData: object - ): Promise { - - if (!attrData['attributeName']) { - throw new BadRequestException('attributeName must be required'); - } - - if (undefined !== attrData['condition'] && '' === attrData['condition'].trim()) { - throw new BadRequestException('condition cannot be empty'); - } - - if (undefined !== attrData['value'] && '' === attrData['value'].trim()) { - throw new BadRequestException('value cannot be empty'); - } - - if (attrData['condition']) { - if (isNaN(attrData['value'])) { - throw new BadRequestException('value must be an integer'); - } - } - } } \ No newline at end of file diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index c85fae1bc..c910fecf3 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -650,7 +650,8 @@ export class VerificationService { try { const getAgentDetails = await this.verificationRepository.getAgentEndPoint(orgId); const verificationMethodLabel = 'get-verified-proof'; - + let credDefId; + let schemaId; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId, '', proofId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); @@ -661,27 +662,39 @@ export class VerificationService { const payload = { apiKey, url }; const getProofPresentationById = await this._getVerifiedProofDetails(payload); + if (!getProofPresentationById?.response?.presentation) { throw new NotFoundException(ResponseMessages.verification.error.proofPresentationNotFound, { cause: new Error(), description: ResponseMessages.errorMessages.notFound }); } + const requestedAttributes = getProofPresentationById?.response?.request?.indy?.requested_attributes; const requestedPredicates = getProofPresentationById?.response?.request?.indy?.requested_predicates; const revealedAttrs = getProofPresentationById?.response?.presentation?.indy?.requested_proof?.revealed_attrs; + const extractedDataArray: IProofPresentationDetails[] = []; - if (requestedAttributes && requestedPredicates) { + if (0 !== Object.keys(requestedAttributes).length && 0 !== Object.keys(requestedPredicates).length) { + for (const key in requestedAttributes) { if (requestedAttributes.hasOwnProperty(key)) { const requestedAttributeKey = requestedAttributes[key]; const attributeName = requestedAttributeKey.name; - const credDefId = requestedAttributeKey?.restrictions[0]?.cred_def_id; - const schemaId = requestedAttributeKey?.restrictions[0]?.schema_id; + + if (requestedAttributeKey?.restrictions) { + + credDefId = requestedAttributeKey?.restrictions[0]?.cred_def_id; + schemaId = requestedAttributeKey?.restrictions[0]?.schema_id; + } else if (getProofPresentationById?.response?.presentation?.indy?.identifiers) { + + credDefId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].cred_def_id; + schemaId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].schema_id; + } if (revealedAttrs.hasOwnProperty(key)) { const extractedData: IProofPresentationDetails = { @@ -710,14 +723,17 @@ export class VerificationService { } } - } else if (requestedAttributes) { + } else if (0 !== Object.keys(requestedAttributes).length) { + for (const key in requestedAttributes) { if (requestedAttributes.hasOwnProperty(key)) { const attribute = requestedAttributes[key]; const attributeName = attribute.name; - const credDefId = attribute?.restrictions[0]?.cred_def_id; - const schemaId = attribute?.restrictions[0]?.schema_id; + + + [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); + if (revealedAttrs.hasOwnProperty(key)) { const extractedData: IProofPresentationDetails = { @@ -729,14 +745,14 @@ export class VerificationService { } } } - } else if (requestedPredicates) { + } else if (0 !== Object.keys(requestedPredicates).length) { for (const key in requestedPredicates) { if (requestedPredicates.hasOwnProperty(key)) { const attribute = requestedPredicates[key]; const attributeName = attribute?.name; - const credDefId = attribute?.restrictions[0]?.cred_def_id; - const schemaId = attribute?.restrictions[0]?.schema_id; + + [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); const extractedData: IProofPresentationDetails = { [attributeName]: `${requestedPredicates?.p_type}${requestedPredicates?.p_value}`, @@ -769,6 +785,23 @@ export class VerificationService { } } + async _schemaCredDefRestriction(attribute, getProofPresentationById): Promise { + let credDefId; + let schemaId; + + if (attribute?.restrictions) { + + credDefId = attribute?.restrictions[0]?.cred_def_id; + schemaId = attribute?.restrictions[0]?.schema_id; + } else if (getProofPresentationById?.response?.presentation?.indy?.identifiers) { + + credDefId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].cred_def_id; + schemaId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].schema_id; + } + + return [credDefId, schemaId]; + } + async _getVerifiedProofDetails(payload: IVerifiedProofData): Promise<{ response; }> { From d633fe1ae93169b0b2d8b23340a463b794af94d2 Mon Sep 17 00:00:00 2001 From: Nishad Date: Fri, 23 Feb 2024 20:24:15 +0530 Subject: [PATCH 034/231] worked on the code optimization of invitations flow, worked on the user access guard and apply on the user controller Signed-off-by: Nishad --- .../src/authz/guards/user-access-guard.ts | 16 + apps/api-gateway/src/user/user.controller.ts | 17 +- apps/organization/src/organization.service.ts | 362 +++++++++++------- 3 files changed, 250 insertions(+), 145 deletions(-) create mode 100644 apps/api-gateway/src/authz/guards/user-access-guard.ts diff --git a/apps/api-gateway/src/authz/guards/user-access-guard.ts b/apps/api-gateway/src/authz/guards/user-access-guard.ts new file mode 100644 index 000000000..ea53f63fc --- /dev/null +++ b/apps/api-gateway/src/authz/guards/user-access-guard.ts @@ -0,0 +1,16 @@ +import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { Observable } from 'rxjs'; + +@Injectable() +export class UserAccessGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + + const { user } = request; + + if (user.hasOwnProperty('client_id')) { + throw new UnauthorizedException('You do not have access'); + } + return true; + } +} diff --git a/apps/api-gateway/src/user/user.controller.ts b/apps/api-gateway/src/user/user.controller.ts index 8e9ee72a4..5e85bbfbc 100644 --- a/apps/api-gateway/src/user/user.controller.ts +++ b/apps/api-gateway/src/user/user.controller.ts @@ -50,6 +50,7 @@ import { OrgRoles } from 'libs/org-roles/enums'; import { AwsService } from '@credebl/aws/aws.service'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; import { CreateCertificateDto } from './dto/share-certificate.dto'; +import { UserAccessGuard } from '../authz/guards/user-access-guard'; @UseFilters(CustomExceptionFilter) @Controller('users') @@ -132,7 +133,7 @@ export class UserController { summary: 'Fetch login user details', description: 'Fetch login user details' }) - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard('jwt'), UserAccessGuard) @ApiBearerAuth() async getProfile(@User() reqUser: user, @Res() res: Response): Promise { const userData = await this.userService.getProfile(reqUser.id); @@ -154,7 +155,7 @@ export class UserController { summary: 'Get all platform and ecosystem settings', description: 'Get all platform and ecosystem settings' }) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard, UserAccessGuard) @Roles(OrgRoles.PLATFORM_ADMIN) @ApiBearerAuth() async getPlatformSettings(@Res() res: Response): Promise { @@ -174,7 +175,7 @@ export class UserController { summary: 'users activity', description: 'Fetch users activity' }) - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard('jwt'), UserAccessGuard) @ApiBearerAuth() @ApiQuery({ name: 'limit', required: true }) async getUserActivities( @@ -201,7 +202,7 @@ export class UserController { summary: 'organization invitations', description: 'Fetch organization invitations' }) - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard('jwt'), UserAccessGuard) @ApiBearerAuth() @ApiQuery({ name: 'pageNumber', @@ -293,7 +294,7 @@ export class UserController { summary: 'accept/reject organization invitation', description: 'Accept or Reject organization invitations' }) - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard('jwt'), UserAccessGuard) @ApiBearerAuth() async acceptRejectInvitaion( @Body() acceptRejectInvitation: AcceptRejectInvitationDto, @@ -349,7 +350,7 @@ export class UserController { }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard('jwt'), UserAccessGuard) async updateUserProfile( @Body() updateUserProfileDto: UpdateUserProfileDto, @User() reqUser: user, @@ -375,7 +376,7 @@ export class UserController { @ApiOperation({ summary: 'Store user password details', description: 'Store user password details' }) @ApiExcludeEndpoint() @ApiBearerAuth() - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard('jwt'), UserAccessGuard) async addPasskey( @Body() userInfo: AddPasskeyDetailsDto, @@ -403,7 +404,7 @@ export class UserController { summary: 'Update platform and ecosystem settings', description: 'Update platform and ecosystem settings' }) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard, UserAccessGuard) @Roles(OrgRoles.PLATFORM_ADMIN) @ApiBearerAuth() async updatePlatformSettings( diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 6f9ed83d2..a301a2a79 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -1,5 +1,6 @@ /* eslint-disable prefer-destructuring */ -import { organisation, user } from '@prisma/client'; +// eslint-disable-next-line camelcase +import { org_invitations, organisation, user } from '@prisma/client'; import { Injectable, Logger, @@ -105,29 +106,35 @@ export class OrganizationService { delete organizationDetails.orgSlug; delete organizationDetails.website; - const orgCredentials = await this.registerToKeycloak( - organizationDetails.name, - organizationDetails.id, - keycloakUserId, - userId, - false - ); - - const { clientId, idpId } = orgCredentials; + try { + const orgCredentials = await this.registerToKeycloak( + organizationDetails.name, + organizationDetails.id, + keycloakUserId, + userId, + false + ); - const updateOrgData = { - clientId, - idpId - }; + const { clientId, idpId } = orgCredentials; - const updatedOrg = await this.organizationRepository.updateOrganizationById( - updateOrgData, - organizationDetails.id - ); - - if (!updatedOrg) { - throw new InternalServerErrorException(ResponseMessages.organisation.error.credentialsNotUpdate); + const updateOrgData = { + clientId, + idpId + }; + + const updatedOrg = await this.organizationRepository.updateOrganizationById( + updateOrgData, + organizationDetails.id + ); + + if (!updatedOrg) { + throw new InternalServerErrorException(ResponseMessages.organisation.error.credentialsNotUpdate); + } + } catch (error) { + this.logger.error(`Error In creating client : ${JSON.stringify(error)}`); + throw new InternalServerErrorException('Unable to create client'); } + if (createOrgDto.notificationWebhook) { await this.storeOrgWebhookEndpoint(organizationDetails.id, createOrgDto.notificationWebhook); } @@ -174,23 +181,29 @@ export class OrganizationService { clientSecret: this.maskString(generatedClientSecret) }; } else { - const orgCredentials = await this.registerToKeycloak( - organizationDetails.name, - organizationDetails.id, - keycloakUserId, - userId, - true - ); - - const { clientId, idpId, clientSecret } = orgCredentials; - generatedClientSecret = clientSecret; - - updateOrgData = { - clientId, - clientSecret: this.maskString(clientSecret), - idpId - }; + try { + const orgCredentials = await this.registerToKeycloak( + organizationDetails.name, + organizationDetails.id, + keycloakUserId, + userId, + true + ); + + const { clientId, idpId, clientSecret } = orgCredentials; + + generatedClientSecret = clientSecret; + + updateOrgData = { + clientId, + clientSecret: this.maskString(clientSecret), + idpId + }; + } catch (error) { + this.logger.error(`Error In creating client : ${JSON.stringify(error)}`); + throw new InternalServerErrorException('Unable to create client'); + } } const updatedOrg = await this.organizationRepository.updateOrganizationById(updateOrgData, orgId); @@ -228,14 +241,9 @@ export class OrganizationService { const orgRolesList = [OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER]; - try { for (const role of orgRolesList) { await this.clientRegistrationService.createClientRole(orgDetails.idpId, token, role, role); - } - } catch (error) { - this.logger.error(`Error In creating client roles : ${JSON.stringify(error)}`); - throw new InternalServerErrorException('Unable to create client roles'); - } + } const ownerRoleClient = await this.clientRegistrationService.getClientSpecificRoles( orgDetails.idpId, @@ -250,24 +258,28 @@ export class OrganizationService { } ]; - await this.clientRegistrationService.createUserClientRole(orgDetails.idpId, token, keycloakUserId, payload); - const ownerRoleData = await this.orgRoleService.getRole(OrgRoles.OWNER); if (!shouldUpdateRole) { - await this.userOrgRoleService.createUserOrgRole(userId, ownerRoleData.id, orgId, ownerRoleClient.id); - } else { - const deleteUserRecords = await this.userOrgRoleService.deleteOrgRoles(userId, orgId); - this.logger.log(`deleteUserRecords::`, deleteUserRecords); + await Promise.all([ + this.clientRegistrationService.createUserClientRole(orgDetails.idpId, token, keycloakUserId, payload), + this.userOrgRoleService.createUserOrgRole(userId, ownerRoleData.id, orgId, ownerRoleClient.id) + ]); + + } else { const roleIdList = [ { roleId: ownerRoleData.id, idpRoleId: ownerRoleClient.id } - ]; - - await this.userOrgRoleService.updateUserOrgRole(userId, orgId, roleIdList); + ]; + + await Promise.all([ + this.clientRegistrationService.createUserClientRole(orgDetails.idpId, token, keycloakUserId, payload), + this.userOrgRoleService.deleteOrgRoles(userId, orgId), + this.userOrgRoleService.updateUserOrgRole(userId, orgId, roleIdList) + ]); } return orgDetails; @@ -576,6 +588,10 @@ export class OrganizationService { throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); } + if (!organizationDetails.idpId) { + return this.orgRoleService.getOrgRoles(); + } + const token = await this.clientRegistrationService.getManagementToken(); return this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); @@ -622,73 +638,134 @@ export class OrganizationService { } } - /** - * - * @Body sendInvitationDto - * @returns createInvitation - */ - - async createInvitation(bulkInvitationDto: BulkSendInvitationDto, userId: string, userEmail: string): Promise { + async createInvitationByOrgRoles( + bulkInvitationDto: BulkSendInvitationDto, + userEmail: string, + userId: string, + orgName: string + ): Promise { const { invitations, orgId } = bulkInvitationDto; - try { - const organizationDetails = await this.organizationRepository.getOrganizationDetails(orgId); - - const token = await this.clientRegistrationService.getManagementToken(); - const clientRolesList = await this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); - const orgRoles = await this.orgRoleService.getOrgRoles(); - for (const invitation of invitations) { const { orgRoleId, email } = invitation; const isUserExist = await this.checkUserExistInPlatform(email); const userData = await this.getUserFirstName(userEmail); - - const { firstName } = userData; - - const matchedRoles = clientRolesList - .filter((role) => orgRoleId.includes(role.id.trim())) - .map((role) => role.name); - - if (orgRoleId.length !== matchedRoles.length) { + + const {firstName} = userData; + const orgRolesDetails = await this.orgRoleService.getOrgRolesByIds(orgRoleId); + + if (0 === orgRolesDetails.length) { throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); } - const filteredOrgRoles = orgRoles.filter((role) => matchedRoles.includes(role.name.trim())); - - // const orgRolesDetails = await this.orgRoleService.getOrgRolesByIds(orgRoleId); - - // if (0 === orgRolesDetails.length) { - // throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); - // } - const isInvitationExist = await this.checkInvitationExist(email, orgId); if (!isInvitationExist && userEmail !== invitation.email) { - // await this.organizationRepository.createSendInvitation(email, String(orgId), String(userId), matchedRoles.map(role => role.id)); - await this.organizationRepository.createSendInvitation( - email, - String(orgId), - String(userId), - filteredOrgRoles.map((role) => role.id) - ); + await this.organizationRepository.createSendInvitation(email, String(orgId), String(userId), orgRoleId); try { - // await this.sendInviteEmailTemplate(email, organizationDetails.name, matchedRoles, firstName, isUserExist); - await this.sendInviteEmailTemplate( - email, - organizationDetails.name, - filteredOrgRoles, - firstName, - isUserExist - ); + await this.sendInviteEmailTemplate(email, orgName, orgRolesDetails, firstName, isUserExist); } catch (error) { throw new InternalServerErrorException(ResponseMessages.user.error.emailSend); } } } + } + + async createInvitationByClientRoles( + bulkInvitationDto: BulkSendInvitationDto, + userEmail: string, + userId: string, + orgName: string, + idpId: string + ): Promise { + const { invitations, orgId } = bulkInvitationDto; + + const token = await this.clientRegistrationService.getManagementToken(); + const clientRolesList = await this.clientRegistrationService.getAllClientRoles(idpId, token); + const orgRoles = await this.orgRoleService.getOrgRoles(); + + for (const invitation of invitations) { + const { orgRoleId, email } = invitation; + + const isUserExist = await this.checkUserExistInPlatform(email); + + const userData = await this.getUserFirstName(userEmail); + + const { firstName } = userData; + + const matchedRoles = clientRolesList + .filter((role) => orgRoleId.includes(role.id.trim())) + .map((role) => role.name); + + if (orgRoleId.length !== matchedRoles.length) { + throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); + } + + const filteredOrgRoles = orgRoles.filter((role) => matchedRoles.includes(role.name.trim())); + + const isInvitationExist = await this.checkInvitationExist(email, orgId); + + if (!isInvitationExist && userEmail !== invitation.email) { + + await this.organizationRepository.createSendInvitation( + email, + String(orgId), + String(userId), + filteredOrgRoles.map((role) => role.id) + ); + + try { + await this.sendInviteEmailTemplate( + email, + orgName, + filteredOrgRoles, + firstName, + isUserExist + ); + } catch (error) { + throw new InternalServerErrorException(ResponseMessages.user.error.emailSend); + } + } + } + } + + /** + * + * @Body sendInvitationDto + * @returns createInvitation + */ + + async createInvitation(bulkInvitationDto: BulkSendInvitationDto, userId: string, userEmail: string): Promise { + const { orgId } = bulkInvitationDto; + + try { + const organizationDetails = await this.organizationRepository.getOrganizationDetails(orgId); + + if (!organizationDetails) { + throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); + } + + if (!organizationDetails.idpId) { + await this.createInvitationByOrgRoles( + bulkInvitationDto, + userEmail, + userId, + organizationDetails.name + ); + } else { + await this.createInvitationByClientRoles( + bulkInvitationDto, + userEmail, + userId, + organizationDetails.name, + organizationDetails.idpId + ); + } + await this.userActivityService.createActivity( userId, organizationDetails.id, @@ -818,6 +895,49 @@ export class OrganizationService { } } + async updateClientInvitation( + // eslint-disable-next-line camelcase + invitation: org_invitations, + idpId: string, + userId: string, + keycloakUserId: string, + orgId: string, + status: string + ): Promise { + const token = await this.clientRegistrationService.getManagementToken(); + const clientRolesList = await this.clientRegistrationService.getAllClientRoles(idpId, token); + + const orgRoles = await this.orgRoleService.getOrgRolesByIds(invitation.orgRoles); + + const rolesPayload: { roleId: string; name: string; idpRoleId: string }[] = orgRoles.map((orgRole: IOrgRole) => { + let roleObj: { roleId: string; name: string; idpRoleId: string} = null; + + for (let index = 0; index < clientRolesList.length; index++) { + if (clientRolesList[index].name === orgRole.name) { + roleObj = { + roleId: orgRole.id, + name: orgRole.name, + idpRoleId: clientRolesList[index].id + }; + break; + } + } + + return roleObj; + }); + + const data = { + status + }; + + await Promise.all([ + this.organizationRepository.updateOrgInvitation(invitation.id, data), + this.clientRegistrationService.createUserClientRole(idpId, token, keycloakUserId, rolesPayload.map(role => ({id: role.idpRoleId, name: role.name}))), + this.userOrgRoleService.updateUserOrgRole(userId, orgId, rolesPayload) + ]); + + } + /** * * @param payload @@ -858,33 +978,15 @@ export class OrganizationService { return ResponseMessages.user.success.invitationReject; } - const token = await this.clientRegistrationService.getManagementToken(); - const clientRolesList = await this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); - - const orgRoles = await this.orgRoleService.getOrgRolesByIds(invitation.orgRoles); - - const rolesPayload: { roleId: string; name: string; idpRoleId: string }[] = orgRoles.map((orgRole: IOrgRole) => { - let roleObj: { roleId: string; name: string; idpRoleId: string} = null; + if (organizationDetails.idpId) { + await this.updateClientInvitation(invitation, organizationDetails.idpId, userId, keycloakUserId, orgId, status); + } else { + await this.organizationRepository.updateOrgInvitation(invitationId, data); - for (let index = 0; index < clientRolesList.length; index++) { - if (clientRolesList[index].name === orgRole.name) { - roleObj = { - roleId: orgRole.id, - name: orgRole.name, - idpRoleId: clientRolesList[index].id - }; - break; - } + for (const roleId of invitation.orgRoles) { + await this.userOrgRoleService.createUserOrgRole(userId, roleId, orgId); } - - return roleObj; - }); - - await Promise.all([ - this.organizationRepository.updateOrgInvitation(invitationId, data), - this.clientRegistrationService.createUserClientRole(organizationDetails.idpId, token, keycloakUserId, rolesPayload.map(role => ({id: role.idpRoleId, name: role.name}))), - this.userOrgRoleService.updateUserOrgRole(userId, orgId, rolesPayload) - ]); + } return ResponseMessages.user.success.invitationAccept; } catch (error) { @@ -914,16 +1016,12 @@ export class OrganizationService { throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); } - // const roleIdsList = []; - const token = await this.clientRegistrationService.getManagementToken(); const clientRolesList = await this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); const orgRoles = await this.orgRoleService.getOrgRoles(); const matchedClientRoles = clientRolesList.filter((role) => roleIds.includes(role.id.trim())); - // .map((role) => role.name); - // const matchedOrgRoles = orgRoles.filter((role) => matchedClientRoles.some(clientRole => clientRole.name === role.name)); - + if (roleIds.length !== matchedClientRoles.length) { throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); } @@ -947,16 +1045,6 @@ export class OrganizationService { const userData = await this.getUserUserId(userId); - // const isRolesExist = await this.orgRoleService.getOrgRolesByIds(roleIds); - - // if (isRolesExist && 0 === isRolesExist.length) { - // throw new NotFoundException(ResponseMessages.organisation.error.rolesNotExist); - // } - - // await this.clientRegistrationService.deleteUserClientRoles(organizationDetails.idpId, token, userData.keycloakUserId); - - // const deleteUserRecords = await this.userOrgRoleService.deleteOrgRoles(userId, orgId); - const [ , deletedUserRoleRecords From ae8e26739a49b7623e431477e5c58306028dd479 Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Sat, 24 Feb 2024 22:23:33 +0530 Subject: [PATCH 035/231] fix:get-token-start_agent.sh (#540) --- apps/agent-provisioning/AFJ/scripts/start_agent.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh index 37047815c..87a595939 100644 --- a/apps/agent-provisioning/AFJ/scripts/start_agent.sh +++ b/apps/agent-provisioning/AFJ/scripts/start_agent.sh @@ -227,7 +227,7 @@ if [ $? -eq 0 ]; then container_logs=$(docker logs $(docker ps -q --filter "name=${AGENCY}_${CONTAINER_NAME}")) # Extract the token from the logs using sed - token=$(echo "$container_logs" | sed -nE 's/.*API Toekn: ([^ ]+).*/\1/p') + token=$(echo "$container_logs" | sed -nE 's/.*API Token: ([^ ]+).*/\1/p') # Print the extracted token echo "Token: $token" From dc2780669230f35631e7567923937af95aebdbd1 Mon Sep 17 00:00:00 2001 From: Krishna Date: Sun, 25 Feb 2024 17:47:21 +0530 Subject: [PATCH 036/231] Utilility endpoint for storing endpoint and returning URL Signed-off-by: Krishna --- .../src/utilities/dtos/store-object.dto.ts | 162 +++++++++++++++++- .../src/utilities/utilities.controller.ts | 4 +- .../src/utilities/utilities.service.ts | 7 +- .../interfaces/shortening-url.interface.ts | 26 +++ apps/utility/src/utilities.controller.ts | 12 +- apps/utility/src/utilities.service.ts | 8 +- 6 files changed, 203 insertions(+), 16 deletions(-) diff --git a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts index d52fd8f5e..d849702ea 100644 --- a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts +++ b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts @@ -1,11 +1,161 @@ -import { ApiExtraModels, ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsNotEmpty, IsOptional, IsString, IsUrl, ValidateNested } from 'class-validator'; -@ApiExtraModels() +// export type StoreObjectDto = InvitationDto; -export class StoreObjectDto { +class ServiceDto { + @ApiProperty({ + example: 'service-id' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid id' }) + id: string; + + @ApiProperty({ + example: 'http://example.com' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) + @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) + serviceEndpoint: string; + + @ApiProperty({ + example: 'service-type' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid type' }) + type: string; + + @ApiProperty({ + example: ['key1', 'key2'] + }) + @IsString({ each: true }) + recipientKeys: string[]; + + @ApiPropertyOptional({ + example: ['key1', 'key2'] + }) + @IsOptional() + @IsString({ each: true }) + routingKeys: string[]; + + @ApiPropertyOptional({ + example: ['true'] + }) + @IsOptional() + @IsString({ each: true }) + accept: string[]; +} + +export class InvitationDto { + @ApiPropertyOptional({ + example: 'your-id' + }) + @IsOptional() + @IsString() + @IsNotEmpty({ message: 'please provide valid @id' }) + '@id': string; @ApiProperty({ - description: 'The data to be stored' + example: 'your-type' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid @type' }) + '@type': string; + + @ApiProperty({ + example: 'your-label' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid label' }) + label: string; + + @ApiPropertyOptional({ + example: 'your-goal-code' + }) + @IsOptional() + @IsString() + @IsNotEmpty({ message: 'please provide valid goalCode' }) + goalCode: string; + + @ApiPropertyOptional({ + example: 'your-goal' + }) + @IsOptional() + @IsString() + @IsNotEmpty({ message: 'please provide valid goal' }) + goal: string; + + @ApiPropertyOptional({ + example: ['accept1', 'accept2'] + }) + @IsOptional() + @IsString({ each: true }) + accept: string[]; + + @ApiPropertyOptional({ + example: ['protocol1', 'protocol2'] + }) + @IsOptional() + @IsString({ each: true }) + // eslint-disable-next-line camelcase + handshake_protocols: string[]; + + @ApiProperty( + // { + // 'example': [ + // { + // id: 'service-id', + // serviceEndpoint: 'http://example.com', + // type: 'service-type', + // recipientKeys: ['key1', 'key2'], + // routingKeys: ['key1', 'key2'], + // accept: ['true'] + // } + // ] + // } + ) + @ValidateNested({ each: true }) + @Type(() => ServiceDto) + services: ServiceDto[]; + + @ApiPropertyOptional({ + example: 'http://example.com/image.jpg' + }) + @IsString() + @IsOptional() + @IsNotEmpty({ message: 'please provide valid imageUrl' }) + @IsString() + imageUrl?: string; +} + +export class StoreObjectDto { + @ApiProperty({ + 'example': { + '@id': 'your-id', + '@type': 'your-type', + label: 'your-label', + goalCode: 'your-goal-code', + goal: 'your-goal', + accept: ['accept1', 'accept2'], + // eslint-disable-next-line camelcase + handshake_protocols: ['protocol1', 'protocol2'], + services: [ + { + id: 'service-id', + serviceEndpoint: 'http://example.com', + type: 'service-type', + recipientKeys: ['key1', 'key2'], + routingKeys: ['key1', 'key2'], + accept: ['true'] + } + // Add more service objects as needed + ], + imageUrl: 'http://example.com/image.jpg' + } }) - data: object; -} \ No newline at end of file + @ValidateNested() + @Type(() => InvitationDto) + data: InvitationDto; +} diff --git a/apps/api-gateway/src/utilities/utilities.controller.ts b/apps/api-gateway/src/utilities/utilities.controller.ts index e0b02fa4c..b9a7af0bd 100644 --- a/apps/api-gateway/src/utilities/utilities.controller.ts +++ b/apps/api-gateway/src/utilities/utilities.controller.ts @@ -39,14 +39,16 @@ export class UtilitiesController { @Post('/store-object/:persistent') @ApiOperation({ summary: 'Store an object and return a short url to it', description: 'Create a short url representing the object' }) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async storeObject(@Body() storeObjectDto: StoreObjectDto, @Param('persistent') persistent: boolean, @Res() res: Response): Promise { // eslint-disable-next-line no-console - console.log(storeObjectDto); + console.log('Reached in api-gateway controller. The object to store is::::::: ', JSON.stringify(storeObjectDto.data)); const shorteningUrl = await this.utilitiesService.storeObject(persistent.valueOf(), storeObjectDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, message: ResponseMessages.storeObject.success.storeObject, data: shorteningUrl + // data: 'success' }; return res.status(HttpStatus.CREATED).json(finalResponse); } diff --git a/apps/api-gateway/src/utilities/utilities.service.ts b/apps/api-gateway/src/utilities/utilities.service.ts index 4aa7aac5f..5e27a6723 100644 --- a/apps/api-gateway/src/utilities/utilities.service.ts +++ b/apps/api-gateway/src/utilities/utilities.service.ts @@ -15,8 +15,9 @@ export class UtilitiesService extends BaseService { } async storeObject(persistent: boolean, storeObj: StoreObjectDto): Promise { - // const payload = {persistent, storeObj}; - return `This is the storeObject:::::::: ${JSON.stringify(storeObj)}`; - // return this.sendNatsMessage(this.serviceProxy, 'store-object-return-url', payload); + const payload = {persistent, storeObj}; + // eslint-disable-next-line no-console + console.log('Reached in api-gateway services. The object to store is::::::: ', JSON.stringify(payload.storeObj)); + return this.sendNatsMessage(this.serviceProxy, 'store-object-return-url', payload); } } diff --git a/apps/utility/interfaces/shortening-url.interface.ts b/apps/utility/interfaces/shortening-url.interface.ts index 00d2e3a29..755649e40 100644 --- a/apps/utility/interfaces/shortening-url.interface.ts +++ b/apps/utility/interfaces/shortening-url.interface.ts @@ -16,4 +16,30 @@ export interface IUtilities { invitationUrl: string; attributes: IAttributes[]; } +// export type StoreObjectDto = InvitationDto; +interface ServiceDto { + id: string; + serviceEndpoint: string; + type: string; + recipientKeys: string[]; + routingKeys: string[]; + accept: string[]; +} + +export interface IInvitation { + '@id': string; + '@type': string; + label: string; + goalCode: string; + goal: string; + accept: string[]; + // eslint-disable-next-line camelcase + handshake_protocols: string[]; + services: ServiceDto[]; + imageUrl?: string; +} + +export interface IStoreObject { + data: IInvitation; +} \ No newline at end of file diff --git a/apps/utility/src/utilities.controller.ts b/apps/utility/src/utilities.controller.ts index 5ddb945f4..d5481dfdd 100644 --- a/apps/utility/src/utilities.controller.ts +++ b/apps/utility/src/utilities.controller.ts @@ -1,8 +1,7 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { UtilitiesService } from './utilities.service'; -import { IShorteningUrlData } from '../interfaces/shortening-url.interface'; -// import { IUtilities } from '../interfaces/shortening-url.interface'; +import { IShorteningUrlData, IStoreObject } from '../interfaces/shortening-url.interface'; @Controller() export class UtilitiesController { @@ -19,7 +18,12 @@ export class UtilitiesController { } @MessagePattern({ cmd: 'store-object-return-url' }) - async storeObject(payload: {persistent: boolean, storeObj: object}): Promise { - return this.utilitiesService.storeObject(payload); + async storeObject(payload: {persistent: boolean, storeObj: IStoreObject}): Promise { + // eslint-disable-next-line no-console + console.log('Reached in Utility microservice controller. The object to store is::::::: ', JSON.stringify(payload.storeObj)); + const url:string = await this.utilitiesService.storeObject(payload); + // eslint-disable-next-line no-console + console.log('Received `url` in Utility microservice controller:::::::', url); + return url; } } \ No newline at end of file diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index cc373f90a..2994a1bff 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -4,6 +4,7 @@ import { UtilitiesRepository } from './utilities.repository'; import { AwsService } from '@credebl/aws'; // import { IUtilities } from '../interfaces/shortening-url.interface'; import { S3 } from 'aws-sdk'; +import { IStoreObject } from '../interfaces/shortening-url.interface'; @Injectable() export class UtilitiesService { @@ -49,12 +50,15 @@ export class UtilitiesService { } } - async storeObject(payload: {persistent: boolean, storeObj: object}): Promise { + async storeObject(payload: {persistent: boolean, storeObj: IStoreObject}): Promise { try { + // eslint-disable-next-line no-console + console.log('received here. StoreObj::::::', JSON.stringify(payload.storeObj.data)); const timestamp = Date.now(); - const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, timestamp, payload.storeObj); + const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, timestamp, payload.storeObj.data); const url: string = `https://${uploadResult.Bucket}.s3.${process.env.AWS_S3_STOREOBJECT_REGION}.amazonaws.com/${uploadResult.Key}`; return url; + // return 'success'; } catch (error) { throw new Error('An error occurred while uploading data to S3.'); } From e2123b44a5f4c04409b25785c63a452ae81153e1 Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:01:49 +0530 Subject: [PATCH 037/231] feat: add question answer protocol (#539) * feat:added question answer protocol Signed-off-by: pallavicoder * fear:added question answer webhook Signed-off-by: pallavicoder * feat:add question answer protocol inside platform Signed-off-by: pallavicoder * fix:removed extra cost variables Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder --- .../AFJ/scripts/start_agent.sh | 0 .../src/agent-service.controller.ts | 16 ++ .../src/agent-service.service.ts | 31 +++- .../src/interface/agent-service.interface.ts | 11 ++ .../src/connection/connection.controller.ts | 84 +++++++++ .../src/connection/connection.service.ts | 20 ++ .../connection/dtos/question-answer.dto.ts | 95 ++++++++++ apps/connection/src/connection.controller.ts | 12 ++ apps/connection/src/connection.service.ts | 175 ++++++++++++++++++ .../interfaces/question-answer.interfaces.ts | 16 ++ libs/common/src/common.constant.ts | 9 +- libs/common/src/response-messages/index.ts | 4 +- 12 files changed, 468 insertions(+), 5 deletions(-) mode change 100644 => 100755 apps/agent-provisioning/AFJ/scripts/start_agent.sh create mode 100644 apps/api-gateway/src/connection/dtos/question-answer.dto.ts create mode 100644 apps/connection/src/interfaces/question-answer.interfaces.ts diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh old mode 100644 new mode 100755 diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 8e96d70b9..55e6452d2 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -208,4 +208,20 @@ export class AgentServiceController { }): Promise { return this.agentServiceService.receiveInvitation(payload.receiveInvitation, payload.url, payload.apiKey); } + + @MessagePattern({ cmd: 'agent-send-question' }) + async sendQuestion(payload: { + url, + apiKey, + questionPayload + }): Promise { + + return this.agentServiceService.sendQuestion(payload.questionPayload, payload.url, payload.apiKey); + } + + @MessagePattern({ cmd: 'agent-get-question-answer-record' }) + async getQuestionAnswersRecord(payload: { url: string, apiKey: string }): Promise { + return this.agentServiceService.getQuestionAnswersRecord(payload.url, payload.apiKey); + } + } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 4e4f540a3..6dca30728 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -19,7 +19,7 @@ import * as dotenv from 'dotenv'; import * as fs from 'fs'; import { map } from 'rxjs/operators'; dotenv.config(); -import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation } from './interface/agent-service.interface'; +import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation, IQuestionPayload } from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; import { ledgers, org_agents, organisation, platform_config } from '@prisma/client'; @@ -1305,5 +1305,34 @@ export class AgentServiceService { throw error; } } + + async sendQuestion(questionPayload: IQuestionPayload, url: string, apiKey: string): Promise { + try { + const sendQuestionRes = await this.commonService + .httpPost(url, questionPayload, { headers: { 'authorization': apiKey } }) + .then(async response => response); + return sendQuestionRes; + } catch (error) { + this.logger.error(`Error in send question in agent service : ${JSON.stringify(error)}`); + throw error; + } + } + + async getQuestionAnswersRecord(url: string, apiKey: string): Promise { + + try { + const data = await this.commonService + .httpGet(url, { headers: { 'authorization': apiKey } }) + .then(async response => response) + .catch(error => this.handleAgentSpinupStatusErrors(error)); + + return data; + } catch (error) { + this.logger.error(`Error in getQuestionAnswersRecord in agent service : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + + } + } diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 663a32e27..f52152eda 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -416,3 +416,14 @@ interface ITags { threadId: string; } +export interface IValidResponses { + text: string; + } + export interface IQuestionPayload { + detail: string; + validResponses: IValidResponses[]; + question: string; + orgId?: string; + connectionId: string; + tenantId: string; + } \ No newline at end of file diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 6ca88310d..b4e7ac48b 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -20,6 +20,7 @@ import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; import { SortFields } from 'apps/connection/src/enum/connection.enum'; import { ClientProxy} from '@nestjs/microservices'; +import { QuestionAnswerWebhookDto, QuestionDto} from './dtos/question-answer.dto'; @UseFilters(CustomExceptionFilter) @Controller() @@ -107,6 +108,29 @@ export class ConnectionController { return res.status(HttpStatus.OK).json(finalResponse); } + + @Get('orgs/:orgId/question-answer/question/:tenantId') + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @ApiOperation({ + summary: `Get question-answer record`, + description: `Get question-answer record` + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + async getQuestionAnswersRecord( + @Param('tenantId') tenantId: string, + @Param('orgId') orgId: string, + @Res() res: Response + ): Promise { + const record = await this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: ResponseMessages.connection.success.questionAnswerRecord, + data: record + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + /** * Create out-of-band connection legacy invitation * @param connectionDto @@ -136,6 +160,33 @@ export class ConnectionController { } + @Post('/orgs/:orgId/question-answer/question/:connectionId/:tenantId') + @ApiOperation({ summary: '', description: 'question-answer/question' }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async sendQuestion( + @Param('orgId') orgId: string, + @Param('connectionId') connectionId: string, + @Param('tenantId') tenantId: string, + @Body() questionDto: QuestionDto, + @User() reqUser: IUserRequestInterface, + @Res() res: Response + ): Promise { + + questionDto.orgId = orgId; + questionDto.connectionId = connectionId; + questionDto.tenantId = tenantId; + const questionData = await this.connectionService.sendQuestion(questionDto); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.connection.success.questionSend, + data: questionData + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + + } + @Post('/orgs/:orgId/receive-invitation-url') @ApiOperation({ summary: 'Receive Invitation URL', description: 'Receive Invitation URL' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @@ -218,4 +269,37 @@ export class ConnectionController { } return res.status(HttpStatus.CREATED).json(finalResponse); } + + + @Post('wh/:orgId/question-answer/') + @ApiExcludeEndpoint() + @ApiOperation({ + summary: 'Catch question-answer webhook responses', + description: 'Callback URL for question-answer' + }) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async getQuestionAnswerWebhook( + @Body() questionAnswerWebhookDto:QuestionAnswerWebhookDto, + @Param('orgId') orgId: string, + @Res() res: Response + ): Promise { + questionAnswerWebhookDto.type = 'question-answer'; + this.logger.debug(`questionAnswer ::: ${JSON.stringify(questionAnswerWebhookDto)} ${orgId}`); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.connection.success.create, + data: '' + }; + const webhookUrl = await this.connectionService._getWebhookUrl(questionAnswerWebhookDto.contextCorrelationId).catch(error => { + this.logger.debug(`error in getting webhook url ::: ${JSON.stringify(error)}`); + + }); + if (webhookUrl) { + await this.connectionService._postWebhookResponse(webhookUrl, { data: questionAnswerWebhookDto }).catch(error => { + this.logger.debug(`error in posting webhook response to webhook url ::: ${JSON.stringify(error)}`); + }); + } + return res.status(HttpStatus.CREATED).json(finalResponse); + } } diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index 0bb79af9a..f80a425b5 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -6,6 +6,7 @@ import { ConnectionDto, CreateConnectionDto, ReceiveInvitationDto, ReceiveInvita import { IReceiveInvitationRes, IUserRequestInterface } from './interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById, IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; +import { QuestionDto } from './dtos/question-answer.dto'; @Injectable() export class ConnectionService extends BaseService { @@ -13,6 +14,16 @@ export class ConnectionService extends BaseService { super('ConnectionService'); } + sendQuestion( + questionDto: QuestionDto + ): Promise { + try { + return this.sendNatsMessage(this.connectionServiceProxy, 'send-question', questionDto); + } catch (error) { + throw new RpcException(error.response); + } + } + createLegacyConnectionInvitation( connectionDto: CreateConnectionDto, user: IUserRequestInterface @@ -75,6 +86,15 @@ export class ConnectionService extends BaseService { return this.sendNatsMessage(this.connectionServiceProxy, 'get-connection-details-by-connectionId', payload); } + + getQuestionAnswersRecord( + tenantId: string, + orgId: string + ): Promise { + const payload = { tenantId, orgId }; + return this.sendNatsMessage(this.connectionServiceProxy, 'get-question-answer-record', payload); + } + receiveInvitationUrl( receiveInvitationUrl: ReceiveInvitationUrlDto, orgId: string, diff --git a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts new file mode 100644 index 000000000..49ceafdb7 --- /dev/null +++ b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts @@ -0,0 +1,95 @@ +import { trim } from '@credebl/common/cast.helper'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Transform, Type } from 'class-transformer'; +import { IsArray, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator'; + +class ValidResponses { + @ApiProperty({ example: 'Emma' }) + @IsNotEmpty({ message: 'text is required' }) + @IsString({ message: 'text should be a string' }) + @Transform(({ value }) => trim(value)) + @Type(() => String) + text: string; +} +export class QuestionDto { + @ApiPropertyOptional() + @IsOptional() + @IsString({ message: 'detail must be a string' }) + @IsNotEmpty({ message: 'please provide valid detail' }) + detail: string; + + @ApiProperty({ example: [{ 'text': 'Emma'}, { 'text': 'Kiva'}] }) + @IsNotEmpty({ message: 'Please provide valid responses' }) + @IsArray({ message: 'Responses should be array' }) + @ValidateNested({ each: true }) + @Type(() => ValidResponses) + validResponses: ValidResponses[]; + + @ApiProperty({ example: 'What is your name'}) + @IsNotEmpty({ message: 'question is required' }) + @IsString({ message: 'question must be a string' }) + @IsNotEmpty({ message: 'please provide valid question' }) + question: string; + + orgId: string; + connectionId: string; + tenantId: string; +} + +export class QuestionAnswerWebhookDto { + + + @ApiPropertyOptional() + @IsOptional() + id: string; + + @ApiPropertyOptional() + @IsOptional() + createdAt: string; + + @ApiPropertyOptional() + @IsOptional() + questionText: string; + + @ApiPropertyOptional() + @IsOptional() + questionDetail: string; + + @ApiPropertyOptional() + @IsOptional() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validResponses:any; + + @ApiPropertyOptional() + @IsOptional() + connectionId: string; + + @ApiPropertyOptional() + @IsOptional() + role: string; + + @ApiPropertyOptional() + @IsOptional() + signatureRequired: boolean; + + @ApiPropertyOptional() + @IsOptional() + state: boolean; + + @ApiPropertyOptional() + @IsOptional() + threadId: string; + + @ApiPropertyOptional() + @IsOptional() + updatedAt: string; + + @ApiPropertyOptional() + @IsOptional() + contextCorrelationId: string; + + @ApiPropertyOptional() + @IsOptional() + type: string; + +} \ No newline at end of file diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index 2e4f49b02..c30bbed6c 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -12,6 +12,7 @@ import { } from './interfaces/connection.interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; +import { IQuestionAnswerPayload, IQuestionPayload } from './interfaces/question-answer.interfaces'; @Controller() export class ConnectionController { @@ -76,4 +77,15 @@ export class ConnectionController { const { user, receiveInvitation, orgId } = payload; return this.connectionService.receiveInvitation(user, receiveInvitation, orgId); } + + @MessagePattern({ cmd: 'send-question' }) + async sendQuestion(payload: IQuestionPayload): Promise { + return this.connectionService.sendQuestion(payload); + } + + @MessagePattern({ cmd: 'get-question-answer-record' }) + async getQuestionAnswersRecord(payload: IQuestionAnswerPayload): Promise { + const { tenantId, orgId } = payload; + return this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + } } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 39b369c80..28734c003 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -21,6 +21,7 @@ import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; +import { IQuestionPayload } from './interfaces/question-answer.interfaces'; @Injectable() export class ConnectionService { @@ -312,6 +313,41 @@ export class ConnectionService { } } + async getQuestionAnswersRecord(tenantId: string, orgId: string): Promise { + try { + const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); + const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + const { agentEndPoint } = agentDetails; + if (!agentDetails) { + throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); + } + + const label = 'get-question-answer-record'; + const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId); + + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this._getOrgAgentApiKey(orgId); + } + const record = await this._getQuestionAnswersRecord(url, apiKey); + return record; + + + } catch (error) { + this.logger.error(`[sendQuestion] - error in get question answer record: ${error}`); + if (error && error?.status && error?.status?.message && error?.status?.message?.error) { + throw new RpcException({ + message: error?.status?.message?.error?.reason + ? error?.status?.message?.error?.reason + : error?.status?.message?.error, + statusCode: error?.status?.code + }); + } else { + throw new RpcException(error.response ? error.response : error); + } + } + } + async _getConnectionsByConnectionId( url: string, apiKey: string @@ -336,6 +372,29 @@ export class ConnectionService { }); } + async _getQuestionAnswersRecord( + url: string, + apiKey: string + ): Promise { + + const pattern = { cmd: 'agent-get-question-answer-record' }; + const payload = { url, apiKey }; + return this.connectionServiceProxy + .send(pattern, payload) + .toPromise() + .catch(error => { + this.logger.error( + `[_getQuestionAnswersRecord] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` + ); + throw new HttpException( + { + status: error.statusCode, + error: error.error?.message?.error ? error.error?.message?.error : error.error, + message: error.message + }, error.error); + }); + } + /** * Description: Fetch agent url * @param referenceId @@ -358,6 +417,51 @@ export class ConnectionService { } } + async getQuestionAnswerAgentUrl( + label: string, + orgAgentType: string, + agentEndPoint: string, + tenantId: string, + connectionId?: string + ): Promise { + try { + + let url; + switch (label) { + case 'send-question': { + url = orgAgentType === OrgAgentType.DEDICATED + ? `${agentEndPoint}${CommonConstants.URL_SEND_QUESTION}`.replace('#', connectionId) + : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_SEND_QUESTION}`.replace('#', connectionId).replace('@', tenantId) + : null; + break; + } + + case 'get-question-answer-record': { + url = orgAgentType === OrgAgentType.DEDICATED + ? `${agentEndPoint}${CommonConstants.URL_QUESTION_ANSWER_RECORD}` + : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_QUESTION_ANSWER_RECORD}`.replace('#', tenantId) + : null; + break; + } + + default: { + break; + } + } + + if (!url) { + throw new NotFoundException(ResponseMessages.issuance.error.agentUrlNotFound); + } + + return url; + } catch (error) { + this.logger.error(`Error get question answer agent Url: ${JSON.stringify(error)}`); + throw error; + } + } + async _getOrgAgentApiKey(orgId: string): Promise { const pattern = { cmd: 'get-org-agent-api-key' }; const payload = { orgId }; @@ -509,5 +613,76 @@ export class ConnectionService { }, error.error); }); } + + async _sendQuestion( + questionPayload: IQuestionPayload, + url: string, + apiKey: string + ): Promise { + + const pattern = { cmd: 'agent-send-question' }; + const payload = { questionPayload, url, apiKey }; + + return this.connectionServiceProxy + .send(pattern, payload) + .toPromise() + .catch(error => { + this.logger.error( + `[_sendQuestion] [NATS call]- error in send question : ${JSON.stringify(error)}` + ); + throw new HttpException( + { + status: error.statusCode, + error: error.error?.message?.error ? error.error?.message?.error : error.error, + message: error.message + }, error.error); + }); + + + } + + async sendQuestion(payload: IQuestionPayload): Promise { + + const { detail, validResponses, question, orgId, connectionId} = payload; + try { + + const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); + + const { agentEndPoint} = agentDetails; + + if (!agentDetails) { + throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); + } + + const questionPayload = { + detail, + validResponses, + question + }; + + const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + const label = 'send-question'; + const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId, connectionId); + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this._getOrgAgentApiKey(orgId); + } + const createQuestion = await this._sendQuestion(questionPayload, url, apiKey); + return createQuestion; + + } catch (error) { + this.logger.error(`[sendQuestion] - error in sending question: ${error}`); + if (error && error?.status && error?.status?.message && error?.status?.message?.error) { + throw new RpcException({ + message: error?.status?.message?.error?.reason + ? error?.status?.message?.error?.reason + : error?.status?.message?.error, + statusCode: error?.status?.code + }); + } else { + throw new RpcException(error.response ? error.response : error); + } + } + } } diff --git a/apps/connection/src/interfaces/question-answer.interfaces.ts b/apps/connection/src/interfaces/question-answer.interfaces.ts new file mode 100644 index 000000000..a1434d2e8 --- /dev/null +++ b/apps/connection/src/interfaces/question-answer.interfaces.ts @@ -0,0 +1,16 @@ +export interface IValidResponses { + text: string; + } + export interface IQuestionPayload { + detail: string; + validResponses: IValidResponses[]; + question: string; + orgId?: string; + connectionId?: string; + tenantId?: string; + } + + export interface IQuestionAnswerPayload { + tenantId: string; + orgId: string; + } \ No newline at end of file diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index abeeebf8f..2f03c0019 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -73,7 +73,8 @@ export enum CommonConstants { URL_ISSUE_GET_CREDS_AFJ_BY_CRED_REC_ID = '/credentials', URL_OUT_OF_BAND_CREDENTIAL_OFFER = '/credentials/create-offer-oob', URL_ACCEPT_CREDENTIALS = '/credentials/accept-offer', - + URL_SEND_QUESTION = '/question-answer/question/#', + URL_QUESTION_ANSWER_RECORD = '/question-answer', // SCHEMA & CRED DEF SERVICES URL_SCHM_CREATE_SCHEMA = '/schemas', @@ -106,8 +107,10 @@ export enum CommonConstants { URL_SHAGENT_ACCEPT_OFFER = '/multi-tenancy/credentials/accept-offer/#', URL_SHAGENT_RECEIVE_INVITATION_URL = '/multi-tenancy/receive-invitation-url/#', URL_SHAGENT_RECEIVE_INVITATION = '/multi-tenancy/receive-invitation/#', - - + URL_SHAGENT_SEND_QUESTION = '/multi-tenancy/question-answer/question/#/@', + URL_SHAGENT_SEND_ANSWER = '/multi-tenancy/question-answer/answer/#/@', + URL_SHAGENT_QUESTION_ANSWER_RECORD = '/multi-tenancy/question-answer/#', + // PROOF SERVICES URL_SEND_PROOF_REQUEST = '/proofs/request-proof', URL_GET_PROOF_PRESENTATIONS = '/proofs', diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 695d8210c..ba3434b24 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -225,7 +225,9 @@ export const ResponseMessages = { create: 'Connection created successfully', receivenvitation: 'Invitation received successfully', fetchConnection: 'Connection details fetched successfully', - fetch: 'Connections details fetched successfully' + fetch: 'Connections details fetched successfully', + questionAnswerRecord: 'Question Answer record fetched successfully', + questionSend:'Question sent successfully' }, error: { exists: 'Connection is already exist', From 950a0edb7d4a72b0862d33d7bd4ddfd1b13f0242 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 26 Feb 2024 12:39:00 +0530 Subject: [PATCH 038/231] Solved the sonar lint issues Signed-off-by: KulkarniShashank --- Dockerfiles/Dockerfile.agent-provisioning | 29 +++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Dockerfiles/Dockerfile.agent-provisioning b/Dockerfiles/Dockerfile.agent-provisioning index 72201a53f..6b6370dcb 100644 --- a/Dockerfiles/Dockerfile.agent-provisioning +++ b/Dockerfiles/Dockerfile.agent-provisioning @@ -6,12 +6,14 @@ FROM node:18-alpine as build # RUN apk add openssh-client # RUN apk update # RUN apk add aws-cli -RUN npm install -g pnpm --ignore-scripts \ - && apk update \ - && apk add openssh-client \ - && apk add aws-cli \ - && apk add docker \ - && apk add docker-compose +RUN set -eux \ + && apk --no-cache add \ + openssh-client \ + aws-cli \ + docker \ + docker-compose \ + && npm install -g pnpm --ignore-scripts \ + && rm -rf /var/cache/apk/* RUN docker --version && \ docker-compose --version @@ -47,12 +49,15 @@ FROM node:18-alpine as prod # RUN apk add openssh-client # RUN apk update # RUN apk add aws-cli -RUN npm install -g pnpm --ignore-scripts \ - && apk update \ - && apk add openssh-client \ - && apk add aws-cli \ - && apk add docker \ - && apk add docker-compose +RUN set -eux \ + && apk --no-cache add \ + openssh-client \ + aws-cli \ + docker \ + docker-compose \ + && npm install -g pnpm --ignore-scripts \ + && rm -rf /var/cache/apk/* + WORKDIR /app From d6eb3790d4e5a8fb92e82d6b8df151e56c01a507 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 26 Feb 2024 12:57:24 +0530 Subject: [PATCH 039/231] Solved the security issues Signed-off-by: KulkarniShashank --- Dockerfiles/Dockerfile.agent-provisioning | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfiles/Dockerfile.agent-provisioning b/Dockerfiles/Dockerfile.agent-provisioning index 6b6370dcb..7ac849872 100644 --- a/Dockerfiles/Dockerfile.agent-provisioning +++ b/Dockerfiles/Dockerfile.agent-provisioning @@ -13,7 +13,8 @@ RUN set -eux \ docker \ docker-compose \ && npm install -g pnpm --ignore-scripts \ - && rm -rf /var/cache/apk/* + && export PATH=$PATH:/usr/lib/node_modules/pnpm/bin \ + && rm -rf /var/cache/apk/* RUN docker --version && \ docker-compose --version @@ -56,9 +57,8 @@ RUN set -eux \ docker \ docker-compose \ && npm install -g pnpm --ignore-scripts \ - && rm -rf /var/cache/apk/* - - + && export PATH=$PATH:/usr/lib/node_modules/pnpm/bin \ + && rm -rf /var/cache/apk/* WORKDIR /app From c3d5fc87a3d2fccae33573e5f52ec625359cb846 Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 26 Feb 2024 13:08:51 +0530 Subject: [PATCH 040/231] fix: question answer protocol (#541) * feat:added question answer protocol Signed-off-by: pallavicoder * fear:added question answer webhook Signed-off-by: pallavicoder * feat:add question answer protocol inside platform Signed-off-by: pallavicoder * fix:removed extra cost variables Signed-off-by: pallavicoder * fix:removed tenant id from API Signed-off-by: pallavicoder * Merge branch 'fix:removed DTO Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder --- .../src/connection/connection.controller.ts | 11 ++++------- apps/api-gateway/src/connection/connection.service.ts | 5 ++--- .../src/connection/dtos/question-answer.dto.ts | 1 - apps/connection/src/connection.controller.ts | 7 +++---- apps/connection/src/connection.service.ts | 4 ++-- .../src/interfaces/question-answer.interfaces.ts | 5 ----- 6 files changed, 11 insertions(+), 22 deletions(-) diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index b4e7ac48b..2cb6bb804 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -111,18 +111,17 @@ export class ConnectionController { @Get('orgs/:orgId/question-answer/question/:tenantId') @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiOperation({ summary: `Get question-answer record`, description: `Get question-answer record` }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) async getQuestionAnswersRecord( - @Param('tenantId') tenantId: string, @Param('orgId') orgId: string, @Res() res: Response ): Promise { - const record = await this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + const record = await this.connectionService.getQuestionAnswersRecord(orgId); const finalResponse: IResponse = { statusCode: HttpStatus.OK, message: ResponseMessages.connection.success.questionAnswerRecord, @@ -161,14 +160,13 @@ export class ConnectionController { } @Post('/orgs/:orgId/question-answer/question/:connectionId/:tenantId') - @ApiOperation({ summary: '', description: 'question-answer/question' }) + @ApiOperation({ summary: '', description: 'send question' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) async sendQuestion( @Param('orgId') orgId: string, @Param('connectionId') connectionId: string, - @Param('tenantId') tenantId: string, @Body() questionDto: QuestionDto, @User() reqUser: IUserRequestInterface, @Res() res: Response @@ -176,7 +174,6 @@ export class ConnectionController { questionDto.orgId = orgId; questionDto.connectionId = connectionId; - questionDto.tenantId = tenantId; const questionData = await this.connectionService.sendQuestion(questionDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index f80a425b5..c01321093 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -88,11 +88,10 @@ export class ConnectionService extends BaseService { getQuestionAnswersRecord( - tenantId: string, orgId: string ): Promise { - const payload = { tenantId, orgId }; - return this.sendNatsMessage(this.connectionServiceProxy, 'get-question-answer-record', payload); + + return this.sendNatsMessage(this.connectionServiceProxy, 'get-question-answer-record', orgId); } receiveInvitationUrl( diff --git a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts index 49ceafdb7..583448183 100644 --- a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts +++ b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts @@ -33,7 +33,6 @@ export class QuestionDto { orgId: string; connectionId: string; - tenantId: string; } export class QuestionAnswerWebhookDto { diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index c30bbed6c..923a15190 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -12,7 +12,7 @@ import { } from './interfaces/connection.interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; -import { IQuestionAnswerPayload, IQuestionPayload } from './interfaces/question-answer.interfaces'; +import { IQuestionPayload } from './interfaces/question-answer.interfaces'; @Controller() export class ConnectionController { @@ -84,8 +84,7 @@ export class ConnectionController { } @MessagePattern({ cmd: 'get-question-answer-record' }) - async getQuestionAnswersRecord(payload: IQuestionAnswerPayload): Promise { - const { tenantId, orgId } = payload; - return this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + async getQuestionAnswersRecord(orgId: string): Promise { + return this.connectionService.getQuestionAnswersRecord(orgId); } } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 28734c003..7dbb306f7 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -313,7 +313,7 @@ export class ConnectionService { } } - async getQuestionAnswersRecord(tenantId: string, orgId: string): Promise { + async getQuestionAnswersRecord(orgId: string): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -421,7 +421,7 @@ export class ConnectionService { label: string, orgAgentType: string, agentEndPoint: string, - tenantId: string, + tenantId?: string, connectionId?: string ): Promise { try { diff --git a/apps/connection/src/interfaces/question-answer.interfaces.ts b/apps/connection/src/interfaces/question-answer.interfaces.ts index a1434d2e8..6fcabd9ef 100644 --- a/apps/connection/src/interfaces/question-answer.interfaces.ts +++ b/apps/connection/src/interfaces/question-answer.interfaces.ts @@ -9,8 +9,3 @@ export interface IValidResponses { connectionId?: string; tenantId?: string; } - - export interface IQuestionAnswerPayload { - tenantId: string; - orgId: string; - } \ No newline at end of file From 3d0e590a2443843a1b187c4eb9dc3b68b4fff832 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 26 Feb 2024 15:45:15 +0530 Subject: [PATCH 041/231] fix: check the schema if exist or not in endorsement Signed-off-by: KulkarniShashank --- apps/ecosystem/src/ecosystem.service.ts | 55 +++++++-- .../interfaces/schema-payload.interface.ts | 5 + .../schema/repositories/schema.repository.ts | 96 ++++++++------- apps/ledger/src/schema/schema.controller.ts | 116 +++++++++++------- apps/ledger/src/schema/schema.service.ts | 26 +++- 5 files changed, 198 insertions(+), 100 deletions(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 97fb35a47..7cf98c99a 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -55,13 +55,13 @@ import { GetAllSchemaList, GetEndorsementsPayload } from '../interfaces/endorsem import { CommonConstants } from '@credebl/common/common.constant'; // eslint-disable-next-line camelcase import { -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase credential_definition, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase endorsement_transaction, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase org_agents, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase platform_config, schema, user @@ -679,9 +679,15 @@ export class EcosystemService { ecosystemId: string ): Promise { try { - const getEcosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); - const { name, version } = requestSchemaPayload; + const alreadySchemaExist = await this._schemaExist(version, name); + this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist)}`); + + if (alreadySchemaExist) { + throw new BadRequestException(ResponseMessages.ecosystem.error.schemaAlreadyExist); + } + + const getEcosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); if (0 === name.length) { throw new BadRequestException(ResponseMessages.schema.error.nameNotEmpty); @@ -711,7 +717,8 @@ export class EcosystemService { this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); - const existSchema = schemaRequestExist?.filter( + const existSchema = + schemaRequestExist?.filter( (schema) => schema.status === endorsementTransactionStatus.REQUESTED || schema.status === endorsementTransactionStatus.SIGNED || schema.status === endorsementTransactionStatus.SUBMITED @@ -803,6 +810,26 @@ export class EcosystemService { } } + async _schemaExist(version: string, schemaName: string): Promise { + const pattern = { cmd: 'schema-exist' }; + const payload = { version, schemaName }; + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const message = await this.ecosystemServiceProxy.send(pattern, payload).toPromise(); + return message; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + async requestCredDeffEndorsement( requestCredDefPayload: RequestCredDeffEndorsement, orgId: string, @@ -824,11 +851,13 @@ export class EcosystemService { this.ecosystemRepository.getAgentDetails(getEcosystemLeadDetails.orgId), this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); - - const existsCredDef = credDefRequestExist?.filter(tag => tag.status === endorsementTransactionStatus.REQUESTED || - tag.status === endorsementTransactionStatus.SIGNED || - tag.status === endorsementTransactionStatus.SUBMITED - ) ?? []; + + const existsCredDef = + credDefRequestExist?.filter( + (tag) => tag.status === endorsementTransactionStatus.REQUESTED || + tag.status === endorsementTransactionStatus.SIGNED || + tag.status === endorsementTransactionStatus.SUBMITED + ) ?? []; if (0 < existsCredDef.length) { throw new ConflictException(ResponseMessages.ecosystem.error.credDefAlreadyExist); @@ -878,7 +907,7 @@ export class EcosystemService { // To return selective response await this.removeEndorsementTransactionFields(storeTransaction); - await new Promise(resolve => setTimeout(resolve, 5000)); + await new Promise((resolve) => setTimeout(resolve, 5000)); return storeTransaction; } else { const orgAgentType = await this.ecosystemRepository.getOrgAgentType(ecosystemMemberDetails.orgAgentTypeId); diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index 68cf783f7..fb5a3f37e 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -67,3 +67,8 @@ export interface ISchemaCredDeffSearchInterface { user: IUserRequestInterface, } +export interface ISchemaExist { + schemaName: string; + version: string; +} + diff --git a/apps/ledger/src/schema/repositories/schema.repository.ts b/apps/ledger/src/schema/repositories/schema.repository.ts index 58c782f6a..418ea2c32 100644 --- a/apps/ledger/src/schema/repositories/schema.repository.ts +++ b/apps/ledger/src/schema/repositories/schema.repository.ts @@ -2,7 +2,7 @@ import { ConflictException, Injectable, InternalServerErrorException, Logger } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; import { ledgers, org_agents, org_agents_type, organisation, schema } from '@prisma/client'; -import { ISchema, ISchemaSearchCriteria } from '../interfaces/schema-payload.interface'; +import { ISchema, ISchemaExist, ISchemaSearchCriteria } from '../interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { AgentDetails, ISchemasWithCount } from '../interfaces/schema.interface'; import { SortValue } from '@credebl/enum/enum'; @@ -12,23 +12,18 @@ import { ICredDefWithCount } from '@credebl/common/interfaces/schema.interface'; export class SchemaRepository { private readonly logger = new Logger('SchemaRepository'); - constructor( - private prisma: PrismaService - ) { } + constructor(private prisma: PrismaService) {} async saveSchema(schemaResult: ISchema): Promise { try { if (schemaResult.schema.schemaName) { - const schema = await this.schemaExists( - schemaResult.schema.schemaName, - schemaResult.schema.schemaVersion - ); + const schema = await this.schemaExists(schemaResult.schema.schemaName, schemaResult.schema.schemaVersion); const schemaLength = 0; - if (schema.length !== schemaLength) { - throw new ConflictException( - ResponseMessages.schema.error.exists, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + if (schema.length !== schemaLength) { + throw new ConflictException(ResponseMessages.schema.error.exists, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } const saveResult = await this.prisma.schema.create({ data: { @@ -96,7 +91,7 @@ export class SchemaRepository { issuerId: true }, orderBy: { - [payload.sortField]: SortValue.ASC === payload.sortBy ? 'asc' : 'desc' + [payload.sortField]: SortValue.ASC === payload.sortBy ? 'asc' : 'desc' }, take: Number(payload.pageSize), skip: (payload.pageNumber - 1) * payload.pageSize @@ -111,11 +106,10 @@ export class SchemaRepository { return { schemasCount, schemasResult }; } catch (error) { this.logger.error(`Error in getting schemas: ${error}`); - throw new InternalServerErrorException( - ResponseMessages.schema.error.failedFetchSchema, - { cause: new Error(), description: error.message } - ); - + throw new InternalServerErrorException(ResponseMessages.schema.error.failedFetchSchema, { + cause: new Error(), + description: error.message + }); } } @@ -138,11 +132,13 @@ export class SchemaRepository { } } - async getAgentType(orgId: string): Promise { + async getAgentType(orgId: string): Promise< + organisation & { + org_agents: (org_agents & { + org_agent_type: org_agents_type; + })[]; + } + > { try { const agentDetails = await this.prisma.organisation.findUnique({ where: { @@ -164,16 +160,12 @@ export class SchemaRepository { } async getSchemasCredDeffList(payload: ISchemaSearchCriteria): Promise { + const { orgId, schemaId } = payload; - const {orgId, schemaId} = payload; - try { const credDefResult = await this.prisma.credential_definition.findMany({ where: { - AND: [ - { orgId }, - { schemaLedgerId: schemaId } - ] + AND: [{ orgId }, { schemaLedgerId: schemaId }] }, select: { tag: true, @@ -190,10 +182,7 @@ export class SchemaRepository { }); const credDefCount = await this.prisma.credential_definition.count({ where: { - AND: [ - { orgId }, - { schemaLedgerId: schemaId } - ] + AND: [{ orgId }, { schemaLedgerId: schemaId }] } }); return { credDefResult, credDefCount }; @@ -202,7 +191,7 @@ export class SchemaRepository { throw error; } } - + async getAllSchemaDetails(payload: ISchemaSearchCriteria): Promise<{ schemasCount: number; schemasResult: { @@ -216,7 +205,7 @@ export class SchemaRepository { issuerId: string; orgId: string; }[]; - }> { + }> { try { const schemasResult = await this.prisma.schema.findMany({ where: { @@ -240,7 +229,7 @@ export class SchemaRepository { issuerId: true }, orderBy: { - [payload.sortField]: 'desc' === payload.sortBy ? 'desc' : 'asc' + [payload.sortField]: 'desc' === payload.sortBy ? 'desc' : 'asc' }, take: Number(payload.pageSize), skip: (payload.pageNumber - 1) * payload.pageSize @@ -265,7 +254,6 @@ export class SchemaRepository { schemaLedgerId: schemaId } }); - } catch (error) { this.logger.error(`Error in getting get schema by schema ledger id: ${error}`); throw error; @@ -274,7 +262,6 @@ export class SchemaRepository { async getOrgAgentType(orgAgentId: string): Promise { try { - const { agent } = await this.prisma.org_agents_type.findFirst({ where: { id: orgAgentId @@ -295,10 +282,37 @@ export class SchemaRepository { indyNamespace: LedgerName } }); - } catch (error) { this.logger.error(`Error in getting get schema by schema ledger id: ${error}`); throw error; } } -} \ No newline at end of file + + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + try { + return this.prisma.schema.findFirstOrThrow({ + where: { + name: payload.schemaName, + version: payload.version + } + }); + } catch (error) { + this.logger.error(`Error in getting get schema by name and version: ${error}`); + throw error; + } + } +} diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 15df46d6a..4384d4b7c 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -1,59 +1,85 @@ import { Controller } from '@nestjs/common'; import { SchemaService } from './schema.service'; import { MessagePattern } from '@nestjs/microservices'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaSearchPayload } from './interfaces/schema-payload.interface'; +import { + ISchema, + ISchemaCredDeffSearchInterface, + ISchemaExist, + ISchemaSearchPayload +} from './interfaces/schema-payload.interface'; import { schema } from '@prisma/client'; -import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; - +import { + ICredDefWithPagination, + ISchemaData, + ISchemasWithPagination +} from '@credebl/common/interfaces/schema.interface'; @Controller('schema') export class SchemaController { - constructor(private readonly schemaService: SchemaService) { } + constructor(private readonly schemaService: SchemaService) {} - @MessagePattern({ cmd: 'create-schema' }) - async createSchema(payload: ISchema): Promise { - const { schema, user, orgId } = payload; - return this.schemaService.createSchema(schema, user, orgId); - } + @MessagePattern({ cmd: 'create-schema' }) + async createSchema(payload: ISchema): Promise { + const { schema, user, orgId } = payload; + return this.schemaService.createSchema(schema, user, orgId); + } - @MessagePattern({ cmd: 'get-schema-by-id' }) - async getSchemaById(payload: ISchema): Promise { - const { schemaId, orgId } = payload; - return this.schemaService.getSchemaById(schemaId, orgId); - } + @MessagePattern({ cmd: 'get-schema-by-id' }) + async getSchemaById(payload: ISchema): Promise { + const { schemaId, orgId } = payload; + return this.schemaService.getSchemaById(schemaId, orgId); + } - @MessagePattern({ cmd: 'get-schemas' }) - async getSchemas(schemaSearch: ISchemaSearchPayload): Promise { - const { schemaSearchCriteria, orgId } = schemaSearch; - return this.schemaService.getSchemas(schemaSearchCriteria, orgId); - } + @MessagePattern({ cmd: 'get-schemas' }) + async getSchemas(schemaSearch: ISchemaSearchPayload): Promise { + const { schemaSearchCriteria, orgId } = schemaSearch; + return this.schemaService.getSchemas(schemaSearchCriteria, orgId); + } - @MessagePattern({ cmd: 'get-cred-deff-list-by-schemas-id' }) - async getcredDeffListBySchemaId(payload: ISchemaCredDeffSearchInterface): Promise { - return this.schemaService.getcredDeffListBySchemaId(payload); - } + @MessagePattern({ cmd: 'get-cred-deff-list-by-schemas-id' }) + async getcredDeffListBySchemaId(payload: ISchemaCredDeffSearchInterface): Promise { + return this.schemaService.getcredDeffListBySchemaId(payload); + } - @MessagePattern({ cmd: 'get-all-schemas' }) - async getAllSchema(schemaSearch: ISchemaSearchPayload): Promise<{ - totalItems: number; - hasNextPage: boolean; - hasPreviousPage: boolean; - nextPage: number; - previousPage: number; - lastPage: number; - data: { - createDateTime: Date; - createdBy: string; - name: string; - schemaLedgerId: string; - version: string; - attributes: string; - publisherDid: string; - issuerId: string; - }[]; - }> { - const { schemaSearchCriteria } = schemaSearch; - return this.schemaService.getAllSchema(schemaSearchCriteria); - } + @MessagePattern({ cmd: 'get-all-schemas' }) + async getAllSchema(schemaSearch: ISchemaSearchPayload): Promise<{ + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + data: { + createDateTime: Date; + createdBy: string; + name: string; + schemaLedgerId: string; + version: string; + attributes: string; + publisherDid: string; + issuerId: string; + }[]; + }> { + const { schemaSearchCriteria } = schemaSearch; + return this.schemaService.getAllSchema(schemaSearchCriteria); + } + @MessagePattern({ cmd: 'schema-exist' }) + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + return this.schemaService.schemaExist(payload); + } } diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index c8c1cd943..4c6d68fc4 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -11,7 +11,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { SchemaRepository } from './repositories/schema.repository'; import { schema } from '@prisma/client'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaPayload, ISchemaSearchCriteria } from './interfaces/schema-payload.interface'; +import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria } from './interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; @@ -481,5 +481,29 @@ export class SchemaService extends BaseService { } } + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + try { + const schemaExist = await this.schemaRepository.schemaExist(payload); + return schemaExist; + + } catch (error) { + this.logger.error(`Error in schema exist: ${error}`); + throw new RpcException(error.response ? error.response : error); + } + } } From c36b5154c44a50ead6f7e5456f9643346af849fa Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:56:47 +0530 Subject: [PATCH 042/231] fix: question answer protocol (#542) * feat:added question answer protocol Signed-off-by: pallavicoder * fear:added question answer webhook Signed-off-by: pallavicoder * feat:add question answer protocol inside platform Signed-off-by: pallavicoder * fix:removed extra cost variables Signed-off-by: pallavicoder * fix:removed tenant id from API Signed-off-by: pallavicoder * Merge branch 'fix:removed DTO Signed-off-by: pallavicoder * fix:removed tenant id from url Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder --- apps/api-gateway/src/connection/connection.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 2cb6bb804..686b97075 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -109,7 +109,7 @@ export class ConnectionController { } - @Get('orgs/:orgId/question-answer/question/:tenantId') + @Get('orgs/:orgId/question-answer/question') @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiOperation({ @@ -159,7 +159,7 @@ export class ConnectionController { } - @Post('/orgs/:orgId/question-answer/question/:connectionId/:tenantId') + @Post('/orgs/:orgId/question-answer/question/:connectionId') @ApiOperation({ summary: '', description: 'send question' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) From 9ce9d8102d94e952a7cb7b3de525bab35381770c Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 26 Feb 2024 16:44:11 +0530 Subject: [PATCH 043/231] fix: solved issues for the schema exist query Signed-off-by: KulkarniShashank --- apps/ecosystem/src/ecosystem.service.ts | 4 ++-- apps/ledger/src/schema/repositories/schema.repository.ts | 4 ++-- apps/ledger/src/schema/schema.controller.ts | 2 +- apps/ledger/src/schema/schema.service.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 7cf98c99a..be222c79c 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -681,9 +681,9 @@ export class EcosystemService { try { const { name, version } = requestSchemaPayload; const alreadySchemaExist = await this._schemaExist(version, name); - this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist)}`); + this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist.length)}`); - if (alreadySchemaExist) { + if (0 !== alreadySchemaExist.length) { throw new BadRequestException(ResponseMessages.ecosystem.error.schemaAlreadyExist); } diff --git a/apps/ledger/src/schema/repositories/schema.repository.ts b/apps/ledger/src/schema/repositories/schema.repository.ts index 418ea2c32..bd84b2785 100644 --- a/apps/ledger/src/schema/repositories/schema.repository.ts +++ b/apps/ledger/src/schema/repositories/schema.repository.ts @@ -302,9 +302,9 @@ export class SchemaRepository { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { try { - return this.prisma.schema.findFirstOrThrow({ + return this.prisma.schema.findMany({ where: { name: payload.schemaName, version: payload.version diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 4384d4b7c..68003ac49 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -79,7 +79,7 @@ export class SchemaController { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { return this.schemaService.schemaExist(payload); } } diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 4c6d68fc4..34ffde1ca 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -495,7 +495,7 @@ export class SchemaService extends BaseService { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { try { const schemaExist = await this.schemaRepository.schemaExist(payload); return schemaExist; From 8ebec4dbeaea5b5594896752aff6170e1eea4da6 Mon Sep 17 00:00:00 2001 From: Nishad Date: Mon, 26 Feb 2024 18:07:55 +0530 Subject: [PATCH 044/231] WIP migrate org as client to keycloak Signed-off-by: Nishad --- .../organization/organization.controller.ts | 22 +++++++++++++++++++ .../src/organization/organization.service.ts | 5 +++++ .../src/organization.controller.ts | 5 +++++ apps/organization/src/organization.service.ts | 12 ++++++++++ 4 files changed, 44 insertions(+) diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index e9f5b82fc..4145490a8 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -389,6 +389,28 @@ export class OrganizationController { return res.status(HttpStatus.OK).json(finalResponse); } + @Post('/resiter-org-map-users') + @ApiOperation({ + summary: 'Register client and map users', + description: 'Register client and map users' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + @Roles(OrgRoles.OWNER, OrgRoles.SUPER_ADMIN, OrgRoles.ADMIN) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiBearerAuth() + async registerOrgsMapUsers(@User() user: user, @Res() res: Response): Promise { + + await this.organizationService.registerOrgsMapUsers(user.id); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: 'Organization client created and users mapped to client' + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + + } + @Post('/:orgId/invitations') @ApiOperation({ summary: 'Create organization invitation', diff --git a/apps/api-gateway/src/organization/organization.service.ts b/apps/api-gateway/src/organization/organization.service.ts index 68277b7dd..98290e009 100644 --- a/apps/api-gateway/src/organization/organization.service.ts +++ b/apps/api-gateway/src/organization/organization.service.ts @@ -148,6 +148,11 @@ export class OrganizationService extends BaseService { return this.sendNatsMessage(this.serviceProxy, 'send-invitation', payload); } + async registerOrgsMapUsers(userId: string): Promise { + const payload = {userId}; + return this.sendNatsMessage(this.serviceProxy, 'register-orgs-users-map', payload); + } + /** * * @param updateUserDto diff --git a/apps/organization/src/organization.controller.ts b/apps/organization/src/organization.controller.ts index 3e6ddc2c0..245f4a8e8 100644 --- a/apps/organization/src/organization.controller.ts +++ b/apps/organization/src/organization.controller.ts @@ -117,6 +117,11 @@ export class OrganizationController { return this.organizationService.getOrgRoles(payload.orgId); } + @MessagePattern({ cmd: 'register-orgs-users-map' }) + async registerOrgsMapUsers(): Promise { + return this.organizationService.registerOrgsMapUsers(); + } + /** * Description: create new organization invitation * @param payload invitation Details diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index a301a2a79..72a429af1 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -1207,6 +1207,18 @@ export class OrganizationService { } } + async registerOrgsMapUsers(): Promise { + + try { + + return ''; + } catch (error) { + this.logger.error(`Error in registerOrgsMapUsers: ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + + } + } + async deleteOrganizationInvitation(orgId: string, invitationId: string): Promise { try { const invitationDetails = await this.organizationRepository.getInvitationById(invitationId); From 86976511d50da1e53d3f057ff3639a2f02075fb7 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Mon, 26 Feb 2024 19:04:40 +0530 Subject: [PATCH 045/231] fix: extra attribute in issuance payload Signed-off-by: pranalidhanavade --- apps/issuance/interfaces/issuance.interfaces.ts | 2 +- apps/issuance/src/issuance.service.ts | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 829984113..bad565712 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -5,9 +5,9 @@ import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-se export interface IAttributes { attributeName: string; - isRequired: boolean; name: string; value: string; + isRequired?: boolean; } export interface IIssuance { user?: IUserRequest; diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 41cfaa105..dcaa60e80 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -110,8 +110,10 @@ export class IssuanceService { connectionId, credentialFormats: { indy: { - attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId + } }, autoAcceptCredential: payload.autoAcceptCredential || 'always', @@ -198,7 +200,8 @@ export class IssuanceService { protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { - attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId } }, @@ -449,7 +452,6 @@ const credefError = []; `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` ); } - // }); }); @@ -490,7 +492,8 @@ const credefError = []; protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { - attributes: iterator.attributes || attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (iterator.attributes || attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId } }, From bda2dba17adc60fd6b1b20bab04dc81a33c373fa Mon Sep 17 00:00:00 2001 From: Krishna Date: Mon, 26 Feb 2024 20:53:12 +0530 Subject: [PATCH 046/231] Added support for shortening URL while creating connection Signed-off-by: Krishna --- .../src/utilities/dtos/store-object.dto.ts | 230 +++++++++--------- apps/connection/src/connection.service.ts | 148 +++++------ .../src/interfaces/connection.interfaces.ts | 23 ++ .../interfaces/shortening-url.interface.ts | 31 ++- apps/utility/src/utilities.controller.ts | 14 +- apps/utility/src/utilities.service.ts | 3 +- libs/aws/src/aws.service.ts | 2 +- 7 files changed, 247 insertions(+), 204 deletions(-) diff --git a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts index d849702ea..2c497d9f4 100644 --- a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts +++ b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts @@ -4,51 +4,59 @@ import { IsNotEmpty, IsOptional, IsString, IsUrl, ValidateNested } from 'class-v // export type StoreObjectDto = InvitationDto; -class ServiceDto { - @ApiProperty({ - example: 'service-id' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid id' }) - id: string; - - @ApiProperty({ - example: 'http://example.com' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) - @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) - serviceEndpoint: string; +// class ServiceDto { +// @ApiProperty({ +// example: 'service-id' +// }) +// @IsString() +// @IsNotEmpty({ message: 'please provide valid id' }) +// id: string; + +// @ApiProperty({ +// example: 'http://example.com' +// }) +// @IsString() +// @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) +// @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) +// serviceEndpoint: string; + +// @ApiProperty({ +// example: 'service-type' +// }) +// @IsString() +// @IsNotEmpty({ message: 'please provide valid type' }) +// type: string; + +// @ApiProperty({ +// example: ['key1', 'key2'] +// }) +// @IsString({ each: true }) +// recipientKeys: string[]; + +// @ApiPropertyOptional({ +// example: ['key1', 'key2'] +// }) +// @IsOptional() +// @IsString({ each: true }) +// routingKeys: string[]; + +// @ApiPropertyOptional({ +// example: ['true'] +// }) +// @IsOptional() +// @IsString({ each: true }) +// accept: string[]; +// } + +export class LegacyInvitationDto { @ApiProperty({ - example: 'service-type' + example: 'your-type' }) @IsString() - @IsNotEmpty({ message: 'please provide valid type' }) - type: string; - - @ApiProperty({ - example: ['key1', 'key2'] - }) - @IsString({ each: true }) - recipientKeys: string[]; - - @ApiPropertyOptional({ - example: ['key1', 'key2'] - }) - @IsOptional() - @IsString({ each: true }) - routingKeys: string[]; - - @ApiPropertyOptional({ - example: ['true'] - }) - @IsOptional() - @IsString({ each: true }) - accept: string[]; -} + @IsNotEmpty({ message: 'please provide valid @type' }) + '@type': string; -export class InvitationDto { @ApiPropertyOptional({ example: 'your-id' }) @@ -57,13 +65,6 @@ export class InvitationDto { @IsNotEmpty({ message: 'please provide valid @id' }) '@id': string; - @ApiProperty({ - example: 'your-type' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid @type' }) - '@type': string; - @ApiProperty({ example: 'your-label' }) @@ -72,90 +73,99 @@ export class InvitationDto { label: string; @ApiPropertyOptional({ - example: 'your-goal-code' + example: 'http://example.com/image.jpg' }) - @IsOptional() @IsString() - @IsNotEmpty({ message: 'please provide valid goalCode' }) - goalCode: string; - - @ApiPropertyOptional({ - example: 'your-goal' - }) @IsOptional() + @IsNotEmpty({ message: 'please provide valid imageUrl' }) @IsString() - @IsNotEmpty({ message: 'please provide valid goal' }) - goal: string; + imageUrl?: string; - @ApiPropertyOptional({ - example: ['accept1', 'accept2'] + @ApiProperty({ + example: ['key1', 'key2'] }) - @IsOptional() @IsString({ each: true }) - accept: string[]; + recipientKeys: string[]; - @ApiPropertyOptional({ - example: ['protocol1', 'protocol2'] + @ApiProperty({ + example: 'http://example.com' }) - @IsOptional() - @IsString({ each: true }) - // eslint-disable-next-line camelcase - handshake_protocols: string[]; - - @ApiProperty( - // { - // 'example': [ - // { - // id: 'service-id', - // serviceEndpoint: 'http://example.com', - // type: 'service-type', - // recipientKeys: ['key1', 'key2'], - // routingKeys: ['key1', 'key2'], - // accept: ['true'] - // } - // ] - // } - ) - @ValidateNested({ each: true }) - @Type(() => ServiceDto) - services: ServiceDto[]; + @IsString() + @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) + @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) + serviceEndpoint: string; @ApiPropertyOptional({ - example: 'http://example.com/image.jpg' + example: ['key1', 'key2'] }) - @IsString() @IsOptional() - @IsNotEmpty({ message: 'please provide valid imageUrl' }) - @IsString() - imageUrl?: string; + @IsString({ each: true }) + routingKeys: string[]; } +// @ApiPropertyOptional({ +// example: 'your-goal-code' +// }) +// @IsOptional() +// @IsString() +// @IsNotEmpty({ message: 'please provide valid goalCode' }) +// goalCode: string; + +// @ApiPropertyOptional({ +// example: 'your-goal' +// }) +// @IsOptional() +// @IsString() +// @IsNotEmpty({ message: 'please provide valid goal' }) +// goal: string; + +// @ApiPropertyOptional({ +// example: ['accept1', 'accept2'] +// }) +// @IsOptional() +// @IsString({ each: true }) +// accept: string[]; + +// @ApiPropertyOptional({ +// example: ['protocol1', 'protocol2'] +// }) +// @IsOptional() +// @IsString({ each: true }) +// // eslint-disable-next-line camelcase +// handshake_protocols: string[]; + +// @ApiProperty( +// // { +// // 'example': [ +// // { +// // id: 'service-id', +// // serviceEndpoint: 'http://example.com', +// // type: 'service-type', +// // recipientKeys: ['key1', 'key2'], +// // routingKeys: ['key1', 'key2'], +// // accept: ['true'] +// // } +// // ] +// // } +// ) +// @ValidateNested({ each: true }) +// @Type(() => ServiceDto) +// services: ServiceDto[]; +// } + export class StoreObjectDto { @ApiProperty({ 'example': { - '@id': 'your-id', '@type': 'your-type', + '@id': 'your-id', label: 'your-label', - goalCode: 'your-goal-code', - goal: 'your-goal', - accept: ['accept1', 'accept2'], - // eslint-disable-next-line camelcase - handshake_protocols: ['protocol1', 'protocol2'], - services: [ - { - id: 'service-id', - serviceEndpoint: 'http://example.com', - type: 'service-type', - recipientKeys: ['key1', 'key2'], - routingKeys: ['key1', 'key2'], - accept: ['true'] - } - // Add more service objects as needed - ], - imageUrl: 'http://example.com/image.jpg' + imageUrl: 'http://example.com/image.jpg', + recipientKeys: ['key1', 'key2'], + serviceEndpoint: 'http://example.com', + routingKeys: ['key1', 'key2'] } }) @ValidateNested() - @Type(() => InvitationDto) - data: InvitationDto; + @Type(() => LegacyInvitationDto) + data: LegacyInvitationDto; } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index bdaf636a4..f9183e1ff 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -30,7 +30,7 @@ export class ConnectionService { private readonly connectionRepository: ConnectionRepository, private readonly logger: Logger, @Inject(CACHE_MANAGER) private cacheService: Cache - ) { } + ) {} /** * Create connection legacy invitation URL @@ -39,10 +39,18 @@ export class ConnectionService { * @returns Connection legacy invitation URL */ async createLegacyConnectionInvitation(payload: IConnection): Promise { - - const { orgId, multiUseInvitation, autoAcceptConnection, alias, imageUrl, goal, goalCode, handshake, handshakeProtocols } = payload; + const { + orgId, + multiUseInvitation, + autoAcceptConnection, + alias, + imageUrl, + goal, + goalCode, + handshake, + handshakeProtocols + } = payload; try { - const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const { agentEndPoint, id, organisation } = agentDetails; @@ -53,8 +61,8 @@ export class ConnectionService { this.logger.log(`logoUrl:::, ${organisation.logoUrl}`); const connectionPayload = { - multiUseInvitation: multiUseInvitation || true, - autoAcceptConnection: autoAcceptConnection || true, + multiUseInvitation: multiUseInvitation ?? true, + autoAcceptConnection: autoAcceptConnection ?? true, alias: alias || undefined, imageUrl: organisation.logoUrl || imageUrl || undefined, label: organisation.name, @@ -72,22 +80,12 @@ export class ConnectionService { apiKey = await this._getOrgAgentApiKey(orgId); } const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); - const invitationObject = createConnectionInvitation?.message?.invitation['@id']; - // Need to implement shortening invitation logic - // Krishna start - // const connectionInvitaion = createConnectionInvitation?.message?.invitation; - // make call to function that will call - // const shortenedUrl = await this.storeObjectAndReturnUrl(connectionInvitaion, multiUseInvitation); - // Krishna end - // eslint-disable-next-line no-console - console.log("This is Invitation object::::::", createConnectionInvitation?.message?.invitation); - let shortenedUrl; - - if (agentDetails?.tenantId) { - shortenedUrl = `${agentEndPoint}/multi-tenancy/url/${agentDetails?.tenantId}/${invitationObject}`; - } else { - shortenedUrl = `${agentEndPoint}/url/${invitationObject}`; - } + const connectionInvitaion = createConnectionInvitation?.message?.invitation; + const shortenedUrl = await this.storeObjectAndReturnUrl( + connectionInvitaion, + connectionPayload.multiUseInvitation + ); + Logger.verbose('This is Invitation object::::::', createConnectionInvitation?.message?.invitation); const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, @@ -135,7 +133,6 @@ export class ConnectionService { url: string, apiKey: string ): Promise { - //nats call in agent-service to create an invitation url const pattern = { cmd: 'agent-create-connection-legacy-invitation' }; const payload = { connectionPayload, url, apiKey }; @@ -230,10 +227,7 @@ export class ConnectionService { }; return connectionResponse; } catch (error) { - - this.logger.error( - `[getConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}` - ); + this.logger.error(`[getConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } @@ -296,7 +290,6 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - // const apiKey = await this._getOrgAgentApiKey(orgId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { @@ -304,8 +297,6 @@ export class ConnectionService { } const createConnectionInvitation = await this._getConnectionsByConnectionId(url, apiKey); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[getConnectionsById] - error in get connections : ${JSON.stringify(error)}`); @@ -321,18 +312,14 @@ export class ConnectionService { } } - async _getConnectionsByConnectionId( - url: string, - apiKey: string - ): Promise { - + async _getConnectionsByConnectionId(url: string, apiKey: string): Promise { //nats call in agent service for fetch connection details const pattern = { cmd: 'agent-get-connection-details-by-connectionId' }; const payload = { url, apiKey }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { + .catch((error) => { this.logger.error( `[_getConnectionsByConnectionId] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` ); @@ -341,7 +328,9 @@ export class ConnectionService { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } @@ -377,14 +366,21 @@ export class ConnectionService { return message; } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException({ - status: error.status, - error: error.message - }, error.status); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); } } - async receiveInvitationUrl(user: IUserRequest, receiveInvitationUrl: IReceiveInvitationUrl, orgId: string): Promise { + async receiveInvitationUrl( + user: IUserRequest, + receiveInvitationUrl: IReceiveInvitationUrl, + orgId: string + ): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -394,13 +390,14 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } - let url; if (orgAgentType === OrgAgentType.DEDICATED) { url = `${agentEndPoint}${CommonConstants.URL_RECEIVE_INVITATION_URL}`; } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION_URL}` - .replace('#', agentDetails.tenantId); + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION_URL}`.replace( + '#', + agentDetails.tenantId + ); } else { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } @@ -411,8 +408,6 @@ export class ConnectionService { } const createConnectionInvitation = await this._receiveInvitationUrl(url, apiKey, receiveInvitationUrl); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[receiveInvitationUrl] - error in receive invitation url : ${JSON.stringify(error)}`); @@ -433,13 +428,12 @@ export class ConnectionService { apiKey: string, receiveInvitationUrl: IReceiveInvitationUrl ): Promise { - const pattern = { cmd: 'agent-receive-invitation-url' }; const payload = { url, apiKey, receiveInvitationUrl }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { + .catch((error) => { this.logger.error( `[_receiveInvitationUrl] [NATS call]- error in receive invitation url : ${JSON.stringify(error)}` ); @@ -448,11 +442,17 @@ export class ConnectionService { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } - async receiveInvitation(user: IUserRequest, receiveInvitation: IReceiveInvitation, orgId: string): Promise { + async receiveInvitation( + user: IUserRequest, + receiveInvitation: IReceiveInvitation, + orgId: string + ): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -466,8 +466,7 @@ export class ConnectionService { if (orgAgentType === OrgAgentType.DEDICATED) { url = `${agentEndPoint}${CommonConstants.URL_RECEIVE_INVITATION}`; } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION}` - .replace('#', agentDetails.tenantId); + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION}`.replace('#', agentDetails.tenantId); } else { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } @@ -478,8 +477,6 @@ export class ConnectionService { } const createConnectionInvitation = await this._receiveInvitation(url, apiKey, receiveInvitation); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[receiveInvitation] - error in receive invitation : ${JSON.stringify(error)}`); @@ -500,37 +497,52 @@ export class ConnectionService { apiKey: string, receiveInvitation: IReceiveInvitation ): Promise { - const pattern = { cmd: 'agent-receive-invitation' }; const payload = { url, apiKey, receiveInvitation }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { - this.logger.error( - `[_receiveInvitation] [NATS call]- error in receive invitation : ${JSON.stringify(error)}` - ); + .catch((error) => { + this.logger.error(`[_receiveInvitation] [NATS call]- error in receive invitation : ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } - // Krishna Start - async storeObjectAndReturnUrl(urlObjectReference: unknown, multiUseInvitation: boolean): Promise { - const persistent:boolean = multiUseInvitation; - const connectionInvitation: unknown = urlObjectReference; + async storeObjectAndReturnUrl(connectionInvitation, persistent: boolean): Promise { + const utilityRequestBodyString = JSON.stringify({ data: connectionInvitation }); + const storeObj = JSON.parse(utilityRequestBodyString); //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; - const payload = { persistent, connectionInvitation}; + const payload = { persistent, storeObj }; try { + const message = await this.connectionServiceProxy // eslint-disable-next-line @typescript-eslint/no-explicit-any - const message = await this.connectionServiceProxy.send(pattern, payload).toPromise(); + .send(pattern, payload) + .toPromise() + .catch((error) => { + this.logger.error( + `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( + error + )}` + ); + throw new HttpException( + { + status: error.statusCode, + error: error.error?.message?.error ? error.error?.message?.error : error.error, + message: error.message + }, + error.error + ); + }); return message; } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); @@ -543,6 +555,4 @@ export class ConnectionService { ); } } - // Krishna End } - diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index f26a6fd2f..0a4d53cb2 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -99,6 +99,29 @@ interface IInvitation { invitation: string; } + +interface IService { + id: string; + serviceEndpoint: string; + type: string; + recipientKeys: string[]; + routingKeys: string[]; + accept: string[]; +} + +export interface ILegacyInvitation { + '@id': string; + '@type': string; + label: string; + goalCode: string; + goal: string; + accept: string[]; + // eslint-disable-next-line camelcase + handshake_protocols: string[]; + services: IService[]; + imageUrl?: string; +} + export interface OrgAgent { organisation: organisation; id: string; diff --git a/apps/utility/interfaces/shortening-url.interface.ts b/apps/utility/interfaces/shortening-url.interface.ts index 755649e40..d8bdba0de 100644 --- a/apps/utility/interfaces/shortening-url.interface.ts +++ b/apps/utility/interfaces/shortening-url.interface.ts @@ -18,28 +18,25 @@ export interface IUtilities { } // export type StoreObjectDto = InvitationDto; -interface ServiceDto { - id: string; - serviceEndpoint: string; - type: string; - recipientKeys: string[]; - routingKeys: string[]; - accept: string[]; -} +// interface IService { +// id: string; +// serviceEndpoint: string; +// type: string; +// recipientKeys: string[]; +// routingKeys: string[]; +// accept: string[]; +// } -export interface IInvitation { - '@id': string; +export interface ILegacyInvitation { '@type': string; + '@id': string; label: string; - goalCode: string; - goal: string; - accept: string[]; - // eslint-disable-next-line camelcase - handshake_protocols: string[]; - services: ServiceDto[]; imageUrl?: string; + recipientKeys: string[]; + serviceEndpoint: string; + routingKeys: string[] } export interface IStoreObject { - data: IInvitation; + data: ILegacyInvitation; } \ No newline at end of file diff --git a/apps/utility/src/utilities.controller.ts b/apps/utility/src/utilities.controller.ts index d5481dfdd..c1fd1fd0c 100644 --- a/apps/utility/src/utilities.controller.ts +++ b/apps/utility/src/utilities.controller.ts @@ -1,4 +1,4 @@ -import { Controller } from '@nestjs/common'; +import { Controller, Logger } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { UtilitiesService } from './utilities.service'; import { IShorteningUrlData, IStoreObject } from '../interfaces/shortening-url.interface'; @@ -19,11 +19,13 @@ export class UtilitiesController { @MessagePattern({ cmd: 'store-object-return-url' }) async storeObject(payload: {persistent: boolean, storeObj: IStoreObject}): Promise { - // eslint-disable-next-line no-console - console.log('Reached in Utility microservice controller. The object to store is::::::: ', JSON.stringify(payload.storeObj)); + try { const url:string = await this.utilitiesService.storeObject(payload); - // eslint-disable-next-line no-console - console.log('Received `url` in Utility microservice controller:::::::', url); - return url; + return url; + } catch (error) { + Logger.error(error); + throw new Error('Error occured in Utility Microservices Controller'); + } + } } \ No newline at end of file diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index 2994a1bff..10e3db107 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -60,7 +60,8 @@ export class UtilitiesService { return url; // return 'success'; } catch (error) { - throw new Error('An error occurred while uploading data to S3.'); + Logger.error(error); + throw new Error('An error occurred while uploading data to S3. Error::::::'); } } } diff --git a/libs/aws/src/aws.service.ts b/libs/aws/src/aws.service.ts index 9c14ab513..9573d07db 100644 --- a/libs/aws/src/aws.service.ts +++ b/libs/aws/src/aws.service.ts @@ -95,7 +95,7 @@ export class AwsService { } async storeObject(persistent: boolean, key: number, body: unknown): Promise { - const objKey: string = persistent ? `persistent/${key}` : `default/${key}`; + const objKey: string = persistent.valueOf() ? `persistent/${key}` : `default/${key}`; const buf = Buffer.from(JSON.stringify(body)); const params: AWS.S3.PutObjectRequest = { Bucket: process.env.AWS_S3_STOREOBJECT_BUCKET, From 3d7b2f1b8fabbcbc49813c40c2d905501a3bac4a Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:17:12 +0530 Subject: [PATCH 047/231] Create new connection invitation (#546) * feat: modify connection invitation url Signed-off-by: bhavanakarwade * changed message pattern Signed-off-by: bhavanakarwade * fix: optimized conditions Signed-off-by: bhavanakarwade * fix: cred-def restrictions Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- .../src/issuance/dtos/issuance.dto.ts | 8 +++++++ .../interfaces/issuance.interfaces.ts | 21 ++++++++++++++++++- apps/issuance/src/issuance.repository.ts | 6 +++++- apps/issuance/src/issuance.service.ts | 20 +++++++++++++----- .../src/interfaces/verification.interface.ts | 1 + apps/verification/src/verification.service.ts | 17 +++++++++++---- libs/common/src/response-messages/index.ts | 2 +- 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 5b7957137..51dc9b192 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -71,6 +71,12 @@ class CredentialsIssuanceDto { @IsString({ message: 'label should be string' }) label?: string; + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'please provide valid imageUrl' }) + @IsString({ message: 'imageUrl must be a string' }) + imageUrl?: string; + @ApiPropertyOptional() @IsOptional() @IsString({ message: 'auto accept proof must be in string' }) @@ -238,6 +244,8 @@ export class OOBCredentialDtoWithEmail { @IsString({ message: 'protocol version should be string' }) protocolVersion?: string; + imageUrl?: string; + orgId: string; } diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index bad565712..d7ccbff71 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -1,6 +1,7 @@ // eslint-disable-next-line camelcase import { AutoAccept } from '@credebl/enum/enum'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; +import { organisation } from '@prisma/client'; import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-service.interface'; export interface IAttributes { @@ -142,7 +143,8 @@ export interface OutOfBandCredentialOfferPayload { goalCode?: string, parentThreadId?: string, willConfirm?: boolean, - label?: string + label?: string, + imageUrl?: string, autoAcceptCredential?: string; } @@ -209,3 +211,20 @@ export interface IIssuedCredentialsSearchCriteria { searchByText: string; user?: IUserRequestInterface; } + +export interface OrgAgent { + organisation: organisation; + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + orgDid: string; + verkey: string; + agentEndPoint: string; + agentId: string; + isDidPublic: boolean; + ledgerId: string; + orgAgentTypeId: string; + tenantId: string; +} diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 6f6efc02b..3b8307f22 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -14,6 +14,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { FileUploadData, IssueCredentialWebhookPayload, + OrgAgent, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; @@ -34,11 +35,14 @@ export class IssuanceRepository { * @returns Get getAgentEndPoint details */ // eslint-disable-next-line camelcase - async getAgentEndPoint(orgId: string): Promise { + async getAgentEndPoint(orgId: string): Promise { try { const agentDetails = await this.prisma.org_agents.findFirst({ where: { orgId + }, + include: { + organisation: true } }); diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index dcaa60e80..1800534ed 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -180,7 +180,8 @@ export class IssuanceService { const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); // eslint-disable-next-line camelcase - const { agentEndPoint } = agentDetails; + const { agentEndPoint, organisation } = agentDetails; + if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } @@ -209,7 +210,8 @@ export class IssuanceService { goalCode: payload.goalCode || undefined, parentThreadId: payload.parentThreadId || undefined, willConfirm: payload.willConfirm || undefined, - label: payload.label || undefined, + imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, + label: organisation?.name, comment: comment || '' }; const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); @@ -459,9 +461,10 @@ const credefError = []; throw new BadRequestException(credefError); } } - - + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); + + const { organisation } = agentDetails; if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } @@ -502,7 +505,8 @@ const credefError = []; goalCode: outOfBandCredential.goalCode || undefined, parentThreadId: outOfBandCredential.parentThreadId || undefined, willConfirm: outOfBandCredential.willConfirm || undefined, - label: outOfBandCredential.label || undefined + label: organisation?.name, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl }; this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); @@ -1093,13 +1097,19 @@ const credefError = []; fileUploadData.createDateTime = new Date(); fileUploadData.referenceId = jobDetails.data.email; fileUploadData.jobId = jobDetails.id; + const {orgId} = jobDetails; + + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); + // eslint-disable-next-line camelcase + const { organisation } = agentDetails; let isErrorOccurred = false; try { const oobIssuancepayload = { credentialDefinitionId: jobDetails.credentialDefinitionId, orgId: jobDetails.orgId, + label: organisation?.name, attributes: [], emailId: jobDetails.data.email }; diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 082fe897f..ff26c3ab9 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -98,6 +98,7 @@ export interface ISendProofRequestPayload { goalCode?: string; parentThreadId?: string; willConfirm?: boolean; + imageUrl?: string; } export interface IProofRequestPayload { diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index c910fecf3..b8fc4c9e9 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -342,11 +342,14 @@ export class VerificationService { // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); - const [getAgentDetails] = await Promise.all([ + const [getAgentDetails, getOrganization] = await Promise.all([ this.verificationRepository.getAgentEndPoint(user.orgId), this.verificationRepository.getOrganization(user.orgId) ]); + const imageUrl = getOrganization?.logoUrl; + outOfBandRequestProof['imageUrl'] = imageUrl; + const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethodLabel = 'create-request-out-of-band'; @@ -514,8 +517,8 @@ export class VerificationService { try { let requestedAttributes = {}; const requestedPredicates = {}; - const attributeWithSchemaIdExists = proofRequestpayload.attributes; - if (attributeWithSchemaIdExists) { + const {attributes} = proofRequestpayload; + if (attributes) { requestedAttributes = Object.fromEntries(proofRequestpayload.attributes.map((attribute, index) => { const attributeElement = attribute.attributeName; @@ -525,7 +528,13 @@ export class VerificationService { return [ attributeReferent, { - name: attributeElement + name: attributeElement, + restrictions: [ + { + cred_def_id: proofRequestpayload.attributes[index].credDefId ? proofRequestpayload.attributes[index].credDefId : undefined, + schema_id: proofRequestpayload.attributes[index].schemaId + } + ] } ]; } else { diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index ba3434b24..436d90506 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -407,5 +407,5 @@ export const ResponseMessages = { notFound: 'Notification record not found.', invalidUrl: 'Invalid URL' } - }, + } }; \ No newline at end of file From 445996daed6f56b06825d6c9219705a4e5ca3248 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 27 Feb 2024 14:08:12 +0530 Subject: [PATCH 048/231] fix: solved verification predicates values Signed-off-by: KulkarniShashank --- apps/verification/src/verification.service.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index b8fc4c9e9..10c1d494a 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -687,8 +687,6 @@ export class VerificationService { const extractedDataArray: IProofPresentationDetails[] = []; if (0 !== Object.keys(requestedAttributes).length && 0 !== Object.keys(requestedPredicates).length) { - - for (const key in requestedAttributes) { if (requestedAttributes.hasOwnProperty(key)) { @@ -699,10 +697,12 @@ export class VerificationService { credDefId = requestedAttributeKey?.restrictions[0]?.cred_def_id; schemaId = requestedAttributeKey?.restrictions[0]?.schema_id; + } else if (getProofPresentationById?.response?.presentation?.indy?.identifiers) { credDefId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].cred_def_id; schemaId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].schema_id; + } if (revealedAttrs.hasOwnProperty(key)) { @@ -717,11 +717,16 @@ export class VerificationService { } for (const key in requestedPredicates) { + if (requestedPredicates.hasOwnProperty(key)) { const attribute = requestedPredicates[key]; + const attributeName = attribute?.name; - const credDefId = attribute?.restrictions[0]?.cred_def_id; - const schemaId = attribute?.restrictions[0]?.schema_id; + + if (attribute?.restrictions) { + credDefId = attribute?.restrictions[0]?.cred_def_id; + schemaId = attribute?.restrictions[0]?.schema_id; + } const extractedData: IProofPresentationDetails = { [attributeName]: `${attribute?.p_type}${attribute?.p_value}`, @@ -755,6 +760,7 @@ export class VerificationService { } } } else if (0 !== Object.keys(requestedPredicates).length) { + for (const key in requestedPredicates) { if (requestedPredicates.hasOwnProperty(key)) { @@ -762,9 +768,8 @@ export class VerificationService { const attributeName = attribute?.name; [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); - const extractedData: IProofPresentationDetails = { - [attributeName]: `${requestedPredicates?.p_type}${requestedPredicates?.p_value}`, + [attributeName]: `${attribute?.p_type}${attribute?.p_value}`, 'credDefId': credDefId || null, 'schemaId': schemaId || null }; From 0d096c5dd5389180c2ca33b98ec1037623dc61ac Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 27 Feb 2024 18:53:05 +0530 Subject: [PATCH 049/231] fix:proof request api for attributes array Signed-off-by: pranalidhanavade --- apps/api-gateway/src/dtos/create-schema.dto.ts | 2 +- .../api-gateway/src/ecosystem/dtos/request-schema.dto.ts | 2 +- .../src/verification/dto/request-proof.dto.ts | 2 +- .../src/interfaces/verification.interface.ts | 3 ++- apps/verification/src/verification.service.ts | 9 +++++---- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index fa246beec..5ba17afe9 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -50,7 +50,7 @@ export class CreateSchemaDto { attributeName: 'name', schemaDataType: 'string', displayName: 'Name', - isRequired: 'true' + isRequired: true } ] }) diff --git a/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts b/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts index d7d496859..708985dda 100644 --- a/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/request-schema.dto.ts @@ -53,7 +53,7 @@ export class RequestSchemaDto { attributeName: 'name', schemaDataType: 'string', displayName: 'Name', - isRequired: 'true' + isRequired: true } ] }) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 7d04d5960..1a08db7e1 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -10,7 +10,7 @@ export class ProofRequestAttribute { @ValidateIf((obj) => obj.attributeNames === undefined) @IsNotEmpty() - @IsString({each:true}) + @IsString() attributeName?: string; @ValidateIf((obj) => obj.attributeName === undefined) diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index ff26c3ab9..d139576bd 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -2,7 +2,8 @@ import { AutoAccept } from "@credebl/enum/enum"; import { IUserRequest } from "@credebl/user-request/user-request.interface"; interface IProofRequestAttribute { - attributeName: string; + attributeName?: string; + attributeNames?:string[]; condition?: string; value?: string; credDefId?: string; diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index b8fc4c9e9..b40cfe585 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -515,20 +515,21 @@ export class VerificationService { requestedPredicates; }> { try { - let requestedAttributes = {}; + let requestedAttributes = {}; const requestedPredicates = {}; const {attributes} = proofRequestpayload; if (attributes) { - requestedAttributes = Object.fromEntries(proofRequestpayload.attributes.map((attribute, index) => { + requestedAttributes = Object.fromEntries(attributes.map((attribute, index) => { - const attributeElement = attribute.attributeName; + const attributeElement = attribute.attributeName || attribute.attributeNames; const attributeReferent = `additionalProp${index + 1}`; + const attributeKey = attribute.attributeName ? 'name' : 'names'; if (!attribute.condition && !attribute.value) { return [ attributeReferent, { - name: attributeElement, + [attributeKey]: attributeElement, restrictions: [ { cred_def_id: proofRequestpayload.attributes[index].credDefId ? proofRequestpayload.attributes[index].credDefId : undefined, From 26a002e315a6f4cea3dcafd3158ebabc124315dc Mon Sep 17 00:00:00 2001 From: Nishad Date: Tue, 27 Feb 2024 18:55:14 +0530 Subject: [PATCH 050/231] fixed bulk issuance upload issue Signed-off-by: Nishad --- apps/api-gateway/src/issuance/issuance.controller.ts | 5 +---- apps/issuance/src/issuance.service.ts | 9 ++++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 9e8fad833..5c58c622b 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -220,7 +220,6 @@ export class IssuanceController { @Param('orgId') orgId: string, @Res() res: Response ): Promise { - try { if (file) { const fileKey: string = uuidv4(); @@ -244,9 +243,7 @@ export class IssuanceController { }; return res.status(HttpStatus.CREATED).json(finalResponse); } - } catch (error) { - throw new RpcException(error.response ? error.response : error); - } + } @Get('/orgs/:orgId/:requestId/preview') diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 41cfaa105..5a96742cd 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -704,6 +704,8 @@ const credefError = []; try { const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails(credentialDefinitionId); + // console.log(`Export CSV`); + const jsonData = []; const attributesArray = JSON.parse(schemaResponse.attributes); @@ -815,11 +817,8 @@ const credefError = []; } catch (error) { this.logger.error(`error in validating credentials : ${error.response}`); - throw new Error(error.response.message ? error.response.message : error); - } finally { - // await this.awsService.deleteFile(importFileDetails.fileKey); - // this.logger.error(`Deleted uploaded file after processing.`); - } + throw new RpcException(error.response ? error.response : error); + } } async previewFileDataForIssuance( From 42f4289111dcf4bd26b438c9efbbeaace1053640 Mon Sep 17 00:00:00 2001 From: Nishad Date: Tue, 27 Feb 2024 19:01:12 +0530 Subject: [PATCH 051/231] removed commented code Signed-off-by: Nishad --- apps/issuance/src/issuance.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 5a96742cd..8ce5526ae 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -703,8 +703,6 @@ const credefError = []; async exportSchemaToCSV(credentialDefinitionId: string): Promise { try { const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails(credentialDefinitionId); - - // console.log(`Export CSV`); const jsonData = []; const attributesArray = JSON.parse(schemaResponse.attributes); From a8bef485733a2598ca00c5bfb8f1bcd085c49ce6 Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 26 Feb 2024 13:08:51 +0530 Subject: [PATCH 052/231] fix: question answer protocol (#541) * feat:added question answer protocol Signed-off-by: pallavicoder * fear:added question answer webhook Signed-off-by: pallavicoder * feat:add question answer protocol inside platform Signed-off-by: pallavicoder * fix:removed extra cost variables Signed-off-by: pallavicoder * fix:removed tenant id from API Signed-off-by: pallavicoder * Merge branch 'fix:removed DTO Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder Signed-off-by: KulkarniShashank --- .../src/connection/connection.controller.ts | 11 ++++------- apps/api-gateway/src/connection/connection.service.ts | 5 ++--- .../src/connection/dtos/question-answer.dto.ts | 1 - apps/connection/src/connection.controller.ts | 7 +++---- apps/connection/src/connection.service.ts | 4 ++-- .../src/interfaces/question-answer.interfaces.ts | 5 ----- 6 files changed, 11 insertions(+), 22 deletions(-) diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index b4e7ac48b..2cb6bb804 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -111,18 +111,17 @@ export class ConnectionController { @Get('orgs/:orgId/question-answer/question/:tenantId') @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiOperation({ summary: `Get question-answer record`, description: `Get question-answer record` }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) async getQuestionAnswersRecord( - @Param('tenantId') tenantId: string, @Param('orgId') orgId: string, @Res() res: Response ): Promise { - const record = await this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + const record = await this.connectionService.getQuestionAnswersRecord(orgId); const finalResponse: IResponse = { statusCode: HttpStatus.OK, message: ResponseMessages.connection.success.questionAnswerRecord, @@ -161,14 +160,13 @@ export class ConnectionController { } @Post('/orgs/:orgId/question-answer/question/:connectionId/:tenantId') - @ApiOperation({ summary: '', description: 'question-answer/question' }) + @ApiOperation({ summary: '', description: 'send question' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) async sendQuestion( @Param('orgId') orgId: string, @Param('connectionId') connectionId: string, - @Param('tenantId') tenantId: string, @Body() questionDto: QuestionDto, @User() reqUser: IUserRequestInterface, @Res() res: Response @@ -176,7 +174,6 @@ export class ConnectionController { questionDto.orgId = orgId; questionDto.connectionId = connectionId; - questionDto.tenantId = tenantId; const questionData = await this.connectionService.sendQuestion(questionDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index f80a425b5..c01321093 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -88,11 +88,10 @@ export class ConnectionService extends BaseService { getQuestionAnswersRecord( - tenantId: string, orgId: string ): Promise { - const payload = { tenantId, orgId }; - return this.sendNatsMessage(this.connectionServiceProxy, 'get-question-answer-record', payload); + + return this.sendNatsMessage(this.connectionServiceProxy, 'get-question-answer-record', orgId); } receiveInvitationUrl( diff --git a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts index 49ceafdb7..583448183 100644 --- a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts +++ b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts @@ -33,7 +33,6 @@ export class QuestionDto { orgId: string; connectionId: string; - tenantId: string; } export class QuestionAnswerWebhookDto { diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index c30bbed6c..923a15190 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -12,7 +12,7 @@ import { } from './interfaces/connection.interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; -import { IQuestionAnswerPayload, IQuestionPayload } from './interfaces/question-answer.interfaces'; +import { IQuestionPayload } from './interfaces/question-answer.interfaces'; @Controller() export class ConnectionController { @@ -84,8 +84,7 @@ export class ConnectionController { } @MessagePattern({ cmd: 'get-question-answer-record' }) - async getQuestionAnswersRecord(payload: IQuestionAnswerPayload): Promise { - const { tenantId, orgId } = payload; - return this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + async getQuestionAnswersRecord(orgId: string): Promise { + return this.connectionService.getQuestionAnswersRecord(orgId); } } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 28734c003..7dbb306f7 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -313,7 +313,7 @@ export class ConnectionService { } } - async getQuestionAnswersRecord(tenantId: string, orgId: string): Promise { + async getQuestionAnswersRecord(orgId: string): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -421,7 +421,7 @@ export class ConnectionService { label: string, orgAgentType: string, agentEndPoint: string, - tenantId: string, + tenantId?: string, connectionId?: string ): Promise { try { diff --git a/apps/connection/src/interfaces/question-answer.interfaces.ts b/apps/connection/src/interfaces/question-answer.interfaces.ts index a1434d2e8..6fcabd9ef 100644 --- a/apps/connection/src/interfaces/question-answer.interfaces.ts +++ b/apps/connection/src/interfaces/question-answer.interfaces.ts @@ -9,8 +9,3 @@ export interface IValidResponses { connectionId?: string; tenantId?: string; } - - export interface IQuestionAnswerPayload { - tenantId: string; - orgId: string; - } \ No newline at end of file From b71c3aa88a59489d2c63dfc79be017ef55d59541 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 26 Feb 2024 15:45:15 +0530 Subject: [PATCH 053/231] fix: check the schema if exist or not in endorsement Signed-off-by: KulkarniShashank --- apps/ecosystem/src/ecosystem.service.ts | 55 +++++++-- .../interfaces/schema-payload.interface.ts | 5 + .../schema/repositories/schema.repository.ts | 96 ++++++++------- apps/ledger/src/schema/schema.controller.ts | 116 +++++++++++------- apps/ledger/src/schema/schema.service.ts | 26 +++- 5 files changed, 198 insertions(+), 100 deletions(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 97fb35a47..7cf98c99a 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -55,13 +55,13 @@ import { GetAllSchemaList, GetEndorsementsPayload } from '../interfaces/endorsem import { CommonConstants } from '@credebl/common/common.constant'; // eslint-disable-next-line camelcase import { -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase credential_definition, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase endorsement_transaction, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase org_agents, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase platform_config, schema, user @@ -679,9 +679,15 @@ export class EcosystemService { ecosystemId: string ): Promise { try { - const getEcosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); - const { name, version } = requestSchemaPayload; + const alreadySchemaExist = await this._schemaExist(version, name); + this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist)}`); + + if (alreadySchemaExist) { + throw new BadRequestException(ResponseMessages.ecosystem.error.schemaAlreadyExist); + } + + const getEcosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); if (0 === name.length) { throw new BadRequestException(ResponseMessages.schema.error.nameNotEmpty); @@ -711,7 +717,8 @@ export class EcosystemService { this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); - const existSchema = schemaRequestExist?.filter( + const existSchema = + schemaRequestExist?.filter( (schema) => schema.status === endorsementTransactionStatus.REQUESTED || schema.status === endorsementTransactionStatus.SIGNED || schema.status === endorsementTransactionStatus.SUBMITED @@ -803,6 +810,26 @@ export class EcosystemService { } } + async _schemaExist(version: string, schemaName: string): Promise { + const pattern = { cmd: 'schema-exist' }; + const payload = { version, schemaName }; + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const message = await this.ecosystemServiceProxy.send(pattern, payload).toPromise(); + return message; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + async requestCredDeffEndorsement( requestCredDefPayload: RequestCredDeffEndorsement, orgId: string, @@ -824,11 +851,13 @@ export class EcosystemService { this.ecosystemRepository.getAgentDetails(getEcosystemLeadDetails.orgId), this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); - - const existsCredDef = credDefRequestExist?.filter(tag => tag.status === endorsementTransactionStatus.REQUESTED || - tag.status === endorsementTransactionStatus.SIGNED || - tag.status === endorsementTransactionStatus.SUBMITED - ) ?? []; + + const existsCredDef = + credDefRequestExist?.filter( + (tag) => tag.status === endorsementTransactionStatus.REQUESTED || + tag.status === endorsementTransactionStatus.SIGNED || + tag.status === endorsementTransactionStatus.SUBMITED + ) ?? []; if (0 < existsCredDef.length) { throw new ConflictException(ResponseMessages.ecosystem.error.credDefAlreadyExist); @@ -878,7 +907,7 @@ export class EcosystemService { // To return selective response await this.removeEndorsementTransactionFields(storeTransaction); - await new Promise(resolve => setTimeout(resolve, 5000)); + await new Promise((resolve) => setTimeout(resolve, 5000)); return storeTransaction; } else { const orgAgentType = await this.ecosystemRepository.getOrgAgentType(ecosystemMemberDetails.orgAgentTypeId); diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index 68cf783f7..fb5a3f37e 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -67,3 +67,8 @@ export interface ISchemaCredDeffSearchInterface { user: IUserRequestInterface, } +export interface ISchemaExist { + schemaName: string; + version: string; +} + diff --git a/apps/ledger/src/schema/repositories/schema.repository.ts b/apps/ledger/src/schema/repositories/schema.repository.ts index 58c782f6a..418ea2c32 100644 --- a/apps/ledger/src/schema/repositories/schema.repository.ts +++ b/apps/ledger/src/schema/repositories/schema.repository.ts @@ -2,7 +2,7 @@ import { ConflictException, Injectable, InternalServerErrorException, Logger } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; import { ledgers, org_agents, org_agents_type, organisation, schema } from '@prisma/client'; -import { ISchema, ISchemaSearchCriteria } from '../interfaces/schema-payload.interface'; +import { ISchema, ISchemaExist, ISchemaSearchCriteria } from '../interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { AgentDetails, ISchemasWithCount } from '../interfaces/schema.interface'; import { SortValue } from '@credebl/enum/enum'; @@ -12,23 +12,18 @@ import { ICredDefWithCount } from '@credebl/common/interfaces/schema.interface'; export class SchemaRepository { private readonly logger = new Logger('SchemaRepository'); - constructor( - private prisma: PrismaService - ) { } + constructor(private prisma: PrismaService) {} async saveSchema(schemaResult: ISchema): Promise { try { if (schemaResult.schema.schemaName) { - const schema = await this.schemaExists( - schemaResult.schema.schemaName, - schemaResult.schema.schemaVersion - ); + const schema = await this.schemaExists(schemaResult.schema.schemaName, schemaResult.schema.schemaVersion); const schemaLength = 0; - if (schema.length !== schemaLength) { - throw new ConflictException( - ResponseMessages.schema.error.exists, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + if (schema.length !== schemaLength) { + throw new ConflictException(ResponseMessages.schema.error.exists, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } const saveResult = await this.prisma.schema.create({ data: { @@ -96,7 +91,7 @@ export class SchemaRepository { issuerId: true }, orderBy: { - [payload.sortField]: SortValue.ASC === payload.sortBy ? 'asc' : 'desc' + [payload.sortField]: SortValue.ASC === payload.sortBy ? 'asc' : 'desc' }, take: Number(payload.pageSize), skip: (payload.pageNumber - 1) * payload.pageSize @@ -111,11 +106,10 @@ export class SchemaRepository { return { schemasCount, schemasResult }; } catch (error) { this.logger.error(`Error in getting schemas: ${error}`); - throw new InternalServerErrorException( - ResponseMessages.schema.error.failedFetchSchema, - { cause: new Error(), description: error.message } - ); - + throw new InternalServerErrorException(ResponseMessages.schema.error.failedFetchSchema, { + cause: new Error(), + description: error.message + }); } } @@ -138,11 +132,13 @@ export class SchemaRepository { } } - async getAgentType(orgId: string): Promise { + async getAgentType(orgId: string): Promise< + organisation & { + org_agents: (org_agents & { + org_agent_type: org_agents_type; + })[]; + } + > { try { const agentDetails = await this.prisma.organisation.findUnique({ where: { @@ -164,16 +160,12 @@ export class SchemaRepository { } async getSchemasCredDeffList(payload: ISchemaSearchCriteria): Promise { + const { orgId, schemaId } = payload; - const {orgId, schemaId} = payload; - try { const credDefResult = await this.prisma.credential_definition.findMany({ where: { - AND: [ - { orgId }, - { schemaLedgerId: schemaId } - ] + AND: [{ orgId }, { schemaLedgerId: schemaId }] }, select: { tag: true, @@ -190,10 +182,7 @@ export class SchemaRepository { }); const credDefCount = await this.prisma.credential_definition.count({ where: { - AND: [ - { orgId }, - { schemaLedgerId: schemaId } - ] + AND: [{ orgId }, { schemaLedgerId: schemaId }] } }); return { credDefResult, credDefCount }; @@ -202,7 +191,7 @@ export class SchemaRepository { throw error; } } - + async getAllSchemaDetails(payload: ISchemaSearchCriteria): Promise<{ schemasCount: number; schemasResult: { @@ -216,7 +205,7 @@ export class SchemaRepository { issuerId: string; orgId: string; }[]; - }> { + }> { try { const schemasResult = await this.prisma.schema.findMany({ where: { @@ -240,7 +229,7 @@ export class SchemaRepository { issuerId: true }, orderBy: { - [payload.sortField]: 'desc' === payload.sortBy ? 'desc' : 'asc' + [payload.sortField]: 'desc' === payload.sortBy ? 'desc' : 'asc' }, take: Number(payload.pageSize), skip: (payload.pageNumber - 1) * payload.pageSize @@ -265,7 +254,6 @@ export class SchemaRepository { schemaLedgerId: schemaId } }); - } catch (error) { this.logger.error(`Error in getting get schema by schema ledger id: ${error}`); throw error; @@ -274,7 +262,6 @@ export class SchemaRepository { async getOrgAgentType(orgAgentId: string): Promise { try { - const { agent } = await this.prisma.org_agents_type.findFirst({ where: { id: orgAgentId @@ -295,10 +282,37 @@ export class SchemaRepository { indyNamespace: LedgerName } }); - } catch (error) { this.logger.error(`Error in getting get schema by schema ledger id: ${error}`); throw error; } } -} \ No newline at end of file + + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + try { + return this.prisma.schema.findFirstOrThrow({ + where: { + name: payload.schemaName, + version: payload.version + } + }); + } catch (error) { + this.logger.error(`Error in getting get schema by name and version: ${error}`); + throw error; + } + } +} diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 15df46d6a..4384d4b7c 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -1,59 +1,85 @@ import { Controller } from '@nestjs/common'; import { SchemaService } from './schema.service'; import { MessagePattern } from '@nestjs/microservices'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaSearchPayload } from './interfaces/schema-payload.interface'; +import { + ISchema, + ISchemaCredDeffSearchInterface, + ISchemaExist, + ISchemaSearchPayload +} from './interfaces/schema-payload.interface'; import { schema } from '@prisma/client'; -import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; - +import { + ICredDefWithPagination, + ISchemaData, + ISchemasWithPagination +} from '@credebl/common/interfaces/schema.interface'; @Controller('schema') export class SchemaController { - constructor(private readonly schemaService: SchemaService) { } + constructor(private readonly schemaService: SchemaService) {} - @MessagePattern({ cmd: 'create-schema' }) - async createSchema(payload: ISchema): Promise { - const { schema, user, orgId } = payload; - return this.schemaService.createSchema(schema, user, orgId); - } + @MessagePattern({ cmd: 'create-schema' }) + async createSchema(payload: ISchema): Promise { + const { schema, user, orgId } = payload; + return this.schemaService.createSchema(schema, user, orgId); + } - @MessagePattern({ cmd: 'get-schema-by-id' }) - async getSchemaById(payload: ISchema): Promise { - const { schemaId, orgId } = payload; - return this.schemaService.getSchemaById(schemaId, orgId); - } + @MessagePattern({ cmd: 'get-schema-by-id' }) + async getSchemaById(payload: ISchema): Promise { + const { schemaId, orgId } = payload; + return this.schemaService.getSchemaById(schemaId, orgId); + } - @MessagePattern({ cmd: 'get-schemas' }) - async getSchemas(schemaSearch: ISchemaSearchPayload): Promise { - const { schemaSearchCriteria, orgId } = schemaSearch; - return this.schemaService.getSchemas(schemaSearchCriteria, orgId); - } + @MessagePattern({ cmd: 'get-schemas' }) + async getSchemas(schemaSearch: ISchemaSearchPayload): Promise { + const { schemaSearchCriteria, orgId } = schemaSearch; + return this.schemaService.getSchemas(schemaSearchCriteria, orgId); + } - @MessagePattern({ cmd: 'get-cred-deff-list-by-schemas-id' }) - async getcredDeffListBySchemaId(payload: ISchemaCredDeffSearchInterface): Promise { - return this.schemaService.getcredDeffListBySchemaId(payload); - } + @MessagePattern({ cmd: 'get-cred-deff-list-by-schemas-id' }) + async getcredDeffListBySchemaId(payload: ISchemaCredDeffSearchInterface): Promise { + return this.schemaService.getcredDeffListBySchemaId(payload); + } - @MessagePattern({ cmd: 'get-all-schemas' }) - async getAllSchema(schemaSearch: ISchemaSearchPayload): Promise<{ - totalItems: number; - hasNextPage: boolean; - hasPreviousPage: boolean; - nextPage: number; - previousPage: number; - lastPage: number; - data: { - createDateTime: Date; - createdBy: string; - name: string; - schemaLedgerId: string; - version: string; - attributes: string; - publisherDid: string; - issuerId: string; - }[]; - }> { - const { schemaSearchCriteria } = schemaSearch; - return this.schemaService.getAllSchema(schemaSearchCriteria); - } + @MessagePattern({ cmd: 'get-all-schemas' }) + async getAllSchema(schemaSearch: ISchemaSearchPayload): Promise<{ + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + data: { + createDateTime: Date; + createdBy: string; + name: string; + schemaLedgerId: string; + version: string; + attributes: string; + publisherDid: string; + issuerId: string; + }[]; + }> { + const { schemaSearchCriteria } = schemaSearch; + return this.schemaService.getAllSchema(schemaSearchCriteria); + } + @MessagePattern({ cmd: 'schema-exist' }) + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + return this.schemaService.schemaExist(payload); + } } diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index c8c1cd943..4c6d68fc4 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -11,7 +11,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { SchemaRepository } from './repositories/schema.repository'; import { schema } from '@prisma/client'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaPayload, ISchemaSearchCriteria } from './interfaces/schema-payload.interface'; +import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria } from './interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; @@ -481,5 +481,29 @@ export class SchemaService extends BaseService { } } + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + try { + const schemaExist = await this.schemaRepository.schemaExist(payload); + return schemaExist; + + } catch (error) { + this.logger.error(`Error in schema exist: ${error}`); + throw new RpcException(error.response ? error.response : error); + } + } } From c3e551876ce5ed3f9956f6caf167777a6765ba2c Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:56:47 +0530 Subject: [PATCH 054/231] fix: question answer protocol (#542) * feat:added question answer protocol Signed-off-by: pallavicoder * fear:added question answer webhook Signed-off-by: pallavicoder * feat:add question answer protocol inside platform Signed-off-by: pallavicoder * fix:removed extra cost variables Signed-off-by: pallavicoder * fix:removed tenant id from API Signed-off-by: pallavicoder * Merge branch 'fix:removed DTO Signed-off-by: pallavicoder * fix:removed tenant id from url Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder Signed-off-by: KulkarniShashank --- apps/api-gateway/src/connection/connection.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 2cb6bb804..686b97075 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -109,7 +109,7 @@ export class ConnectionController { } - @Get('orgs/:orgId/question-answer/question/:tenantId') + @Get('orgs/:orgId/question-answer/question') @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiOperation({ @@ -159,7 +159,7 @@ export class ConnectionController { } - @Post('/orgs/:orgId/question-answer/question/:connectionId/:tenantId') + @Post('/orgs/:orgId/question-answer/question/:connectionId') @ApiOperation({ summary: '', description: 'send question' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) From f3e5dcd9c92c8ff558142d4ce4b558e2f3d800e8 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 26 Feb 2024 16:44:11 +0530 Subject: [PATCH 055/231] fix: solved issues for the schema exist query Signed-off-by: KulkarniShashank --- apps/ecosystem/src/ecosystem.service.ts | 4 ++-- apps/ledger/src/schema/repositories/schema.repository.ts | 4 ++-- apps/ledger/src/schema/schema.controller.ts | 2 +- apps/ledger/src/schema/schema.service.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 7cf98c99a..be222c79c 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -681,9 +681,9 @@ export class EcosystemService { try { const { name, version } = requestSchemaPayload; const alreadySchemaExist = await this._schemaExist(version, name); - this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist)}`); + this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist.length)}`); - if (alreadySchemaExist) { + if (0 !== alreadySchemaExist.length) { throw new BadRequestException(ResponseMessages.ecosystem.error.schemaAlreadyExist); } diff --git a/apps/ledger/src/schema/repositories/schema.repository.ts b/apps/ledger/src/schema/repositories/schema.repository.ts index 418ea2c32..bd84b2785 100644 --- a/apps/ledger/src/schema/repositories/schema.repository.ts +++ b/apps/ledger/src/schema/repositories/schema.repository.ts @@ -302,9 +302,9 @@ export class SchemaRepository { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { try { - return this.prisma.schema.findFirstOrThrow({ + return this.prisma.schema.findMany({ where: { name: payload.schemaName, version: payload.version diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 4384d4b7c..68003ac49 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -79,7 +79,7 @@ export class SchemaController { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { return this.schemaService.schemaExist(payload); } } diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 4c6d68fc4..34ffde1ca 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -495,7 +495,7 @@ export class SchemaService extends BaseService { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { try { const schemaExist = await this.schemaRepository.schemaExist(payload); return schemaExist; From 4e5d6c48fce5e5261b64adbccbdbd36e252f4e13 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Mon, 26 Feb 2024 19:04:40 +0530 Subject: [PATCH 056/231] fix: extra attribute in issuance payload Signed-off-by: pranalidhanavade Signed-off-by: KulkarniShashank --- apps/issuance/interfaces/issuance.interfaces.ts | 2 +- apps/issuance/src/issuance.service.ts | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 829984113..bad565712 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -5,9 +5,9 @@ import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-se export interface IAttributes { attributeName: string; - isRequired: boolean; name: string; value: string; + isRequired?: boolean; } export interface IIssuance { user?: IUserRequest; diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 41cfaa105..dcaa60e80 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -110,8 +110,10 @@ export class IssuanceService { connectionId, credentialFormats: { indy: { - attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId + } }, autoAcceptCredential: payload.autoAcceptCredential || 'always', @@ -198,7 +200,8 @@ export class IssuanceService { protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { - attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId } }, @@ -449,7 +452,6 @@ const credefError = []; `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` ); } - // }); }); @@ -490,7 +492,8 @@ const credefError = []; protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { - attributes: iterator.attributes || attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (iterator.attributes || attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId } }, From 36975597138ac7c94e2c74b51d8b82fb0e3a76de Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:17:12 +0530 Subject: [PATCH 057/231] Create new connection invitation (#546) * feat: modify connection invitation url Signed-off-by: bhavanakarwade * changed message pattern Signed-off-by: bhavanakarwade * fix: optimized conditions Signed-off-by: bhavanakarwade * fix: cred-def restrictions Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade Signed-off-by: KulkarniShashank --- .../src/issuance/dtos/issuance.dto.ts | 8 +++++++ .../interfaces/issuance.interfaces.ts | 21 ++++++++++++++++++- apps/issuance/src/issuance.repository.ts | 6 +++++- apps/issuance/src/issuance.service.ts | 20 +++++++++++++----- .../src/interfaces/verification.interface.ts | 1 + apps/verification/src/verification.service.ts | 17 +++++++++++---- libs/common/src/response-messages/index.ts | 2 +- 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 5b7957137..51dc9b192 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -71,6 +71,12 @@ class CredentialsIssuanceDto { @IsString({ message: 'label should be string' }) label?: string; + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'please provide valid imageUrl' }) + @IsString({ message: 'imageUrl must be a string' }) + imageUrl?: string; + @ApiPropertyOptional() @IsOptional() @IsString({ message: 'auto accept proof must be in string' }) @@ -238,6 +244,8 @@ export class OOBCredentialDtoWithEmail { @IsString({ message: 'protocol version should be string' }) protocolVersion?: string; + imageUrl?: string; + orgId: string; } diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index bad565712..d7ccbff71 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -1,6 +1,7 @@ // eslint-disable-next-line camelcase import { AutoAccept } from '@credebl/enum/enum'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; +import { organisation } from '@prisma/client'; import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-service.interface'; export interface IAttributes { @@ -142,7 +143,8 @@ export interface OutOfBandCredentialOfferPayload { goalCode?: string, parentThreadId?: string, willConfirm?: boolean, - label?: string + label?: string, + imageUrl?: string, autoAcceptCredential?: string; } @@ -209,3 +211,20 @@ export interface IIssuedCredentialsSearchCriteria { searchByText: string; user?: IUserRequestInterface; } + +export interface OrgAgent { + organisation: organisation; + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + orgDid: string; + verkey: string; + agentEndPoint: string; + agentId: string; + isDidPublic: boolean; + ledgerId: string; + orgAgentTypeId: string; + tenantId: string; +} diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 6f6efc02b..3b8307f22 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -14,6 +14,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { FileUploadData, IssueCredentialWebhookPayload, + OrgAgent, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; @@ -34,11 +35,14 @@ export class IssuanceRepository { * @returns Get getAgentEndPoint details */ // eslint-disable-next-line camelcase - async getAgentEndPoint(orgId: string): Promise { + async getAgentEndPoint(orgId: string): Promise { try { const agentDetails = await this.prisma.org_agents.findFirst({ where: { orgId + }, + include: { + organisation: true } }); diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index dcaa60e80..1800534ed 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -180,7 +180,8 @@ export class IssuanceService { const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); // eslint-disable-next-line camelcase - const { agentEndPoint } = agentDetails; + const { agentEndPoint, organisation } = agentDetails; + if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } @@ -209,7 +210,8 @@ export class IssuanceService { goalCode: payload.goalCode || undefined, parentThreadId: payload.parentThreadId || undefined, willConfirm: payload.willConfirm || undefined, - label: payload.label || undefined, + imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, + label: organisation?.name, comment: comment || '' }; const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); @@ -459,9 +461,10 @@ const credefError = []; throw new BadRequestException(credefError); } } - - + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); + + const { organisation } = agentDetails; if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } @@ -502,7 +505,8 @@ const credefError = []; goalCode: outOfBandCredential.goalCode || undefined, parentThreadId: outOfBandCredential.parentThreadId || undefined, willConfirm: outOfBandCredential.willConfirm || undefined, - label: outOfBandCredential.label || undefined + label: organisation?.name, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl }; this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); @@ -1093,13 +1097,19 @@ const credefError = []; fileUploadData.createDateTime = new Date(); fileUploadData.referenceId = jobDetails.data.email; fileUploadData.jobId = jobDetails.id; + const {orgId} = jobDetails; + + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); + // eslint-disable-next-line camelcase + const { organisation } = agentDetails; let isErrorOccurred = false; try { const oobIssuancepayload = { credentialDefinitionId: jobDetails.credentialDefinitionId, orgId: jobDetails.orgId, + label: organisation?.name, attributes: [], emailId: jobDetails.data.email }; diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 082fe897f..ff26c3ab9 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -98,6 +98,7 @@ export interface ISendProofRequestPayload { goalCode?: string; parentThreadId?: string; willConfirm?: boolean; + imageUrl?: string; } export interface IProofRequestPayload { diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index c910fecf3..b8fc4c9e9 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -342,11 +342,14 @@ export class VerificationService { // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); - const [getAgentDetails] = await Promise.all([ + const [getAgentDetails, getOrganization] = await Promise.all([ this.verificationRepository.getAgentEndPoint(user.orgId), this.verificationRepository.getOrganization(user.orgId) ]); + const imageUrl = getOrganization?.logoUrl; + outOfBandRequestProof['imageUrl'] = imageUrl; + const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethodLabel = 'create-request-out-of-band'; @@ -514,8 +517,8 @@ export class VerificationService { try { let requestedAttributes = {}; const requestedPredicates = {}; - const attributeWithSchemaIdExists = proofRequestpayload.attributes; - if (attributeWithSchemaIdExists) { + const {attributes} = proofRequestpayload; + if (attributes) { requestedAttributes = Object.fromEntries(proofRequestpayload.attributes.map((attribute, index) => { const attributeElement = attribute.attributeName; @@ -525,7 +528,13 @@ export class VerificationService { return [ attributeReferent, { - name: attributeElement + name: attributeElement, + restrictions: [ + { + cred_def_id: proofRequestpayload.attributes[index].credDefId ? proofRequestpayload.attributes[index].credDefId : undefined, + schema_id: proofRequestpayload.attributes[index].schemaId + } + ] } ]; } else { diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index ba3434b24..436d90506 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -407,5 +407,5 @@ export const ResponseMessages = { notFound: 'Notification record not found.', invalidUrl: 'Invalid URL' } - }, + } }; \ No newline at end of file From d6107859a958a0ab3575dd5e89c794356a701068 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 27 Feb 2024 14:08:12 +0530 Subject: [PATCH 058/231] fix: solved verification predicates values Signed-off-by: KulkarniShashank --- apps/verification/src/verification.service.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index b8fc4c9e9..10c1d494a 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -687,8 +687,6 @@ export class VerificationService { const extractedDataArray: IProofPresentationDetails[] = []; if (0 !== Object.keys(requestedAttributes).length && 0 !== Object.keys(requestedPredicates).length) { - - for (const key in requestedAttributes) { if (requestedAttributes.hasOwnProperty(key)) { @@ -699,10 +697,12 @@ export class VerificationService { credDefId = requestedAttributeKey?.restrictions[0]?.cred_def_id; schemaId = requestedAttributeKey?.restrictions[0]?.schema_id; + } else if (getProofPresentationById?.response?.presentation?.indy?.identifiers) { credDefId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].cred_def_id; schemaId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].schema_id; + } if (revealedAttrs.hasOwnProperty(key)) { @@ -717,11 +717,16 @@ export class VerificationService { } for (const key in requestedPredicates) { + if (requestedPredicates.hasOwnProperty(key)) { const attribute = requestedPredicates[key]; + const attributeName = attribute?.name; - const credDefId = attribute?.restrictions[0]?.cred_def_id; - const schemaId = attribute?.restrictions[0]?.schema_id; + + if (attribute?.restrictions) { + credDefId = attribute?.restrictions[0]?.cred_def_id; + schemaId = attribute?.restrictions[0]?.schema_id; + } const extractedData: IProofPresentationDetails = { [attributeName]: `${attribute?.p_type}${attribute?.p_value}`, @@ -755,6 +760,7 @@ export class VerificationService { } } } else if (0 !== Object.keys(requestedPredicates).length) { + for (const key in requestedPredicates) { if (requestedPredicates.hasOwnProperty(key)) { @@ -762,9 +768,8 @@ export class VerificationService { const attributeName = attribute?.name; [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); - const extractedData: IProofPresentationDetails = { - [attributeName]: `${requestedPredicates?.p_type}${requestedPredicates?.p_value}`, + [attributeName]: `${attribute?.p_type}${attribute?.p_value}`, 'credDefId': credDefId || null, 'schemaId': schemaId || null }; From 73bff37249ab3113429f2f8c6bde8a03dd734466 Mon Sep 17 00:00:00 2001 From: Nishad Date: Tue, 27 Feb 2024 18:55:14 +0530 Subject: [PATCH 059/231] fixed bulk issuance upload issue Signed-off-by: Nishad Signed-off-by: KulkarniShashank --- apps/api-gateway/src/issuance/issuance.controller.ts | 5 +---- apps/issuance/src/issuance.service.ts | 9 ++++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 9e8fad833..5c58c622b 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -220,7 +220,6 @@ export class IssuanceController { @Param('orgId') orgId: string, @Res() res: Response ): Promise { - try { if (file) { const fileKey: string = uuidv4(); @@ -244,9 +243,7 @@ export class IssuanceController { }; return res.status(HttpStatus.CREATED).json(finalResponse); } - } catch (error) { - throw new RpcException(error.response ? error.response : error); - } + } @Get('/orgs/:orgId/:requestId/preview') diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 1800534ed..4c6616db5 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -711,6 +711,8 @@ const credefError = []; try { const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails(credentialDefinitionId); + // console.log(`Export CSV`); + const jsonData = []; const attributesArray = JSON.parse(schemaResponse.attributes); @@ -822,11 +824,8 @@ const credefError = []; } catch (error) { this.logger.error(`error in validating credentials : ${error.response}`); - throw new Error(error.response.message ? error.response.message : error); - } finally { - // await this.awsService.deleteFile(importFileDetails.fileKey); - // this.logger.error(`Deleted uploaded file after processing.`); - } + throw new RpcException(error.response ? error.response : error); + } } async previewFileDataForIssuance( From cebb42089519f6635049b57114df8d2bc48aa6fe Mon Sep 17 00:00:00 2001 From: Nishad Date: Tue, 27 Feb 2024 19:01:12 +0530 Subject: [PATCH 060/231] removed commented code Signed-off-by: Nishad Signed-off-by: KulkarniShashank --- apps/issuance/src/issuance.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 4c6616db5..1d0a1562d 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -710,8 +710,6 @@ const credefError = []; async exportSchemaToCSV(credentialDefinitionId: string): Promise { try { const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails(credentialDefinitionId); - - // console.log(`Export CSV`); const jsonData = []; const attributesArray = JSON.parse(schemaResponse.attributes); From 5c3b5c6fea945e8fcdd6de34d2e90bb7a46b91de Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:43:02 +0530 Subject: [PATCH 061/231] feat: Create new connection invitation (#520) * feat: modify connection invitation url Signed-off-by: bhavanakarwade * changed message pattern Signed-off-by: bhavanakarwade * fix: optimized conditions Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- apps/connection/src/connection.service.ts | 4 -- apps/issuance/src/issuance.repository.ts | 30 ----------- .../interfaces/organization.interface.ts | 12 +++++ .../repositories/organization.repository.ts | 39 ++++++++++++-- apps/organization/src/organization.service.ts | 52 ++++++++++++++++--- 5 files changed, 90 insertions(+), 47 deletions(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index b064518ed..39b369c80 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -42,10 +42,6 @@ export class ConnectionService { const { orgId, multiUseInvitation, autoAcceptConnection, alias, imageUrl, goal, goalCode, handshake, handshakeProtocols } = payload; try { - const connectionInvitationExist = await this.connectionRepository.getConnectionInvitationByOrgId(orgId); - if (connectionInvitationExist) { - return connectionInvitationExist; - } const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index c6078c128..6f6efc02b 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -3,7 +3,6 @@ import { Injectable, InternalServerErrorException, Logger, NotFoundException } f import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase import { - agent_invitations, credentials, file_data, file_upload, @@ -184,35 +183,6 @@ export class IssuanceRepository { } } - /** - * Description: Save connection details - * @param connectionInvitation - * @param agentId - * @param orgId - * @returns Get connection details - */ - // eslint-disable-next-line camelcase - async saveAgentConnectionInvitations( - connectionInvitation: string, - agentId: string, - orgId: string - ): Promise { - try { - const agentInvitationData = await this.prisma.agent_invitations.create({ - data: { - orgId, - agentId, - connectionInvitation, - multiUse: true - } - }); - return agentInvitationData; - } catch (error) { - this.logger.error(`Error in saveAgentConnectionInvitations: ${error.message} `); - throw error; - } - } - /** * Get platform config details * @returns diff --git a/apps/organization/interfaces/organization.interface.ts b/apps/organization/interfaces/organization.interface.ts index adbd4f8f2..032dd76ac 100644 --- a/apps/organization/interfaces/organization.interface.ts +++ b/apps/organization/interfaces/organization.interface.ts @@ -23,6 +23,18 @@ export interface IUpdateOrganization { } +export interface ICreateConnectionUrl { + id: string; + orgId: string; + agentId: string; + connectionInvitation: string; + multiUse: boolean; + createDateTime: Date; + createdBy: number; + lastChangedDateTime: Date; + lastChangedBy: number; +} + export interface IOrgAgent { url: string; apiKey: string; diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index 2c37e07c7..930fe04d7 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -3,7 +3,7 @@ import { Injectable, Logger, NotFoundException } from '@nestjs/common'; // eslint-disable-next-line camelcase -import { org_agents, org_invitations, user_org_roles } from '@prisma/client'; +import { Prisma, agent_invitations, org_agents, org_invitations, user_org_roles } from '@prisma/client'; import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { IGetOrgById, IGetOrganization, IUpdateOrganization } from '../interfaces/organization.interface'; @@ -38,7 +38,7 @@ export class OrganizationRepository { }); } catch (error) { this.logger.error(`error: ${JSON.stringify(error)}`); - throw new InternalServerErrorException(error); + throw error; } } @@ -97,6 +97,35 @@ export class OrganizationRepository { } } + async getAgentInvitationDetails(orgId: string): Promise { + try { + const response = await this.prisma.agent_invitations.findUnique({ + where: { + id: orgId + } + }); + return response; + } catch (error) { + this.logger.error(`error in getting agent invitation details: ${JSON.stringify(error)}`); + throw error; + } + } + + async updateConnectionInvitationDetails(orgId: string, connectionInvitation: string): Promise { + try { + const temp = await this.prisma.agent_invitations.updateMany({ + where: {orgId}, + data: { + connectionInvitation + } + }); + return temp; + + } catch (error) { + this.logger.error(`Error in updating connection invitation details: ${JSON.stringify(error)}`); + throw error; + } + } /** * @@ -538,9 +567,9 @@ export class OrganizationRepository { * @returns Organization exist details */ - async checkOrganizationExist(name: string, orgId: string): Promise { + async checkOrganizationExist(name: string, orgId: string): Promise { try { - return this.prisma.organisation.findMany({ + return this.prisma.organisation.findUnique({ where: { id: orgId, name @@ -548,7 +577,7 @@ export class OrganizationRepository { }); } catch (error) { this.logger.error(`error: ${JSON.stringify(error)}`); - throw new InternalServerErrorException(error); + throw error; } } diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 973c14c2f..a82df7f25 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -17,7 +17,7 @@ import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; import { Invitation, OrgAgentType, transition } from '@credebl/enum/enum'; -import { IGetOrgById, IGetOrganization, IUpdateOrganization, IOrgAgent, IClientCredentials } from '../interfaces/organization.interface'; +import { IGetOrgById, IGetOrganization, IUpdateOrganization, IOrgAgent, IClientCredentials, ICreateConnectionUrl } from '../interfaces/organization.interface'; import { UserActivityService } from '@credebl/user-activity'; import { CommonConstants } from '@credebl/common/common.constant'; import { ClientRegistrationService } from '@credebl/client-registration/client-registration.service'; @@ -252,13 +252,10 @@ export class OrganizationService { async updateOrganization(updateOrgDto: IUpdateOrganization, userId: string, orgId: string): Promise { try { - const organizationExist = await this.organizationRepository.checkOrganizationExist(updateOrgDto.name, orgId); + const organizationExist = await this.organizationRepository.checkOrganizationNameExist(updateOrgDto.name); - if (0 === organizationExist.length) { - const organizationExist = await this.organizationRepository.checkOrganizationNameExist(updateOrgDto.name); - if (organizationExist) { - throw new ConflictException(ResponseMessages.organisation.error.exists); - } + if (organizationExist && organizationExist.id !== orgId) { + throw new ConflictException(ResponseMessages.organisation.error.exists); } const orgSlug = await this.createOrgSlug(updateOrgDto.name); @@ -272,7 +269,16 @@ export class OrganizationService { delete updateOrgDto.logo; } - const organizationDetails = await this.organizationRepository.updateOrganization(updateOrgDto); + let organizationDetails; + const checkAgentIsExists = await this.organizationRepository.getAgentInvitationDetails(orgId); + + if (!checkAgentIsExists?.connectionInvitation && !checkAgentIsExists?.agentId) { + organizationDetails = await this.organizationRepository.updateOrganization(updateOrgDto); + } else if (organizationDetails?.logoUrl !== organizationExist?.logoUrl || organizationDetails?.name !== organizationExist?.name) { + const invitationData = await this._createConnection(updateOrgDto?.logo, updateOrgDto?.name, orgId); + await this.organizationRepository.updateConnectionInvitationDetails(orgId, invitationData?.connectionInvitation); + } + await this.userActivityService.createActivity(userId, organizationDetails.id, `${organizationDetails.name} organization updated`, 'Organization details updated successfully'); return organizationDetails; } catch (error) { @@ -281,6 +287,36 @@ export class OrganizationService { } } + + async _createConnection( + orgName: string, + logoUrl: string, + orgId: string + ): Promise { + const pattern = { cmd: 'create-connection' }; + + const payload = { + orgName, + logoUrl, + orgId + }; + const connectionInvitationData = await this.organizationServiceProxy + .send(pattern, payload) + .toPromise() + .catch((error) => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + }); + + return connectionInvitationData; + } + /** * @returns Get created organizations details */ From fe79b04fc888320469bba8f6c9672f44facb1392 Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:36:26 +0530 Subject: [PATCH 062/231] fix: issuance API with required attribute (#528) * fix:issuance api Signed-off-by: pranalidhanavade * fix:issuance api issue Signed-off-by: pranalidhanavade * fix:sonarlint issues Signed-off-by: pranalidhanavade * fix:requried attribute in issuance api Signed-off-by: pranalidhanavade * fix:requried attributes in csv file Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade Signed-off-by: bhavanakarwade --- .../src/issuance/dtos/issuance.dto.ts | 22 +- apps/issuance/src/issuance.service.ts | 261 +++++++++++------- 2 files changed, 168 insertions(+), 115 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 0d81ebe75..5b7957137 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -20,10 +20,11 @@ class Attribute { @Transform(({ value }) => trim(value)) value: string; - @ApiProperty() + @ApiProperty({ default: false }) @IsBoolean() + @IsOptional() @IsNotEmpty({ message: 'isRequired property is required' }) - isRequired: boolean; + isRequired?: boolean = false; } @@ -87,8 +88,7 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { example: [ { value: 'string', - name: 'string', - isRequired: 'boolean' + name: 'string' } ] }) @@ -100,14 +100,14 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { } class CredentialOffer { - @ApiProperty({ example: [{ 'value': 'string', 'name': 'string', 'isRequired':'boolean' }] }) + @ApiProperty({ example: [{ 'value': 'string', 'name': 'string' }] }) @IsNotEmpty({ message: 'Attribute name is required' }) @IsArray({ message: 'Attributes should be an array' }) @ValidateNested({ each: true }) @Type(() => Attribute) attributes: Attribute[]; - @ApiProperty({ example: 'testmail@mailinator.com' }) + @ApiProperty({ example: 'testmail@xyz.com' }) @IsEmail({}, { message: 'Please provide a valid email' }) @IsNotEmpty({ message: 'Email is required' }) @IsString({ message: 'Email should be a string' }) @@ -212,7 +212,7 @@ export class CredentialAttributes { } export class OOBCredentialDtoWithEmail { - @ApiProperty({ example: [{ 'emailId': 'abc@example.com', 'attributes': [{ 'value': 'string', 'name': 'string', 'isRequired':'boolean' }] }] }) + @ApiProperty({ example: [{ 'emailId': 'abc@example.com', 'attributes': [{ 'value': 'string', 'name': 'string' }] }] }) @IsNotEmpty({ message: 'Please provide valid attributes' }) @IsArray({ message: 'attributes should be array' }) @ArrayMaxSize(Number(process.env.OOB_BATCH_SIZE), { message: `Limit reached (${process.env.OOB_BATCH_SIZE} credentials max). Easily handle larger batches via seamless CSV file uploads` }) @@ -220,14 +220,6 @@ export class OOBCredentialDtoWithEmail { @Type(() => CredentialOffer) credentialOffer: CredentialOffer[]; - @ApiProperty({ example: 'awqx@getnada.com' }) - @IsEmail({}, { message: 'Please provide a valid email' }) - @IsNotEmpty({ message: 'Please provide valid email' }) - @IsString({ message: 'email should be string' }) - @Transform(({ value }) => value.trim().toLowerCase()) - @IsOptional() - emailId: string; - @ApiProperty({ example: 'string' }) @IsNotEmpty({ message: 'Please provide valid credential definition id' }) @IsString({ message: 'credential definition id should be string' }) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 68ca23842..f44d2c216 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -9,7 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs'; // import { ClientDetails, FileUploadData, ICredentialAttributesInterface, ImportFileDetails, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; -import { FileUploadData, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; +import { FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; import { OrgAgentType } from '@credebl/enum/enum'; // import { platform_config } from '@prisma/client'; import * as QRCode from 'qrcode'; @@ -51,26 +51,37 @@ export class IssuanceService { async sendCredentialCreateOffer(payload: IIssuance): Promise { + try { const { orgId, credentialDefinitionId, comment, connectionId, attributes } = payload || {}; - const attrError = []; - if (0 < attributes?.length) { - attributes?.forEach((attribute, i) => { - - if (attribute.isRequired && !attribute.value) { - attrError.push(`attributes.${i}.Value of "${attribute.name}" is required`); - return true; - } + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); + + if (schemaResponse?.attributes) { + const schemaResponseError = []; + const attributesArray: IAttributes[] = JSON.parse(schemaResponse.attributes); + + attributesArray.forEach((attribute) => { + if (attribute.attributeName && attribute.isRequired) { - return attribute.isRequired && !attribute.value; - }); - - if (0 < attrError.length) { - throw new BadRequestException(attrError); - } + payload.attributes.map((attr) => { + if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { + schemaResponseError.push( + `Attribute ${attribute.attributeName} is required` + ); + } + return true; + }); + } + }); + if (0 < schemaResponseError.length) { + throw new BadRequestException(schemaResponseError); + } - + + } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); @@ -133,28 +144,37 @@ export class IssuanceService { } } - - async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object; }> { + async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { const { orgId, credentialDefinitionId, comment, attributes, protocolVersion } = payload; - const attrError = []; - if (0 < attributes?.length) { - attributes?.forEach((attribute, i) => { - - if (attribute.isRequired && !attribute.value) { - attrError.push(`attributes.${i}.Value of "${attribute.name}" is required`); - return true; - } + const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); + + if (schemadetailsResponse?.attributes) { + const schemadetailsResponseError = []; + const attributesArray: IAttributes[] = JSON.parse(schemadetailsResponse.attributes); + + attributesArray.forEach((attribute) => { + if (attribute.attributeName && attribute.isRequired) { - return attribute.isRequired && !attribute.value; - }); - - if (0 < attrError.length) { - throw new BadRequestException(attrError); - } + payload.attributes.map((attr) => { + if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { + schemadetailsResponseError.push( + `Attribute '${attribute.attributeName}' is required but has an empty value.` + ); + } + return true; + }); + } + }); + if (0 < schemadetailsResponseError.length) { + throw new BadRequestException(schemadetailsResponseError); } + } + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); // eslint-disable-next-line camelcase @@ -239,8 +259,8 @@ export class IssuanceService { .pipe( map((response) => ( { - response - })) + response + })) ).toPromise() .catch(error => { this.logger.error(`catch: ${JSON.stringify(error)}`); @@ -387,26 +407,58 @@ export class IssuanceService { emailId } = outOfBandCredential; - const attrError = []; -if (0 < credentialOffer?.length) { - credentialOffer?.forEach((credential, i) => { - credential.attributes.forEach((attribute, i2) => { + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); - if (attribute.isRequired && !attribute.value) { - attrError.push(`credentialOffer.${i}.attributes.${i2}.Value of "${attribute.name}" is required`); - return true; - } - - return attribute.isRequired && !attribute.value; - }); + let attributesArray:IAttributes[] = []; + if (schemaResponse?.attributes) { - }); + attributesArray = JSON.parse(schemaResponse.attributes); + } - if (0 < attrError.length) { - throw new BadRequestException(attrError); - } - } - + if (0 < attributes?.length) { +const attrError = []; + attributesArray.forEach((schemaAttribute, i) => { + if (schemaAttribute.isRequired) { + + const attribute = attributes.find(attribute => attribute.name === schemaAttribute.attributeName); + if (!attribute?.value) { + attrError.push( + `attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` + ); + } + + } + + }); + if (0 < attrError.length) { + throw new BadRequestException(attrError); + } + } + if (0 < credentialOffer?.length) { +const credefError = []; + credentialOffer.forEach((credentialAttribute, index) => { + + attributesArray.forEach((schemaAttribute, i) => { + + const attribute = credentialAttribute.attributes.find(attribute => attribute.name === schemaAttribute.attributeName); + + if (schemaAttribute.isRequired && !attribute?.value) { + credefError.push( + `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` + ); + } + // + + }); + }); + if (0 < credefError.length) { + throw new BadRequestException(credefError); + } + } + + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); @@ -422,9 +474,6 @@ if (0 < credentialOffer?.length) { throw new NotFoundException(ResponseMessages.issuance.error.organizationNotFound); } - // if (!(credentialOffer && 0 < credentialOffer.length)) { - // throw new NotFoundException(ResponseMessages.issuance.error.credentialOfferNotFound); - // } let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { @@ -494,10 +543,10 @@ if (0 < credentialOffer?.length) { disposition: 'attachment' } ]; - + const isEmailSent = await sendEmail(this.emailData); this.logger.log(`isEmailSent ::: ${JSON.stringify(isEmailSent)}`); - + if (!isEmailSent) { errors.push(new InternalServerErrorException(ResponseMessages.issuance.error.emailSend)); return false; @@ -568,7 +617,7 @@ if (0 < credentialOffer?.length) { } } - + async _outOfBandCredentialOffer(outOfBandIssuancePayload: object, url: string, apiKey: string): Promise<{ response; }> { @@ -583,10 +632,10 @@ if (0 < credentialOffer?.length) { } /** - * Description: Fetch agent url - * @param referenceId - * @returns agent URL - */ + * Description: Fetch agent url + * @param referenceId + * @returns agent URL + */ async getAgentUrl( issuanceMethodLabel: string, orgAgentType: string, @@ -600,8 +649,8 @@ if (0 < credentialOffer?.length) { switch (issuanceMethodLabel) { case 'create-offer': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_CREATE_CRED_OFFER_AFJ}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_CREATE_CRED_OFFER_AFJ}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_OFFER}`.replace('#', tenantId) : null; break; @@ -609,8 +658,8 @@ if (0 < credentialOffer?.length) { case 'create-offer-oob': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_OUT_OF_BAND_CREDENTIAL_OFFER}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_OUT_OF_BAND_CREDENTIAL_OFFER}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_OFFER_OUT_OF_BAND}`.replace('#', tenantId) : null; break; @@ -618,18 +667,18 @@ if (0 < credentialOffer?.length) { case 'get-issue-credentials': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREDENTIALS}`.replace('#', tenantId) : null; break; } case 'get-issue-credential-by-credential-id': { - + url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ_BY_CRED_REC_ID}/${credentialRecordId}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ_BY_CRED_REC_ID}/${credentialRecordId}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREDENTIALS_BY_CREDENTIAL_ID}`.replace('#', credentialRecordId).replace('@', tenantId) : null; break; @@ -697,7 +746,7 @@ if (0 < credentialOffer?.length) { async importAndPreviewDataForIssuance(importFileDetails: ImportFileDetails, requestId?: string): Promise { try { - + const credDefResponse = await this.issuanceRepository.getCredentialDefinitionDetails(importFileDetails.credDefId); @@ -734,7 +783,7 @@ if (0 < credentialOffer?.length) { throw new BadRequestException(`Invalid emails found in the chosen file`); } - const fileData: string[] = parsedData.data.map(Object.values); + const fileData: string[][] = parsedData.data.map(Object.values); const fileHeader: string[] = parsedData.meta.fields; const attributesArray = JSON.parse(credDefResponse.attributes); @@ -749,7 +798,7 @@ if (0 < credentialOffer?.length) { } await this.validateFileHeaders(fileHeader, attributeNameArray); - await this.validateFileData(fileData); + await this.validateFileData(fileData, attributesArray, fileHeader); const resData = { schemaLedgerId: credDefResponse.schemaLedgerId, @@ -765,8 +814,8 @@ if (0 < credentialOffer?.length) { return newCacheKey; } catch (error) { - this.logger.error(`error in validating credentials : ${error}`); - throw new RpcException(error.response ? error.response : error); + this.logger.error(`error in validating credentials : ${error.response}`); + throw new RpcException(error.response ? error.response : error); } finally { // await this.awsService.deleteFile(importFileDetails.fileKey); // this.logger.error(`Deleted uploaded file after processing.`); @@ -892,11 +941,11 @@ if (0 < credentialOffer?.length) { } if (cachedData && clientDetails?.isSelectiveIssuance) { - await this.cacheManager.del(requestId); - await this.importAndPreviewDataForIssuance(reqPayload, requestId); + await this.cacheManager.del(requestId); + await this.importAndPreviewDataForIssuance(reqPayload, requestId); // await this.cacheManager.set(requestId, reqPayload); cachedData = await this.cacheManager.get(requestId); - } + } const parsedData = JSON.parse(cachedData as string).fileData.data; const parsedPrimeDetails = JSON.parse(cachedData as string); @@ -1117,7 +1166,6 @@ if (0 < credentialOffer?.length) { ): Promise { try { const fileSchemaHeader: string[] = fileHeader.slice(); - if ('email' === fileHeader[0]) { fileSchemaHeader.splice(0, 1); } else { @@ -1141,25 +1189,38 @@ if (0 < credentialOffer?.length) { } } - async validateFileData(fileData: string[]): Promise { - let rowIndex: number = 0; - let columnIndex: number = 0; - const isNullish = Object.values(fileData).some((value) => { - columnIndex = 0; - rowIndex++; - const isFalsyForColumnValue = Object.values(value).some((colvalue) => { - columnIndex++; - if (null === colvalue || '' == colvalue) { - return true; - } - return false; + async validateFileData(fileData: string[][], attributesArray: { attributeName: string, schemaDataType: string, displayName: string, isRequired: boolean }[], fileHeader: string[]): Promise { + try { + const filedata = fileData.map((item: string[]) => { + const fileHeaderData = item?.map((element, j) => ({ + value: element, + header: fileHeader[j] + })); + return fileHeaderData; }); - return isFalsyForColumnValue; - }); - if (isNullish) { - throw new BadRequestException( - `Empty data found at row ${rowIndex} and column ${columnIndex}` - ); + + const errorFileData = []; + + filedata.forEach((attr, i) => { + attr.forEach((eachElement) => { + + attributesArray.forEach((eachItem) => { + if (eachItem.attributeName === eachElement.header) { + if (eachItem.isRequired && !eachElement.value) { + errorFileData.push(`Attribute ${eachItem.attributeName} is required at row ${i + 1}`); + } + } + }); + return eachElement; + }); + return attr; + }); + + if (0 < errorFileData.length) { + throw new BadRequestException(errorFileData); + } + } catch (error) { + throw error; } } @@ -1174,9 +1235,9 @@ if (0 < credentialOffer?.length) { } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException({ - status: error.status, - error: error.message - }, error.status); + status: error.status, + error: error.message + }, error.status); } } From 35348339ada14ef8d8ae541c1cf4e3c57289438f Mon Sep 17 00:00:00 2001 From: sanjay-k1910 Date: Wed, 21 Feb 2024 17:43:23 +0530 Subject: [PATCH 063/231] fix: user friendly message of ecosystem roles if user does not have access Signed-off-by: sanjay-k1910 Signed-off-by: bhavanakarwade --- .../src/authz/guards/ecosystem-roles.guard.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts index a0f5542fc..9f011b92c 100644 --- a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts @@ -1,4 +1,4 @@ -import { BadRequestException, CanActivate, ExecutionContext, Logger } from '@nestjs/common'; +import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Logger } from '@nestjs/common'; import { HttpException } from '@nestjs/common'; import { HttpStatus } from '@nestjs/common'; @@ -69,7 +69,12 @@ export class EcosystemRolesGuard implements CanActivate { throw new HttpException('organization & ecosystem is required', HttpStatus.BAD_REQUEST); } - return requiredRoles.some((role) => user.ecosystemOrgRole === role); - + // Sending user friendly message if a user attempts to access an API that is inaccessible to their role + const roleAccess = requiredRoles.some((role) => user.ecosystemOrgRole === role); + if (!roleAccess) { + throw new ForbiddenException(ResponseMessages.organisation.error.roleNotMatch, { cause: new Error(), description: ResponseMessages.errorMessages.forbidden }); + } + + return roleAccess; } } \ No newline at end of file From 2f89f86be04d80a820cf71fe127c388ccbdf3801 Mon Sep 17 00:00:00 2001 From: Nishad Date: Thu, 22 Feb 2024 19:27:02 +0530 Subject: [PATCH 064/231] Worked on the fix of user registration if alredy registered Signed-off-by: Nishad Signed-off-by: bhavanakarwade --- apps/user/src/user.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index 37e6de446..f9e112b41 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -221,7 +221,7 @@ export class UserService { if (!checkUserDetails) { throw new NotFoundException(ResponseMessages.user.error.emailIsNotVerified); } - if (checkUserDetails.keycloakUserId) { + if (checkUserDetails.keycloakUserId || (!checkUserDetails.keycloakUserId && checkUserDetails.supabaseUserId)) { throw new ConflictException(ResponseMessages.user.error.exists); } if (false === checkUserDetails.isEmailVerified) { @@ -974,6 +974,8 @@ export class UserService { throw new ConflictException(ResponseMessages.user.error.verificationAlreadySent); } else if (userDetails && userDetails.keycloakUserId) { throw new ConflictException(ResponseMessages.user.error.exists); + } else if (userDetails && !userDetails.keycloakUserId && userDetails.supabaseUserId) { + throw new ConflictException(ResponseMessages.user.error.exists); } else if (null === userDetails) { return { isRegistrationCompleted: false, From 46161b7b71c1581cef366f87a12ed7eeefc40ada Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Thu, 22 Feb 2024 19:51:20 +0530 Subject: [PATCH 065/231] fix:csv file error handling (#536) Signed-off-by: pranalidhanavade Signed-off-by: bhavanakarwade --- apps/issuance/src/issuance.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index f44d2c216..41cfaa105 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -815,7 +815,7 @@ const credefError = []; } catch (error) { this.logger.error(`error in validating credentials : ${error.response}`); - throw new RpcException(error.response ? error.response : error); + throw new Error(error.response.message ? error.response.message : error); } finally { // await this.awsService.deleteFile(importFileDetails.fileKey); // this.logger.error(`Deleted uploaded file after processing.`); From d791fbccda91a546c876c1c9d773f92b0554b276 Mon Sep 17 00:00:00 2001 From: sanjay-k1910 Date: Thu, 22 Feb 2024 18:24:58 +0530 Subject: [PATCH 066/231] fix: search and sorting issue in ecosystem member list API Signed-off-by: sanjay-k1910 Signed-off-by: bhavanakarwade --- .../src/authz/guards/ecosystem-roles.guard.ts | 16 +-- .../src/ecosystem/dtos/get-members.dto.ts | 29 +--- .../src/ecosystem/ecosystem.controller.ts | 13 +- .../src/ecosystem/ecosystem.service.ts | 9 +- .../interfaces/ecosystemMembers.interface.ts | 1 + apps/ecosystem/src/ecosystem.controller.ts | 2 +- apps/ecosystem/src/ecosystem.repository.ts | 134 +++++++++--------- apps/ecosystem/src/ecosystem.service.ts | 7 +- libs/enum/src/enum.ts | 7 + libs/http-exception.filter.ts | 4 + 10 files changed, 110 insertions(+), 112 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts index 9f011b92c..099f1146a 100644 --- a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts @@ -1,8 +1,4 @@ -import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Logger } from '@nestjs/common'; - -import { HttpException } from '@nestjs/common'; -import { HttpStatus } from '@nestjs/common'; -import { Injectable } from '@nestjs/common'; +import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Logger, Injectable } from '@nestjs/common'; import { ECOSYSTEM_ROLES_KEY } from '../decorators/roles.decorator'; import { Reflector } from '@nestjs/core'; import { EcosystemService } from '../../ecosystem/ecosystem.service'; @@ -54,19 +50,17 @@ export class EcosystemRolesGuard implements CanActivate { const ecosystemOrgData = await this.ecosystemService.fetchEcosystemOrg(ecosystemId, orgId); if (!ecosystemOrgData) { - throw new HttpException('Organization does not match', HttpStatus.FORBIDDEN); + throw new ForbiddenException('Organization does not match'); } - const {response} = ecosystemOrgData; - - user.ecosystemOrgRole = response['ecosystemRole']['name']; + user.ecosystemOrgRole = ecosystemOrgData['ecosystemRole']['name']; if (!user.ecosystemOrgRole) { - throw new HttpException('Ecosystem role not match', HttpStatus.FORBIDDEN); + throw new ForbiddenException('Ecosystem role not match'); } } else { - throw new HttpException('organization & ecosystem is required', HttpStatus.BAD_REQUEST); + throw new BadRequestException('organization & ecosystem is required'); } // Sending user friendly message if a user attempts to access an API that is inaccessible to their role diff --git a/apps/api-gateway/src/ecosystem/dtos/get-members.dto.ts b/apps/api-gateway/src/ecosystem/dtos/get-members.dto.ts index 3a7e78f78..f12af3ea4 100644 --- a/apps/api-gateway/src/ecosystem/dtos/get-members.dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/get-members.dto.ts @@ -1,34 +1,20 @@ -import { Transform, Type } from 'class-transformer'; +import { Transform } from 'class-transformer'; import { trim } from '@credebl/common/cast.helper'; import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsOptional } from 'class-validator'; -import { SortFields } from 'apps/connection/src/enum/connection.enum'; -import { SortValue } from '@credebl/enum/enum'; - -export class GetAllEcosystemMembersDto { - - @ApiProperty({ required: false, example: '1' }) - @IsOptional() - pageNumber: number; - - @ApiProperty({ required: false, example: '10' }) - @IsOptional() - pageSize: number; - - @ApiProperty({ required: false }) - @IsOptional() - @Transform(({ value }) => trim(value)) - @Type(() => String) - search: string = ''; +import { SortMembers, SortValue } from '@credebl/enum/enum'; +import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; +export class GetAllEcosystemMembersDto extends PaginationDto { @ApiProperty({ + enum: [SortMembers.CREATED_DATE_TIME, SortMembers.ID, SortMembers.ORGANIZATION, SortMembers.STATUS], required: false }) @Transform(({ value }) => trim(value)) @IsOptional() - @IsEnum(SortFields) - sortField: string = SortFields.CREATED_DATE_TIME; + @IsEnum(SortMembers) + sortField: string = SortMembers.CREATED_DATE_TIME; @ApiProperty({ enum: [SortValue.DESC, SortValue.ASC], @@ -38,5 +24,4 @@ export class GetAllEcosystemMembersDto { @IsOptional() @IsEnum(SortValue) sortBy: string = SortValue.DESC; - } diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index bfc3371c8..05a0ba0b6 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -1,6 +1,6 @@ import { ApiBearerAuth, ApiExcludeEndpoint, ApiForbiddenResponse, ApiOperation, ApiQuery, ApiResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; import { EcosystemService } from './ecosystem.service'; -import { Controller, UseFilters, Put, Post, Get, Body, Param, UseGuards, Query, BadRequestException, Delete, HttpStatus, Res } from '@nestjs/common'; +import { Controller, UseFilters, Put, Post, Get, Body, Param, UseGuards, Query, BadRequestException, Delete, HttpStatus, Res, ParseUUIDPipe } from '@nestjs/common'; import { RequestCredDefDto, RequestSchemaDto } from './dtos/request-schema.dto'; import IResponse from '@credebl/common/interfaces/response.interface'; import { Response } from 'express'; @@ -256,7 +256,7 @@ export class EcosystemController { @EcosystemsRoles(EcosystemRoles.ECOSYSTEM_OWNER, EcosystemRoles.ECOSYSTEM_LEAD, EcosystemRoles.ECOSYSTEM_MEMBER) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard, OrgRolesGuard) - @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiOperation({ summary: 'Get ecosystem members list', description: 'Get ecosystem members list.' }) @ApiQuery({ name: 'pageNumber', @@ -274,18 +274,19 @@ export class EcosystemController { required: false }) async getEcosystemMembers( - @Param('ecosystemId') ecosystemId: string, + @Param('ecosystemId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for ecosystemId`); }})) ecosystemId: string, @Param('orgId') orgId: string, @Query() getEcosystemMembers: GetAllEcosystemMembersDto, @Res() res: Response): Promise { + const members = await this.ecosystemService.getEcosystemMembers(ecosystemId, getEcosystemMembers); const finalResponse: IResponse = { - statusCode: 200, + statusCode: HttpStatus.OK, message: ResponseMessages.ecosystem.success.fetchMembers, - data: members?.response + data: members }; - return res.status(200).json(finalResponse); + return res.status(HttpStatus.OK).json(finalResponse); } @Post('/:ecosystemId/:orgId/transaction/schema') diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index 2494fee1b..92822bf1d 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -93,11 +93,10 @@ export class EcosystemService extends BaseService { */ async getEcosystemMembers( ecosystemId: string, - getEcosystemMembers: GetAllEcosystemMembersDto + payload: GetAllEcosystemMembersDto ): Promise<{ response: object }> { - const { pageNumber, pageSize, search, sortBy } = getEcosystemMembers; - const payload = { ecosystemId, pageNumber, pageSize, search, sortBy }; - return this.sendNats(this.serviceProxy, 'fetch-ecosystem-members', payload); + payload['ecosystemId'] = ecosystemId; + return this.sendNatsMessage(this.serviceProxy, 'fetch-ecosystem-members', payload); } /** @@ -128,7 +127,7 @@ export class EcosystemService extends BaseService { async fetchEcosystemOrg(ecosystemId: string, orgId: string): Promise<{ response: object }> { const payload = { ecosystemId, orgId }; - return this.sendNats(this.serviceProxy, 'fetch-ecosystem-org-data', payload); + return this.sendNatsMessage(this.serviceProxy, 'fetch-ecosystem-org-data', payload); } async getEndorsementTranasactions( diff --git a/apps/ecosystem/interfaces/ecosystemMembers.interface.ts b/apps/ecosystem/interfaces/ecosystemMembers.interface.ts index 8ea39f7cf..58b12fd4b 100644 --- a/apps/ecosystem/interfaces/ecosystemMembers.interface.ts +++ b/apps/ecosystem/interfaces/ecosystemMembers.interface.ts @@ -5,4 +5,5 @@ export interface EcosystemMembersPayload { pageSize: number; search: string; sortBy: string; + sortField: string; } \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index db3ab3281..4797821ed 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -79,7 +79,7 @@ export class EcosystemController { */ @MessagePattern({ cmd: 'fetch-ecosystem-members' }) async getEcosystemMembers(@Body() payload: EcosystemMembersPayload): Promise { - return this.ecosystemService.getEcoystemMembers(payload); + return this.ecosystemService.getEcosystemMembers(payload); } /** diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index da17cd2f0..98be181dc 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -9,6 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { NotFoundException } from '@nestjs/common'; import { CommonConstants } from '@credebl/common/common.constant'; import { GetAllSchemaList } from '../interfaces/endorsements.interface'; +import { SortValue } from '@credebl/enum/enum'; // eslint-disable-next-line camelcase @Injectable() @@ -160,7 +161,7 @@ export class EcosystemRepository { } }) ]); - + return { ecosystemDetails, totalCount: ecosystemCount @@ -170,7 +171,7 @@ export class EcosystemRepository { throw error; } } - + /** * @@ -486,62 +487,67 @@ export class EcosystemRepository { * @returns users list */ -async findEcosystemMembers( - ecosystemId: string, - pageNumber: number, - pageSize: number, - search: string, - sortBy: string -): Promise { - try { - const result = await this.prisma.$transaction([ - this.prisma.ecosystem_orgs.findMany({ - where: { - ecosystemId, - OR: [ - { - organisation: { - name: { contains: search, mode: 'insensitive' }, - // eslint-disable-next-line camelcase - org_agents: { - some: { - orgDid: { contains: search, mode: 'insensitive' } + async findEcosystemMembers( + ecosystemId: string, + pageNumber: number, + pageSize: number, + search: string, + sortBy: string, + sortField: string + ): Promise { + try { + const result = await this.prisma.$transaction([ + this.prisma.ecosystem_orgs.findMany({ + where: { + ecosystemId, + OR: [ + { + organisation: { + name: { contains: search, mode: 'insensitive' } + } + }, + { + organisation: { + // eslint-disable-next-line camelcase + org_agents: { + some: { + orgDid: { contains: search, mode: 'insensitive' } + } } } } + ] + }, + include: { + ecosystem: true, + ecosystemRole: true, + organisation: { + select: { + name: true, + orgSlug: true, + // eslint-disable-next-line camelcase + org_agents: true + } } - ] - }, - include: { - ecosystem: true, - ecosystemRole: true, - organisation: { - select: { - name: true, - orgSlug: true, - // eslint-disable-next-line camelcase - org_agents: true - } + }, + take: Number(pageSize), + skip: (pageNumber - 1) * pageSize, + orderBy: { + [sortField]: SortValue.ASC === sortBy ? 'asc' : 'desc' } - }, - take: Number(pageSize), - skip: (pageNumber - 1) * pageSize, - orderBy: { - createDateTime: 'asc' === sortBy ? 'asc' : 'desc' - } - }), - this.prisma.ecosystem_orgs.count({ - where: { - ecosystemId - } - }) - ]); - return result; - } catch (error) { - this.logger.error(`error: ${JSON.stringify(error)}`); - throw error; + }), + this.prisma.ecosystem_orgs.count({ + where: { + ecosystemId + } + }) + ]); + return result; + } catch (error) { + this.logger.error(`error: ${JSON.stringify(error)}`); + throw error; + } } -} async getEcosystemInvitationsPagination(queryObject: object, pageNumber: number, pageSize: number): Promise { try { @@ -590,18 +596,18 @@ async findEcosystemMembers( async fetchEcosystemOrg( payload: object ): Promise { - + return this.prisma.ecosystem_orgs.findFirst({ - where: { - ...payload - }, - select: { - ecosystem: true, - ecosystemRole: true, - organisation: true - } - }); - + where: { + ...payload + }, + select: { + ecosystem: true, + ecosystemRole: true, + organisation: true + } + }); + } @@ -833,7 +839,7 @@ async findEcosystemMembers( schemaTransactionResponse: SchemaTransactionResponse, requestBody: object, type: endorsementTransactionType - // eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase ): Promise { try { const { endorserDid, authorDid, requestPayload, status, ecosystemOrgId, userId } = schemaTransactionResponse; diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 40c540167..97fb35a47 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -1109,15 +1109,16 @@ export class EcosystemService { * @returns Ecosystem members list */ - async getEcoystemMembers(payload: EcosystemMembersPayload): Promise { + async getEcosystemMembers(payload: EcosystemMembersPayload): Promise { try { - const { ecosystemId, pageNumber, pageSize, search, sortBy } = payload; + const { ecosystemId, pageNumber, pageSize, search, sortBy, sortField } = payload; const getEcosystemMember = await this.ecosystemRepository.findEcosystemMembers( ecosystemId, pageNumber, pageSize, search, - sortBy + sortBy, + sortField ); const ecosystemMemberResponse = { diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 2dd5470ed..3536f0bbc 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -70,6 +70,13 @@ export enum AutoAccept { Never = "never" } +export enum SortMembers { + CREATED_DATE_TIME = 'createDateTime', + STATUS = 'status', + ID = 'id', + ORGANIZATION = 'organization' +} + const transitionMap: { [key in Invitation]: Invitation[] } = { [Invitation.PENDING]: [Invitation.ACCEPTED, Invitation.REJECTED], [Invitation.ACCEPTED]: [], diff --git a/libs/http-exception.filter.ts b/libs/http-exception.filter.ts index 645b20b3a..c7dc008fb 100644 --- a/libs/http-exception.filter.ts +++ b/libs/http-exception.filter.ts @@ -39,6 +39,10 @@ export class HttpExceptionFilter implements ExceptionFilter { httpStatus = HttpStatus.BAD_REQUEST; message = exception?.response?.message || exception?.message; break; + case 'P2023': // Inconsistent column data: {message} + httpStatus = HttpStatus.BAD_REQUEST; + message = exception?.meta?.message || exception?.message; + break; case 'P2018': // The required connected records were not found. {details} case 'P2025': // An operation failed because it depends on one or more records that were required but not found. {cause} case 'P2015': // A related record could not be found. {details} From 7fd36c319dc34fb882fe9578962d803b93b08227 Mon Sep 17 00:00:00 2001 From: sanjay-k1910 Date: Thu, 22 Feb 2024 18:34:04 +0530 Subject: [PATCH 067/231] refactor: type Signed-off-by: sanjay-k1910 Signed-off-by: bhavanakarwade --- apps/api-gateway/src/ecosystem/ecosystem.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index 92822bf1d..a55c21d13 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -125,7 +125,7 @@ export class EcosystemService extends BaseService { return this.sendNats(this.serviceProxy, 'accept-reject-ecosystem-invitations', payload); } - async fetchEcosystemOrg(ecosystemId: string, orgId: string): Promise<{ response: object }> { + async fetchEcosystemOrg(ecosystemId: string, orgId: string): Promise { const payload = { ecosystemId, orgId }; return this.sendNatsMessage(this.serviceProxy, 'fetch-ecosystem-org-data', payload); } From 52116a4c5fa885634f8476f773cf23e9f2905b9c Mon Sep 17 00:00:00 2001 From: sanjay-k1910 Date: Thu, 22 Feb 2024 19:42:59 +0530 Subject: [PATCH 068/231] refactor: error messages in common messages Signed-off-by: sanjay-k1910 Signed-off-by: bhavanakarwade --- apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts | 6 +++--- libs/common/src/response-messages/index.ts | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts index 099f1146a..af5b5b619 100644 --- a/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts @@ -50,17 +50,17 @@ export class EcosystemRolesGuard implements CanActivate { const ecosystemOrgData = await this.ecosystemService.fetchEcosystemOrg(ecosystemId, orgId); if (!ecosystemOrgData) { - throw new ForbiddenException('Organization does not match'); + throw new ForbiddenException(ResponseMessages.organisation.error.orgDoesNotMatch); } user.ecosystemOrgRole = ecosystemOrgData['ecosystemRole']['name']; if (!user.ecosystemOrgRole) { - throw new ForbiddenException('Ecosystem role not match'); + throw new ForbiddenException(ResponseMessages.ecosystem.error.ecosystemRoleNotMatch); } } else { - throw new BadRequestException('organization & ecosystem is required'); + throw new BadRequestException(ResponseMessages.ecosystem.error.orgEcoIdRequired); } // Sending user friendly message if a user attempts to access an API that is inaccessible to their role diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 5f6cb2cb3..695d8210c 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -107,7 +107,8 @@ export const ResponseMessages = { invalidUserId:'Invalid format of userId', invalidInvitationId:'Invalid format for invitation id', ecosystemIdIsRequired:'ecosystemId is required', - roleNotMatch: 'User does not have access' + roleNotMatch: 'User does not have access', + orgDoesNotMatch: 'Organization does not match' } }, @@ -353,7 +354,9 @@ export const ResponseMessages = { updateSchemaId: 'Error while updating the schema id', updateCredDefId: 'Error while updating the credential-definition', invalidMessage: 'Invalid transaction details. Missing "message" property.', - invalidTransactionMessage: 'Invalid transaction details' + invalidTransactionMessage: 'Invalid transaction details', + ecosystemRoleNotMatch: 'Ecosystem role not match', + orgEcoIdRequired: 'OrgId & EcosystemId is required' } }, bulkIssuance: { From d1fb4fd7f2fdd81afd8cde3b5bac8a1c28f05a89 Mon Sep 17 00:00:00 2001 From: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Date: Thu, 22 Feb 2024 20:15:27 +0530 Subject: [PATCH 069/231] Updated OOB Proof Verification endpoint (#533) * fix: removed the unnecessary logger from the agent-service module (#419) Signed-off-by: KulkarniShashank * WIP:OOB Proof Request Signed-off-by: ankita_patidar * WIP:OOB Proof Request Signed-off-by: ankita_patidar * fix:OOB Credential Offer restore changes Signed-off-by: ankita_patidar * fix:add email as optional Signed-off-by: ankita_patidar * fix:take response from presentation request payload Signed-off-by: ankita_patidar * fix: resolved sonar lint checks Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * expose agent format of proof request to API endpoint, disabled send offer by email Signed-off-by: ankita_patidar * update OOB verification input Signed-off-by: ankita_patidar * added few more allowed restrictions, corrected API description Signed-off-by: ankita_patidar * added few more allowed restrictions Signed-off-by: ankita_patidar --------- Signed-off-by: KulkarniShashank Signed-off-by: ankita_patidar Signed-off-by: bhavanakarwade Signed-off-by: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Co-authored-by: Nishad Shirsat <103021375+nishad-ayanworks@users.noreply.github.com> Co-authored-by: Nishad Co-authored-by: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Co-authored-by: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Co-authored-by: bhavanakarwade Signed-off-by: bhavanakarwade --- .../src/interface/agent-service.interface.ts | 3 + .../src/verification/dto/request-proof.dto.ts | 87 +++++++++++-------- .../interfaces/verification.interface.ts | 39 +++++++++ .../verification/verification.controller.ts | 6 +- .../src/verification/verification.service.ts | 4 +- .../src/interfaces/verification.interface.ts | 7 +- 6 files changed, 105 insertions(+), 41 deletions(-) diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 685122c49..663a32e27 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -277,6 +277,9 @@ interface IRequestedPredicatesName { interface IRequestedRestriction { cred_def_id?: string; schema_id?: string; + schema_issuer_did?: string; + schema_name?: string; + issuer_did?: string; } export interface IAgentSpinUpSatus { diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 01789e009..dc765e008 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,8 +1,9 @@ -import { IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString } from 'class-validator'; +import { IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, IsUUID } from 'class-validator'; import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { AutoAccept } from '@credebl/enum/enum'; +import { IProofFormats } from '../interfaces/verification.interface'; export class ProofRequestAttribute { @IsString() @@ -135,46 +136,62 @@ export class OutOfBandRequestProof extends ProofPayload { autoAcceptProof: string; } +export class SendProofRequestPayload { -interface IProofFormats { - indy: IndyProof -} + @ApiPropertyOptional() + @IsString({ message: 'protocolVersion must be in string' }) + @IsNotEmpty({ message: 'please provide valid protocol version' }) + @IsOptional() + protocolVersion: string; -interface IndyProof { - name: string; - version: string; - requested_attributes: IRequestedAttributes; - requested_predicates: IRequestedPredicates; -} + @ApiPropertyOptional() + @IsOptional() + @IsString({ message: 'comment must be in string' }) + comment: string; -interface IRequestedAttributes { - [key: string]: IRequestedAttributesName; -} + @ApiProperty() + @IsString() + @Transform(({ value }) => trim(value)) + @Transform(({ value }) => toLowerCase(value)) + @IsNotEmpty({ message: 'connectionId is required.' }) + connectionId: string; -interface IRequestedAttributesName { - name: string; - restrictions: IRequestedRestriction[] -} + @ApiProperty({ + 'example': [ + { + indy: { + name: 'Verify national identity', + version: '1.0', + // eslint-disable-next-line camelcase + requested_attributes: {}, + // eslint-disable-next-line camelcase + requested_predicates: {} + } + } + ] + }) + @IsObject({ each: true }) + @IsNotEmpty({ message: 'please provide valid proofFormat' }) + proofFormats: IProofFormats; -interface IRequestedPredicates { - [key: string]: IRequestedPredicatesName; -} + @ApiPropertyOptional() + @IsString({ message: 'auto accept proof must be in string' }) + @IsNotEmpty({ message: 'please provide from valid auto accept proof options' }) + @IsOptional() + @IsEnum(AutoAccept, { + message: `Invalid auto accept proof. It should be one of: ${Object.values(AutoAccept).join(', ')}` + }) + autoAcceptProof: AutoAccept; -interface IRequestedPredicatesName { - name: string; - restrictions: IRequestedRestriction[] -} + @ApiPropertyOptional() + @IsOptional() + @IsString({ message: 'label must be in string' }) + label: string; -interface IRequestedRestriction { - cred_def_id?: string; - schema_id?: string; + @ApiPropertyOptional() + @IsOptional() + @IsUUID() + @IsNotEmpty({ message: 'please provide valid parentThreadId' }) + parentThreadId: string; } -export interface ISendProofRequestPayload { - protocolVersion?: string; - comment?: string; - connectionId?: string; - proofFormats: IProofFormats; - autoAcceptProof?: string; - label?: string; -} diff --git a/apps/api-gateway/src/verification/interfaces/verification.interface.ts b/apps/api-gateway/src/verification/interfaces/verification.interface.ts index 7937c6aee..a722da72a 100644 --- a/apps/api-gateway/src/verification/interfaces/verification.interface.ts +++ b/apps/api-gateway/src/verification/interfaces/verification.interface.ts @@ -51,3 +51,42 @@ interface ITags { state: string; threadId: string; } + +export interface IProofFormats { + indy: IndyProof +} + +interface IndyProof { + name: string; + version: string; + requested_attributes: IRequestedAttributes; + requested_predicates: IRequestedPredicates; +} + +interface IRequestedAttributes { + [key: string]: IRequestedAttributesName; +} + +interface IRequestedAttributesName { + name?: string; + names?: string; + restrictions: IRequestedRestriction[] +} + +interface IRequestedPredicates { + [key: string]: IRequestedPredicatesName; +} + +interface IRequestedPredicatesName { + name: string; + restrictions: IRequestedRestriction[] +} + +interface IRequestedRestriction { + cred_def_id?: string; + schema_id?: string; + schema_issuer_did?: string; + schema_name?: string; + issuer_did?: string; + schema_version?: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index e35baaba0..7f9bb9b45 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -16,7 +16,7 @@ import { Controller, Logger, Post, Body, Get, Query, HttpStatus, Res, UseGuards, import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; -import { ISendProofRequestPayload, OutOfBandRequestProof, RequestProofDto } from './dto/request-proof.dto'; +import { SendProofRequestPayload, RequestProofDto } from './dto/request-proof.dto'; import { VerificationService } from './verification.service'; import IResponseType, { IResponse } from '@credebl/common/interfaces/response.interface'; import { Response } from 'express'; @@ -247,14 +247,14 @@ export class VerificationController { @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) @ApiUnauthorizedResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Unauthorized', type: UnauthorizedErrorDto }) @ApiForbiddenResponse({ status: HttpStatus.FORBIDDEN, description: 'Forbidden', type: ForbiddenErrorDto }) - @ApiBody({ type: OutOfBandRequestProof }) + @ApiBody({ type: SendProofRequestPayload }) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.VERIFIER) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), OrgRolesGuard) async sendOutOfBandPresentationRequest( @Res() res: Response, @User() user: IUserRequest, - @Body() outOfBandRequestProof: ISendProofRequestPayload, + @Body() outOfBandRequestProof: SendProofRequestPayload, @Param('orgId') orgId: string ): Promise { user.orgId = orgId; diff --git a/apps/api-gateway/src/verification/verification.service.ts b/apps/api-gateway/src/verification/verification.service.ts index 44dffc4b4..8a51651b4 100644 --- a/apps/api-gateway/src/verification/verification.service.ts +++ b/apps/api-gateway/src/verification/verification.service.ts @@ -1,7 +1,7 @@ import { Injectable, Inject} from '@nestjs/common'; import { ClientProxy} from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; -import { ISendProofRequestPayload, RequestProofDto } from './dto/request-proof.dto'; +import { SendProofRequestPayload, RequestProofDto } from './dto/request-proof.dto'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { WebhookPresentationProofDto } from './dto/webhook-proof.dto'; import { IProofPresentationDetails, IProofPresentationList } from '@credebl/common/interfaces/verification.interface'; @@ -70,7 +70,7 @@ export class VerificationService extends BaseService { * @param outOfBandRequestProof * @returns Get out-of-band requested proof presentation details */ - sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { + sendOutOfBandPresentationRequest(outOfBandRequestProof: SendProofRequestPayload, user: IUserRequest): Promise { const payload = { outOfBandRequestProof, user }; return this.sendNatsMessage(this.verificationServiceProxy, 'send-out-of-band-proof-request', payload); } diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 8e2da7579..082fe897f 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -65,7 +65,8 @@ interface IRequestedAttributes { } interface IRequestedAttributesName { - name: string; + name?: string; + names?: string; restrictions: IRequestedRestriction[] } @@ -81,6 +82,10 @@ interface IRequestedPredicatesName { interface IRequestedRestriction { cred_def_id?: string; schema_id?: string; + schema_issuer_did?: string; + schema_name?: string; + issuer_did?: string; + schema_version?: string; } export interface ISendProofRequestPayload { From 0424ecc53af8cb38758ba241034f6ece35a3dd5c Mon Sep 17 00:00:00 2001 From: KambleSahil3 Date: Thu, 22 Feb 2024 21:09:22 +0530 Subject: [PATCH 070/231] fixed Puppeteer issue Signed-off-by: bhavanakarwade --- Dockerfiles/Dockerfile.user | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfiles/Dockerfile.user b/Dockerfiles/Dockerfile.user index 4b59f0a25..775c12779 100644 --- a/Dockerfiles/Dockerfile.user +++ b/Dockerfiles/Dockerfile.user @@ -4,6 +4,7 @@ RUN npm install -g pnpm # We don't need the standalone Chromium ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true +ENV PUPPETEER_SKIP_DOWNLOAD true # Install Google Chrome Stable and fonts # Note: this installs the necessary libs to make the browser work with Puppeteer. @@ -36,6 +37,7 @@ FROM node:18-slim # We don't need the standalone Chromium ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true +ENV PUPPETEER_SKIP_DOWNLOAD true # Install Google Chrome Stable and fonts # Note: this installs the necessary libs to make the browser work with Puppeteer. @@ -64,4 +66,4 @@ CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx pri # docker build -t user -f Dockerfiles/Dockerfile.user . # docker run -d --env-file .env --name user docker.io/library/user -# docker logs -f user \ No newline at end of file +# docker logs -f user From 65288f9331d7d5a061f0d961964d0b2fa053c601 Mon Sep 17 00:00:00 2001 From: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Date: Fri, 23 Feb 2024 10:19:02 +0530 Subject: [PATCH 071/231] Remove unwanted attributes, Added support for more restrictions (#538) * fix: removed the unnecessary logger from the agent-service module (#419) Signed-off-by: KulkarniShashank * WIP:OOB Proof Request Signed-off-by: ankita_patidar * WIP:OOB Proof Request Signed-off-by: ankita_patidar * fix:OOB Credential Offer restore changes Signed-off-by: ankita_patidar * fix:add email as optional Signed-off-by: ankita_patidar * fix:take response from presentation request payload Signed-off-by: ankita_patidar * fix: resolved sonar lint checks Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * fix: dco error Signed-off-by: bhavanakarwade * expose agent format of proof request to API endpoint, disabled send offer by email Signed-off-by: ankita_patidar * update OOB verification input Signed-off-by: ankita_patidar * added few more allowed restrictions, corrected API description Signed-off-by: ankita_patidar * added few more allowed restrictions Signed-off-by: ankita_patidar * removed unnecessary attribute,updated example Signed-off-by: ankita_patidar --------- Signed-off-by: KulkarniShashank Signed-off-by: ankita_patidar Signed-off-by: bhavanakarwade Signed-off-by: Ankita Patidar <35130088+ankita-p17@users.noreply.github.com> Co-authored-by: Nishad Shirsat <103021375+nishad-ayanworks@users.noreply.github.com> Co-authored-by: Nishad Co-authored-by: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Co-authored-by: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Co-authored-by: bhavanakarwade Signed-off-by: bhavanakarwade --- .../src/verification/dto/request-proof.dto.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index dc765e008..3ab0d48ee 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -149,13 +149,6 @@ export class SendProofRequestPayload { @IsString({ message: 'comment must be in string' }) comment: string; - @ApiProperty() - @IsString() - @Transform(({ value }) => trim(value)) - @Transform(({ value }) => toLowerCase(value)) - @IsNotEmpty({ message: 'connectionId is required.' }) - connectionId: string; - @ApiProperty({ 'example': [ { @@ -163,7 +156,16 @@ export class SendProofRequestPayload { name: 'Verify national identity', version: '1.0', // eslint-disable-next-line camelcase - requested_attributes: {}, + requested_attributes: { + verifynameAddress: { + names: ['name', 'address'], + restrictions: [{'schema_id': 'KU583UbI4yAKfaBTSz1rqG:2:National ID:1.0.0'}] + }, + verifyBirthPlace: { + name: 'Place', + restrictions: [{'schema_id': 'KU583UbI4yAKfaBTSz1rqG:2:Birth Certificate:1.0.0'}] + } + }, // eslint-disable-next-line camelcase requested_predicates: {} } From 819bb0f8b3c63234195603b91053699073b8316f Mon Sep 17 00:00:00 2001 From: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Date: Fri, 23 Feb 2024 18:09:32 +0530 Subject: [PATCH 072/231] Add multiple attributes in single proof request (#531) * feat:add multiple attributes in single proof request Signed-off-by: pranalidhanavade * feat:add multiple attributes in single proof request Signed-off-by: pranalidhanavade * fix: verification issue Signed-off-by: pranalidhanavade * fix: verification issue while verifying credebtials Signed-off-by: pranalidhanavade --------- Signed-off-by: pranalidhanavade Signed-off-by: bhavanakarwade --- .../src/verification/dto/request-proof.dto.ts | 34 +++++++++--- .../verification/verification.controller.ts | 26 +-------- apps/verification/src/verification.service.ts | 53 +++++++++++++++---- 3 files changed, 71 insertions(+), 42 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 3ab0d48ee..7d04d5960 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,29 +1,43 @@ -import { IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, IsUUID } from 'class-validator'; +import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID } from 'class-validator'; import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; +import { Transform, Type } from 'class-transformer'; import { AutoAccept } from '@credebl/enum/enum'; import { IProofFormats } from '../interfaces/verification.interface'; + export class ProofRequestAttribute { - @IsString() - @IsNotEmpty({ message: 'attributeName is required.' }) - attributeName: string; + @ValidateIf((obj) => obj.attributeNames === undefined) + @IsNotEmpty() + @IsString({each:true}) + attributeName?: string; + + @ValidateIf((obj) => obj.attributeName === undefined) + @IsArray({ message: 'attributeNames must be an array.' }) + @ArrayNotEmpty({ message: 'array can not be empty' }) + @IsString({ each: true}) + @IsNotEmpty({ each: true, message: 'each element cannot be empty' }) + attributeNames?: string[]; + + @ApiPropertyOptional() @IsString() @IsOptional() schemaId?: string; + @ApiPropertyOptional() @IsString() @IsOptional() @IsNotEmpty({ message: 'condition is required.' }) condition?: string; + @ApiPropertyOptional() @IsOptional() @IsNotEmpty({ message: 'value is required.' }) @IsNumberString({}, { message: 'Value must be a number' }) value?: string; + @ApiPropertyOptional() @IsString() @IsOptional() credDefId?: string; @@ -72,11 +86,14 @@ export class RequestProofDto extends ProofPayload { credDefId: 'string', schemaId: 'string' } - ] + ], + type: () => [ProofRequestAttribute] }) @IsArray({ message: 'attributes must be in array' }) + @ValidateNested() @IsObject({ each: true }) @IsNotEmpty({ message: 'please provide valid attributes' }) + @Type(() => ProofRequestAttribute) attributes: ProofRequestAttribute[]; @ApiPropertyOptional() @@ -106,11 +123,14 @@ export class OutOfBandRequestProof extends ProofPayload { credDefId: '', schemaId: '' } - ] + ], + type: () => [ProofRequestAttribute] }) @IsArray({ message: 'attributes must be in array' }) + @ValidateNested({each: true}) @IsObject({ each: true }) @IsNotEmpty({ message: 'please provide valid attributes' }) + @Type(() => ProofRequestAttribute) attributes: ProofRequestAttribute[]; @ApiProperty() diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index 7f9bb9b45..efb8570a7 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -188,8 +188,7 @@ export class VerificationController { } else { throw new BadRequestException('Please provide unique attribute names'); } - - await this.validateAttribute(attrData); + } requestProof.orgId = orgId; @@ -313,27 +312,4 @@ export class VerificationController { return res.status(HttpStatus.CREATED).json(finalResponse); } - - async validateAttribute( - attrData: object - ): Promise { - - if (!attrData['attributeName']) { - throw new BadRequestException('attributeName must be required'); - } - - if (undefined !== attrData['condition'] && '' === attrData['condition'].trim()) { - throw new BadRequestException('condition cannot be empty'); - } - - if (undefined !== attrData['value'] && '' === attrData['value'].trim()) { - throw new BadRequestException('value cannot be empty'); - } - - if (attrData['condition']) { - if (isNaN(attrData['value'])) { - throw new BadRequestException('value must be an integer'); - } - } - } } \ No newline at end of file diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index c85fae1bc..c910fecf3 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -650,7 +650,8 @@ export class VerificationService { try { const getAgentDetails = await this.verificationRepository.getAgentEndPoint(orgId); const verificationMethodLabel = 'get-verified-proof'; - + let credDefId; + let schemaId; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId, '', proofId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); @@ -661,27 +662,39 @@ export class VerificationService { const payload = { apiKey, url }; const getProofPresentationById = await this._getVerifiedProofDetails(payload); + if (!getProofPresentationById?.response?.presentation) { throw new NotFoundException(ResponseMessages.verification.error.proofPresentationNotFound, { cause: new Error(), description: ResponseMessages.errorMessages.notFound }); } + const requestedAttributes = getProofPresentationById?.response?.request?.indy?.requested_attributes; const requestedPredicates = getProofPresentationById?.response?.request?.indy?.requested_predicates; const revealedAttrs = getProofPresentationById?.response?.presentation?.indy?.requested_proof?.revealed_attrs; + const extractedDataArray: IProofPresentationDetails[] = []; - if (requestedAttributes && requestedPredicates) { + if (0 !== Object.keys(requestedAttributes).length && 0 !== Object.keys(requestedPredicates).length) { + for (const key in requestedAttributes) { if (requestedAttributes.hasOwnProperty(key)) { const requestedAttributeKey = requestedAttributes[key]; const attributeName = requestedAttributeKey.name; - const credDefId = requestedAttributeKey?.restrictions[0]?.cred_def_id; - const schemaId = requestedAttributeKey?.restrictions[0]?.schema_id; + + if (requestedAttributeKey?.restrictions) { + + credDefId = requestedAttributeKey?.restrictions[0]?.cred_def_id; + schemaId = requestedAttributeKey?.restrictions[0]?.schema_id; + } else if (getProofPresentationById?.response?.presentation?.indy?.identifiers) { + + credDefId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].cred_def_id; + schemaId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].schema_id; + } if (revealedAttrs.hasOwnProperty(key)) { const extractedData: IProofPresentationDetails = { @@ -710,14 +723,17 @@ export class VerificationService { } } - } else if (requestedAttributes) { + } else if (0 !== Object.keys(requestedAttributes).length) { + for (const key in requestedAttributes) { if (requestedAttributes.hasOwnProperty(key)) { const attribute = requestedAttributes[key]; const attributeName = attribute.name; - const credDefId = attribute?.restrictions[0]?.cred_def_id; - const schemaId = attribute?.restrictions[0]?.schema_id; + + + [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); + if (revealedAttrs.hasOwnProperty(key)) { const extractedData: IProofPresentationDetails = { @@ -729,14 +745,14 @@ export class VerificationService { } } } - } else if (requestedPredicates) { + } else if (0 !== Object.keys(requestedPredicates).length) { for (const key in requestedPredicates) { if (requestedPredicates.hasOwnProperty(key)) { const attribute = requestedPredicates[key]; const attributeName = attribute?.name; - const credDefId = attribute?.restrictions[0]?.cred_def_id; - const schemaId = attribute?.restrictions[0]?.schema_id; + + [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); const extractedData: IProofPresentationDetails = { [attributeName]: `${requestedPredicates?.p_type}${requestedPredicates?.p_value}`, @@ -769,6 +785,23 @@ export class VerificationService { } } + async _schemaCredDefRestriction(attribute, getProofPresentationById): Promise { + let credDefId; + let schemaId; + + if (attribute?.restrictions) { + + credDefId = attribute?.restrictions[0]?.cred_def_id; + schemaId = attribute?.restrictions[0]?.schema_id; + } else if (getProofPresentationById?.response?.presentation?.indy?.identifiers) { + + credDefId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].cred_def_id; + schemaId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].schema_id; + } + + return [credDefId, schemaId]; + } + async _getVerifiedProofDetails(payload: IVerifiedProofData): Promise<{ response; }> { From 6f6c4daa95a50dd5d9a6d0e721d3e1f767c14ea1 Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Sat, 24 Feb 2024 22:23:33 +0530 Subject: [PATCH 073/231] fix:get-token-start_agent.sh (#540) Signed-off-by: bhavanakarwade --- apps/agent-provisioning/AFJ/scripts/start_agent.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh index 37047815c..87a595939 100644 --- a/apps/agent-provisioning/AFJ/scripts/start_agent.sh +++ b/apps/agent-provisioning/AFJ/scripts/start_agent.sh @@ -227,7 +227,7 @@ if [ $? -eq 0 ]; then container_logs=$(docker logs $(docker ps -q --filter "name=${AGENCY}_${CONTAINER_NAME}")) # Extract the token from the logs using sed - token=$(echo "$container_logs" | sed -nE 's/.*API Toekn: ([^ ]+).*/\1/p') + token=$(echo "$container_logs" | sed -nE 's/.*API Token: ([^ ]+).*/\1/p') # Print the extracted token echo "Token: $token" From 2d3c5937e5c0ca5ba8b846d99ace9b6afee6b8ad Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:01:49 +0530 Subject: [PATCH 074/231] feat: add question answer protocol (#539) * feat:added question answer protocol Signed-off-by: pallavicoder * fear:added question answer webhook Signed-off-by: pallavicoder * feat:add question answer protocol inside platform Signed-off-by: pallavicoder * fix:removed extra cost variables Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder Signed-off-by: bhavanakarwade --- .../AFJ/scripts/start_agent.sh | 0 .../src/agent-service.controller.ts | 16 ++ .../src/agent-service.service.ts | 31 +++- .../src/interface/agent-service.interface.ts | 11 ++ .../src/connection/connection.controller.ts | 84 +++++++++ .../src/connection/connection.service.ts | 20 ++ .../connection/dtos/question-answer.dto.ts | 95 ++++++++++ apps/connection/src/connection.controller.ts | 12 ++ apps/connection/src/connection.service.ts | 175 ++++++++++++++++++ .../interfaces/question-answer.interfaces.ts | 16 ++ libs/common/src/common.constant.ts | 9 +- libs/common/src/response-messages/index.ts | 4 +- 12 files changed, 468 insertions(+), 5 deletions(-) mode change 100644 => 100755 apps/agent-provisioning/AFJ/scripts/start_agent.sh create mode 100644 apps/api-gateway/src/connection/dtos/question-answer.dto.ts create mode 100644 apps/connection/src/interfaces/question-answer.interfaces.ts diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh old mode 100644 new mode 100755 diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 8e96d70b9..55e6452d2 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -208,4 +208,20 @@ export class AgentServiceController { }): Promise { return this.agentServiceService.receiveInvitation(payload.receiveInvitation, payload.url, payload.apiKey); } + + @MessagePattern({ cmd: 'agent-send-question' }) + async sendQuestion(payload: { + url, + apiKey, + questionPayload + }): Promise { + + return this.agentServiceService.sendQuestion(payload.questionPayload, payload.url, payload.apiKey); + } + + @MessagePattern({ cmd: 'agent-get-question-answer-record' }) + async getQuestionAnswersRecord(payload: { url: string, apiKey: string }): Promise { + return this.agentServiceService.getQuestionAnswersRecord(payload.url, payload.apiKey); + } + } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 4e4f540a3..6dca30728 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -19,7 +19,7 @@ import * as dotenv from 'dotenv'; import * as fs from 'fs'; import { map } from 'rxjs/operators'; dotenv.config(); -import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation } from './interface/agent-service.interface'; +import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation, IQuestionPayload } from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; import { ledgers, org_agents, organisation, platform_config } from '@prisma/client'; @@ -1305,5 +1305,34 @@ export class AgentServiceService { throw error; } } + + async sendQuestion(questionPayload: IQuestionPayload, url: string, apiKey: string): Promise { + try { + const sendQuestionRes = await this.commonService + .httpPost(url, questionPayload, { headers: { 'authorization': apiKey } }) + .then(async response => response); + return sendQuestionRes; + } catch (error) { + this.logger.error(`Error in send question in agent service : ${JSON.stringify(error)}`); + throw error; + } + } + + async getQuestionAnswersRecord(url: string, apiKey: string): Promise { + + try { + const data = await this.commonService + .httpGet(url, { headers: { 'authorization': apiKey } }) + .then(async response => response) + .catch(error => this.handleAgentSpinupStatusErrors(error)); + + return data; + } catch (error) { + this.logger.error(`Error in getQuestionAnswersRecord in agent service : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + + } + } diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 663a32e27..f52152eda 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -416,3 +416,14 @@ interface ITags { threadId: string; } +export interface IValidResponses { + text: string; + } + export interface IQuestionPayload { + detail: string; + validResponses: IValidResponses[]; + question: string; + orgId?: string; + connectionId: string; + tenantId: string; + } \ No newline at end of file diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 6ca88310d..b4e7ac48b 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -20,6 +20,7 @@ import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; import { SortFields } from 'apps/connection/src/enum/connection.enum'; import { ClientProxy} from '@nestjs/microservices'; +import { QuestionAnswerWebhookDto, QuestionDto} from './dtos/question-answer.dto'; @UseFilters(CustomExceptionFilter) @Controller() @@ -107,6 +108,29 @@ export class ConnectionController { return res.status(HttpStatus.OK).json(finalResponse); } + + @Get('orgs/:orgId/question-answer/question/:tenantId') + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @ApiOperation({ + summary: `Get question-answer record`, + description: `Get question-answer record` + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + async getQuestionAnswersRecord( + @Param('tenantId') tenantId: string, + @Param('orgId') orgId: string, + @Res() res: Response + ): Promise { + const record = await this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: ResponseMessages.connection.success.questionAnswerRecord, + data: record + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + /** * Create out-of-band connection legacy invitation * @param connectionDto @@ -136,6 +160,33 @@ export class ConnectionController { } + @Post('/orgs/:orgId/question-answer/question/:connectionId/:tenantId') + @ApiOperation({ summary: '', description: 'question-answer/question' }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async sendQuestion( + @Param('orgId') orgId: string, + @Param('connectionId') connectionId: string, + @Param('tenantId') tenantId: string, + @Body() questionDto: QuestionDto, + @User() reqUser: IUserRequestInterface, + @Res() res: Response + ): Promise { + + questionDto.orgId = orgId; + questionDto.connectionId = connectionId; + questionDto.tenantId = tenantId; + const questionData = await this.connectionService.sendQuestion(questionDto); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.connection.success.questionSend, + data: questionData + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + + } + @Post('/orgs/:orgId/receive-invitation-url') @ApiOperation({ summary: 'Receive Invitation URL', description: 'Receive Invitation URL' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @@ -218,4 +269,37 @@ export class ConnectionController { } return res.status(HttpStatus.CREATED).json(finalResponse); } + + + @Post('wh/:orgId/question-answer/') + @ApiExcludeEndpoint() + @ApiOperation({ + summary: 'Catch question-answer webhook responses', + description: 'Callback URL for question-answer' + }) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async getQuestionAnswerWebhook( + @Body() questionAnswerWebhookDto:QuestionAnswerWebhookDto, + @Param('orgId') orgId: string, + @Res() res: Response + ): Promise { + questionAnswerWebhookDto.type = 'question-answer'; + this.logger.debug(`questionAnswer ::: ${JSON.stringify(questionAnswerWebhookDto)} ${orgId}`); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.connection.success.create, + data: '' + }; + const webhookUrl = await this.connectionService._getWebhookUrl(questionAnswerWebhookDto.contextCorrelationId).catch(error => { + this.logger.debug(`error in getting webhook url ::: ${JSON.stringify(error)}`); + + }); + if (webhookUrl) { + await this.connectionService._postWebhookResponse(webhookUrl, { data: questionAnswerWebhookDto }).catch(error => { + this.logger.debug(`error in posting webhook response to webhook url ::: ${JSON.stringify(error)}`); + }); + } + return res.status(HttpStatus.CREATED).json(finalResponse); + } } diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index 0bb79af9a..f80a425b5 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -6,6 +6,7 @@ import { ConnectionDto, CreateConnectionDto, ReceiveInvitationDto, ReceiveInvita import { IReceiveInvitationRes, IUserRequestInterface } from './interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById, IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; +import { QuestionDto } from './dtos/question-answer.dto'; @Injectable() export class ConnectionService extends BaseService { @@ -13,6 +14,16 @@ export class ConnectionService extends BaseService { super('ConnectionService'); } + sendQuestion( + questionDto: QuestionDto + ): Promise { + try { + return this.sendNatsMessage(this.connectionServiceProxy, 'send-question', questionDto); + } catch (error) { + throw new RpcException(error.response); + } + } + createLegacyConnectionInvitation( connectionDto: CreateConnectionDto, user: IUserRequestInterface @@ -75,6 +86,15 @@ export class ConnectionService extends BaseService { return this.sendNatsMessage(this.connectionServiceProxy, 'get-connection-details-by-connectionId', payload); } + + getQuestionAnswersRecord( + tenantId: string, + orgId: string + ): Promise { + const payload = { tenantId, orgId }; + return this.sendNatsMessage(this.connectionServiceProxy, 'get-question-answer-record', payload); + } + receiveInvitationUrl( receiveInvitationUrl: ReceiveInvitationUrlDto, orgId: string, diff --git a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts new file mode 100644 index 000000000..49ceafdb7 --- /dev/null +++ b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts @@ -0,0 +1,95 @@ +import { trim } from '@credebl/common/cast.helper'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Transform, Type } from 'class-transformer'; +import { IsArray, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator'; + +class ValidResponses { + @ApiProperty({ example: 'Emma' }) + @IsNotEmpty({ message: 'text is required' }) + @IsString({ message: 'text should be a string' }) + @Transform(({ value }) => trim(value)) + @Type(() => String) + text: string; +} +export class QuestionDto { + @ApiPropertyOptional() + @IsOptional() + @IsString({ message: 'detail must be a string' }) + @IsNotEmpty({ message: 'please provide valid detail' }) + detail: string; + + @ApiProperty({ example: [{ 'text': 'Emma'}, { 'text': 'Kiva'}] }) + @IsNotEmpty({ message: 'Please provide valid responses' }) + @IsArray({ message: 'Responses should be array' }) + @ValidateNested({ each: true }) + @Type(() => ValidResponses) + validResponses: ValidResponses[]; + + @ApiProperty({ example: 'What is your name'}) + @IsNotEmpty({ message: 'question is required' }) + @IsString({ message: 'question must be a string' }) + @IsNotEmpty({ message: 'please provide valid question' }) + question: string; + + orgId: string; + connectionId: string; + tenantId: string; +} + +export class QuestionAnswerWebhookDto { + + + @ApiPropertyOptional() + @IsOptional() + id: string; + + @ApiPropertyOptional() + @IsOptional() + createdAt: string; + + @ApiPropertyOptional() + @IsOptional() + questionText: string; + + @ApiPropertyOptional() + @IsOptional() + questionDetail: string; + + @ApiPropertyOptional() + @IsOptional() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validResponses:any; + + @ApiPropertyOptional() + @IsOptional() + connectionId: string; + + @ApiPropertyOptional() + @IsOptional() + role: string; + + @ApiPropertyOptional() + @IsOptional() + signatureRequired: boolean; + + @ApiPropertyOptional() + @IsOptional() + state: boolean; + + @ApiPropertyOptional() + @IsOptional() + threadId: string; + + @ApiPropertyOptional() + @IsOptional() + updatedAt: string; + + @ApiPropertyOptional() + @IsOptional() + contextCorrelationId: string; + + @ApiPropertyOptional() + @IsOptional() + type: string; + +} \ No newline at end of file diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index 2e4f49b02..c30bbed6c 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -12,6 +12,7 @@ import { } from './interfaces/connection.interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; +import { IQuestionAnswerPayload, IQuestionPayload } from './interfaces/question-answer.interfaces'; @Controller() export class ConnectionController { @@ -76,4 +77,15 @@ export class ConnectionController { const { user, receiveInvitation, orgId } = payload; return this.connectionService.receiveInvitation(user, receiveInvitation, orgId); } + + @MessagePattern({ cmd: 'send-question' }) + async sendQuestion(payload: IQuestionPayload): Promise { + return this.connectionService.sendQuestion(payload); + } + + @MessagePattern({ cmd: 'get-question-answer-record' }) + async getQuestionAnswersRecord(payload: IQuestionAnswerPayload): Promise { + const { tenantId, orgId } = payload; + return this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + } } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 39b369c80..28734c003 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -21,6 +21,7 @@ import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; +import { IQuestionPayload } from './interfaces/question-answer.interfaces'; @Injectable() export class ConnectionService { @@ -312,6 +313,41 @@ export class ConnectionService { } } + async getQuestionAnswersRecord(tenantId: string, orgId: string): Promise { + try { + const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); + const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + const { agentEndPoint } = agentDetails; + if (!agentDetails) { + throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); + } + + const label = 'get-question-answer-record'; + const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId); + + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this._getOrgAgentApiKey(orgId); + } + const record = await this._getQuestionAnswersRecord(url, apiKey); + return record; + + + } catch (error) { + this.logger.error(`[sendQuestion] - error in get question answer record: ${error}`); + if (error && error?.status && error?.status?.message && error?.status?.message?.error) { + throw new RpcException({ + message: error?.status?.message?.error?.reason + ? error?.status?.message?.error?.reason + : error?.status?.message?.error, + statusCode: error?.status?.code + }); + } else { + throw new RpcException(error.response ? error.response : error); + } + } + } + async _getConnectionsByConnectionId( url: string, apiKey: string @@ -336,6 +372,29 @@ export class ConnectionService { }); } + async _getQuestionAnswersRecord( + url: string, + apiKey: string + ): Promise { + + const pattern = { cmd: 'agent-get-question-answer-record' }; + const payload = { url, apiKey }; + return this.connectionServiceProxy + .send(pattern, payload) + .toPromise() + .catch(error => { + this.logger.error( + `[_getQuestionAnswersRecord] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` + ); + throw new HttpException( + { + status: error.statusCode, + error: error.error?.message?.error ? error.error?.message?.error : error.error, + message: error.message + }, error.error); + }); + } + /** * Description: Fetch agent url * @param referenceId @@ -358,6 +417,51 @@ export class ConnectionService { } } + async getQuestionAnswerAgentUrl( + label: string, + orgAgentType: string, + agentEndPoint: string, + tenantId: string, + connectionId?: string + ): Promise { + try { + + let url; + switch (label) { + case 'send-question': { + url = orgAgentType === OrgAgentType.DEDICATED + ? `${agentEndPoint}${CommonConstants.URL_SEND_QUESTION}`.replace('#', connectionId) + : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_SEND_QUESTION}`.replace('#', connectionId).replace('@', tenantId) + : null; + break; + } + + case 'get-question-answer-record': { + url = orgAgentType === OrgAgentType.DEDICATED + ? `${agentEndPoint}${CommonConstants.URL_QUESTION_ANSWER_RECORD}` + : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_QUESTION_ANSWER_RECORD}`.replace('#', tenantId) + : null; + break; + } + + default: { + break; + } + } + + if (!url) { + throw new NotFoundException(ResponseMessages.issuance.error.agentUrlNotFound); + } + + return url; + } catch (error) { + this.logger.error(`Error get question answer agent Url: ${JSON.stringify(error)}`); + throw error; + } + } + async _getOrgAgentApiKey(orgId: string): Promise { const pattern = { cmd: 'get-org-agent-api-key' }; const payload = { orgId }; @@ -509,5 +613,76 @@ export class ConnectionService { }, error.error); }); } + + async _sendQuestion( + questionPayload: IQuestionPayload, + url: string, + apiKey: string + ): Promise { + + const pattern = { cmd: 'agent-send-question' }; + const payload = { questionPayload, url, apiKey }; + + return this.connectionServiceProxy + .send(pattern, payload) + .toPromise() + .catch(error => { + this.logger.error( + `[_sendQuestion] [NATS call]- error in send question : ${JSON.stringify(error)}` + ); + throw new HttpException( + { + status: error.statusCode, + error: error.error?.message?.error ? error.error?.message?.error : error.error, + message: error.message + }, error.error); + }); + + + } + + async sendQuestion(payload: IQuestionPayload): Promise { + + const { detail, validResponses, question, orgId, connectionId} = payload; + try { + + const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); + + const { agentEndPoint} = agentDetails; + + if (!agentDetails) { + throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); + } + + const questionPayload = { + detail, + validResponses, + question + }; + + const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + const label = 'send-question'; + const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId, connectionId); + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this._getOrgAgentApiKey(orgId); + } + const createQuestion = await this._sendQuestion(questionPayload, url, apiKey); + return createQuestion; + + } catch (error) { + this.logger.error(`[sendQuestion] - error in sending question: ${error}`); + if (error && error?.status && error?.status?.message && error?.status?.message?.error) { + throw new RpcException({ + message: error?.status?.message?.error?.reason + ? error?.status?.message?.error?.reason + : error?.status?.message?.error, + statusCode: error?.status?.code + }); + } else { + throw new RpcException(error.response ? error.response : error); + } + } + } } diff --git a/apps/connection/src/interfaces/question-answer.interfaces.ts b/apps/connection/src/interfaces/question-answer.interfaces.ts new file mode 100644 index 000000000..a1434d2e8 --- /dev/null +++ b/apps/connection/src/interfaces/question-answer.interfaces.ts @@ -0,0 +1,16 @@ +export interface IValidResponses { + text: string; + } + export interface IQuestionPayload { + detail: string; + validResponses: IValidResponses[]; + question: string; + orgId?: string; + connectionId?: string; + tenantId?: string; + } + + export interface IQuestionAnswerPayload { + tenantId: string; + orgId: string; + } \ No newline at end of file diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index abeeebf8f..2f03c0019 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -73,7 +73,8 @@ export enum CommonConstants { URL_ISSUE_GET_CREDS_AFJ_BY_CRED_REC_ID = '/credentials', URL_OUT_OF_BAND_CREDENTIAL_OFFER = '/credentials/create-offer-oob', URL_ACCEPT_CREDENTIALS = '/credentials/accept-offer', - + URL_SEND_QUESTION = '/question-answer/question/#', + URL_QUESTION_ANSWER_RECORD = '/question-answer', // SCHEMA & CRED DEF SERVICES URL_SCHM_CREATE_SCHEMA = '/schemas', @@ -106,8 +107,10 @@ export enum CommonConstants { URL_SHAGENT_ACCEPT_OFFER = '/multi-tenancy/credentials/accept-offer/#', URL_SHAGENT_RECEIVE_INVITATION_URL = '/multi-tenancy/receive-invitation-url/#', URL_SHAGENT_RECEIVE_INVITATION = '/multi-tenancy/receive-invitation/#', - - + URL_SHAGENT_SEND_QUESTION = '/multi-tenancy/question-answer/question/#/@', + URL_SHAGENT_SEND_ANSWER = '/multi-tenancy/question-answer/answer/#/@', + URL_SHAGENT_QUESTION_ANSWER_RECORD = '/multi-tenancy/question-answer/#', + // PROOF SERVICES URL_SEND_PROOF_REQUEST = '/proofs/request-proof', URL_GET_PROOF_PRESENTATIONS = '/proofs', diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 695d8210c..ba3434b24 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -225,7 +225,9 @@ export const ResponseMessages = { create: 'Connection created successfully', receivenvitation: 'Invitation received successfully', fetchConnection: 'Connection details fetched successfully', - fetch: 'Connections details fetched successfully' + fetch: 'Connections details fetched successfully', + questionAnswerRecord: 'Question Answer record fetched successfully', + questionSend:'Question sent successfully' }, error: { exists: 'Connection is already exist', From 2e62ad5e9ebe319ad539d92c27b91b685e05984f Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 26 Feb 2024 13:08:51 +0530 Subject: [PATCH 075/231] fix: question answer protocol (#541) * feat:added question answer protocol Signed-off-by: pallavicoder * fear:added question answer webhook Signed-off-by: pallavicoder * feat:add question answer protocol inside platform Signed-off-by: pallavicoder * fix:removed extra cost variables Signed-off-by: pallavicoder * fix:removed tenant id from API Signed-off-by: pallavicoder * Merge branch 'fix:removed DTO Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder Signed-off-by: bhavanakarwade --- .../src/connection/connection.controller.ts | 11 ++++------- apps/api-gateway/src/connection/connection.service.ts | 5 ++--- .../src/connection/dtos/question-answer.dto.ts | 1 - apps/connection/src/connection.controller.ts | 7 +++---- apps/connection/src/connection.service.ts | 4 ++-- .../src/interfaces/question-answer.interfaces.ts | 5 ----- 6 files changed, 11 insertions(+), 22 deletions(-) diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index b4e7ac48b..2cb6bb804 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -111,18 +111,17 @@ export class ConnectionController { @Get('orgs/:orgId/question-answer/question/:tenantId') @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiOperation({ summary: `Get question-answer record`, description: `Get question-answer record` }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) async getQuestionAnswersRecord( - @Param('tenantId') tenantId: string, @Param('orgId') orgId: string, @Res() res: Response ): Promise { - const record = await this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + const record = await this.connectionService.getQuestionAnswersRecord(orgId); const finalResponse: IResponse = { statusCode: HttpStatus.OK, message: ResponseMessages.connection.success.questionAnswerRecord, @@ -161,14 +160,13 @@ export class ConnectionController { } @Post('/orgs/:orgId/question-answer/question/:connectionId/:tenantId') - @ApiOperation({ summary: '', description: 'question-answer/question' }) + @ApiOperation({ summary: '', description: 'send question' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) async sendQuestion( @Param('orgId') orgId: string, @Param('connectionId') connectionId: string, - @Param('tenantId') tenantId: string, @Body() questionDto: QuestionDto, @User() reqUser: IUserRequestInterface, @Res() res: Response @@ -176,7 +174,6 @@ export class ConnectionController { questionDto.orgId = orgId; questionDto.connectionId = connectionId; - questionDto.tenantId = tenantId; const questionData = await this.connectionService.sendQuestion(questionDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index f80a425b5..c01321093 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -88,11 +88,10 @@ export class ConnectionService extends BaseService { getQuestionAnswersRecord( - tenantId: string, orgId: string ): Promise { - const payload = { tenantId, orgId }; - return this.sendNatsMessage(this.connectionServiceProxy, 'get-question-answer-record', payload); + + return this.sendNatsMessage(this.connectionServiceProxy, 'get-question-answer-record', orgId); } receiveInvitationUrl( diff --git a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts index 49ceafdb7..583448183 100644 --- a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts +++ b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts @@ -33,7 +33,6 @@ export class QuestionDto { orgId: string; connectionId: string; - tenantId: string; } export class QuestionAnswerWebhookDto { diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index c30bbed6c..923a15190 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -12,7 +12,7 @@ import { } from './interfaces/connection.interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; -import { IQuestionAnswerPayload, IQuestionPayload } from './interfaces/question-answer.interfaces'; +import { IQuestionPayload } from './interfaces/question-answer.interfaces'; @Controller() export class ConnectionController { @@ -84,8 +84,7 @@ export class ConnectionController { } @MessagePattern({ cmd: 'get-question-answer-record' }) - async getQuestionAnswersRecord(payload: IQuestionAnswerPayload): Promise { - const { tenantId, orgId } = payload; - return this.connectionService.getQuestionAnswersRecord(tenantId, orgId); + async getQuestionAnswersRecord(orgId: string): Promise { + return this.connectionService.getQuestionAnswersRecord(orgId); } } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 28734c003..7dbb306f7 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -313,7 +313,7 @@ export class ConnectionService { } } - async getQuestionAnswersRecord(tenantId: string, orgId: string): Promise { + async getQuestionAnswersRecord(orgId: string): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -421,7 +421,7 @@ export class ConnectionService { label: string, orgAgentType: string, agentEndPoint: string, - tenantId: string, + tenantId?: string, connectionId?: string ): Promise { try { diff --git a/apps/connection/src/interfaces/question-answer.interfaces.ts b/apps/connection/src/interfaces/question-answer.interfaces.ts index a1434d2e8..6fcabd9ef 100644 --- a/apps/connection/src/interfaces/question-answer.interfaces.ts +++ b/apps/connection/src/interfaces/question-answer.interfaces.ts @@ -9,8 +9,3 @@ export interface IValidResponses { connectionId?: string; tenantId?: string; } - - export interface IQuestionAnswerPayload { - tenantId: string; - orgId: string; - } \ No newline at end of file From b2b4336ae813426beafc6474b4e057dfaeb59257 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 26 Feb 2024 15:45:15 +0530 Subject: [PATCH 076/231] fix: check the schema if exist or not in endorsement Signed-off-by: KulkarniShashank Signed-off-by: bhavanakarwade --- apps/ecosystem/src/ecosystem.service.ts | 55 +++++++-- .../interfaces/schema-payload.interface.ts | 5 + .../schema/repositories/schema.repository.ts | 96 ++++++++------- apps/ledger/src/schema/schema.controller.ts | 116 +++++++++++------- apps/ledger/src/schema/schema.service.ts | 26 +++- 5 files changed, 198 insertions(+), 100 deletions(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 97fb35a47..7cf98c99a 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -55,13 +55,13 @@ import { GetAllSchemaList, GetEndorsementsPayload } from '../interfaces/endorsem import { CommonConstants } from '@credebl/common/common.constant'; // eslint-disable-next-line camelcase import { -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase credential_definition, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase endorsement_transaction, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase org_agents, -// eslint-disable-next-line camelcase + // eslint-disable-next-line camelcase platform_config, schema, user @@ -679,9 +679,15 @@ export class EcosystemService { ecosystemId: string ): Promise { try { - const getEcosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); - const { name, version } = requestSchemaPayload; + const alreadySchemaExist = await this._schemaExist(version, name); + this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist)}`); + + if (alreadySchemaExist) { + throw new BadRequestException(ResponseMessages.ecosystem.error.schemaAlreadyExist); + } + + const getEcosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); if (0 === name.length) { throw new BadRequestException(ResponseMessages.schema.error.nameNotEmpty); @@ -711,7 +717,8 @@ export class EcosystemService { this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); - const existSchema = schemaRequestExist?.filter( + const existSchema = + schemaRequestExist?.filter( (schema) => schema.status === endorsementTransactionStatus.REQUESTED || schema.status === endorsementTransactionStatus.SIGNED || schema.status === endorsementTransactionStatus.SUBMITED @@ -803,6 +810,26 @@ export class EcosystemService { } } + async _schemaExist(version: string, schemaName: string): Promise { + const pattern = { cmd: 'schema-exist' }; + const payload = { version, schemaName }; + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const message = await this.ecosystemServiceProxy.send(pattern, payload).toPromise(); + return message; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + async requestCredDeffEndorsement( requestCredDefPayload: RequestCredDeffEndorsement, orgId: string, @@ -824,11 +851,13 @@ export class EcosystemService { this.ecosystemRepository.getAgentDetails(getEcosystemLeadDetails.orgId), this.ecosystemRepository.getEcosystemOrgDetailsbyId(orgId, ecosystemId) ]); - - const existsCredDef = credDefRequestExist?.filter(tag => tag.status === endorsementTransactionStatus.REQUESTED || - tag.status === endorsementTransactionStatus.SIGNED || - tag.status === endorsementTransactionStatus.SUBMITED - ) ?? []; + + const existsCredDef = + credDefRequestExist?.filter( + (tag) => tag.status === endorsementTransactionStatus.REQUESTED || + tag.status === endorsementTransactionStatus.SIGNED || + tag.status === endorsementTransactionStatus.SUBMITED + ) ?? []; if (0 < existsCredDef.length) { throw new ConflictException(ResponseMessages.ecosystem.error.credDefAlreadyExist); @@ -878,7 +907,7 @@ export class EcosystemService { // To return selective response await this.removeEndorsementTransactionFields(storeTransaction); - await new Promise(resolve => setTimeout(resolve, 5000)); + await new Promise((resolve) => setTimeout(resolve, 5000)); return storeTransaction; } else { const orgAgentType = await this.ecosystemRepository.getOrgAgentType(ecosystemMemberDetails.orgAgentTypeId); diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index 68cf783f7..fb5a3f37e 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -67,3 +67,8 @@ export interface ISchemaCredDeffSearchInterface { user: IUserRequestInterface, } +export interface ISchemaExist { + schemaName: string; + version: string; +} + diff --git a/apps/ledger/src/schema/repositories/schema.repository.ts b/apps/ledger/src/schema/repositories/schema.repository.ts index 58c782f6a..418ea2c32 100644 --- a/apps/ledger/src/schema/repositories/schema.repository.ts +++ b/apps/ledger/src/schema/repositories/schema.repository.ts @@ -2,7 +2,7 @@ import { ConflictException, Injectable, InternalServerErrorException, Logger } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; import { ledgers, org_agents, org_agents_type, organisation, schema } from '@prisma/client'; -import { ISchema, ISchemaSearchCriteria } from '../interfaces/schema-payload.interface'; +import { ISchema, ISchemaExist, ISchemaSearchCriteria } from '../interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { AgentDetails, ISchemasWithCount } from '../interfaces/schema.interface'; import { SortValue } from '@credebl/enum/enum'; @@ -12,23 +12,18 @@ import { ICredDefWithCount } from '@credebl/common/interfaces/schema.interface'; export class SchemaRepository { private readonly logger = new Logger('SchemaRepository'); - constructor( - private prisma: PrismaService - ) { } + constructor(private prisma: PrismaService) {} async saveSchema(schemaResult: ISchema): Promise { try { if (schemaResult.schema.schemaName) { - const schema = await this.schemaExists( - schemaResult.schema.schemaName, - schemaResult.schema.schemaVersion - ); + const schema = await this.schemaExists(schemaResult.schema.schemaName, schemaResult.schema.schemaVersion); const schemaLength = 0; - if (schema.length !== schemaLength) { - throw new ConflictException( - ResponseMessages.schema.error.exists, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + if (schema.length !== schemaLength) { + throw new ConflictException(ResponseMessages.schema.error.exists, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } const saveResult = await this.prisma.schema.create({ data: { @@ -96,7 +91,7 @@ export class SchemaRepository { issuerId: true }, orderBy: { - [payload.sortField]: SortValue.ASC === payload.sortBy ? 'asc' : 'desc' + [payload.sortField]: SortValue.ASC === payload.sortBy ? 'asc' : 'desc' }, take: Number(payload.pageSize), skip: (payload.pageNumber - 1) * payload.pageSize @@ -111,11 +106,10 @@ export class SchemaRepository { return { schemasCount, schemasResult }; } catch (error) { this.logger.error(`Error in getting schemas: ${error}`); - throw new InternalServerErrorException( - ResponseMessages.schema.error.failedFetchSchema, - { cause: new Error(), description: error.message } - ); - + throw new InternalServerErrorException(ResponseMessages.schema.error.failedFetchSchema, { + cause: new Error(), + description: error.message + }); } } @@ -138,11 +132,13 @@ export class SchemaRepository { } } - async getAgentType(orgId: string): Promise { + async getAgentType(orgId: string): Promise< + organisation & { + org_agents: (org_agents & { + org_agent_type: org_agents_type; + })[]; + } + > { try { const agentDetails = await this.prisma.organisation.findUnique({ where: { @@ -164,16 +160,12 @@ export class SchemaRepository { } async getSchemasCredDeffList(payload: ISchemaSearchCriteria): Promise { + const { orgId, schemaId } = payload; - const {orgId, schemaId} = payload; - try { const credDefResult = await this.prisma.credential_definition.findMany({ where: { - AND: [ - { orgId }, - { schemaLedgerId: schemaId } - ] + AND: [{ orgId }, { schemaLedgerId: schemaId }] }, select: { tag: true, @@ -190,10 +182,7 @@ export class SchemaRepository { }); const credDefCount = await this.prisma.credential_definition.count({ where: { - AND: [ - { orgId }, - { schemaLedgerId: schemaId } - ] + AND: [{ orgId }, { schemaLedgerId: schemaId }] } }); return { credDefResult, credDefCount }; @@ -202,7 +191,7 @@ export class SchemaRepository { throw error; } } - + async getAllSchemaDetails(payload: ISchemaSearchCriteria): Promise<{ schemasCount: number; schemasResult: { @@ -216,7 +205,7 @@ export class SchemaRepository { issuerId: string; orgId: string; }[]; - }> { + }> { try { const schemasResult = await this.prisma.schema.findMany({ where: { @@ -240,7 +229,7 @@ export class SchemaRepository { issuerId: true }, orderBy: { - [payload.sortField]: 'desc' === payload.sortBy ? 'desc' : 'asc' + [payload.sortField]: 'desc' === payload.sortBy ? 'desc' : 'asc' }, take: Number(payload.pageSize), skip: (payload.pageNumber - 1) * payload.pageSize @@ -265,7 +254,6 @@ export class SchemaRepository { schemaLedgerId: schemaId } }); - } catch (error) { this.logger.error(`Error in getting get schema by schema ledger id: ${error}`); throw error; @@ -274,7 +262,6 @@ export class SchemaRepository { async getOrgAgentType(orgAgentId: string): Promise { try { - const { agent } = await this.prisma.org_agents_type.findFirst({ where: { id: orgAgentId @@ -295,10 +282,37 @@ export class SchemaRepository { indyNamespace: LedgerName } }); - } catch (error) { this.logger.error(`Error in getting get schema by schema ledger id: ${error}`); throw error; } } -} \ No newline at end of file + + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + try { + return this.prisma.schema.findFirstOrThrow({ + where: { + name: payload.schemaName, + version: payload.version + } + }); + } catch (error) { + this.logger.error(`Error in getting get schema by name and version: ${error}`); + throw error; + } + } +} diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 15df46d6a..4384d4b7c 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -1,59 +1,85 @@ import { Controller } from '@nestjs/common'; import { SchemaService } from './schema.service'; import { MessagePattern } from '@nestjs/microservices'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaSearchPayload } from './interfaces/schema-payload.interface'; +import { + ISchema, + ISchemaCredDeffSearchInterface, + ISchemaExist, + ISchemaSearchPayload +} from './interfaces/schema-payload.interface'; import { schema } from '@prisma/client'; -import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; - +import { + ICredDefWithPagination, + ISchemaData, + ISchemasWithPagination +} from '@credebl/common/interfaces/schema.interface'; @Controller('schema') export class SchemaController { - constructor(private readonly schemaService: SchemaService) { } + constructor(private readonly schemaService: SchemaService) {} - @MessagePattern({ cmd: 'create-schema' }) - async createSchema(payload: ISchema): Promise { - const { schema, user, orgId } = payload; - return this.schemaService.createSchema(schema, user, orgId); - } + @MessagePattern({ cmd: 'create-schema' }) + async createSchema(payload: ISchema): Promise { + const { schema, user, orgId } = payload; + return this.schemaService.createSchema(schema, user, orgId); + } - @MessagePattern({ cmd: 'get-schema-by-id' }) - async getSchemaById(payload: ISchema): Promise { - const { schemaId, orgId } = payload; - return this.schemaService.getSchemaById(schemaId, orgId); - } + @MessagePattern({ cmd: 'get-schema-by-id' }) + async getSchemaById(payload: ISchema): Promise { + const { schemaId, orgId } = payload; + return this.schemaService.getSchemaById(schemaId, orgId); + } - @MessagePattern({ cmd: 'get-schemas' }) - async getSchemas(schemaSearch: ISchemaSearchPayload): Promise { - const { schemaSearchCriteria, orgId } = schemaSearch; - return this.schemaService.getSchemas(schemaSearchCriteria, orgId); - } + @MessagePattern({ cmd: 'get-schemas' }) + async getSchemas(schemaSearch: ISchemaSearchPayload): Promise { + const { schemaSearchCriteria, orgId } = schemaSearch; + return this.schemaService.getSchemas(schemaSearchCriteria, orgId); + } - @MessagePattern({ cmd: 'get-cred-deff-list-by-schemas-id' }) - async getcredDeffListBySchemaId(payload: ISchemaCredDeffSearchInterface): Promise { - return this.schemaService.getcredDeffListBySchemaId(payload); - } + @MessagePattern({ cmd: 'get-cred-deff-list-by-schemas-id' }) + async getcredDeffListBySchemaId(payload: ISchemaCredDeffSearchInterface): Promise { + return this.schemaService.getcredDeffListBySchemaId(payload); + } - @MessagePattern({ cmd: 'get-all-schemas' }) - async getAllSchema(schemaSearch: ISchemaSearchPayload): Promise<{ - totalItems: number; - hasNextPage: boolean; - hasPreviousPage: boolean; - nextPage: number; - previousPage: number; - lastPage: number; - data: { - createDateTime: Date; - createdBy: string; - name: string; - schemaLedgerId: string; - version: string; - attributes: string; - publisherDid: string; - issuerId: string; - }[]; - }> { - const { schemaSearchCriteria } = schemaSearch; - return this.schemaService.getAllSchema(schemaSearchCriteria); - } + @MessagePattern({ cmd: 'get-all-schemas' }) + async getAllSchema(schemaSearch: ISchemaSearchPayload): Promise<{ + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + data: { + createDateTime: Date; + createdBy: string; + name: string; + schemaLedgerId: string; + version: string; + attributes: string; + publisherDid: string; + issuerId: string; + }[]; + }> { + const { schemaSearchCriteria } = schemaSearch; + return this.schemaService.getAllSchema(schemaSearchCriteria); + } + @MessagePattern({ cmd: 'schema-exist' }) + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + return this.schemaService.schemaExist(payload); + } } diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index c8c1cd943..4c6d68fc4 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -11,7 +11,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { SchemaRepository } from './repositories/schema.repository'; import { schema } from '@prisma/client'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaPayload, ISchemaSearchCriteria } from './interfaces/schema-payload.interface'; +import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria } from './interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; @@ -481,5 +481,29 @@ export class SchemaService extends BaseService { } } + async schemaExist(payload: ISchemaExist): Promise<{ + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; + }> { + try { + const schemaExist = await this.schemaRepository.schemaExist(payload); + return schemaExist; + + } catch (error) { + this.logger.error(`Error in schema exist: ${error}`); + throw new RpcException(error.response ? error.response : error); + } + } } From 810e1b2f118c54181b89f4e04fa65ef1b41303f6 Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:56:47 +0530 Subject: [PATCH 077/231] fix: question answer protocol (#542) * feat:added question answer protocol Signed-off-by: pallavicoder * fear:added question answer webhook Signed-off-by: pallavicoder * feat:add question answer protocol inside platform Signed-off-by: pallavicoder * fix:removed extra cost variables Signed-off-by: pallavicoder * fix:removed tenant id from API Signed-off-by: pallavicoder * Merge branch 'fix:removed DTO Signed-off-by: pallavicoder * fix:removed tenant id from url Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder Signed-off-by: bhavanakarwade --- apps/api-gateway/src/connection/connection.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 2cb6bb804..686b97075 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -109,7 +109,7 @@ export class ConnectionController { } - @Get('orgs/:orgId/question-answer/question/:tenantId') + @Get('orgs/:orgId/question-answer/question') @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiOperation({ @@ -159,7 +159,7 @@ export class ConnectionController { } - @Post('/orgs/:orgId/question-answer/question/:connectionId/:tenantId') + @Post('/orgs/:orgId/question-answer/question/:connectionId') @ApiOperation({ summary: '', description: 'send question' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) From 763be929edfc5e303a13823364f70246b0a5b35b Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 26 Feb 2024 16:44:11 +0530 Subject: [PATCH 078/231] fix: solved issues for the schema exist query Signed-off-by: KulkarniShashank Signed-off-by: bhavanakarwade --- apps/ecosystem/src/ecosystem.service.ts | 4 ++-- apps/ledger/src/schema/repositories/schema.repository.ts | 4 ++-- apps/ledger/src/schema/schema.controller.ts | 2 +- apps/ledger/src/schema/schema.service.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 7cf98c99a..be222c79c 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -681,9 +681,9 @@ export class EcosystemService { try { const { name, version } = requestSchemaPayload; const alreadySchemaExist = await this._schemaExist(version, name); - this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist)}`); + this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist.length)}`); - if (alreadySchemaExist) { + if (0 !== alreadySchemaExist.length) { throw new BadRequestException(ResponseMessages.ecosystem.error.schemaAlreadyExist); } diff --git a/apps/ledger/src/schema/repositories/schema.repository.ts b/apps/ledger/src/schema/repositories/schema.repository.ts index 418ea2c32..bd84b2785 100644 --- a/apps/ledger/src/schema/repositories/schema.repository.ts +++ b/apps/ledger/src/schema/repositories/schema.repository.ts @@ -302,9 +302,9 @@ export class SchemaRepository { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { try { - return this.prisma.schema.findFirstOrThrow({ + return this.prisma.schema.findMany({ where: { name: payload.schemaName, version: payload.version diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 4384d4b7c..68003ac49 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -79,7 +79,7 @@ export class SchemaController { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { return this.schemaService.schemaExist(payload); } } diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 4c6d68fc4..34ffde1ca 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -495,7 +495,7 @@ export class SchemaService extends BaseService { issuerId: string; orgId: string; ledgerId: string; - }> { + }[]> { try { const schemaExist = await this.schemaRepository.schemaExist(payload); return schemaExist; From f15de475ef477acafe1c8284a11406f20adbe378 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Mon, 26 Feb 2024 19:04:40 +0530 Subject: [PATCH 079/231] fix: extra attribute in issuance payload Signed-off-by: pranalidhanavade Signed-off-by: bhavanakarwade --- apps/issuance/interfaces/issuance.interfaces.ts | 2 +- apps/issuance/src/issuance.service.ts | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 829984113..bad565712 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -5,9 +5,9 @@ import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-se export interface IAttributes { attributeName: string; - isRequired: boolean; name: string; value: string; + isRequired?: boolean; } export interface IIssuance { user?: IUserRequest; diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 41cfaa105..dcaa60e80 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -110,8 +110,10 @@ export class IssuanceService { connectionId, credentialFormats: { indy: { - attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId + } }, autoAcceptCredential: payload.autoAcceptCredential || 'always', @@ -198,7 +200,8 @@ export class IssuanceService { protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { - attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId } }, @@ -449,7 +452,6 @@ const credefError = []; `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` ); } - // }); }); @@ -490,7 +492,8 @@ const credefError = []; protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { - attributes: iterator.attributes || attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (iterator.attributes || attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId } }, From 576f8849e3d2a2effe9ced1c1d3e658fa057e5c1 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:17:12 +0530 Subject: [PATCH 080/231] Create new connection invitation (#546) * feat: modify connection invitation url Signed-off-by: bhavanakarwade * changed message pattern Signed-off-by: bhavanakarwade * fix: optimized conditions Signed-off-by: bhavanakarwade * fix: cred-def restrictions Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- .../src/issuance/dtos/issuance.dto.ts | 8 +++++++ .../interfaces/issuance.interfaces.ts | 21 ++++++++++++++++++- apps/issuance/src/issuance.repository.ts | 6 +++++- apps/issuance/src/issuance.service.ts | 20 +++++++++++++----- .../src/interfaces/verification.interface.ts | 1 + apps/verification/src/verification.service.ts | 17 +++++++++++---- libs/common/src/response-messages/index.ts | 2 +- 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 5b7957137..51dc9b192 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -71,6 +71,12 @@ class CredentialsIssuanceDto { @IsString({ message: 'label should be string' }) label?: string; + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'please provide valid imageUrl' }) + @IsString({ message: 'imageUrl must be a string' }) + imageUrl?: string; + @ApiPropertyOptional() @IsOptional() @IsString({ message: 'auto accept proof must be in string' }) @@ -238,6 +244,8 @@ export class OOBCredentialDtoWithEmail { @IsString({ message: 'protocol version should be string' }) protocolVersion?: string; + imageUrl?: string; + orgId: string; } diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index bad565712..d7ccbff71 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -1,6 +1,7 @@ // eslint-disable-next-line camelcase import { AutoAccept } from '@credebl/enum/enum'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; +import { organisation } from '@prisma/client'; import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-service.interface'; export interface IAttributes { @@ -142,7 +143,8 @@ export interface OutOfBandCredentialOfferPayload { goalCode?: string, parentThreadId?: string, willConfirm?: boolean, - label?: string + label?: string, + imageUrl?: string, autoAcceptCredential?: string; } @@ -209,3 +211,20 @@ export interface IIssuedCredentialsSearchCriteria { searchByText: string; user?: IUserRequestInterface; } + +export interface OrgAgent { + organisation: organisation; + id: string; + createDateTime: Date; + createdBy: string; + lastChangedDateTime: Date; + lastChangedBy: string; + orgDid: string; + verkey: string; + agentEndPoint: string; + agentId: string; + isDidPublic: boolean; + ledgerId: string; + orgAgentTypeId: string; + tenantId: string; +} diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 6f6efc02b..3b8307f22 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -14,6 +14,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { FileUploadData, IssueCredentialWebhookPayload, + OrgAgent, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; @@ -34,11 +35,14 @@ export class IssuanceRepository { * @returns Get getAgentEndPoint details */ // eslint-disable-next-line camelcase - async getAgentEndPoint(orgId: string): Promise { + async getAgentEndPoint(orgId: string): Promise { try { const agentDetails = await this.prisma.org_agents.findFirst({ where: { orgId + }, + include: { + organisation: true } }); diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index dcaa60e80..1800534ed 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -180,7 +180,8 @@ export class IssuanceService { const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); // eslint-disable-next-line camelcase - const { agentEndPoint } = agentDetails; + const { agentEndPoint, organisation } = agentDetails; + if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } @@ -209,7 +210,8 @@ export class IssuanceService { goalCode: payload.goalCode || undefined, parentThreadId: payload.parentThreadId || undefined, willConfirm: payload.willConfirm || undefined, - label: payload.label || undefined, + imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, + label: organisation?.name, comment: comment || '' }; const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); @@ -459,9 +461,10 @@ const credefError = []; throw new BadRequestException(credefError); } } - - + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); + + const { organisation } = agentDetails; if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } @@ -502,7 +505,8 @@ const credefError = []; goalCode: outOfBandCredential.goalCode || undefined, parentThreadId: outOfBandCredential.parentThreadId || undefined, willConfirm: outOfBandCredential.willConfirm || undefined, - label: outOfBandCredential.label || undefined + label: organisation?.name, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl }; this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); @@ -1093,13 +1097,19 @@ const credefError = []; fileUploadData.createDateTime = new Date(); fileUploadData.referenceId = jobDetails.data.email; fileUploadData.jobId = jobDetails.id; + const {orgId} = jobDetails; + + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); + // eslint-disable-next-line camelcase + const { organisation } = agentDetails; let isErrorOccurred = false; try { const oobIssuancepayload = { credentialDefinitionId: jobDetails.credentialDefinitionId, orgId: jobDetails.orgId, + label: organisation?.name, attributes: [], emailId: jobDetails.data.email }; diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 082fe897f..ff26c3ab9 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -98,6 +98,7 @@ export interface ISendProofRequestPayload { goalCode?: string; parentThreadId?: string; willConfirm?: boolean; + imageUrl?: string; } export interface IProofRequestPayload { diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index c910fecf3..b8fc4c9e9 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -342,11 +342,14 @@ export class VerificationService { // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); - const [getAgentDetails] = await Promise.all([ + const [getAgentDetails, getOrganization] = await Promise.all([ this.verificationRepository.getAgentEndPoint(user.orgId), this.verificationRepository.getOrganization(user.orgId) ]); + const imageUrl = getOrganization?.logoUrl; + outOfBandRequestProof['imageUrl'] = imageUrl; + const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethodLabel = 'create-request-out-of-band'; @@ -514,8 +517,8 @@ export class VerificationService { try { let requestedAttributes = {}; const requestedPredicates = {}; - const attributeWithSchemaIdExists = proofRequestpayload.attributes; - if (attributeWithSchemaIdExists) { + const {attributes} = proofRequestpayload; + if (attributes) { requestedAttributes = Object.fromEntries(proofRequestpayload.attributes.map((attribute, index) => { const attributeElement = attribute.attributeName; @@ -525,7 +528,13 @@ export class VerificationService { return [ attributeReferent, { - name: attributeElement + name: attributeElement, + restrictions: [ + { + cred_def_id: proofRequestpayload.attributes[index].credDefId ? proofRequestpayload.attributes[index].credDefId : undefined, + schema_id: proofRequestpayload.attributes[index].schemaId + } + ] } ]; } else { diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index ba3434b24..436d90506 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -407,5 +407,5 @@ export const ResponseMessages = { notFound: 'Notification record not found.', invalidUrl: 'Invalid URL' } - }, + } }; \ No newline at end of file From 4de110a639fcc2543b1813cda5cbaab9a1137baf Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 27 Feb 2024 14:08:12 +0530 Subject: [PATCH 081/231] fix: solved verification predicates values Signed-off-by: KulkarniShashank Signed-off-by: bhavanakarwade --- apps/verification/src/verification.service.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index b8fc4c9e9..10c1d494a 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -687,8 +687,6 @@ export class VerificationService { const extractedDataArray: IProofPresentationDetails[] = []; if (0 !== Object.keys(requestedAttributes).length && 0 !== Object.keys(requestedPredicates).length) { - - for (const key in requestedAttributes) { if (requestedAttributes.hasOwnProperty(key)) { @@ -699,10 +697,12 @@ export class VerificationService { credDefId = requestedAttributeKey?.restrictions[0]?.cred_def_id; schemaId = requestedAttributeKey?.restrictions[0]?.schema_id; + } else if (getProofPresentationById?.response?.presentation?.indy?.identifiers) { credDefId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].cred_def_id; schemaId = getProofPresentationById?.response?.presentation?.indy?.identifiers[0].schema_id; + } if (revealedAttrs.hasOwnProperty(key)) { @@ -717,11 +717,16 @@ export class VerificationService { } for (const key in requestedPredicates) { + if (requestedPredicates.hasOwnProperty(key)) { const attribute = requestedPredicates[key]; + const attributeName = attribute?.name; - const credDefId = attribute?.restrictions[0]?.cred_def_id; - const schemaId = attribute?.restrictions[0]?.schema_id; + + if (attribute?.restrictions) { + credDefId = attribute?.restrictions[0]?.cred_def_id; + schemaId = attribute?.restrictions[0]?.schema_id; + } const extractedData: IProofPresentationDetails = { [attributeName]: `${attribute?.p_type}${attribute?.p_value}`, @@ -755,6 +760,7 @@ export class VerificationService { } } } else if (0 !== Object.keys(requestedPredicates).length) { + for (const key in requestedPredicates) { if (requestedPredicates.hasOwnProperty(key)) { @@ -762,9 +768,8 @@ export class VerificationService { const attributeName = attribute?.name; [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); - const extractedData: IProofPresentationDetails = { - [attributeName]: `${requestedPredicates?.p_type}${requestedPredicates?.p_value}`, + [attributeName]: `${attribute?.p_type}${attribute?.p_value}`, 'credDefId': credDefId || null, 'schemaId': schemaId || null }; From 037b92bc9ef40fdf696c3fd4386d943b88b3ce50 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 27 Feb 2024 22:29:10 +0530 Subject: [PATCH 082/231] fix: add restriction for predicates Signed-off-by: KulkarniShashank --- apps/verification/src/verification.service.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index bbe73f485..9322b2acd 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -348,7 +348,10 @@ export class VerificationService { ]); const imageUrl = getOrganization?.logoUrl; + const label = getOrganization?.name; + outOfBandRequestProof['imageUrl'] = imageUrl; + outOfBandRequestProof['label'] = label; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); @@ -520,10 +523,10 @@ export class VerificationService { const {attributes} = proofRequestpayload; if (attributes) { requestedAttributes = Object.fromEntries(attributes.map((attribute, index) => { - const attributeElement = attribute.attributeName || attribute.attributeNames; const attributeReferent = `additionalProp${index + 1}`; const attributeKey = attribute.attributeName ? 'name' : 'names'; + if (!attribute.condition && !attribute.value) { return [ @@ -542,7 +545,13 @@ export class VerificationService { requestedPredicates[attributeReferent] = { p_type: attribute.condition, name: attributeElement, - p_value: parseInt(attribute.value) + p_value: parseInt(attribute.value), + restrictions: [ + { + cred_def_id: proofRequestpayload.attributes[index].credDefId ? proofRequestpayload.attributes[index].credDefId : undefined, + schema_id: proofRequestpayload.attributes[index].schemaId + } + ] }; } From dc0ca1bfe5249997bf66d7e1f0dbf2e0bd8a49e7 Mon Sep 17 00:00:00 2001 From: Krishna Date: Tue, 27 Feb 2024 22:49:48 +0530 Subject: [PATCH 083/231] Update: shortening URL Signed-off-by: Krishna --- .../src/utilities/dtos/store-object.dto.ts | 219 +++++++++++++++++- .../src/utilities/utilities.controller.ts | 7 +- .../src/utilities/utilities.service.ts | 5 +- apps/connection/src/connection.service.ts | 63 +---- .../interfaces/shortening-url.interface.ts | 42 +++- apps/utility/src/utilities.controller.ts | 8 +- apps/utility/src/utilities.service.ts | 12 +- 7 files changed, 272 insertions(+), 84 deletions(-) diff --git a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts index 2c497d9f4..a305c1364 100644 --- a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts +++ b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsNotEmpty, IsOptional, IsString, IsUrl, ValidateNested } from 'class-validator'; +import { IsArray, IsNotEmpty, IsOptional, IsString, IsUrl, ValidateNested } from 'class-validator'; // export type StoreObjectDto = InvitationDto; @@ -153,6 +153,219 @@ export class LegacyInvitationDto { // services: ServiceDto[]; // } +class ServiceDto { + @ApiProperty({ + example: 'service-id' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid id' }) + id: string; + + // @ApiProperty({ + // example: 'http://example.com' + // }) + // @IsString() + // @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) + // @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) + // serviceEndpoint: string; + + @ApiProperty({ + example: 'http://example.com' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) + @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) + serviceEndpoint: string; + + @ApiProperty({ + example: 'service-type' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid type' }) + type: string; + + // @ApiProperty({ + // example: ['key1', 'key2'] + // }) + // @IsArray({groups:['']}) + // // @IsString({ each: true, message: 'Every recepient key must be a string' }) + // recipientKeys: string[]; + @ApiProperty({ + example: ['key1', 'key2'] + }) + @IsArray() + // @IsString({ each: true, message: 'Every recipient key must be a string' }) + recipientKeys: string[]; + + @ApiPropertyOptional({ + example: ['key1', 'key2'] + }) + @IsOptional() + @IsString({ each: true }) + routingKeys: string[]; + + @ApiPropertyOptional({ + example: ['true'] + }) + @IsOptional() + @IsString({ each: true }) + accept: string[]; +} + +class DataDto { + @ApiProperty({ + example: 'base64encoded' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid base64encoded in request attach' }) + 'base64': string; +} + +class RequestAttachDto { + @ApiProperty({ + example: 'your-id' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid id' }) + '@id': string; + + @ApiProperty({ + example: 'application/json' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid mime-type' }) + 'mime-type': string; + + @ApiProperty({ + example: { + 'base64': 'base64encoded' + } + }) + @Type(() => DataDto) + @IsNotEmpty({ message: 'please provide valid base64encoded' }) + data: DataDto; +} + +export class OobIssuanceInvitationDto { + + @ApiProperty({ + example: 'your-type' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid @type' }) + '@type': string; + + @ApiPropertyOptional({ + example: 'your-id' + }) + @IsOptional() + @IsString() + @IsNotEmpty({ message: 'please provide valid @id' }) + '@id': string; + + @ApiProperty({ + example: 'your-label' + }) + @IsString() + @IsNotEmpty({ message: 'please provide valid label' }) + label: string; + + // @ApiPropertyOptional({ + // example: 'http://example.com/image.jpg' + // }) + // @IsString() + // @IsOptional() + // @IsNotEmpty({ message: 'please provide valid imageUrl' }) + // @IsString() + // imageUrl?: string; + + @ApiPropertyOptional({ + example: ['accept1', 'accept2'] + }) + @IsOptional() + @IsString({ each: true }) + accept: string[]; + + @ApiPropertyOptional({ + example: ['protocol1', 'protocol2'] + }) + @IsOptional() + @IsString({ each: true }) + // eslint-disable-next-line camelcase + handshake_protocols: string[]; + + // @ApiProperty({ + // example: ['key1', 'key2'] + // }) + // @IsString({ each: true }) + // recipientKeys: string[]; + + // @ApiProperty({ + // example: 'http://example.com' + // }) + // @IsString() + // @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) + // @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) + // serviceEndpoint: string; + + // @ApiPropertyOptional({ + // example: ['key1', 'key2'] + // }) + // @IsOptional() + // @IsString({ each: true }) + // routingKeys: string[]; + + // @ApiPropertyOptional({ + // example: 'your-goal-code' + // }) + // @IsOptional() + // @IsString() + // @IsNotEmpty({ message: 'please provide valid goalCode' }) + // goalCode: string; + + // @ApiPropertyOptional({ + // example: 'your-goal' + // }) + // @IsOptional() + // @IsString() + // @IsNotEmpty({ message: 'please provide valid goal' }) + // goal: string; + + @ApiProperty( + { + 'example': [ + { + id: 'service-id', + serviceEndpoint: 'http://example.com', + type: 'service-type', + recipientKeys: ['key1', 'key2'], + routingKeys: ['key1', 'key2'], + accept: ['true'] + } + ] + } + ) + @ValidateNested({ each: true }) + @Type(() => ServiceDto) + services: ServiceDto[]; + + @ApiProperty( + { + 'example': [ + { + '@id': 'request-id', + 'mime-type': 'application/json', + 'data':{ 'base64': 'base64encoded' } + } + ] + } + ) + @ValidateNested({ each: true }) + @Type(() => RequestAttachDto) + 'requests~attach': RequestAttachDto[]; +} + + export class StoreObjectDto { @ApiProperty({ 'example': { @@ -166,6 +379,6 @@ export class StoreObjectDto { } }) @ValidateNested() - @Type(() => LegacyInvitationDto) - data: LegacyInvitationDto; + @Type(() => (LegacyInvitationDto)) + data: LegacyInvitationDto | OobIssuanceInvitationDto; } diff --git a/apps/api-gateway/src/utilities/utilities.controller.ts b/apps/api-gateway/src/utilities/utilities.controller.ts index b9a7af0bd..780100c3a 100644 --- a/apps/api-gateway/src/utilities/utilities.controller.ts +++ b/apps/api-gateway/src/utilities/utilities.controller.ts @@ -9,7 +9,8 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { UtilitiesDto } from './dtos/shortening-url.dto'; import { UtilitiesService } from './utilities.service'; -import { StoreObjectDto } from './dtos/store-object.dto'; +import { LegacyInvitationDto, OobIssuanceInvitationDto } from './dtos/store-object.dto'; +// import { StoreObjectDto } from './dtos/store-object.dto'; @UseFilters(CustomExceptionFilter) @Controller('utilities') @@ -40,9 +41,9 @@ export class UtilitiesController { @ApiOperation({ summary: 'Store an object and return a short url to it', description: 'Create a short url representing the object' }) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async storeObject(@Body() storeObjectDto: StoreObjectDto, @Param('persistent') persistent: boolean, @Res() res: Response): Promise { + async storeObject(@Body() storeObjectDto: OobIssuanceInvitationDto | LegacyInvitationDto, @Param('persistent') persistent: boolean, @Res() res: Response): Promise { // eslint-disable-next-line no-console - console.log('Reached in api-gateway controller. The object to store is::::::: ', JSON.stringify(storeObjectDto.data)); + console.log('Reached in api-gateway controller. The object to store is::::::: ', JSON.stringify(storeObjectDto)); const shorteningUrl = await this.utilitiesService.storeObject(persistent.valueOf(), storeObjectDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, diff --git a/apps/api-gateway/src/utilities/utilities.service.ts b/apps/api-gateway/src/utilities/utilities.service.ts index 5e27a6723..a853df88b 100644 --- a/apps/api-gateway/src/utilities/utilities.service.ts +++ b/apps/api-gateway/src/utilities/utilities.service.ts @@ -2,7 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { UtilitiesDto } from './dtos/shortening-url.dto'; -import { StoreObjectDto } from './dtos/store-object.dto'; +import { LegacyInvitationDto, OobIssuanceInvitationDto } from './dtos/store-object.dto'; +// import { StoreObjectDto } from './dtos/store-object.dto'; @Injectable() export class UtilitiesService extends BaseService { @@ -14,7 +15,7 @@ export class UtilitiesService extends BaseService { return this.sendNatsMessage(this.serviceProxy, 'create-shortening-url', shorteningUrlDto); } - async storeObject(persistent: boolean, storeObj: StoreObjectDto): Promise { + async storeObject(persistent: boolean, storeObj: LegacyInvitationDto | OobIssuanceInvitationDto): Promise { const payload = {persistent, storeObj}; // eslint-disable-next-line no-console console.log('Reached in api-gateway services. The object to store is::::::: ', JSON.stringify(payload.storeObj)); diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index ad45c2e93..a51b82752 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -348,41 +348,6 @@ export class ConnectionService { } } - async getQuestionAnswersRecord(orgId: string): Promise { - try { - const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); - const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); - const { agentEndPoint } = agentDetails; - if (!agentDetails) { - throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); - } - - const label = 'get-question-answer-record'; - const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId); - - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const record = await this._getQuestionAnswersRecord(url, apiKey); - return record; - - - } catch (error) { - this.logger.error(`[sendQuestion] - error in get question answer record: ${error}`); - if (error && error?.status && error?.status?.message && error?.status?.message?.error) { - throw new RpcException({ - message: error?.status?.message?.error?.reason - ? error?.status?.message?.error?.reason - : error?.status?.message?.error, - statusCode: error?.status?.code - }); - } else { - throw new RpcException(error.response ? error.response : error); - } - } - } - async _getConnectionsByConnectionId(url: string, apiKey: string): Promise { //nats call in agent service for fetch connection details const pattern = { cmd: 'agent-get-connection-details-by-connectionId' }; @@ -405,31 +370,6 @@ export class ConnectionService { }); } - async _getQuestionAnswersRecord( - url: string, - apiKey: string - ): Promise { - - const pattern = { cmd: 'agent-get-question-answer-record' }; - const payload = { url, apiKey }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch(error => { - this.logger.error( - `[_getQuestionAnswersRecord] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); - } - async _getQuestionAnswersRecord( url: string, apiKey: string @@ -453,6 +393,7 @@ export class ConnectionService { }); } + /** * Description: Fetch agent url * @param referenceId @@ -751,7 +692,7 @@ export class ConnectionService { } async storeObjectAndReturnUrl(connectionInvitation, persistent: boolean): Promise { - const utilityRequestBodyString = JSON.stringify({ data: connectionInvitation }); + const utilityRequestBodyString = JSON.stringify(connectionInvitation); const storeObj = JSON.parse(utilityRequestBodyString); //nats call in agent-service to create an invitation url diff --git a/apps/utility/interfaces/shortening-url.interface.ts b/apps/utility/interfaces/shortening-url.interface.ts index d8bdba0de..21a5fe71b 100644 --- a/apps/utility/interfaces/shortening-url.interface.ts +++ b/apps/utility/interfaces/shortening-url.interface.ts @@ -18,14 +18,14 @@ export interface IUtilities { } // export type StoreObjectDto = InvitationDto; -// interface IService { -// id: string; -// serviceEndpoint: string; -// type: string; -// recipientKeys: string[]; -// routingKeys: string[]; -// accept: string[]; -// } +interface IService { + id: string; + serviceEndpoint: string; + type: string; + recipientKeys: string[]; + routingKeys: string[]; + accept: string[]; +} export interface ILegacyInvitation { '@type': string; @@ -37,6 +37,30 @@ export interface ILegacyInvitation { routingKeys: string[] } +interface IData{ + 'base64': string +} + +interface IRequestAttach{ + '@id': string, + 'mime-type': string, + data: IData; +} + +export interface IOobIssuanceInvitation { + '@type': string; + '@id': string; + label: string; + accept: string[]; + handshake_protocols: string[]; + services: IService[]; + 'requests~attach': IRequestAttach[] + // imageUrl?: string; + // recipientKeys: string[]; + // serviceEndpoint: string; + // routingKeys: string[] +} + export interface IStoreObject { - data: ILegacyInvitation; + data: ILegacyInvitation | unknown; } \ No newline at end of file diff --git a/apps/utility/src/utilities.controller.ts b/apps/utility/src/utilities.controller.ts index c1fd1fd0c..cdaebad6e 100644 --- a/apps/utility/src/utilities.controller.ts +++ b/apps/utility/src/utilities.controller.ts @@ -1,7 +1,11 @@ import { Controller, Logger } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { UtilitiesService } from './utilities.service'; -import { IShorteningUrlData, IStoreObject } from '../interfaces/shortening-url.interface'; +import { IShorteningUrlData, + // IStoreObject, + IOobIssuanceInvitation, + ILegacyInvitation + } from '../interfaces/shortening-url.interface'; @Controller() export class UtilitiesController { @@ -18,7 +22,7 @@ export class UtilitiesController { } @MessagePattern({ cmd: 'store-object-return-url' }) - async storeObject(payload: {persistent: boolean, storeObj: IStoreObject}): Promise { + async storeObject(payload: {persistent: boolean, storeObj: IOobIssuanceInvitation | ILegacyInvitation}): Promise { try { const url:string = await this.utilitiesService.storeObject(payload); return url; diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index 10e3db107..4b87317df 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -4,7 +4,11 @@ import { UtilitiesRepository } from './utilities.repository'; import { AwsService } from '@credebl/aws'; // import { IUtilities } from '../interfaces/shortening-url.interface'; import { S3 } from 'aws-sdk'; -import { IStoreObject } from '../interfaces/shortening-url.interface'; +import { + // IStoreObject, + IOobIssuanceInvitation, + ILegacyInvitation + } from '../interfaces/shortening-url.interface'; @Injectable() export class UtilitiesService { @@ -50,12 +54,12 @@ export class UtilitiesService { } } - async storeObject(payload: {persistent: boolean, storeObj: IStoreObject}): Promise { + async storeObject(payload: {persistent: boolean, storeObj: IOobIssuanceInvitation | ILegacyInvitation}): Promise { try { // eslint-disable-next-line no-console - console.log('received here. StoreObj::::::', JSON.stringify(payload.storeObj.data)); + console.log('received here. StoreObj::::::', JSON.stringify(payload.storeObj)); const timestamp = Date.now(); - const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, timestamp, payload.storeObj.data); + const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, timestamp, payload.storeObj); const url: string = `https://${uploadResult.Bucket}.s3.${process.env.AWS_S3_STOREOBJECT_REGION}.amazonaws.com/${uploadResult.Key}`; return url; // return 'success'; From e8de841a1ccf59ee5c9cd1cd98be08a4f922c35e Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 28 Feb 2024 00:58:13 +0530 Subject: [PATCH 084/231] Added Shorten URL for Issuance Signed-off-by: Krishna --- .../src/issuance/dtos/issuance.dto.ts | 6 ++ .../src/issuance/issuance.service.ts | 7 ++- apps/issuance/src/issuance.service.ts | 60 ++++++++++++++++++- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 51dc9b192..0dea2539b 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -103,6 +103,12 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { @ArrayMinSize(1) @Type(() => Attribute) attributes: Attribute[]; + + @ApiPropertyOptional({ + example: true + }) + @IsBoolean({message: 'shortenUrl must be boolean'}) + shortenUrl: boolean; } class CredentialOffer { diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index 42b1f03ea..4533f181a 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -29,7 +29,12 @@ export class IssuanceService extends BaseService { sendCredentialOutOfBand(issueCredentialDto: OOBIssueCredentialDto): Promise<{ response: object; }> { - const payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential }; + // Krishna + // Add shortenUrl flag while constructing the payload and pass it to the microservice + // Add logic to handle it in the micoservice controller + const payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, shortenUrl: issueCredentialDto.shortenUrl }; + // eslint-disable-next-line no-console + console.log('This is the shortenUrl I\'m sending from api-ageway shortenUrl', issueCredentialDto.shortenUrl); return this.sendNats(this.issuanceProxy, 'send-credential-create-offer-oob', payload); } diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 1800534ed..d183b2774 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -148,7 +148,7 @@ export class IssuanceService { async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { - const { orgId, credentialDefinitionId, comment, attributes, protocolVersion } = payload; + const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, shortenUrl } = payload; const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( credentialDefinitionId @@ -215,7 +215,22 @@ export class IssuanceService { comment: comment || '' }; const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); - + // eslint-disable-next-line no-console + console.log('Hello I\'m here Krish');// eslint-disable-next-line no-console + console.log('This is shortenURl::::', shortenUrl.valueOf()); + // eslint-disable-next-line no-console + console.log('This is credentialCreateOfferDetails', JSON.stringify(credentialCreateOfferDetails)); + if (shortenUrl.valueOf()) { + const invitationObject = credentialCreateOfferDetails.response?.invitation; + const url: string = await this.storeObjectReturnUrl(invitationObject); //storeObjectReturnURl + credentialCreateOfferDetails.response['shortenUrl'] = url; + // credentialCreateOfferDetails.response = { shortenUrl: url, ...credentialCreateOfferDetails.response}; + } + // Krishna + // if (shortenUrl){ + // const invitationObject = credentialCreateOfferDetails?.response?. + // } + // We are getting the credentialOfferDetails here return credentialCreateOfferDetails; } catch (error) { this.logger.error(`[sendCredentialCreateOffer] - error in create credentials : ${JSON.stringify(error)}`); @@ -233,6 +248,47 @@ export class IssuanceService { } } + async storeObjectReturnUrl(connectionInvitation): Promise { + const utilityRequestBodyString = JSON.stringify(connectionInvitation); + const storeObj = JSON.parse(utilityRequestBodyString); + const persistent:boolean = false; + //nats call in agent-service to create an invitation url + const pattern = { cmd: 'store-object-return-url' }; + const payload = { persistent, storeObj }; + + try { + const message = await this.issuanceServiceProxy + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .send(pattern, payload) + .toPromise() + .catch((error) => { + this.logger.error( + `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( + error + )}` + ); + throw new HttpException( + { + status: error.statusCode, + error: error.error?.message?.error ? error.error?.message?.error : error.error, + message: error.message + }, + error.error + ); + }); + return message; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + // Created this function to avoid the impact of actual "natsCall" function for other operations // Once implement this for all component then we'll remove the duplicate function async natsCallAgent(pattern: IPattern, payload: ISendOfferNatsPayload): Promise { From 246adf8f7fd2fd1c75c1fe001568a2ced198432c Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 28 Feb 2024 13:57:12 +0530 Subject: [PATCH 085/231] Added flag name Signed-off-by: Krishna --- .../src/issuance/dtos/issuance.dto.ts | 9 +++--- .../src/issuance/issuance.service.ts | 9 ++---- .../interfaces/issuance.interfaces.ts | 30 +++++++++++++++++++ apps/issuance/src/issuance.service.ts | 27 +++++------------ 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 0dea2539b..22018d0ad 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -104,11 +104,12 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { @Type(() => Attribute) attributes: Attribute[]; - @ApiPropertyOptional({ - example: true + @ApiProperty({ + example: false }) - @IsBoolean({message: 'shortenUrl must be boolean'}) - shortenUrl: boolean; + @IsNotEmpty() + @IsBoolean({message: 'IsShortenUrl must be boolean'}) + IsShortenUrl: boolean; } class CredentialOffer { diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index 4533f181a..270e4b466 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -29,13 +29,8 @@ export class IssuanceService extends BaseService { sendCredentialOutOfBand(issueCredentialDto: OOBIssueCredentialDto): Promise<{ response: object; }> { - // Krishna - // Add shortenUrl flag while constructing the payload and pass it to the microservice - // Add logic to handle it in the micoservice controller - const payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, shortenUrl: issueCredentialDto.shortenUrl }; - // eslint-disable-next-line no-console - console.log('This is the shortenUrl I\'m sending from api-ageway shortenUrl', issueCredentialDto.shortenUrl); - return this.sendNats(this.issuanceProxy, 'send-credential-create-offer-oob', payload); + const payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, IsShortenUrl: issueCredentialDto.IsShortenUrl }; + return this.sendNatsMessage(this.issuanceProxy, 'send-credential-create-offer-oob', payload); } getIssueCredentials(issuedCredentialsSearchCriteria: IIssuedCredentialSearchParams, user: IUserRequest, orgId: string): Promise { diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index d7ccbff71..98b154eeb 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -228,3 +228,33 @@ export interface OrgAgent { orgAgentTypeId: string; tenantId: string; } + +interface IData{ + 'base64': string +} + +interface IRequestAttach{ + '@id': string, + 'mime-type': string, + data: IData; +} + +interface IService { + id: string; + serviceEndpoint: string; + type: string; + recipientKeys: string[]; + routingKeys: string[]; + accept: string[]; +} + +// This is the Interface for response of out of band issuance +export interface IOobIssuanceInvitation { + '@type': string; + '@id': string; + label: string; + accept: string[]; + handshake_protocols: string[]; + services: IService[]; + 'requests~attach': IRequestAttach[] +} \ No newline at end of file diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index d183b2774..6cccc043b 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -9,7 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs'; // import { ClientDetails, FileUploadData, ICredentialAttributesInterface, ImportFileDetails, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; -import { FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; +import { FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IOobIssuanceInvitation, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; import { OrgAgentType } from '@credebl/enum/enum'; // import { platform_config } from '@prisma/client'; import * as QRCode from 'qrcode'; @@ -148,7 +148,7 @@ export class IssuanceService { async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { - const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, shortenUrl } = payload; + const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, IsShortenUrl } = payload; const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( credentialDefinitionId @@ -215,22 +215,11 @@ export class IssuanceService { comment: comment || '' }; const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); - // eslint-disable-next-line no-console - console.log('Hello I\'m here Krish');// eslint-disable-next-line no-console - console.log('This is shortenURl::::', shortenUrl.valueOf()); - // eslint-disable-next-line no-console - console.log('This is credentialCreateOfferDetails', JSON.stringify(credentialCreateOfferDetails)); - if (shortenUrl.valueOf()) { - const invitationObject = credentialCreateOfferDetails.response?.invitation; - const url: string = await this.storeObjectReturnUrl(invitationObject); //storeObjectReturnURl - credentialCreateOfferDetails.response['shortenUrl'] = url; - // credentialCreateOfferDetails.response = { shortenUrl: url, ...credentialCreateOfferDetails.response}; + if (IsShortenUrl.valueOf()) { + const invitationObject: IOobIssuanceInvitation = credentialCreateOfferDetails.response?.invitation; + const url: string = await this.storeObjectReturnUrl(invitationObject); + credentialCreateOfferDetails.response['invitationUrl'] = url; } - // Krishna - // if (shortenUrl){ - // const invitationObject = credentialCreateOfferDetails?.response?. - // } - // We are getting the credentialOfferDetails here return credentialCreateOfferDetails; } catch (error) { this.logger.error(`[sendCredentialCreateOffer] - error in create credentials : ${JSON.stringify(error)}`); @@ -248,9 +237,7 @@ export class IssuanceService { } } - async storeObjectReturnUrl(connectionInvitation): Promise { - const utilityRequestBodyString = JSON.stringify(connectionInvitation); - const storeObj = JSON.parse(utilityRequestBodyString); + async storeObjectReturnUrl(storeObj: IOobIssuanceInvitation): Promise { const persistent:boolean = false; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; From 655cf1b43a728fc85da5118093f634fe09948c70 Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 28 Feb 2024 15:38:27 +0530 Subject: [PATCH 086/231] Remove console log Signed-off-by: Krishna --- apps/api-gateway/src/utilities/utilities.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/api-gateway/src/utilities/utilities.service.ts b/apps/api-gateway/src/utilities/utilities.service.ts index a853df88b..12b86fecf 100644 --- a/apps/api-gateway/src/utilities/utilities.service.ts +++ b/apps/api-gateway/src/utilities/utilities.service.ts @@ -17,8 +17,6 @@ export class UtilitiesService extends BaseService { async storeObject(persistent: boolean, storeObj: LegacyInvitationDto | OobIssuanceInvitationDto): Promise { const payload = {persistent, storeObj}; - // eslint-disable-next-line no-console - console.log('Reached in api-gateway services. The object to store is::::::: ', JSON.stringify(payload.storeObj)); return this.sendNatsMessage(this.serviceProxy, 'store-object-return-url', payload); } } From 037fe307b4eb414a7bdb45e98204c5d2a5b9be73 Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 28 Feb 2024 15:43:49 +0530 Subject: [PATCH 087/231] Remove console log Signed-off-by: Krishna --- apps/utility/src/utilities.service.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index 4b87317df..e5b35bdc9 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -4,6 +4,7 @@ import { UtilitiesRepository } from './utilities.repository'; import { AwsService } from '@credebl/aws'; // import { IUtilities } from '../interfaces/shortening-url.interface'; import { S3 } from 'aws-sdk'; +import { v4 as uuidv4 } from 'uuid'; import { // IStoreObject, IOobIssuanceInvitation, @@ -56,13 +57,10 @@ export class UtilitiesService { async storeObject(payload: {persistent: boolean, storeObj: IOobIssuanceInvitation | ILegacyInvitation}): Promise { try { - // eslint-disable-next-line no-console - console.log('received here. StoreObj::::::', JSON.stringify(payload.storeObj)); - const timestamp = Date.now(); - const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, timestamp, payload.storeObj); + const uuid = uuidv4(); + const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, uuid, payload.storeObj); const url: string = `https://${uploadResult.Bucket}.s3.${process.env.AWS_S3_STOREOBJECT_REGION}.amazonaws.com/${uploadResult.Key}`; return url; - // return 'success'; } catch (error) { Logger.error(error); throw new Error('An error occurred while uploading data to S3. Error::::::'); From d64d01d9c74d18d687c66fb1ae4a81bb70c41318 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Wed, 28 Feb 2024 16:01:57 +0530 Subject: [PATCH 088/231] fix: solved issue for the ledgerId restriction as a string of array Signed-off-by: KulkarniShashank --- apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts index 9204d6370..56d33e86c 100644 --- a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts @@ -25,11 +25,13 @@ export class CreateTenantDto { }) seed: string; - @ApiProperty({ example: [1] }) + @ApiProperty({ example: ["b942473d-6fdd-4a38-b76e-a3314fca66b6"] }) @ApiPropertyOptional() @IsOptional() @IsArray({ message: 'ledgerId must be an array' }) @IsNotEmpty({ message: 'please provide valid ledgerId' }) + @IsString({ each: true, message: 'Each ledgerId must be a string' }) + @MaxLength(36, { each: true, message: 'ledgerId must be at most 36 characters.' }) ledgerId?: string[]; @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) From 1c5034dcd63ff1a9adba2c81379f9f55aedbcbb1 Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 28 Feb 2024 16:29:11 +0530 Subject: [PATCH 089/231] Add uuid in aws Signed-off-by: Krishna --- libs/aws/src/aws.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/aws/src/aws.service.ts b/libs/aws/src/aws.service.ts index 9573d07db..fae4aec70 100644 --- a/libs/aws/src/aws.service.ts +++ b/libs/aws/src/aws.service.ts @@ -94,7 +94,7 @@ export class AwsService { } } - async storeObject(persistent: boolean, key: number, body: unknown): Promise { + async storeObject(persistent: boolean, key: string, body: unknown): Promise { const objKey: string = persistent.valueOf() ? `persistent/${key}` : `default/${key}`; const buf = Buffer.from(JSON.stringify(body)); const params: AWS.S3.PutObjectRequest = { From 0ba61cc78be7d0b1796030742bc082a2cf34e694 Mon Sep 17 00:00:00 2001 From: Nishad Date: Wed, 28 Feb 2024 16:44:12 +0530 Subject: [PATCH 090/231] WIP register org to keycloak and map owner Signed-off-by: Nishad --- .../organization/organization.controller.ts | 7 +--- .../src/organization/organization.service.ts | 4 +- .../repositories/organization.repository.ts | 38 +++++++++++++++++++ apps/organization/src/organization.service.ts | 36 ++++++++++++++++++ 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 4145490a8..8ea50d754 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -395,12 +395,9 @@ export class OrganizationController { description: 'Register client and map users' }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) - @Roles(OrgRoles.OWNER, OrgRoles.SUPER_ADMIN, OrgRoles.ADMIN) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @ApiBearerAuth() - async registerOrgsMapUsers(@User() user: user, @Res() res: Response): Promise { + async registerOrgsMapUsers(@Res() res: Response): Promise { - await this.organizationService.registerOrgsMapUsers(user.id); + await this.organizationService.registerOrgsMapUsers(); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, diff --git a/apps/api-gateway/src/organization/organization.service.ts b/apps/api-gateway/src/organization/organization.service.ts index 98290e009..0baf88fa0 100644 --- a/apps/api-gateway/src/organization/organization.service.ts +++ b/apps/api-gateway/src/organization/organization.service.ts @@ -148,8 +148,8 @@ export class OrganizationService extends BaseService { return this.sendNatsMessage(this.serviceProxy, 'send-invitation', payload); } - async registerOrgsMapUsers(userId: string): Promise { - const payload = {userId}; + async registerOrgsMapUsers(): Promise { + const payload = {}; return this.sendNatsMessage(this.serviceProxy, 'register-orgs-users-map', payload); } diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index 930fe04d7..05f9eebd6 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -14,6 +14,7 @@ import { UserOrgRolesService } from '@credebl/user-org-roles'; import { organisation } from '@prisma/client'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IOrganizationInvitations, IOrganization, IOrganizationDashboard} from '@credebl/common/interfaces/organization.interface'; +import { OrgRoles } from 'libs/org-roles/enums'; @Injectable() export class OrganizationRepository { @@ -478,6 +479,43 @@ export class OrganizationRepository { } } + async getUnregisteredClientOrgs(): Promise { + try { + const recordsWithNullValues = await this.prisma.organisation.findMany({ + where: { + idpId: null + }, + include: { + userOrgRoles: { + where: { + orgRole: { + name: OrgRoles.OWNER + } + }, + include: { + user: { + select: { + email: true, + username: true, + id: true, + keycloakUserId: true, + isEmailVerified: true + } + }, + orgRole: true + } + } + } + }); + + return recordsWithNullValues; + + } catch (error) { + this.logger.error(`error: ${JSON.stringify(error)}`); + throw error; + } + } + /** * * @param queryObject diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index d1da417bf..b96528b8a 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -1235,6 +1235,42 @@ export class OrganizationService { async registerOrgsMapUsers(): Promise { try { + + const unregisteredOrgsList = await this.organizationRepository.getUnregisteredClientOrgs(); + + for (const org of unregisteredOrgsList) { + const ownerUser = 0 < org['userOrgRoles'].length && org['userOrgRoles'][0].user; + const orgObj = { + id: org.id, + idpId: org.idpId, + name: org.name, + ownerId: ownerUser.id, + ownerEmail: ownerUser.email, + ownerKeycloakId: ownerUser.keycloakUserId + }; + + if (orgObj.ownerKeycloakId) { + const orgCredentials = await this.registerToKeycloak( + orgObj.name, + orgObj.id, + orgObj.ownerKeycloakId, + orgObj.ownerId, + true + ); + + const { clientId, idpId, clientSecret } = orgCredentials; + + const updateOrgData = { + clientId, + clientSecret: this.maskString(clientSecret), + idpId + }; + + const updatedOrg = await this.organizationRepository.updateOrganizationById(updateOrgData, orgObj.id); + + this.logger.log(`updatedOrg::`, updatedOrg); + } + } return ''; } catch (error) { From 4a5f48e99815342a83de94fff0c7b21bf4018cdf Mon Sep 17 00:00:00 2001 From: Nishad Date: Wed, 28 Feb 2024 17:07:49 +0530 Subject: [PATCH 091/231] removed unnecessary comments from client service Signed-off-by: Nishad --- .../src/client-registration.service.ts | 8 +------- libs/common/src/common.service.ts | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 7766e0926..b836b3422 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -181,6 +181,7 @@ export class ClientRegistrationService { const mgmtTokenResponse = await this.getToken(payload); return mgmtTokenResponse.access_token; } catch (error) { + this.logger.error(`Error in getManagementToken: ${JSON.stringify(error)}`); throw error; } @@ -471,7 +472,6 @@ export class ClientRegistrationService { async getToken(payload: ClientCredentialTokenPayloadDto) { try { - this.logger.log(`getting token : ${JSON.stringify(payload)}`); if ( 'client_credentials' !== payload.grant_type || !payload.client_id || @@ -566,9 +566,6 @@ export class ClientRegistrationService { payload.username = email; payload.password = password; - this.logger.log(`User Token Payload: ${JSON.stringify(payload)}`); - - if ( 'password' !== payload.grant_type || !payload.client_id || @@ -593,9 +590,6 @@ export class ClientRegistrationService { qs.stringify(payload) , config); - this.logger.debug( - `ClientRegistrationService token ${JSON.stringify(tokenResponse)}` - ); return tokenResponse; } catch (error) { diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index 5a34f2247..f6050f09b 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -36,7 +36,6 @@ export class CommonService { .post(url, payload, apiKey) .toPromise() .then((response: any) => { - this.logger.error(response.data); return response.data; }); } catch (error) { From a5c0fe73b45f679486fa311a41464a51f39a9f9d Mon Sep 17 00:00:00 2001 From: Nishad Date: Wed, 28 Feb 2024 17:31:40 +0530 Subject: [PATCH 092/231] removed unnecessary comments from common service Signed-off-by: Nishad --- libs/common/src/common.service.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index f6050f09b..871c2b6fa 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -27,11 +27,6 @@ export class CommonService { async httpPost(url: string, payload?: any, apiKey?: any) { try { - this.logger.debug( - `httpPost service: URL : ${url} \nAPI KEY : ${JSON.stringify( - apiKey - )} \nPAYLOAD : ${JSON.stringify(payload)}` - ); return await this.httpService .post(url, payload, apiKey) .toPromise() @@ -98,7 +93,6 @@ export class CommonService { async httpGet(url: string, config?: any) { try { - this.logger.debug(`httpGet service URL: ${url}`); return await this.httpService .get(url, config) .toPromise() @@ -162,11 +156,6 @@ export class CommonService { async httpPatch(url: string, payload?: any, apiKey?: any) { try { - this.logger.debug( - `httpPatch service: URL : ${url} \nAPI KEY : ${JSON.stringify( - apiKey - )} \nPAYLOAD : ${JSON.stringify(payload)}` - ); return await this.httpService .patch(url, payload, apiKey) .toPromise() @@ -230,7 +219,6 @@ export class CommonService { async httpDelete(url: string, config?: unknown): Promise { try { - this.logger.debug(`httpDelete service URL: ${url}`); return await this.httpService .delete(url, config) .toPromise() @@ -298,11 +286,6 @@ export class CommonService { config?: any ): Promise { try { - this.logger.debug( - `httpPut service: URL : ${url} \nCONFIG : ${JSON.stringify( - config - )} \nPAYLOAD : ${JSON.stringify(payload)}` - ); const response = await this.httpService .put(url, payload, config) .toPromise(); From 8719985b5b0c13081bd212e32fc723fd55a4093e Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 28 Feb 2024 17:39:07 +0530 Subject: [PATCH 093/231] Removed logs Signed-off-by: Krishna --- apps/api-gateway/src/utilities/utilities.controller.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/api-gateway/src/utilities/utilities.controller.ts b/apps/api-gateway/src/utilities/utilities.controller.ts index 780100c3a..3b01780e5 100644 --- a/apps/api-gateway/src/utilities/utilities.controller.ts +++ b/apps/api-gateway/src/utilities/utilities.controller.ts @@ -42,14 +42,11 @@ export class UtilitiesController { @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async storeObject(@Body() storeObjectDto: OobIssuanceInvitationDto | LegacyInvitationDto, @Param('persistent') persistent: boolean, @Res() res: Response): Promise { - // eslint-disable-next-line no-console - console.log('Reached in api-gateway controller. The object to store is::::::: ', JSON.stringify(storeObjectDto)); const shorteningUrl = await this.utilitiesService.storeObject(persistent.valueOf(), storeObjectDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, message: ResponseMessages.storeObject.success.storeObject, data: shorteningUrl - // data: 'success' }; return res.status(HttpStatus.CREATED).json(finalResponse); } From 07f006b5b42ef0aaae2cc6913330043605603321 Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 28 Feb 2024 20:37:36 +0530 Subject: [PATCH 094/231] Added sortenUrl to connection Signed-off-by: Krishna --- .../src/utilities/dtos/store-object.dto.ts | 384 ------------------ .../src/utilities/utilities.controller.ts | 8 +- .../src/utilities/utilities.service.ts | 4 +- apps/connection/src/connection.service.ts | 14 +- .../src/interfaces/connection.interfaces.ts | 3 +- .../interfaces/shortening-url.interface.ts | 48 --- apps/utility/src/utilities.controller.ts | 8 +- apps/utility/src/utilities.service.ts | 7 +- libs/aws/src/aws.service.ts | 2 +- 9 files changed, 16 insertions(+), 462 deletions(-) delete mode 100644 apps/api-gateway/src/utilities/dtos/store-object.dto.ts diff --git a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts deleted file mode 100644 index a305c1364..000000000 --- a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts +++ /dev/null @@ -1,384 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsArray, IsNotEmpty, IsOptional, IsString, IsUrl, ValidateNested } from 'class-validator'; - -// export type StoreObjectDto = InvitationDto; - -// class ServiceDto { -// @ApiProperty({ -// example: 'service-id' -// }) -// @IsString() -// @IsNotEmpty({ message: 'please provide valid id' }) -// id: string; - -// @ApiProperty({ -// example: 'http://example.com' -// }) -// @IsString() -// @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) -// @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) -// serviceEndpoint: string; - -// @ApiProperty({ -// example: 'service-type' -// }) -// @IsString() -// @IsNotEmpty({ message: 'please provide valid type' }) -// type: string; - -// @ApiProperty({ -// example: ['key1', 'key2'] -// }) -// @IsString({ each: true }) -// recipientKeys: string[]; - -// @ApiPropertyOptional({ -// example: ['key1', 'key2'] -// }) -// @IsOptional() -// @IsString({ each: true }) -// routingKeys: string[]; - -// @ApiPropertyOptional({ -// example: ['true'] -// }) -// @IsOptional() -// @IsString({ each: true }) -// accept: string[]; -// } - -export class LegacyInvitationDto { - - @ApiProperty({ - example: 'your-type' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid @type' }) - '@type': string; - - @ApiPropertyOptional({ - example: 'your-id' - }) - @IsOptional() - @IsString() - @IsNotEmpty({ message: 'please provide valid @id' }) - '@id': string; - - @ApiProperty({ - example: 'your-label' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid label' }) - label: string; - - @ApiPropertyOptional({ - example: 'http://example.com/image.jpg' - }) - @IsString() - @IsOptional() - @IsNotEmpty({ message: 'please provide valid imageUrl' }) - @IsString() - imageUrl?: string; - - @ApiProperty({ - example: ['key1', 'key2'] - }) - @IsString({ each: true }) - recipientKeys: string[]; - - @ApiProperty({ - example: 'http://example.com' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) - @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) - serviceEndpoint: string; - - @ApiPropertyOptional({ - example: ['key1', 'key2'] - }) - @IsOptional() - @IsString({ each: true }) - routingKeys: string[]; -} - -// @ApiPropertyOptional({ -// example: 'your-goal-code' -// }) -// @IsOptional() -// @IsString() -// @IsNotEmpty({ message: 'please provide valid goalCode' }) -// goalCode: string; - -// @ApiPropertyOptional({ -// example: 'your-goal' -// }) -// @IsOptional() -// @IsString() -// @IsNotEmpty({ message: 'please provide valid goal' }) -// goal: string; - -// @ApiPropertyOptional({ -// example: ['accept1', 'accept2'] -// }) -// @IsOptional() -// @IsString({ each: true }) -// accept: string[]; - -// @ApiPropertyOptional({ -// example: ['protocol1', 'protocol2'] -// }) -// @IsOptional() -// @IsString({ each: true }) -// // eslint-disable-next-line camelcase -// handshake_protocols: string[]; - -// @ApiProperty( -// // { -// // 'example': [ -// // { -// // id: 'service-id', -// // serviceEndpoint: 'http://example.com', -// // type: 'service-type', -// // recipientKeys: ['key1', 'key2'], -// // routingKeys: ['key1', 'key2'], -// // accept: ['true'] -// // } -// // ] -// // } -// ) -// @ValidateNested({ each: true }) -// @Type(() => ServiceDto) -// services: ServiceDto[]; -// } - -class ServiceDto { - @ApiProperty({ - example: 'service-id' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid id' }) - id: string; - - // @ApiProperty({ - // example: 'http://example.com' - // }) - // @IsString() - // @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) - // @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) - // serviceEndpoint: string; - - @ApiProperty({ - example: 'http://example.com' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) - @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) - serviceEndpoint: string; - - @ApiProperty({ - example: 'service-type' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid type' }) - type: string; - - // @ApiProperty({ - // example: ['key1', 'key2'] - // }) - // @IsArray({groups:['']}) - // // @IsString({ each: true, message: 'Every recepient key must be a string' }) - // recipientKeys: string[]; - @ApiProperty({ - example: ['key1', 'key2'] - }) - @IsArray() - // @IsString({ each: true, message: 'Every recipient key must be a string' }) - recipientKeys: string[]; - - @ApiPropertyOptional({ - example: ['key1', 'key2'] - }) - @IsOptional() - @IsString({ each: true }) - routingKeys: string[]; - - @ApiPropertyOptional({ - example: ['true'] - }) - @IsOptional() - @IsString({ each: true }) - accept: string[]; -} - -class DataDto { - @ApiProperty({ - example: 'base64encoded' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid base64encoded in request attach' }) - 'base64': string; -} - -class RequestAttachDto { - @ApiProperty({ - example: 'your-id' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid id' }) - '@id': string; - - @ApiProperty({ - example: 'application/json' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid mime-type' }) - 'mime-type': string; - - @ApiProperty({ - example: { - 'base64': 'base64encoded' - } - }) - @Type(() => DataDto) - @IsNotEmpty({ message: 'please provide valid base64encoded' }) - data: DataDto; -} - -export class OobIssuanceInvitationDto { - - @ApiProperty({ - example: 'your-type' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid @type' }) - '@type': string; - - @ApiPropertyOptional({ - example: 'your-id' - }) - @IsOptional() - @IsString() - @IsNotEmpty({ message: 'please provide valid @id' }) - '@id': string; - - @ApiProperty({ - example: 'your-label' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid label' }) - label: string; - - // @ApiPropertyOptional({ - // example: 'http://example.com/image.jpg' - // }) - // @IsString() - // @IsOptional() - // @IsNotEmpty({ message: 'please provide valid imageUrl' }) - // @IsString() - // imageUrl?: string; - - @ApiPropertyOptional({ - example: ['accept1', 'accept2'] - }) - @IsOptional() - @IsString({ each: true }) - accept: string[]; - - @ApiPropertyOptional({ - example: ['protocol1', 'protocol2'] - }) - @IsOptional() - @IsString({ each: true }) - // eslint-disable-next-line camelcase - handshake_protocols: string[]; - - // @ApiProperty({ - // example: ['key1', 'key2'] - // }) - // @IsString({ each: true }) - // recipientKeys: string[]; - - // @ApiProperty({ - // example: 'http://example.com' - // }) - // @IsString() - // @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) - // @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) - // serviceEndpoint: string; - - // @ApiPropertyOptional({ - // example: ['key1', 'key2'] - // }) - // @IsOptional() - // @IsString({ each: true }) - // routingKeys: string[]; - - // @ApiPropertyOptional({ - // example: 'your-goal-code' - // }) - // @IsOptional() - // @IsString() - // @IsNotEmpty({ message: 'please provide valid goalCode' }) - // goalCode: string; - - // @ApiPropertyOptional({ - // example: 'your-goal' - // }) - // @IsOptional() - // @IsString() - // @IsNotEmpty({ message: 'please provide valid goal' }) - // goal: string; - - @ApiProperty( - { - 'example': [ - { - id: 'service-id', - serviceEndpoint: 'http://example.com', - type: 'service-type', - recipientKeys: ['key1', 'key2'], - routingKeys: ['key1', 'key2'], - accept: ['true'] - } - ] - } - ) - @ValidateNested({ each: true }) - @Type(() => ServiceDto) - services: ServiceDto[]; - - @ApiProperty( - { - 'example': [ - { - '@id': 'request-id', - 'mime-type': 'application/json', - 'data':{ 'base64': 'base64encoded' } - } - ] - } - ) - @ValidateNested({ each: true }) - @Type(() => RequestAttachDto) - 'requests~attach': RequestAttachDto[]; -} - - -export class StoreObjectDto { - @ApiProperty({ - 'example': { - '@type': 'your-type', - '@id': 'your-id', - label: 'your-label', - imageUrl: 'http://example.com/image.jpg', - recipientKeys: ['key1', 'key2'], - serviceEndpoint: 'http://example.com', - routingKeys: ['key1', 'key2'] - } - }) - @ValidateNested() - @Type(() => (LegacyInvitationDto)) - data: LegacyInvitationDto | OobIssuanceInvitationDto; -} diff --git a/apps/api-gateway/src/utilities/utilities.controller.ts b/apps/api-gateway/src/utilities/utilities.controller.ts index 3b01780e5..f5f366fda 100644 --- a/apps/api-gateway/src/utilities/utilities.controller.ts +++ b/apps/api-gateway/src/utilities/utilities.controller.ts @@ -9,8 +9,6 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { UtilitiesDto } from './dtos/shortening-url.dto'; import { UtilitiesService } from './utilities.service'; -import { LegacyInvitationDto, OobIssuanceInvitationDto } from './dtos/store-object.dto'; -// import { StoreObjectDto } from './dtos/store-object.dto'; @UseFilters(CustomExceptionFilter) @Controller('utilities') @@ -37,12 +35,12 @@ export class UtilitiesController { return res.status(HttpStatus.CREATED).json(finalResponse); } - @Post('/store-object/:persistent') + @Post('/store-object/:persist') @ApiOperation({ summary: 'Store an object and return a short url to it', description: 'Create a short url representing the object' }) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async storeObject(@Body() storeObjectDto: OobIssuanceInvitationDto | LegacyInvitationDto, @Param('persistent') persistent: boolean, @Res() res: Response): Promise { - const shorteningUrl = await this.utilitiesService.storeObject(persistent.valueOf(), storeObjectDto); + async storeObject(@Body() storeObjectDto: unknown, @Param('persist') persist: boolean, @Res() res: Response): Promise { + const shorteningUrl = await this.utilitiesService.storeObject(persist.valueOf(), storeObjectDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, message: ResponseMessages.storeObject.success.storeObject, diff --git a/apps/api-gateway/src/utilities/utilities.service.ts b/apps/api-gateway/src/utilities/utilities.service.ts index 12b86fecf..08aa46f5f 100644 --- a/apps/api-gateway/src/utilities/utilities.service.ts +++ b/apps/api-gateway/src/utilities/utilities.service.ts @@ -2,8 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { UtilitiesDto } from './dtos/shortening-url.dto'; -import { LegacyInvitationDto, OobIssuanceInvitationDto } from './dtos/store-object.dto'; -// import { StoreObjectDto } from './dtos/store-object.dto'; @Injectable() export class UtilitiesService extends BaseService { @@ -15,7 +13,7 @@ export class UtilitiesService extends BaseService { return this.sendNatsMessage(this.serviceProxy, 'create-shortening-url', shorteningUrlDto); } - async storeObject(persistent: boolean, storeObj: LegacyInvitationDto | OobIssuanceInvitationDto): Promise { + async storeObject(persistent: boolean, storeObj: unknown): Promise { const payload = {persistent, storeObj}; return this.sendNatsMessage(this.serviceProxy, 'store-object-return-url', payload); } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index a51b82752..b7824af7d 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -20,6 +20,7 @@ import { OrgAgentType } from '@credebl/enum/enum'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; +// Not a valid way to import: Krishna import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IQuestionPayload } from './interfaces/question-answer.interfaces'; @@ -81,12 +82,11 @@ export class ConnectionService { apiKey = await this._getOrgAgentApiKey(orgId); } const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); - const connectionInvitaion = createConnectionInvitation?.message?.invitation; - const shortenedUrl = await this.storeObjectAndReturnUrl( - connectionInvitaion, + const connectionInvitaionUrl = createConnectionInvitation?.message?.invitationUrl; + const shortenedUrl: string = await this.storeObjectAndReturnUrl( + connectionInvitaionUrl, connectionPayload.multiUseInvitation ); - Logger.verbose('This is Invitation object::::::', createConnectionInvitation?.message?.invitation); const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, @@ -691,10 +691,8 @@ export class ConnectionService { } } - async storeObjectAndReturnUrl(connectionInvitation, persistent: boolean): Promise { - const utilityRequestBodyString = JSON.stringify(connectionInvitation); - const storeObj = JSON.parse(utilityRequestBodyString); - + async storeObjectAndReturnUrl(connectionInvitationUrl: string, persistent: boolean): Promise { + const storeObj = connectionInvitationUrl; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index 0a4d53cb2..5ba94a0eb 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -1,6 +1,7 @@ // eslint-disable-next-line camelcase import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { organisation } from '@prisma/client'; +// Not a valid way to import: Krishna import { UserRoleOrgPermsDto } from 'apps/api-gateway/src/dtos/user-role-org-perms.dto'; export interface IConnection { @@ -97,7 +98,7 @@ export interface IConnectionInvitation { } interface IInvitation { invitation: string; - + invitationUrl: string; } interface IService { diff --git a/apps/utility/interfaces/shortening-url.interface.ts b/apps/utility/interfaces/shortening-url.interface.ts index 21a5fe71b..34c8ffd2e 100644 --- a/apps/utility/interfaces/shortening-url.interface.ts +++ b/apps/utility/interfaces/shortening-url.interface.ts @@ -15,52 +15,4 @@ export interface IUtilities { credDefId: string; invitationUrl: string; attributes: IAttributes[]; -} -// export type StoreObjectDto = InvitationDto; - -interface IService { - id: string; - serviceEndpoint: string; - type: string; - recipientKeys: string[]; - routingKeys: string[]; - accept: string[]; -} - -export interface ILegacyInvitation { - '@type': string; - '@id': string; - label: string; - imageUrl?: string; - recipientKeys: string[]; - serviceEndpoint: string; - routingKeys: string[] -} - -interface IData{ - 'base64': string -} - -interface IRequestAttach{ - '@id': string, - 'mime-type': string, - data: IData; -} - -export interface IOobIssuanceInvitation { - '@type': string; - '@id': string; - label: string; - accept: string[]; - handshake_protocols: string[]; - services: IService[]; - 'requests~attach': IRequestAttach[] - // imageUrl?: string; - // recipientKeys: string[]; - // serviceEndpoint: string; - // routingKeys: string[] -} - -export interface IStoreObject { - data: ILegacyInvitation | unknown; } \ No newline at end of file diff --git a/apps/utility/src/utilities.controller.ts b/apps/utility/src/utilities.controller.ts index cdaebad6e..af20b9918 100644 --- a/apps/utility/src/utilities.controller.ts +++ b/apps/utility/src/utilities.controller.ts @@ -1,11 +1,7 @@ import { Controller, Logger } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { UtilitiesService } from './utilities.service'; -import { IShorteningUrlData, - // IStoreObject, - IOobIssuanceInvitation, - ILegacyInvitation - } from '../interfaces/shortening-url.interface'; +import { IShorteningUrlData } from '../interfaces/shortening-url.interface'; @Controller() export class UtilitiesController { @@ -22,7 +18,7 @@ export class UtilitiesController { } @MessagePattern({ cmd: 'store-object-return-url' }) - async storeObject(payload: {persistent: boolean, storeObj: IOobIssuanceInvitation | ILegacyInvitation}): Promise { + async storeObject(payload: {persistent: boolean, storeObj: unknown}): Promise { try { const url:string = await this.utilitiesService.storeObject(payload); return url; diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index e5b35bdc9..5b1c658bf 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -5,11 +5,6 @@ import { AwsService } from '@credebl/aws'; // import { IUtilities } from '../interfaces/shortening-url.interface'; import { S3 } from 'aws-sdk'; import { v4 as uuidv4 } from 'uuid'; -import { - // IStoreObject, - IOobIssuanceInvitation, - ILegacyInvitation - } from '../interfaces/shortening-url.interface'; @Injectable() export class UtilitiesService { @@ -55,7 +50,7 @@ export class UtilitiesService { } } - async storeObject(payload: {persistent: boolean, storeObj: IOobIssuanceInvitation | ILegacyInvitation}): Promise { + async storeObject(payload: {persistent: boolean, storeObj: unknown}): Promise { try { const uuid = uuidv4(); const uploadResult:S3.ManagedUpload.SendData = await this.awsService.storeObject(payload.persistent, uuid, payload.storeObj); diff --git a/libs/aws/src/aws.service.ts b/libs/aws/src/aws.service.ts index fae4aec70..f9c7cb7e4 100644 --- a/libs/aws/src/aws.service.ts +++ b/libs/aws/src/aws.service.ts @@ -95,7 +95,7 @@ export class AwsService { } async storeObject(persistent: boolean, key: string, body: unknown): Promise { - const objKey: string = persistent.valueOf() ? `persistent/${key}` : `default/${key}`; + const objKey: string = persistent.valueOf() ? `persist/${key}` : `default/${key}`; const buf = Buffer.from(JSON.stringify(body)); const params: AWS.S3.PutObjectRequest = { Bucket: process.env.AWS_S3_STOREOBJECT_BUCKET, From a4dd14adeeac06926414a50ba1021ed8d5e8a8e9 Mon Sep 17 00:00:00 2001 From: "rohit.shitre" Date: Wed, 28 Feb 2024 21:37:38 +0530 Subject: [PATCH 095/231] fix: shorten url changes Signed-off-by: rohit.shitre --- .../src/issuance/dtos/issuance.dto.ts | 15 +- .../src/issuance/issuance.service.ts | 2 +- .../src/verification/dto/request-proof.dto.ts | 22 +- apps/issuance/src/issuance.service.ts | 212 +++++++++--------- .../src/interfaces/verification.interface.ts | 9 +- apps/verification/src/verification.service.ts | 201 ++++++++++------- 6 files changed, 259 insertions(+), 202 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 22018d0ad..265cc0f8a 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -1,11 +1,11 @@ - -import { IsArray, IsNotEmpty, IsOptional, IsString, IsEmail, ArrayMaxSize, ValidateNested, ArrayMinSize, IsBoolean, IsDefined, MaxLength, IsEnum } from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { ArrayMaxSize, ArrayMinSize, IsArray, IsBoolean, IsDefined, IsEmail, IsEnum, IsNotEmpty, IsOptional, IsString, MaxLength, ValidateNested } from 'class-validator'; import { Transform, Type } from 'class-transformer'; -import { trim } from '@credebl/common/cast.helper'; -import { SortValue } from '../../enum'; -import { SortFields } from 'apps/connection/src/enum/connection.enum'; + import { AutoAccept } from '@credebl/enum/enum'; +import { SortFields } from 'apps/connection/src/enum/connection.enum'; +import { SortValue } from '../../enum'; +import { trim } from '@credebl/common/cast.helper'; class Attribute { @ApiProperty() @@ -107,9 +107,10 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { @ApiProperty({ example: false }) + @IsOptional() @IsNotEmpty() - @IsBoolean({message: 'IsShortenUrl must be boolean'}) - IsShortenUrl: boolean; + @IsBoolean({message: 'isShortenUrl must be boolean'}) + isShortenUrl?: boolean; } class CredentialOffer { diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index 270e4b466..2d4ee9134 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -29,7 +29,7 @@ export class IssuanceService extends BaseService { sendCredentialOutOfBand(issueCredentialDto: OOBIssueCredentialDto): Promise<{ response: object; }> { - const payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, IsShortenUrl: issueCredentialDto.IsShortenUrl }; + const payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, isShortenUrl: issueCredentialDto.isShortenUrl }; return this.sendNatsMessage(this.issuanceProxy, 'send-credential-create-offer-oob', payload); } diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 1a08db7e1..e7cd8abed 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,11 +1,11 @@ -import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID } from 'class-validator'; -import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, IsUUID, ValidateIf, ValidateNested } from 'class-validator'; import { Transform, Type } from 'class-transformer'; +import { toLowerCase, trim } from '@credebl/common/cast.helper'; + import { AutoAccept } from '@credebl/enum/enum'; import { IProofFormats } from '../interfaces/verification.interface'; - export class ProofRequestAttribute { @ValidateIf((obj) => obj.attributeNames === undefined) @@ -16,7 +16,7 @@ export class ProofRequestAttribute { @ValidateIf((obj) => obj.attributeName === undefined) @IsArray({ message: 'attributeNames must be an array.' }) @ArrayNotEmpty({ message: 'array can not be empty' }) - @IsString({ each: true}) + @IsString({ each: true }) @IsNotEmpty({ each: true, message: 'each element cannot be empty' }) attributeNames?: string[]; @@ -127,7 +127,7 @@ export class OutOfBandRequestProof extends ProofPayload { type: () => [ProofRequestAttribute] }) @IsArray({ message: 'attributes must be in array' }) - @ValidateNested({each: true}) + @ValidateNested({ each: true }) @IsObject({ each: true }) @IsNotEmpty({ message: 'please provide valid attributes' }) @Type(() => ProofRequestAttribute) @@ -179,11 +179,11 @@ export class SendProofRequestPayload { requested_attributes: { verifynameAddress: { names: ['name', 'address'], - restrictions: [{'schema_id': 'KU583UbI4yAKfaBTSz1rqG:2:National ID:1.0.0'}] + restrictions: [{ 'schema_id': 'KU583UbI4yAKfaBTSz1rqG:2:National ID:1.0.0' }] }, verifyBirthPlace: { name: 'Place', - restrictions: [{'schema_id': 'KU583UbI4yAKfaBTSz1rqG:2:Birth Certificate:1.0.0'}] + restrictions: [{ 'schema_id': 'KU583UbI4yAKfaBTSz1rqG:2:Birth Certificate:1.0.0' }] } }, // eslint-disable-next-line camelcase @@ -214,6 +214,12 @@ export class SendProofRequestPayload { @IsOptional() @IsUUID() @IsNotEmpty({ message: 'please provide valid parentThreadId' }) - parentThreadId: string; + parentThreadId: string; + + @ApiProperty({ example: true }) + @IsBoolean() + @IsOptional() + @IsNotEmpty({message:'Please provide the flag for shorten url.'}) + isShortenUrl?: boolean; } diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 2a674c062..64de264dd 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -65,20 +65,20 @@ export class IssuanceService { attributesArray.forEach((attribute) => { if (attribute.attributeName && attribute.isRequired) { - - payload.attributes.map((attr) => { + + payload.attributes.map((attr) => { if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { schemaResponseError.push( `Attribute ${attribute.attributeName} is required` ); } return true; - }); + }); } }); if (0 < schemaResponseError.length) { - throw new BadRequestException(schemaResponseError); - + throw new BadRequestException(schemaResponseError); + } } @@ -113,7 +113,7 @@ export class IssuanceService { // eslint-disable-next-line @typescript-eslint/no-unused-vars attributes: (attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId - + } }, autoAcceptCredential: payload.autoAcceptCredential || 'always', @@ -148,7 +148,7 @@ export class IssuanceService { async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { - const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, IsShortenUrl } = payload; + const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, isShortenUrl } = payload; const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( credentialDefinitionId @@ -160,15 +160,15 @@ export class IssuanceService { attributesArray.forEach((attribute) => { if (attribute.attributeName && attribute.isRequired) { - - payload.attributes.map((attr) => { + + payload.attributes.map((attr) => { if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { schemadetailsResponseError.push( `Attribute '${attribute.attributeName}' is required but has an empty value.` ); } return true; - }); + }); } }); if (0 < schemadetailsResponseError.length) { @@ -215,8 +215,8 @@ export class IssuanceService { comment: comment || '' }; const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); - if (IsShortenUrl.valueOf()) { - const invitationObject: IOobIssuanceInvitation = credentialCreateOfferDetails.response?.invitation; + if (isShortenUrl) { + const invitationObject: IOobIssuanceInvitation = credentialCreateOfferDetails.response?.invitationUrl; const url: string = await this.storeObjectReturnUrl(invitationObject); credentialCreateOfferDetails.response['invitationUrl'] = url; } @@ -238,14 +238,14 @@ export class IssuanceService { } async storeObjectReturnUrl(storeObj: IOobIssuanceInvitation): Promise { - const persistent:boolean = false; + const persistent: boolean = false; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; try { const message = await this.issuanceServiceProxy - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any .send(pattern, payload) .toPromise() .catch((error) => { @@ -307,8 +307,8 @@ export class IssuanceService { .pipe( map((response) => ( { - response - })) + response + })) ).toPromise() .catch(error => { this.logger.error(`catch: ${JSON.stringify(error)}`); @@ -450,61 +450,61 @@ export class IssuanceService { comment, credentialDefinitionId, orgId, - protocolVersion, + protocolVersion, attributes, emailId } = outOfBandCredential; - const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( - credentialDefinitionId - ); + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); + + let attributesArray: IAttributes[] = []; + if (schemaResponse?.attributes) { - let attributesArray:IAttributes[] = []; - if (schemaResponse?.attributes) { + attributesArray = JSON.parse(schemaResponse.attributes); + } - attributesArray = JSON.parse(schemaResponse.attributes); - } + if (0 < attributes?.length) { + const attrError = []; + attributesArray.forEach((schemaAttribute, i) => { + if (schemaAttribute.isRequired) { - if (0 < attributes?.length) { -const attrError = []; - attributesArray.forEach((schemaAttribute, i) => { - if (schemaAttribute.isRequired) { - - const attribute = attributes.find(attribute => attribute.name === schemaAttribute.attributeName); - if (!attribute?.value) { - attrError.push( - `attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` - ); - } - - } - - }); - if (0 < attrError.length) { - throw new BadRequestException(attrError); + const attribute = attributes.find(attribute => attribute.name === schemaAttribute.attributeName); + if (!attribute?.value) { + attrError.push( + `attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` + ); } + + } + + }); + if (0 < attrError.length) { + throw new BadRequestException(attrError); } - if (0 < credentialOffer?.length) { -const credefError = []; - credentialOffer.forEach((credentialAttribute, index) => { - - attributesArray.forEach((schemaAttribute, i) => { - - const attribute = credentialAttribute.attributes.find(attribute => attribute.name === schemaAttribute.attributeName); - - if (schemaAttribute.isRequired && !attribute?.value) { - credefError.push( - `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` - ); - } - - }); - }); - if (0 < credefError.length) { - throw new BadRequestException(credefError); + } + if (0 < credentialOffer?.length) { + const credefError = []; + credentialOffer.forEach((credentialAttribute, index) => { + + attributesArray.forEach((schemaAttribute, i) => { + + const attribute = credentialAttribute.attributes.find(attribute => attribute.name === schemaAttribute.attributeName); + + if (schemaAttribute.isRequired && !attribute?.value) { + credefError.push( + `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` + ); } + + }); + }); + if (0 < credefError.length) { + throw new BadRequestException(credefError); } - + } + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); const { organisation } = agentDetails; @@ -628,7 +628,7 @@ const credefError = []; const batchPromises = batch.map((iterator, index) => sendEmailForCredentialOffer(iterator, iterator.emailId, index)); emailPromises.push(Promise.all(batchPromises)); } - } else { + } else { emailPromises.push(sendEmailForCredentialOffer({}, emailId, 1)); } @@ -667,7 +667,7 @@ const credefError = []; } } - + async _outOfBandCredentialOffer(outOfBandIssuancePayload: object, url: string, apiKey: string): Promise<{ response; }> { @@ -699,8 +699,8 @@ const credefError = []; switch (issuanceMethodLabel) { case 'create-offer': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_CREATE_CRED_OFFER_AFJ}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_CREATE_CRED_OFFER_AFJ}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_OFFER}`.replace('#', tenantId) : null; break; @@ -708,8 +708,8 @@ const credefError = []; case 'create-offer-oob': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_OUT_OF_BAND_CREDENTIAL_OFFER}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_OUT_OF_BAND_CREDENTIAL_OFFER}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_OFFER_OUT_OF_BAND}`.replace('#', tenantId) : null; break; @@ -717,18 +717,18 @@ const credefError = []; case 'get-issue-credentials': { url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREDENTIALS}`.replace('#', tenantId) : null; break; } case 'get-issue-credential-by-credential-id': { - + url = orgAgentType === OrgAgentType.DEDICATED - ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ_BY_CRED_REC_ID}/${credentialRecordId}` - : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_ISSUE_GET_CREDS_AFJ_BY_CRED_REC_ID}/${credentialRecordId}` + : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREDENTIALS_BY_CREDENTIAL_ID}`.replace('#', credentialRecordId).replace('@', tenantId) : null; break; @@ -753,7 +753,7 @@ const credefError = []; async exportSchemaToCSV(credentialDefinitionId: string): Promise { try { const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails(credentialDefinitionId); - + const jsonData = []; const attributesArray = JSON.parse(schemaResponse.attributes); @@ -796,7 +796,7 @@ const credefError = []; async importAndPreviewDataForIssuance(importFileDetails: ImportFileDetails, requestId?: string): Promise { try { - + const credDefResponse = await this.issuanceRepository.getCredentialDefinitionDetails(importFileDetails.credDefId); @@ -865,8 +865,8 @@ const credefError = []; } catch (error) { this.logger.error(`error in validating credentials : ${error.response}`); - throw new RpcException(error.response ? error.response : error); - } + throw new RpcException(error.response ? error.response : error); + } } async previewFileDataForIssuance( @@ -1137,7 +1137,7 @@ const credefError = []; fileUploadData.createDateTime = new Date(); fileUploadData.referenceId = jobDetails.data.email; fileUploadData.jobId = jobDetails.id; - const {orgId} = jobDetails; + const { orgId } = jobDetails; const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); // eslint-disable-next-line camelcase @@ -1190,7 +1190,7 @@ const credefError = []; 0 === errorCount ? FileUploadStatus.completed : FileUploadStatus.partially_completed; if (!jobDetails.isRetry) { - socket.emit('bulk-issuance-process-completed', {clientId: jobDetails.clientId, fileUploadId: jobDetails.fileUploadId}); + socket.emit('bulk-issuance-process-completed', { clientId: jobDetails.clientId, fileUploadId: jobDetails.fileUploadId }); this.cacheManager.del(jobDetails.cacheId); } else { socket.emit('bulk-issuance-process-retry-completed', { clientId: jobDetails.clientId }); @@ -1242,36 +1242,36 @@ const credefError = []; } } - async validateFileData(fileData: string[][], attributesArray: { attributeName: string, schemaDataType: string, displayName: string, isRequired: boolean }[], fileHeader: string[]): Promise { - try { + async validateFileData(fileData: string[][], attributesArray: { attributeName: string, schemaDataType: string, displayName: string, isRequired: boolean }[], fileHeader: string[]): Promise { + try { const filedata = fileData.map((item: string[]) => { - const fileHeaderData = item?.map((element, j) => ({ - value: element, - header: fileHeader[j] - })); - return fileHeaderData; + const fileHeaderData = item?.map((element, j) => ({ + value: element, + header: fileHeader[j] + })); + return fileHeaderData; }); - - const errorFileData = []; - - filedata.forEach((attr, i) => { + + const errorFileData = []; + + filedata.forEach((attr, i) => { attr.forEach((eachElement) => { - attributesArray.forEach((eachItem) => { - if (eachItem.attributeName === eachElement.header) { - if (eachItem.isRequired && !eachElement.value) { - errorFileData.push(`Attribute ${eachItem.attributeName} is required at row ${i + 1}`); - } - } - }); - return eachElement; - }); - return attr; - }); - - if (0 < errorFileData.length) { - throw new BadRequestException(errorFileData); - } + attributesArray.forEach((eachItem) => { + if (eachItem.attributeName === eachElement.header) { + if (eachItem.isRequired && !eachElement.value) { + errorFileData.push(`Attribute ${eachItem.attributeName} is required at row ${i + 1}`); + } + } + }); + return eachElement; + }); + return attr; + }); + + if (0 < errorFileData.length) { + throw new BadRequestException(errorFileData); + } } catch (error) { throw error; } @@ -1288,9 +1288,9 @@ const credefError = []; } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException({ - status: error.status, - error: error.message - }, error.status); + status: error.status, + error: error.message + }, error.status); } } diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index d139576bd..86bc59e45 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -46,7 +46,7 @@ export interface IVerifiedProofData { export interface IProofPresentationData { proofId: string; - orgId: string; + orgId: string; user: IUserRequest; } @@ -100,6 +100,7 @@ export interface ISendProofRequestPayload { parentThreadId?: string; willConfirm?: boolean; imageUrl?: string; + isShortenUrl?: boolean; } export interface IProofRequestPayload { @@ -139,7 +140,7 @@ export interface IProofRequests { proofRequestsSearchCriteria: IProofRequestSearchCriteria; user: IUserRequest; orgId: string; - } +} export interface IProofRequestSearchCriteria { pageNumber: number; @@ -147,5 +148,5 @@ export interface IProofRequestSearchCriteria { sortField: string; sortBy: string; searchByText: string; - } - +} + diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 9322b2acd..9499748f4 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -81,15 +81,15 @@ export class VerificationService { data: getProofRequestsList.proofRequestsList }; - return proofPresentationsResponse; - } catch (error) { + return proofPresentationsResponse; + } catch (error) { - this.logger.error( - `[getProofRequests] [NATS call]- error in fetch proof requests details : ${JSON.stringify(error)}` - ); - throw new RpcException(error.response ? error.response : error); - } -} + this.logger.error( + `[getProofRequests] [NATS call]- error in fetch proof requests details : ${JSON.stringify(error)}` + ); + throw new RpcException(error.response ? error.response : error); + } + } /** * Consume agent API for get all proof presentations @@ -139,9 +139,9 @@ export class VerificationService { statusCode: error?.response?.status, error: errorStack }); - } else { - throw new RpcException(error.response ? error.response : error); - } + } else { + throw new RpcException(error.response ? error.response : error); + } } } @@ -233,7 +233,7 @@ export class VerificationService { } catch (error) { this.logger.error(`[verifyPresentation] - error in verify presentation : ${JSON.stringify(error)}`); this.verificationErrorHandling(error); - + } } @@ -258,20 +258,20 @@ export class VerificationService { } } - /** - * Verify proof presentation - * @param proofId - * @param orgId - * @returns Verified proof presentation details - */ - async verifyPresentation(proofId: string, orgId: string): Promise { + /** + * Verify proof presentation + * @param proofId + * @param orgId + * @returns Verified proof presentation details + */ + async verifyPresentation(proofId: string, orgId: string): Promise { try { const getAgentData = await this.verificationRepository.getAgentEndPoint(orgId); const orgAgentTypeData = await this.verificationRepository.getOrgAgentType(getAgentData?.orgAgentTypeId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethod = 'accept-presentation'; - + const url = await this.getAgentUrl(verificationMethod, orgAgentTypeData, getAgentData?.agentEndPoint, getAgentData?.tenantId, '', proofId); if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(orgId); @@ -290,8 +290,8 @@ export class VerificationService { error: errorStack }); } else { - throw new RpcException(error.response ? error.response : error); - } + throw new RpcException(error.response ? error.response : error); + } } } @@ -333,7 +333,7 @@ export class VerificationService { * @param outOfBandRequestProof * @returns Get requested proof presentation details */ - async sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { + async sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { try { this.logger.log(`-------outOfBandRequestProof------${JSON.stringify(outOfBandRequestProof)}`); @@ -357,19 +357,27 @@ export class VerificationService { let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethodLabel = 'create-request-out-of-band'; const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); - this.logger.log(`cachedApiKey----${apiKey}`); if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(user.orgId); } + const { isShortenUrl, ...updateOutOfBandRequestProof } = outOfBandRequestProof; const payload: IProofRequestPayload = { apiKey, url, - proofRequestPayload: outOfBandRequestProof + proofRequestPayload: updateOutOfBandRequestProof }; - + const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - this.logger.log(`-----getProofPresentation---${JSON.stringify(getProofPresentation)}`); + //apply presentation shorting URL + if (isShortenUrl) { + const proofRequestInvitation = getProofPresentation?.response?.invitationUrl; + const shortenedUrl = await this.storeObjectAndReturnUrl(proofRequestInvitation, false); + this.logger.log('shortenedUrl', shortenedUrl); + if (shortenedUrl) { + getProofPresentation.response.invitationUrl = shortenedUrl; + } + } if (!getProofPresentation) { throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); } @@ -390,6 +398,47 @@ export class VerificationService { } } + async storeObjectAndReturnUrl(proofRequestInvitation, persistent: boolean): Promise { + const utilityRequestBodyString = JSON.stringify(proofRequestInvitation); + const storeObj = JSON.parse(utilityRequestBodyString); + + //nats call in agent-service to create an invitation url + const pattern = { cmd: 'store-object-return-url' }; + const payload = { persistent, storeObj }; + + try { + const message = await this.verificationServiceProxy + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .send(pattern, payload) + .toPromise() + .catch((error) => { + this.logger.error( + `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( + error + )}` + ); + throw new HttpException( + { + status: error.statusCode, + error: error.error?.message?.error ? error.error?.message?.error : error.error, + message: error.message + }, + error.error + ); + }); + return message; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + private async generateOOBProofReq(payload: IProofRequestPayload, getAgentDetails: org_agents): Promise { let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); @@ -520,7 +569,7 @@ export class VerificationService { try { let requestedAttributes = {}; const requestedPredicates = {}; - const {attributes} = proofRequestpayload; + const { attributes } = proofRequestpayload; if (attributes) { requestedAttributes = Object.fromEntries(attributes.map((attribute, index) => { const attributeElement = attribute.attributeName || attribute.attributeNames; @@ -528,7 +577,7 @@ export class VerificationService { const attributeKey = attribute.attributeName ? 'name' : 'names'; if (!attribute.condition && !attribute.value) { - + return [ attributeReferent, { @@ -554,10 +603,10 @@ export class VerificationService { ] }; } - + return [attributeReferent]; })); - + return { requestedAttributes, requestedPredicates @@ -567,10 +616,10 @@ export class VerificationService { } } catch (error) { this.logger.error(`[proofRequestPayload] - error in proof request payload : ${JSON.stringify(error)}`); - throw new RpcException(error.response ? error.response : error); - - } - } + throw new RpcException(error.response ? error.response : error); + + } + } /** * Description: Fetch agent url @@ -681,13 +730,13 @@ export class VerificationService { const payload = { apiKey, url }; const getProofPresentationById = await this._getVerifiedProofDetails(payload); - + if (!getProofPresentationById?.response?.presentation) { throw new NotFoundException(ResponseMessages.verification.error.proofPresentationNotFound, { - cause: new Error(), - description: ResponseMessages.errorMessages.notFound + cause: new Error(), + description: ResponseMessages.errorMessages.notFound }); - } + } const requestedAttributes = getProofPresentationById?.response?.request?.indy?.requested_attributes; const requestedPredicates = getProofPresentationById?.response?.request?.indy?.requested_predicates; @@ -702,7 +751,7 @@ export class VerificationService { if (requestedAttributes.hasOwnProperty(key)) { const requestedAttributeKey = requestedAttributes[key]; const attributeName = requestedAttributeKey.name; - + if (requestedAttributeKey?.restrictions) { credDefId = requestedAttributeKey?.restrictions[0]?.cred_def_id; @@ -755,7 +804,7 @@ export class VerificationService { const attribute = requestedAttributes[key]; const attributeName = attribute.name; - + [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); @@ -776,7 +825,7 @@ export class VerificationService { if (requestedPredicates.hasOwnProperty(key)) { const attribute = requestedPredicates[key]; const attributeName = attribute?.name; - + [credDefId, schemaId] = await this._schemaCredDefRestriction(attribute, getProofPresentationById); const extractedData: IProofPresentationDetails = { [attributeName]: `${attribute?.p_type}${attribute?.p_value}`, @@ -788,25 +837,25 @@ export class VerificationService { } } else { throw new InternalServerErrorException(ResponseMessages.errorMessages.serverError, { - cause: new Error(), - description: ResponseMessages.errorMessages.serverError + cause: new Error(), + description: ResponseMessages.errorMessages.serverError }); } return extractedDataArray; } catch (error) { - this.logger.error(`[getVerifiedProofDetails] - error in get verified proof details : ${JSON.stringify(error)}`); - const errorStack = error?.response?.error?.reason; - - if (errorStack) { - throw new RpcException({ - message: ResponseMessages.verification.error.verifiedProofNotFound, - statusCode: error?.response?.status, - error: errorStack - }); + this.logger.error(`[getVerifiedProofDetails] - error in get verified proof details : ${JSON.stringify(error)}`); + const errorStack = error?.response?.error?.reason; + + if (errorStack) { + throw new RpcException({ + message: ResponseMessages.verification.error.verifiedProofNotFound, + statusCode: error?.response?.status, + error: errorStack + }); } else { - throw new RpcException(error.response ? error.response : error); - } + throw new RpcException(error.response ? error.response : error); } + } } async _schemaCredDefRestriction(attribute, getProofPresentationById): Promise { @@ -814,7 +863,7 @@ export class VerificationService { let schemaId; if (attribute?.restrictions) { - + credDefId = attribute?.restrictions[0]?.cred_def_id; schemaId = attribute?.restrictions[0]?.schema_id; } else if (getProofPresentationById?.response?.presentation?.indy?.identifiers) { @@ -863,7 +912,7 @@ export class VerificationService { verificationErrorHandling(error): void { if (!error && !error?.status && !error?.status?.message && !error?.status?.message?.error) { - + throw new RpcException(error.response ? error.response : error); } else { throw new RpcException({ @@ -876,23 +925,23 @@ export class VerificationService { async natsCall(pattern: object, payload: object): Promise<{ response: string; }> { - return this.verificationServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ( - { - response - })) - ) - .toPromise() - .catch(error => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException({ - status: error.statusCode, - error: error.error, - message: error.message - }, error.error); - }); - } - + return this.verificationServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ( + { + response + })) + ) + .toPromise() + .catch(error => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException({ + status: error.statusCode, + error: error.error, + message: error.message + }, error.error); + }); + } + } \ No newline at end of file From 3c1ba1072073defa1503c768c6af709abbafb686 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:02:53 +0530 Subject: [PATCH 096/231] fix: reset ports in start_agent script Signed-off-by: Krishna --- apps/agent-provisioning/AFJ/scripts/start_agent.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh index 4926428f0..fbb449988 100755 --- a/apps/agent-provisioning/AFJ/scripts/start_agent.sh +++ b/apps/agent-provisioning/AFJ/scripts/start_agent.sh @@ -21,8 +21,8 @@ INBOUND_ENDPOINT=${16} ADMIN_PORT_FILE="$PWD/apps/agent-provisioning/AFJ/port-file/last-admin-port.txt" INBOUND_PORT_FILE="$PWD/apps/agent-provisioning/AFJ/port-file/last-inbound-port.txt" -ADMIN_PORT=4001 -INBOUND_PORT=5001 +ADMIN_PORT=8001 +INBOUND_PORT=9001 increment_port() { local port="$1" From 62d76ea8ae8b1230e14e181b7f2697479a9d991b Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:05:13 +0530 Subject: [PATCH 097/231] fix: uncommented helmet Signed-off-by: Krishna --- apps/api-gateway/src/main.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/api-gateway/src/main.ts b/apps/api-gateway/src/main.ts index 606427ac4..80d22c6ad 100644 --- a/apps/api-gateway/src/main.ts +++ b/apps/api-gateway/src/main.ts @@ -10,7 +10,7 @@ import { AllExceptionsFilter } from '@credebl/common/exception-handler'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { getNatsOptions } from '@credebl/common/nats.config'; -// import helmet from 'helmet'; +import helmet from 'helmet'; import { NodeEnvironment } from '@credebl/enum/enum'; dotenv.config(); @@ -61,9 +61,9 @@ async function bootstrap(): Promise { app.use(express.static('uploadedFiles/bulk-verification-templates')); app.use(express.static('uploadedFiles/import')); app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); - // app.use(helmet({ - // xssFilter:true - // })); + app.use(helmet({ + xssFilter:true + })); await app.listen(process.env.API_GATEWAY_PORT, `${process.env.API_GATEWAY_HOST}`); Logger.log(`API Gateway is listening on port ${process.env.API_GATEWAY_PORT}`); } From aa01c005afd4cce9700b440776f4fd6495429b4b Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:09:29 +0530 Subject: [PATCH 098/231] Removed comments from connection service Signed-off-by: Krishna --- apps/connection/src/connection.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index b7824af7d..bac81ad94 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -20,7 +20,6 @@ import { OrgAgentType } from '@credebl/enum/enum'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; -// Not a valid way to import: Krishna import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IQuestionPayload } from './interfaces/question-answer.interfaces'; From cc56c0cb377825d27006052b5e2f65160d2617e8 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:13:33 +0530 Subject: [PATCH 099/231] fix: datatype for invitation variable in connection service Signed-off-by: Krishna --- apps/connection/src/connection.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index bac81ad94..cdd67c628 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -81,7 +81,7 @@ export class ConnectionService { apiKey = await this._getOrgAgentApiKey(orgId); } const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); - const connectionInvitaionUrl = createConnectionInvitation?.message?.invitationUrl; + const connectionInvitaionUrl: string = createConnectionInvitation?.message?.invitationUrl; const shortenedUrl: string = await this.storeObjectAndReturnUrl( connectionInvitaionUrl, connectionPayload.multiUseInvitation From 2d81937ab65c7550fa708279309aac0d0b3d11ad Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:17:30 +0530 Subject: [PATCH 100/231] fix: remove comments from interface Signed-off-by: Krishna --- apps/connection/src/interfaces/connection.interfaces.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index 5ba94a0eb..55442522c 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -1,7 +1,6 @@ // eslint-disable-next-line camelcase import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { organisation } from '@prisma/client'; -// Not a valid way to import: Krishna import { UserRoleOrgPermsDto } from 'apps/api-gateway/src/dtos/user-role-org-perms.dto'; export interface IConnection { From f1e3ec5313a5eda3c946e20242737dfbfaa91ade Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:21:37 +0530 Subject: [PATCH 101/231] fix: minute fixes in connection service Signed-off-by: Krishna --- apps/issuance/src/issuance.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 64de264dd..8fd87fc32 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -216,8 +216,8 @@ export class IssuanceService { }; const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); if (isShortenUrl) { - const invitationObject: IOobIssuanceInvitation = credentialCreateOfferDetails.response?.invitationUrl; - const url: string = await this.storeObjectReturnUrl(invitationObject); + const invitationUrl: IOobIssuanceInvitation = credentialCreateOfferDetails.response?.invitationUrl; + const url: string = await this.storeObjectReturnUrl(invitationUrl); credentialCreateOfferDetails.response['invitationUrl'] = url; } return credentialCreateOfferDetails; @@ -238,6 +238,7 @@ export class IssuanceService { } async storeObjectReturnUrl(storeObj: IOobIssuanceInvitation): Promise { + // Set default to false, since currently our invitation are not multi-use const persistent: boolean = false; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; From 0da8a39afa844f5eb302bec6470d16ba26fed55f Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:28:25 +0530 Subject: [PATCH 102/231] fix: remove unused Interface Signed-off-by: Krishna --- .../interfaces/shortening-url.interface.ts | 22 ++++++------------- apps/utility/src/utilities.service.ts | 1 - 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/apps/utility/interfaces/shortening-url.interface.ts b/apps/utility/interfaces/shortening-url.interface.ts index 34c8ffd2e..7772e04ad 100644 --- a/apps/utility/interfaces/shortening-url.interface.ts +++ b/apps/utility/interfaces/shortening-url.interface.ts @@ -1,18 +1,10 @@ export interface IShorteningUrlData { - referenceId: string, - schemaId: string, - credDefId: string, - invitationUrl: string, - attributes: IAttributes[] + referenceId: string; + schemaId: string; + credDefId: string; + invitationUrl: string; + attributes: IAttributes[]; } export interface IAttributes { - [key: string]: string - } - -export interface IUtilities { - credentialId: string; - schemaId: string; - credDefId: string; - invitationUrl: string; - attributes: IAttributes[]; -} \ No newline at end of file + [key: string]: string; +} diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index 5b1c658bf..7edd90934 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -2,7 +2,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { RpcException } from '@nestjs/microservices'; import { UtilitiesRepository } from './utilities.repository'; import { AwsService } from '@credebl/aws'; -// import { IUtilities } from '../interfaces/shortening-url.interface'; import { S3 } from 'aws-sdk'; import { v4 as uuidv4 } from 'uuid'; From 0ce9da5c136eb1b6f32df90510cb9710efe2b926 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:37:33 +0530 Subject: [PATCH 103/231] fix: logging error correctly using contructor object Signed-off-by: Krishna --- apps/utility/src/utilities.controller.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/utility/src/utilities.controller.ts b/apps/utility/src/utilities.controller.ts index af20b9918..f9199625d 100644 --- a/apps/utility/src/utilities.controller.ts +++ b/apps/utility/src/utilities.controller.ts @@ -5,7 +5,10 @@ import { IShorteningUrlData } from '../interfaces/shortening-url.interface'; @Controller() export class UtilitiesController { - constructor(private readonly utilitiesService: UtilitiesService) {} + constructor( + private readonly utilitiesService: UtilitiesService, + private readonly logger: Logger + ) {} @MessagePattern({ cmd: 'create-shortening-url' }) async createAndStoreShorteningUrl(payload: IShorteningUrlData): Promise { @@ -18,14 +21,13 @@ export class UtilitiesController { } @MessagePattern({ cmd: 'store-object-return-url' }) - async storeObject(payload: {persistent: boolean, storeObj: unknown}): Promise { + async storeObject(payload: { persistent: boolean; storeObj: unknown }): Promise { try { - const url:string = await this.utilitiesService.storeObject(payload); - return url; + const url: string = await this.utilitiesService.storeObject(payload); + return url; } catch (error) { - Logger.error(error); + this.logger.error(error); throw new Error('Error occured in Utility Microservices Controller'); } - } -} \ No newline at end of file +} From 3c14fca6664e7746d483cf2411b699b4f3980c68 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:39:28 +0530 Subject: [PATCH 104/231] fix: logging error correctly using contructor object Signed-off-by: Krishna --- apps/utility/src/utilities.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index 7edd90934..a1c66bdd8 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -56,7 +56,7 @@ export class UtilitiesService { const url: string = `https://${uploadResult.Bucket}.s3.${process.env.AWS_S3_STOREOBJECT_REGION}.amazonaws.com/${uploadResult.Key}`; return url; } catch (error) { - Logger.error(error); + this.logger.error(error); throw new Error('An error occurred while uploading data to S3. Error::::::'); } } From 245b0fb05e51725c0c5214241bbf6fcffe1dbae9 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:48:56 +0530 Subject: [PATCH 105/231] fix: updated object storing in verification Signed-off-by: Krishna --- apps/verification/src/verification.service.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 9499748f4..cc6bf9e8f 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -371,8 +371,8 @@ export class VerificationService { const getProofPresentation = await this._sendOutOfBandProofRequest(payload); //apply presentation shorting URL if (isShortenUrl) { - const proofRequestInvitation = getProofPresentation?.response?.invitationUrl; - const shortenedUrl = await this.storeObjectAndReturnUrl(proofRequestInvitation, false); + const proofRequestInvitation: string = getProofPresentation?.response?.invitationUrl; + const shortenedUrl: string = await this.storeObjectAndReturnUrl(proofRequestInvitation, false); this.logger.log('shortenedUrl', shortenedUrl); if (shortenedUrl) { getProofPresentation.response.invitationUrl = shortenedUrl; @@ -398,10 +398,7 @@ export class VerificationService { } } - async storeObjectAndReturnUrl(proofRequestInvitation, persistent: boolean): Promise { - const utilityRequestBodyString = JSON.stringify(proofRequestInvitation); - const storeObj = JSON.parse(utilityRequestBodyString); - + async storeObjectAndReturnUrl(storeObj: string, persistent: boolean): Promise { //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; From 724e85d1960588bff8a1f3f6e28901e355f9a8d9 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:52:49 +0530 Subject: [PATCH 106/231] fix: remove unnecessary interfaces Signed-off-by: Krishna --- .../src/interfaces/connection.interfaces.ts | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index 55442522c..326dd05ed 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -100,28 +100,6 @@ interface IInvitation { invitationUrl: string; } -interface IService { - id: string; - serviceEndpoint: string; - type: string; - recipientKeys: string[]; - routingKeys: string[]; - accept: string[]; -} - -export interface ILegacyInvitation { - '@id': string; - '@type': string; - label: string; - goalCode: string; - goal: string; - accept: string[]; - // eslint-disable-next-line camelcase - handshake_protocols: string[]; - services: IService[]; - imageUrl?: string; -} - export interface OrgAgent { organisation: organisation; id: string; From a31523cff28b015a49c16dd4816c03f2cca2794d Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 13:59:29 +0530 Subject: [PATCH 107/231] fix: remove unnecessary interfaces Signed-off-by: Krishna --- .../interfaces/issuance.interfaces.ts | 30 ------------------- apps/issuance/src/issuance.service.ts | 6 ++-- 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 98b154eeb..d7ccbff71 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -228,33 +228,3 @@ export interface OrgAgent { orgAgentTypeId: string; tenantId: string; } - -interface IData{ - 'base64': string -} - -interface IRequestAttach{ - '@id': string, - 'mime-type': string, - data: IData; -} - -interface IService { - id: string; - serviceEndpoint: string; - type: string; - recipientKeys: string[]; - routingKeys: string[]; - accept: string[]; -} - -// This is the Interface for response of out of band issuance -export interface IOobIssuanceInvitation { - '@type': string; - '@id': string; - label: string; - accept: string[]; - handshake_protocols: string[]; - services: IService[]; - 'requests~attach': IRequestAttach[] -} \ No newline at end of file diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 8fd87fc32..af2322884 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -9,7 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs'; // import { ClientDetails, FileUploadData, ICredentialAttributesInterface, ImportFileDetails, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; -import { FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IOobIssuanceInvitation, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; +import { FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; import { OrgAgentType } from '@credebl/enum/enum'; // import { platform_config } from '@prisma/client'; import * as QRCode from 'qrcode'; @@ -216,7 +216,7 @@ export class IssuanceService { }; const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); if (isShortenUrl) { - const invitationUrl: IOobIssuanceInvitation = credentialCreateOfferDetails.response?.invitationUrl; + const invitationUrl: string = credentialCreateOfferDetails.response?.invitationUrl; const url: string = await this.storeObjectReturnUrl(invitationUrl); credentialCreateOfferDetails.response['invitationUrl'] = url; } @@ -237,7 +237,7 @@ export class IssuanceService { } } - async storeObjectReturnUrl(storeObj: IOobIssuanceInvitation): Promise { + async storeObjectReturnUrl(storeObj: string): Promise { // Set default to false, since currently our invitation are not multi-use const persistent: boolean = false; //nats call in agent-service to create an invitation url From 202dd7562b9e8c779a62dd044e4cde6c04b793e2 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 14:06:01 +0530 Subject: [PATCH 108/231] fix: minor fixes in verification service Signed-off-by: Krishna --- apps/verification/src/verification.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index cc6bf9e8f..f458de014 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -371,8 +371,8 @@ export class VerificationService { const getProofPresentation = await this._sendOutOfBandProofRequest(payload); //apply presentation shorting URL if (isShortenUrl) { - const proofRequestInvitation: string = getProofPresentation?.response?.invitationUrl; - const shortenedUrl: string = await this.storeObjectAndReturnUrl(proofRequestInvitation, false); + const proofRequestInvitationUrl: string = getProofPresentation?.response?.invitationUrl; + const shortenedUrl: string = await this.storeObjectAndReturnUrl(proofRequestInvitationUrl, false); this.logger.log('shortenedUrl', shortenedUrl); if (shortenedUrl) { getProofPresentation.response.invitationUrl = shortenedUrl; From bb2c4b918c24c68a63a86d2df03e7d2aa56145d3 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 29 Feb 2024 15:06:08 +0530 Subject: [PATCH 109/231] added the dockerfile for the notification service Signed-off-by: KulkarniShashank --- Dockerfiles/Dockerfile.notification | 41 +++++++++++++++++++++++++++++ apps/notification/src/main.ts | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 Dockerfiles/Dockerfile.notification diff --git a/Dockerfiles/Dockerfile.notification b/Dockerfiles/Dockerfile.notification new file mode 100644 index 000000000..dcc67684b --- /dev/null +++ b/Dockerfiles/Dockerfile.notification @@ -0,0 +1,41 @@ +# Stage 1: Build the application +FROM node:18-alpine as build +RUN npm install -g pnpm +# Set the working directory +WORKDIR /app + +# Copy package.json and package-lock.json +COPY package.json ./ +#COPY package-lock.json ./ + +# Install dependencies +RUN pnpm i + +# Copy the rest of the application code +COPY . . +RUN cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate + +# Build the notification service +RUN npm run build notification + + +# Stage 2: Create the final image +FROM node:18-alpine +RUN npm install -g pnpm +# Set the working directory +WORKDIR /app + +# Copy the compiled code from the build stage +COPY --from=build /app/dist/apps/notification/ ./dist/apps/notification/ + +# Copy the libs folder from the build stage +COPY --from=build /app/libs/ ./libs/ +#COPY --from=build /app/package.json ./ +COPY --from=build /app/node_modules ./node_modules + +# Set the command to run the microservice +CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/notification/main.js"] + +# docker build -t notification -f Dockerfiles/Dockerfile.notification . +# docker run -d --env-file .env --name notification docker.io/library/notification +# docker logs -f notification diff --git a/apps/notification/src/main.ts b/apps/notification/src/main.ts index c41d3ac60..0b4b87bf8 100644 --- a/apps/notification/src/main.ts +++ b/apps/notification/src/main.ts @@ -18,6 +18,6 @@ async function bootstrap(): Promise { app.useGlobalFilters(new HttpExceptionFilter()); await app.listen(); - logger.log('Issuance-Service Microservice is listening to NATS '); + logger.log('Notification-Service Microservice is listening to NATS '); } bootstrap(); \ No newline at end of file From 7f6b6af911a05714e65c186263722c5b6e9063f5 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 29 Feb 2024 15:24:32 +0530 Subject: [PATCH 110/231] added the notification nkey on the main and notification module Signed-off-by: KulkarniShashank --- apps/notification/src/notification.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/notification/src/notification.module.ts b/apps/notification/src/notification.module.ts index 2383d7779..658d3a736 100644 --- a/apps/notification/src/notification.module.ts +++ b/apps/notification/src/notification.module.ts @@ -16,7 +16,7 @@ import { NotificationRepository } from './notification.repository'; { name: 'NATS_CLIENT', transport: Transport.NATS, - options: getNatsOptions(process.env.ISSUANCE_NKEY_SEED) + options: getNatsOptions(process.env.NOTIFICATION_NKEY_SEED) } ]), CommonModule, From e08c7874d73fd37727b9d5f609497cd89b701d57 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 29 Feb 2024 15:33:16 +0530 Subject: [PATCH 111/231] fix sonar cloud issues on the notification dockerfile Signed-off-by: KulkarniShashank --- Dockerfiles/Dockerfile.notification | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/Dockerfile.notification b/Dockerfiles/Dockerfile.notification index dcc67684b..eb7c78963 100644 --- a/Dockerfiles/Dockerfile.notification +++ b/Dockerfiles/Dockerfile.notification @@ -1,6 +1,6 @@ # Stage 1: Build the application FROM node:18-alpine as build -RUN npm install -g pnpm +RUN npm install -g pnpm --ignore-scripts # Set the working directory WORKDIR /app @@ -9,7 +9,7 @@ COPY package.json ./ #COPY package-lock.json ./ # Install dependencies -RUN pnpm i +RUN pnpm i --ignore-scripts # Copy the rest of the application code COPY . . @@ -21,7 +21,7 @@ RUN npm run build notification # Stage 2: Create the final image FROM node:18-alpine -RUN npm install -g pnpm +RUN npm install -g pnpm --ignore-scripts # Set the working directory WORKDIR /app From d8ee72ff5fa0c7af4ab7f58ba25bf3244d935444 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 18:03:45 +0530 Subject: [PATCH 112/231] Update: success message after storing object Signed-off-by: Krishna --- libs/common/src/response-messages/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index d54e52030..e0525ed53 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -410,7 +410,7 @@ export const ResponseMessages = { }, storeObject: { success: { - storeObject: 'Object stored successfully' + storeObject: 'Data stored successfully' } } }; \ No newline at end of file From 4737b19153c1d70e02e185aea152d4a1798c09aa Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 18:04:55 +0530 Subject: [PATCH 113/231] Update: Dto update Signed-off-by: Krishna --- apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts | 6 ++++++ apps/api-gateway/src/utilities/utilities.controller.ts | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts b/apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts index 287778e40..8098ae6c1 100644 --- a/apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts +++ b/apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts @@ -39,3 +39,9 @@ interface Attribute { name: string; value: string; } + + export class GenericDto { + @ApiProperty() + @IsNotEmpty() // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: string | object; +} \ No newline at end of file diff --git a/apps/api-gateway/src/utilities/utilities.controller.ts b/apps/api-gateway/src/utilities/utilities.controller.ts index f5f366fda..782b90c6e 100644 --- a/apps/api-gateway/src/utilities/utilities.controller.ts +++ b/apps/api-gateway/src/utilities/utilities.controller.ts @@ -7,7 +7,7 @@ import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; import { ResponseMessages } from '@credebl/common/response-messages'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; -import { UtilitiesDto } from './dtos/shortening-url.dto'; +import { GenericDto, UtilitiesDto } from './dtos/shortening-url.dto'; import { UtilitiesService } from './utilities.service'; @UseFilters(CustomExceptionFilter) @@ -38,8 +38,7 @@ export class UtilitiesController { @Post('/store-object/:persist') @ApiOperation({ summary: 'Store an object and return a short url to it', description: 'Create a short url representing the object' }) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async storeObject(@Body() storeObjectDto: unknown, @Param('persist') persist: boolean, @Res() res: Response): Promise { + async storeObject(@Body() storeObjectDto: GenericDto, @Param('persist') persist: boolean, @Res() res: Response): Promise { const shorteningUrl = await this.utilitiesService.storeObject(persist.valueOf(), storeObjectDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, From d2e0de90bcafde141546ba44ac8d8e1e430d2096 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Thu, 29 Feb 2024 18:13:57 +0530 Subject: [PATCH 114/231] fix:ecosystem error messages for endorsement flow Signed-off-by: pranalidhanavade --- .../src/issuance/issuance.controller.ts | 1 - .../verification/verification.controller.ts | 1 - apps/ecosystem/src/ecosystem.repository.ts | 20 ++++++++++++++ apps/ecosystem/src/ecosystem.service.ts | 26 +++++++++++++++---- .../credential-definition.service.ts | 3 +-- apps/verification/src/verification.service.ts | 9 +------ .../src/client-registration.service.ts | 2 -- 7 files changed, 43 insertions(+), 19 deletions(-) diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 5c58c622b..02956f16a 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -603,7 +603,6 @@ export class IssuanceController { @Res() res: Response ): Promise { issueCredentialDto.type = 'Issuance'; - this.logger.debug(`issueCredentialDto ::: ${JSON.stringify(issueCredentialDto)}`); const getCredentialDetails = await this.issueCredentialService.getIssueCredentialWebhook(issueCredentialDto, id).catch(error => { this.logger.debug(`error in saving issuance webhook ::: ${JSON.stringify(error)}`); diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index efb8570a7..b396b8b31 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -286,7 +286,6 @@ export class VerificationController { @Res() res: Response ): Promise { proofPresentationPayload.type = 'Verification'; - this.logger.debug(`proofPresentationPayload ::: ${JSON.stringify(proofPresentationPayload)}`); const webhookProofPresentation = await this.verificationService.webhookProofPresentation(orgId, proofPresentationPayload).catch(error => { this.logger.debug(`error in saving verification webhook ::: ${JSON.stringify(error)}`); diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index 98be181dc..225598d0e 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -1035,6 +1035,26 @@ export class EcosystemRepository { } } + async schemaExist(schemaName: string, schemaVersion: string): Promise { + try { + return this.prisma.schema.findMany({ + where: { + name: { + contains: schemaName, + mode: 'insensitive' + }, + version: { + contains: schemaVersion, + mode: 'insensitive' + } + } + }); + } catch (error) { + this.logger.error(`Error in schemaExists: ${error}`); + throw error; + } + } + // eslint-disable-next-line camelcase async saveCredDef(credDefResult: saveCredDef): Promise { try { diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index be222c79c..eed9f4e52 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -684,7 +684,7 @@ export class EcosystemService { this.logger.log(`alreadySchemaExist ::: ${JSON.stringify(alreadySchemaExist.length)}`); if (0 !== alreadySchemaExist.length) { - throw new BadRequestException(ResponseMessages.ecosystem.error.schemaAlreadyExist); + throw new ConflictException(ResponseMessages.ecosystem.error.schemaAlreadyExist); } const getEcosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); @@ -1321,11 +1321,13 @@ export class EcosystemService { async submitTransaction(transactionPayload: TransactionPayload): Promise { try { - const { endorsementId, ecosystemId, ecosystemLeadAgentEndPoint, orgId } = transactionPayload; + const { endorsementId, ecosystemId, ecosystemLeadAgentEndPoint, orgId } = transactionPayload; const endorsementTransactionPayload = await this.ecosystemRepository.getEndorsementTransactionById( endorsementId, endorsementTransactionStatus.SIGNED ); + + if (!endorsementTransactionPayload) { throw new InternalServerErrorException(ResponseMessages.ecosystem.error.invalidTransaction); } @@ -1353,6 +1355,19 @@ export class EcosystemService { ecosystemMemberDetails, ecosystemLeadAgentDetails ); + + const isSchemaExists = await this.ecosystemRepository.schemaExist( + payload.schema.name, + payload.schema.version + ); + + if (0 !== isSchemaExists.length) { + this.logger.error(ResponseMessages.ecosystem.error.schemaAlreadyExist); + throw new ConflictException( + ResponseMessages.ecosystem.error.schemaAlreadyExist, + { cause: new Error(), description: ResponseMessages.errorMessages.conflict } + ); + } let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { @@ -1407,7 +1422,7 @@ export class EcosystemService { throw new InternalServerErrorException(ResponseMessages.ecosystem.error.updateCredDefId); } return this.handleCredDefSubmission( - endorsementTransactionPayload, + endorsementTransactionPayload, ecosystemMemberDetails, submitTransactionRequest ); @@ -1442,11 +1457,12 @@ export class EcosystemService { const message = await this.ecosystemServiceProxy.send(pattern, payload).toPromise(); return { message }; } catch (error) { - this.logger.error(`catch: ${JSON.stringify(error)}`); + this.logger.error(` agent-submit-transaction catch: ${JSON.stringify(error)}`); throw new HttpException( { status: error.status, - error: error.message + message: error.message, + error: error.error }, error.status ); diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index c68bfed2d..23d6f33f2 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -38,7 +38,6 @@ export class CredentialDefinitionService extends BaseService { const getAgentDetails = await this.credentialDefinitionRepository.getAgentType(credDef.orgId); // const apiKey = await this._getOrgAgentApiKey(credDef.orgId); let apiKey:string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - this.logger.log(`cachedApiKey----${apiKey}`); if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(credDef.orgId); } @@ -184,7 +183,7 @@ export class CredentialDefinitionService extends BaseService { const orgAgentType = await this.credentialDefinitionRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); // const apiKey = await this._getOrgAgentApiKey(String(orgId)); let apiKey:string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - this.logger.log(`cachedApiKey----${apiKey}`); + if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(String(orgId)); } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 9322b2acd..735c5f25e 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -222,7 +222,6 @@ export class VerificationService { const verificationMethodLabel = 'request-proof'; const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - this.logger.log(`cachedApiKey----${apiKey}`); if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(requestProof.orgId); } @@ -336,7 +335,6 @@ export class VerificationService { async sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { try { - this.logger.log(`-------outOfBandRequestProof------${JSON.stringify(outOfBandRequestProof)}`); outOfBandRequestProof.protocolVersion = outOfBandRequestProof.protocolVersion || 'v1'; outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; @@ -357,7 +355,6 @@ export class VerificationService { let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethodLabel = 'create-request-out-of-band'; const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); - this.logger.log(`cachedApiKey----${apiKey}`); if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(user.orgId); } @@ -369,7 +366,6 @@ export class VerificationService { }; const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - this.logger.log(`-----getProofPresentation---${JSON.stringify(getProofPresentation)}`); if (!getProofPresentation) { throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); } @@ -393,14 +389,12 @@ export class VerificationService { private async generateOOBProofReq(payload: IProofRequestPayload, getAgentDetails: org_agents): Promise { let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - this.logger.log(`cachedApiKey----${agentApiKey}`); if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { agentApiKey = await this._getOrgAgentApiKey(getAgentDetails.orgId); } payload.apiKey = agentApiKey; const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - this.logger.log(`-----getProofPresentation---${JSON.stringify(getProofPresentation)}`); if (!getProofPresentation) { throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); @@ -439,7 +433,6 @@ export class VerificationService { async sendOutOfBandProofRequest(payload: IProofRequestPayload, email: string, getAgentDetails: org_agents, organizationDetails: organisation): Promise { let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - this.logger.log(`cachedApiKey----${agentApiKey}`); if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { agentApiKey = await this._getOrgAgentApiKey(getAgentDetails.orgId); } @@ -674,7 +667,7 @@ export class VerificationService { const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId, '', proofId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - this.logger.log(`cachedApiKey----${apiKey}`); + if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(orgId); } diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index b836b3422..d5fed0c93 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -164,7 +164,6 @@ export class ClientRegistrationService { `${process.env.KEYCLOAK_DOMAIN}admin/realms/${process.env.KEYCLOAK_REALM}/users/${payload['sub']}`, this.getAuthHeader(token) ); - this.logger.debug(`keycloak user ${JSON.stringify(userInfoResponse)}`); return userInfoResponse.data; } catch (error) { this.logger.error(`[getUserInfo]: ${JSON.stringify(error)}`); @@ -236,7 +235,6 @@ export class ClientRegistrationService { this.getAuthHeader(token) ); - this.logger.debug(`Existing apps response ${JSON.stringify(response)}`); return { clientId: client_id, From 9d8d4cbb45007305920fa4932332fcb932f5e134 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 18:37:18 +0530 Subject: [PATCH 115/231] Update: function name for sonar linting issue Signed-off-by: Krishna --- apps/connection/src/connection.service.ts | 6 +++--- apps/issuance/src/issuance.service.ts | 6 +++--- apps/verification/src/verification.service.ts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index cdd67c628..9814a504f 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -82,7 +82,7 @@ export class ConnectionService { } const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); const connectionInvitaionUrl: string = createConnectionInvitation?.message?.invitationUrl; - const shortenedUrl: string = await this.storeObjectAndReturnUrl( + const shortenedUrl: string = await this.storeConnectionObjectAndReturnUrl( connectionInvitaionUrl, connectionPayload.multiUseInvitation ); @@ -690,7 +690,7 @@ export class ConnectionService { } } - async storeObjectAndReturnUrl(connectionInvitationUrl: string, persistent: boolean): Promise { + async storeConnectionObjectAndReturnUrl(connectionInvitationUrl: string, persistent: boolean): Promise { const storeObj = connectionInvitationUrl; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; @@ -703,7 +703,7 @@ export class ConnectionService { .toPromise() .catch((error) => { this.logger.error( - `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( + `[storeConnectionObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( error )}` ); diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index af2322884..14f5e5c55 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -217,12 +217,12 @@ export class IssuanceService { const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); if (isShortenUrl) { const invitationUrl: string = credentialCreateOfferDetails.response?.invitationUrl; - const url: string = await this.storeObjectReturnUrl(invitationUrl); + const url: string = await this.storeIssuanceObjectReturnUrl(invitationUrl); credentialCreateOfferDetails.response['invitationUrl'] = url; } return credentialCreateOfferDetails; } catch (error) { - this.logger.error(`[sendCredentialCreateOffer] - error in create credentials : ${JSON.stringify(error)}`); + this.logger.error(`[storeIssuanceObjectReturnUrl] - error in create credentials : ${JSON.stringify(error)}`); const errorStack = error?.status?.message?.error; if (errorStack) { @@ -237,7 +237,7 @@ export class IssuanceService { } } - async storeObjectReturnUrl(storeObj: string): Promise { + async storeIssuanceObjectReturnUrl(storeObj: string): Promise { // Set default to false, since currently our invitation are not multi-use const persistent: boolean = false; //nats call in agent-service to create an invitation url diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index f458de014..477e6108a 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -372,7 +372,7 @@ export class VerificationService { //apply presentation shorting URL if (isShortenUrl) { const proofRequestInvitationUrl: string = getProofPresentation?.response?.invitationUrl; - const shortenedUrl: string = await this.storeObjectAndReturnUrl(proofRequestInvitationUrl, false); + const shortenedUrl: string = await this.storeVerificationObjectAndReturnUrl(proofRequestInvitationUrl, false); this.logger.log('shortenedUrl', shortenedUrl); if (shortenedUrl) { getProofPresentation.response.invitationUrl = shortenedUrl; @@ -398,7 +398,7 @@ export class VerificationService { } } - async storeObjectAndReturnUrl(storeObj: string, persistent: boolean): Promise { + async storeVerificationObjectAndReturnUrl(storeObj: string, persistent: boolean): Promise { //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; @@ -410,7 +410,7 @@ export class VerificationService { .toPromise() .catch((error) => { this.logger.error( - `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( + `[storeVerificationObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( error )}` ); From 92dcd3a7fe4fb4d2ddeae7da79e82e7df392a078 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 29 Feb 2024 18:40:42 +0530 Subject: [PATCH 116/231] fix: dto comments Signed-off-by: Krishna --- apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts b/apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts index 8098ae6c1..f1467028b 100644 --- a/apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts +++ b/apps/api-gateway/src/utilities/dtos/shortening-url.dto.ts @@ -42,6 +42,6 @@ interface Attribute { export class GenericDto { @ApiProperty() - @IsNotEmpty() // eslint-disable-next-line @typescript-eslint/no-explicit-any + @IsNotEmpty() data: string | object; } \ No newline at end of file From 7e36041988ca26ac94ec42a4e6f754386c87e3ea Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Thu, 29 Feb 2024 18:41:23 +0530 Subject: [PATCH 117/231] feat: add W3C credential issuance (#555) * feat:added w3c issuance Signed-off-by: pallavicoder * feat:added jsonld credentail issuance Signed-off-by: pallavicoder * feat:added W3C issuance Signed-off-by: pallavicoder --------- Signed-off-by: pallavicoder --- .../src/issuance/dtos/issuance.dto.ts | 193 +++++++++++++++++- .../src/issuance/interfaces/index.ts | 24 +++ .../src/issuance/issuance.controller.ts | 29 ++- .../src/issuance/issuance.service.ts | 11 +- apps/api-gateway/src/issuance/utils/helper.ts | 33 +++ .../interfaces/issuance.interfaces.ts | 14 +- apps/issuance/src/issuance.service.ts | 145 ++++++++----- libs/common/src/response-messages/index.ts | 5 +- 8 files changed, 396 insertions(+), 58 deletions(-) create mode 100644 apps/api-gateway/src/issuance/utils/helper.ts diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 51dc9b192..b8381e531 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -1,12 +1,109 @@ +/* eslint-disable @typescript-eslint/array-type */ -import { IsArray, IsNotEmpty, IsOptional, IsString, IsEmail, ArrayMaxSize, ValidateNested, ArrayMinSize, IsBoolean, IsDefined, MaxLength, IsEnum } from 'class-validator'; +import { IsArray, IsNotEmpty, IsOptional, IsString, IsEmail, ArrayMaxSize, ValidateNested, ArrayMinSize, IsBoolean, IsDefined, MaxLength, IsEnum, IsObject} from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { trim } from '@credebl/common/cast.helper'; import { SortValue } from '../../enum'; import { SortFields } from 'apps/connection/src/enum/connection.enum'; import { AutoAccept } from '@credebl/enum/enum'; +import { IssueCredentialType, JsonLdCredentialDetailCredentialStatusOptions, JsonLdCredentialDetailOptionsOptions, JsonObject } from '../interfaces'; +import { IsCredentialJsonLdContext, SingleOrArray } from '../utils/helper'; + +class Issuer { + @ApiProperty() + @IsNotEmpty({ message: 'id is required' }) + @Type(() => String) + id:string | { id?: string }; +} +class Credential { + @ApiProperty() + @IsNotEmpty({ message: 'context is required' }) + @IsCredentialJsonLdContext() + '@context': Array; + + @ApiProperty() + @IsNotEmpty({ message: 'type is required' }) + type: string[]; + + @ApiProperty() + @IsString({ message: 'id should be string' }) + @IsNotEmpty({ message: 'id is required' }) + @Type(() => String) + @IsOptional() + id?:string; + + + @ApiProperty() + @ValidateNested({ each: true }) + @Type(() => Issuer) + issuer:Issuer; + @ApiProperty() + @IsString({ message: 'issuance date should be string' }) + @IsNotEmpty({ message: 'issuance date is required' }) + @Type(() => String) + issuanceDate:string; + + @ApiProperty() + @IsString({ message: 'expiration date should be string' }) + @IsNotEmpty({ message: 'expiration date is required' }) + @Type(() => String) + @IsOptional() + expirationDate?:string; + + @ApiProperty() + @IsNotEmpty({ message: ' credential subject required' }) + credentialSubject: SingleOrArray; + [key: string]: unknown + + } + + export class JsonLdCredentialDetailCredentialStatus { + public constructor(options: JsonLdCredentialDetailCredentialStatusOptions) { + if (options) { + this.type = options.type; + } + } + @IsString() + public type!: string; + } + export class JsonLdCredentialDetailOptions { + public constructor(options: JsonLdCredentialDetailOptionsOptions) { + if (options) { + this.proofPurpose = options.proofPurpose; + this.created = options.created; + this.domain = options.domain; + this.challenge = options.challenge; + this.credentialStatus = options.credentialStatus; + this.proofType = options.proofType; + } + } + + @IsString() + @IsNotEmpty({ message: 'proof purpose is required' }) + public proofPurpose!: string; + + @IsString() + @IsOptional() + public created?: string; + + @IsString() + @IsOptional() + public domain?: string; + + @IsString() + @IsOptional() + public challenge?: string; + + @IsString() + @IsNotEmpty({ message: 'proof type is required' }) + public proofType!: string; + + @IsOptional() + @IsObject() + public credentialStatus?: JsonLdCredentialDetailCredentialStatus; + } class Attribute { @ApiProperty() @IsString({ message: 'Attribute name should be string' }) @@ -33,7 +130,8 @@ class CredentialsIssuanceDto { @IsNotEmpty({ message: 'Please provide valid credential definition id' }) @IsString({ message: 'credential definition id should be string' }) @Transform(({ value }) => value.trim()) - credentialDefinitionId: string; + @IsOptional() + credentialDefinitionId?: string; @ApiProperty({ example: 'string' }) @IsNotEmpty({ message: 'Please provide valid comment' }) @@ -86,6 +184,12 @@ class CredentialsIssuanceDto { }) autoAcceptCredential?: string; + @ApiProperty({ example: 'jsonld' }) + @IsNotEmpty({ message: 'Please provide credential type ' }) + @Transform(({ value }) => trim(value).toLocaleLowerCase()) + @IsOptional() + credentialType:IssueCredentialType; + orgId: string; } @@ -101,8 +205,29 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { @IsArray() @ValidateNested({ each: true }) @ArrayMinSize(1) + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid attributes' }) @Type(() => Attribute) - attributes: Attribute[]; + attributes?: Attribute[]; + + + @ApiProperty() + @IsNotEmpty({ message: 'Please provide valid credential' }) + @IsObject({ message: 'credential should be an object' }) + @Type(() => Credential) + @IsOptional() + @ValidateNested({ each: true }) + credential?:Credential; + + + @ApiProperty() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid options' }) + @IsObject({ message: 'options should be an object' }) + @ValidateNested({ each: true }) + @Type(() => JsonLdCredentialDetailOptions) + options?:JsonLdCredentialDetailOptions; + } class CredentialOffer { @@ -111,7 +236,8 @@ class CredentialOffer { @IsArray({ message: 'Attributes should be an array' }) @ValidateNested({ each: true }) @Type(() => Attribute) - attributes: Attribute[]; + @IsOptional() + attributes?: Attribute[]; @ApiProperty({ example: 'testmail@xyz.com' }) @IsEmail({}, { message: 'Please provide a valid email' }) @@ -121,6 +247,22 @@ class CredentialOffer { @Transform(({ value }) => trim(value)) @Type(() => String) emailId: string; + + @IsNotEmpty({ message: 'Please provide valid credential' }) + @IsObject({ message: 'credential should be an object' }) + @Type(() => Credential) + @IsOptional() + @ValidateNested({ each: true }) + credential?:Credential; + + @ApiProperty() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid options' }) + @IsObject({ message: 'options should be an object' }) + @ValidateNested({ each: true }) + @Type(() => JsonLdCredentialDetailOptions) + options?:JsonLdCredentialDetailOptions; + } export class IssueCredentialDto extends OOBIssueCredentialDto { @@ -218,7 +360,39 @@ export class CredentialAttributes { } export class OOBCredentialDtoWithEmail { - @ApiProperty({ example: [{ 'emailId': 'abc@example.com', 'attributes': [{ 'value': 'string', 'name': 'string' }] }] }) + @ApiProperty({ example: [ + { + 'emailId': 'xyz@example.com', + 'credential': { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://www.w3.org/2018/credentials/examples/v1' + ], + 'type': [ + 'VerifiableCredential', + 'UniversityDegreeCredential' + ], + 'issuer': { + 'id': 'did:key:z6Mkn72LVp3mq1fWSefkSMh5V7qrmGfCV4KH3K6SoTM21ouM' + }, + 'issuanceDate': '2019-10-12T07:20:50.52Z', + 'credentialSubject': { + 'id': 'did:key:z6Mkn72LVp3mq1fWSefkSMh5V7qrmGfCV4KH3K6SoTM21ouM', + 'degree': { + 'type': 'BachelorDegree', + 'name': 'Bachelor of Science and Arts' + } + } + }, + 'options': { + 'proofType': 'Ed25519Signature2018', + 'proofPurpose': 'assertionMethod' + } + } + ] + + + }) @IsNotEmpty({ message: 'Please provide valid attributes' }) @IsArray({ message: 'attributes should be array' }) @ArrayMaxSize(Number(process.env.OOB_BATCH_SIZE), { message: `Limit reached (${process.env.OOB_BATCH_SIZE} credentials max). Easily handle larger batches via seamless CSV file uploads` }) @@ -229,8 +403,9 @@ export class OOBCredentialDtoWithEmail { @ApiProperty({ example: 'string' }) @IsNotEmpty({ message: 'Please provide valid credential definition id' }) @IsString({ message: 'credential definition id should be string' }) + @IsOptional() @Transform(({ value }) => value.trim()) - credentialDefinitionId: string; + credentialDefinitionId?: string; @ApiProperty({ example: 'string' }) @IsOptional() @@ -244,6 +419,12 @@ export class OOBCredentialDtoWithEmail { @IsString({ message: 'protocol version should be string' }) protocolVersion?: string; + @ApiProperty({ example: 'jsonld' }) + @IsNotEmpty({ message: 'Please provide credential type ' }) + @Transform(({ value }) => trim(value).toLocaleLowerCase()) + @IsOptional() + credentialType:IssueCredentialType; + imageUrl?: string; orgId: string; diff --git a/apps/api-gateway/src/issuance/interfaces/index.ts b/apps/api-gateway/src/issuance/interfaces/index.ts index 834a33b4d..e06d34539 100644 --- a/apps/api-gateway/src/issuance/interfaces/index.ts +++ b/apps/api-gateway/src/issuance/interfaces/index.ts @@ -1,3 +1,6 @@ +import { JsonLdCredentialDetailCredentialStatus } from '../dtos/issuance.dto'; +import { JsonValue } from '../utils/helper'; + export interface IUserRequestInterface { userId: string; email: string; @@ -75,3 +78,24 @@ export interface IIssuedCredentialSearchParams { searchByText: string; } +export enum IssueCredentialType { + JSONLD = 'jsonld', + INDY = 'indy' +} + +export interface JsonObject { + [property: string]: JsonValue + } + + export interface JsonLdCredentialDetailCredentialStatusOptions { + type: string + } + + export interface JsonLdCredentialDetailOptionsOptions { + proofPurpose: string + created?: string + domain?: string + challenge?: string + credentialStatus?: JsonLdCredentialDetailCredentialStatus + proofType: string + } \ No newline at end of file diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 02956f16a..56c1c059c 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -1,3 +1,4 @@ +/* eslint-disable default-param-last */ /* eslint-disable no-param-reassign */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable camelcase */ @@ -15,7 +16,9 @@ import { Header, UploadedFile, UseInterceptors, - Logger + Logger, + BadRequestException, + NotFoundException } from '@nestjs/common'; import { ApiTags, @@ -52,7 +55,7 @@ import { Roles } from '../authz/decorators/roles.decorator'; import { OrgRoles } from 'libs/org-roles/enums'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; -import { FileExportResponse, IIssuedCredentialSearchParams, RequestPayload } from './interfaces'; +import { FileExportResponse, IIssuedCredentialSearchParams, IssueCredentialType, RequestPayload } from './interfaces'; import { AwsService } from '@credebl/aws'; import { FileInterceptor } from '@nestjs/platform-express'; import { v4 as uuidv4 } from 'uuid'; @@ -536,14 +539,30 @@ export class IssuanceController { @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER) + @ApiQuery({ + name:'credentialType', + enum: IssueCredentialType + }) async outOfBandCredentialOffer( @User() user: IUserRequest, @Body() outOfBandCredentialDto: OOBCredentialDtoWithEmail, + @Query('credentialType') credentialType: IssueCredentialType = IssueCredentialType.INDY, @Param('orgId') orgId: string, @Res() res: Response ): Promise { outOfBandCredentialDto.orgId = orgId; + outOfBandCredentialDto.credentialType = credentialType; + const credOffer = outOfBandCredentialDto?.credentialOffer || []; + if (IssueCredentialType.INDY !== credentialType && IssueCredentialType.JSONLD !== credentialType) { + throw new NotFoundException(ResponseMessages.issuance.error.invalidCredentialType); +} + if (outOfBandCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.credential || 0 === Object.keys(offer?.credential).length))) { + throw new BadRequestException(ResponseMessages.issuance.error.credentialNotPresent); + } + if (outOfBandCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.options || 0 === Object.keys(offer?.options).length))) { + throw new BadRequestException(ResponseMessages.issuance.error.optionsNotPresent); + } const getCredentialDetails = await this.issueCredentialService.outOfBandCredentialOffer( user, outOfBandCredentialDto @@ -568,15 +587,21 @@ export class IssuanceController { summary: `Create out-of-band credential offer`, description: `Creates an out-of-band credential offer` }) + @ApiQuery({ + name:'credentialType', + enum: IssueCredentialType + }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER) @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) async createOOBCredentialOffer( + @Query('credentialType') credentialType: IssueCredentialType = IssueCredentialType.INDY, @Param('orgId') orgId: string, @Body() issueCredentialDto: OOBIssueCredentialDto, @Res() res: Response ): Promise { issueCredentialDto.orgId = orgId; + issueCredentialDto.credentialType = credentialType; const getCredentialDetails = await this.issueCredentialService.sendCredentialOutOfBand(issueCredentialDto); const finalResponse: IResponseType = { statusCode: HttpStatus.CREATED, diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index 42b1f03ea..bd58cba92 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -4,7 +4,7 @@ import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { ClientDetails, FileParameter, IssuanceDto, IssueCredentialDto, OOBCredentialDtoWithEmail, OOBIssueCredentialDto, PreviewFileDetails } from './dtos/issuance.dto'; -import { FileExportResponse, IIssuedCredentialSearchParams, RequestPayload } from './interfaces'; +import { FileExportResponse, IIssuedCredentialSearchParams, IssueCredentialType, RequestPayload } from './interfaces'; import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; @Injectable() @@ -29,7 +29,14 @@ export class IssuanceService extends BaseService { sendCredentialOutOfBand(issueCredentialDto: OOBIssueCredentialDto): Promise<{ response: object; }> { - const payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential }; + let payload; + if (IssueCredentialType.INDY === issueCredentialDto.credentialType) { + payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType }; + } + if (IssueCredentialType.JSONLD === issueCredentialDto.credentialType) { + payload = { credential: issueCredentialDto.credential, options:issueCredentialDto.options, comment: issueCredentialDto.comment, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType }; + } + return this.sendNats(this.issuanceProxy, 'send-credential-create-offer-oob', payload); } diff --git a/apps/api-gateway/src/issuance/utils/helper.ts b/apps/api-gateway/src/issuance/utils/helper.ts new file mode 100644 index 000000000..574e5eb69 --- /dev/null +++ b/apps/api-gateway/src/issuance/utils/helper.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/array-type */ +import { ValidateBy, ValidationOptions, buildMessage, isString, isURL } from 'class-validator'; +import { JsonObject } from '../interfaces'; +export type SingleOrArray = T | T[] +export type JsonValue = string | number | boolean | null | JsonObject | JsonArray +export type JsonArray = Array + + +export const isJsonObject = (value: unknown): value is JsonObject => value !== undefined && 'object' === typeof value && null !== value && !Array.isArray(value); +export const CREDENTIALS_CONTEXT_V1_URL = 'https://www.w3.org/2018/credentials/v1'; +export function IsCredentialJsonLdContext(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'IsCredentialJsonLdContext', + validator: { + validate: (value): boolean => { + if (!Array.isArray(value)) { return false; } + + // First item must be the verifiable credential context + if (value[0] !== CREDENTIALS_CONTEXT_V1_URL) { return false; } + + return value.every((v) => (isString(v) && isURL(v)) || isJsonObject(v)); + }, + defaultMessage: buildMessage( + (eachPrefix) => `${eachPrefix + }$property must be an array of strings or objects, where the first item is the verifiable credential context URL.`, + validationOptions + ) + } + }, + validationOptions + ); +} \ No newline at end of file diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index d7ccbff71..fdb743271 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -3,6 +3,7 @@ import { AutoAccept } from '@credebl/enum/enum'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { organisation } from '@prisma/client'; import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-service.interface'; +import { IssueCredentialType } from 'apps/api-gateway/src/issuance/interfaces'; export interface IAttributes { attributeName: string; @@ -128,12 +129,22 @@ export interface ICredentialAttributesInterface { value: string; } +export interface ICredential{ + '@context':[]; + type: string[]; +} +export interface IOptions{ + proofType:string; + proofPurpose:string; +} export interface CredentialOffer { emailId: string; attributes: IAttributes[]; + credential?:ICredential; + options?:IOptions } export interface OutOfBandCredentialOfferPayload { - credentialDefinitionId: string; + credentialDefinitionId?: string; orgId: string; comment?: string; credentialOffer?: CredentialOffer[]; @@ -146,6 +157,7 @@ export interface OutOfBandCredentialOfferPayload { label?: string, imageUrl?: string, autoAcceptCredential?: string; + credentialType?:IssueCredentialType; } export interface OutOfBandCredentialOffer { diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 1d0a1562d..0b4ff8ddd 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -29,7 +29,7 @@ import { Queue } from 'bull'; import { FileUploadStatus, FileUploadType } from 'apps/api-gateway/src/enum'; import { AwsService } from '@credebl/aws'; import { io } from 'socket.io-client'; -import { IIssuedCredentialSearchParams } from 'apps/api-gateway/src/issuance/interfaces'; +import { IIssuedCredentialSearchParams, IssueCredentialType } from 'apps/api-gateway/src/issuance/interfaces'; import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto'; @@ -148,33 +148,35 @@ export class IssuanceService { async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { - const { orgId, credentialDefinitionId, comment, attributes, protocolVersion } = payload; - - const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( - credentialDefinitionId - ); - - if (schemadetailsResponse?.attributes) { - const schemadetailsResponseError = []; - const attributesArray: IAttributes[] = JSON.parse(schemadetailsResponse.attributes); - - attributesArray.forEach((attribute) => { - if (attribute.attributeName && attribute.isRequired) { - - payload.attributes.map((attr) => { - if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { - schemadetailsResponseError.push( - `Attribute '${attribute.attributeName}' is required but has an empty value.` - ); - } - return true; - }); + + const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, credential, options, credentialType} = payload; + if (credentialType === IssueCredentialType.INDY) { + const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); + + if (schemadetailsResponse?.attributes) { + const schemadetailsResponseError = []; + const attributesArray: IAttributes[] = JSON.parse(schemadetailsResponse.attributes); + + attributesArray.forEach((attribute) => { + if (attribute.attributeName && attribute.isRequired) { + + payload.attributes.map((attr) => { + if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { + schemadetailsResponseError.push( + `Attribute '${attribute.attributeName}' is required but has an empty value.` + ); + } + return true; + }); + } + }); + if (0 < schemadetailsResponseError.length) { + throw new BadRequestException(schemadetailsResponseError); } - }); - if (0 < schemadetailsResponseError.length) { - throw new BadRequestException(schemadetailsResponseError); + } - } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); @@ -196,8 +198,10 @@ export class IssuanceService { if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(orgId); } + let issueData; + if (credentialType === IssueCredentialType.INDY) { - const issueData = { + issueData = { protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { @@ -214,6 +218,28 @@ export class IssuanceService { label: organisation?.name, comment: comment || '' }; + + } + + if (credentialType === IssueCredentialType.JSONLD) { + issueData = { + protocolVersion: protocolVersion || 'v2', + credentialFormats: { + jsonld: { + credential, + options + } + }, + autoAcceptCredential: payload.autoAcceptCredential || 'always', + goalCode: payload.goalCode || undefined, + parentThreadId: payload.parentThreadId || undefined, + willConfirm: payload.willConfirm || undefined, + imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, + label: organisation?.name, + comment: comment || '' + }; + } + const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); return credentialCreateOfferDetails; @@ -409,9 +435,13 @@ export class IssuanceService { orgId, protocolVersion, attributes, - emailId + emailId, + credentialType } = outOfBandCredential; + + if (IssueCredentialType.INDY === credentialType) { + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( credentialDefinitionId ); @@ -461,6 +491,7 @@ const credefError = []; throw new BadRequestException(credefError); } } + } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); @@ -487,27 +518,49 @@ const credefError = []; const errors = []; const emailPromises = []; - + let outOfBandIssuancePayload; const sendEmailForCredentialOffer = async (iterator, emailId, index): Promise => { const iterationNo = index + 1; try { - const outOfBandIssuancePayload = { - protocolVersion: protocolVersion || 'v1', - credentialFormats: { - indy: { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - attributes: (iterator.attributes || attributes).map(({ isRequired, ...rest }) => rest), - credentialDefinitionId - } - }, - autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', - comment, - goalCode: outOfBandCredential.goalCode || undefined, - parentThreadId: outOfBandCredential.parentThreadId || undefined, - willConfirm: outOfBandCredential.willConfirm || undefined, - label: organisation?.name, - imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl - }; + if (IssueCredentialType.INDY === credentialType) { + + outOfBandIssuancePayload = { + protocolVersion: protocolVersion || 'v1', + credentialFormats: { + indy: { + attributes: iterator.attributes || attributes, + credentialDefinitionId + } + }, + autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', + comment, + goalCode: outOfBandCredential.goalCode || undefined, + parentThreadId: outOfBandCredential.parentThreadId || undefined, + willConfirm: outOfBandCredential.willConfirm || undefined, + label: outOfBandCredential.label || undefined, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl + }; + } + + if (IssueCredentialType.JSONLD === credentialType) { + outOfBandIssuancePayload = { + protocolVersion:'v2', + credentialFormats: { + jsonld: { + credential: iterator.credential, + options: iterator.options + } + }, + autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', + comment, + goalCode: outOfBandCredential.goalCode || undefined, + parentThreadId: outOfBandCredential.parentThreadId || undefined, + willConfirm: outOfBandCredential.willConfirm || undefined, + label: outOfBandCredential.label || undefined, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl + }; + } + this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 436d90506..e94b566c3 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -269,7 +269,10 @@ export const ResponseMessages = { emailIdNotPresent: 'EmailId is empty or not present', attributesNotPresent: 'Attributes are not present or not empty', unableToCreateOffer: 'Unable to create offer', - orgAgentTypeNotFound: 'Organization agent type not found' + orgAgentTypeNotFound: 'Organization agent type not found', + credentialNotPresent: 'credential is required', + optionsNotPresent:'options are required', + invalidCredentialType:'invalid credential type' } }, verification: { From d14a8f57c138d0ff9a2c051e93788527e82ec9df Mon Sep 17 00:00:00 2001 From: Nishad Date: Thu, 29 Feb 2024 19:59:08 +0530 Subject: [PATCH 118/231] refactored role guard, worked on the API to register clients and map users in keycloak Signed-off-by: Nishad --- .../src/authz/guards/org-roles.guard.ts | 10 +- .../organization/organization.controller.ts | 4 + .../repositories/organization.repository.ts | 6 - apps/organization/src/organization.service.ts | 182 +++++++++++++----- 4 files changed, 145 insertions(+), 57 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/org-roles.guard.ts b/apps/api-gateway/src/authz/guards/org-roles.guard.ts index 5888b1dc7..b9018781b 100644 --- a/apps/api-gateway/src/authz/guards/org-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/org-roles.guard.ts @@ -1,7 +1,5 @@ import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Logger } from '@nestjs/common'; -import { HttpException } from '@nestjs/common'; -import { HttpStatus } from '@nestjs/common'; import { Injectable } from '@nestjs/common'; import { OrgRoles } from 'libs/org-roles/enums'; import { ROLES_KEY } from '../decorators/roles.decorator'; @@ -34,15 +32,11 @@ export class OrgRolesGuard implements CanActivate { const orgId = req.params.orgId || req.query.orgId || req.body.orgId; - if (!orgId) { - throw new BadRequestException(ResponseMessages.organisation.error.orgIdIsRequired); - } + if (orgId) { if (!isValidUUID(orgId)) { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); } - - if (orgId) { const orgDetails = user.resource_access[orgId]; @@ -91,7 +85,7 @@ export class OrgRolesGuard implements CanActivate { return false; } else { - throw new HttpException('organization is required', HttpStatus.BAD_REQUEST); + throw new BadRequestException('organization is required'); } // Sending user friendly message if a user attempts to access an API that is inaccessible to their role diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 8ea50d754..f894e091d 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -394,6 +394,9 @@ export class OrganizationController { summary: 'Register client and map users', description: 'Register client and map users' }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.PLATFORM_ADMIN) + @ApiBearerAuth() @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) async registerOrgsMapUsers(@Res() res: Response): Promise { @@ -509,6 +512,7 @@ export class OrganizationController { @ApiOperation({ summary: 'Delete Organization Client Credentials', description: 'Delete Organization Client Credentials' }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() + @ApiExcludeEndpoint() @UseGuards(AuthGuard('jwt')) async deleteOrgClientCredentials(@Param('orgId') orgId: string, @Res() res: Response): Promise { diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index 05f9eebd6..bbf32493d 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -14,7 +14,6 @@ import { UserOrgRolesService } from '@credebl/user-org-roles'; import { organisation } from '@prisma/client'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IOrganizationInvitations, IOrganization, IOrganizationDashboard} from '@credebl/common/interfaces/organization.interface'; -import { OrgRoles } from 'libs/org-roles/enums'; @Injectable() export class OrganizationRepository { @@ -487,11 +486,6 @@ export class OrganizationRepository { }, include: { userOrgRoles: { - where: { - orgRole: { - name: OrgRoles.OWNER - } - }, include: { user: { select: { diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index b96528b8a..76917cff3 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -1020,6 +1020,73 @@ export class OrganizationService { } } + async updateUserClientRoles( + // eslint-disable-next-line camelcase + roleIds: string[], + idpId: string, + userId: string, + orgId: string + ): Promise { + const token = await this.clientRegistrationService.getManagementToken(); + const clientRolesList = await this.clientRegistrationService.getAllClientRoles( + idpId, + token + ); + const orgRoles = await this.orgRoleService.getOrgRoles(); + + const matchedClientRoles = clientRolesList.filter((role) => roleIds.includes(role.id.trim())); + + if (roleIds.length !== matchedClientRoles.length) { + throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); + } + + const rolesPayload: { roleId: string; name: string; idpRoleId: string }[] = matchedClientRoles.map( + (clientRole: IClientRoles) => { + let roleObj: { roleId: string; name: string; idpRoleId: string } = null; + + for (let index = 0; index < orgRoles.length; index++) { + if (orgRoles[index].name === clientRole.name) { + roleObj = { + roleId: orgRoles[index].id, + name: orgRoles[index].name, + idpRoleId: clientRole.id + }; + break; + } + } + + return roleObj; + } + ); + + const userData = await this.getUserUserId(userId); + + const [, deletedUserRoleRecords] = await Promise.all([ + this.clientRegistrationService.deleteUserClientRoles( + idpId, + token, + userData.keycloakUserId + ), + this.userOrgRoleService.deleteOrgRoles(userId, orgId) + ]); + + if (0 === deletedUserRoleRecords['count']) { + throw new InternalServerErrorException(ResponseMessages.organisation.error.updateUserRoles); + } + + const [, isUserRoleUpdated] = await Promise.all([ + this.clientRegistrationService.createUserClientRole( + idpId, + token, + userData.keycloakUserId, + rolesPayload.map((role) => ({ id: role.idpRoleId, name: role.name })) + ), + this.userOrgRoleService.updateUserOrgRole(userId, orgId, rolesPayload) + ]); + + return isUserRoleUpdated; + } + /** * * @param orgId @@ -1041,56 +1108,34 @@ export class OrganizationService { throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); } - const token = await this.clientRegistrationService.getManagementToken(); - const clientRolesList = await this.clientRegistrationService.getAllClientRoles(organizationDetails.idpId, token); - const orgRoles = await this.orgRoleService.getOrgRoles(); + if (!organizationDetails.idpId) { + const isRolesExist = await this.orgRoleService.getOrgRolesByIds(roleIds); - const matchedClientRoles = clientRolesList.filter((role) => roleIds.includes(role.id.trim())); - - if (roleIds.length !== matchedClientRoles.length) { - throw new NotFoundException(ResponseMessages.organisation.error.orgRoleIdNotFound); - } + if (isRolesExist && 0 === isRolesExist.length) { + throw new NotFoundException(ResponseMessages.organisation.error.rolesNotExist); + } - const rolesPayload: { roleId: string; name: string; idpRoleId: string }[] = matchedClientRoles.map((clientRole: IClientRoles) => { - let roleObj: { roleId: string; name: string; idpRoleId: string} = null; + const deleteUserRecords = await this.userOrgRoleService.deleteOrgRoles(userId, orgId); - for (let index = 0; index < orgRoles.length; index++) { - if (orgRoles[index].name === clientRole.name) { - roleObj = { - roleId: orgRoles[index].id, - name: orgRoles[index].name, - idpRoleId: clientRole.id - }; - break; - } + if (0 === deleteUserRecords['count']) { + throw new InternalServerErrorException(ResponseMessages.organisation.error.updateUserRoles); } - return roleObj; - }); - - const userData = await this.getUserUserId(userId); + for (const role of roleIds) { + this.userOrgRoleService.createUserOrgRole(userId, role, orgId); + } - const [ - , - deletedUserRoleRecords - ] = await Promise.all([ - this.clientRegistrationService.deleteUserClientRoles(organizationDetails.idpId, token, userData.keycloakUserId), - this.userOrgRoleService.deleteOrgRoles(userId, orgId) - ]); + return true; + } else { - if (0 === deletedUserRoleRecords['count']) { - throw new InternalServerErrorException(ResponseMessages.organisation.error.updateUserRoles); - } + return this.updateUserClientRoles( + roleIds, + organizationDetails.idpId, + userId, + organizationDetails.id + ); + } - const [ - , - isUserRoleUpdated - ] = await Promise.all([ - this.clientRegistrationService.createUserClientRole(organizationDetails.idpId, token, userData.keycloakUserId, rolesPayload.map(role => ({id: role.idpRoleId, name: role.name}))), - this.userOrgRoleService.updateUserOrgRole(userId, orgId, rolesPayload) - ]); - - return isUserRoleUpdated; } catch (error) { this.logger.error(`Error in updateUserRoles: ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -1237,9 +1282,19 @@ export class OrganizationService { try { const unregisteredOrgsList = await this.organizationRepository.getUnregisteredClientOrgs(); + + if (!unregisteredOrgsList || 0 === unregisteredOrgsList.length) { + throw new NotFoundException('Unregistered client organizations not found'); + } for (const org of unregisteredOrgsList) { - const ownerUser = 0 < org['userOrgRoles'].length && org['userOrgRoles'][0].user; + const userOrgRoles = 0 < org['userOrgRoles'].length && org['userOrgRoles']; + + const ownerUserList = 0 < org['userOrgRoles'].length + && userOrgRoles.filter(userOrgRole => userOrgRole.orgRole.name === OrgRoles.OWNER); + + const ownerUser = 0 < ownerUserList.length && ownerUserList[0].user; + const orgObj = { id: org.id, idpId: org.idpId, @@ -1269,6 +1324,47 @@ export class OrganizationService { const updatedOrg = await this.organizationRepository.updateOrganizationById(updateOrgData, orgObj.id); this.logger.log(`updatedOrg::`, updatedOrg); + + const usersToRegisterList = userOrgRoles.filter(userOrgRole => null !== userOrgRole.user.keycloakUserId); + + const token = await this.clientRegistrationService.getManagementToken(); + const clientRolesList = await this.clientRegistrationService.getAllClientRoles(idpId, token); + + const deletedUserDetails: string[] = []; + for (const userRole of usersToRegisterList) { + const user = userRole.user; + + const matchedClientRoles = clientRolesList.filter((role) => userRole.orgRole.name === role.name) + .map(clientRole => ({roleId: userRole.orgRole.id, idpRoleId: clientRole.id, name: clientRole.name})); + + if (!deletedUserDetails.includes(user.id)) { + const [, deletedUserRoleRecords] = await Promise.all([ + this.clientRegistrationService.deleteUserClientRoles(idpId, token, user.keycloakUserId), + this.userOrgRoleService.deleteOrgRoles(user.id, orgObj.id) + ]); + + this.logger.log(`deletedUserRoleRecords::`, deletedUserRoleRecords); + + deletedUserDetails.push(user.id); + } + + + await Promise.all([ + this.clientRegistrationService.createUserClientRole( + idpId, + token, + user.keycloakUserId, + matchedClientRoles.map((role) => ({ id: role.idpRoleId, name: role.name })) + ), + this.userOrgRoleService.updateUserOrgRole( + user.id, + orgObj.id, + matchedClientRoles.map((role) => ({ roleId: role.roleId, idpRoleId: role.idpRoleId })) + ) + ]); + this.logger.log(`Organization client created and users mapped to roles`); + + } } } From a60ed6396bcf370ae06e1a66b3d6118e9cdca5c4 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Thu, 29 Feb 2024 20:37:45 +0530 Subject: [PATCH 119/231] fix: resolved ecosystem invitation self accepted by ecosystem lead bug Signed-off-by: bhavanakarwade --- apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts | 1 + apps/ecosystem/src/ecosystem.controller.ts | 5 +++-- apps/ecosystem/src/ecosystem.repository.ts | 5 +++-- apps/ecosystem/src/ecosystem.service.ts | 5 ++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts b/apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts index 1bc76de4f..99bcc61c5 100644 --- a/apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts +++ b/apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts @@ -7,4 +7,5 @@ export class AcceptRejectEcosystemInvitationDto { orgName?: string; orgDid?: string; userId?: string; + userEmail?: string; } diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index 4797821ed..a2544ec30 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -104,9 +104,10 @@ export class EcosystemController { */ @MessagePattern({ cmd: 'accept-reject-ecosystem-invitations' }) async acceptRejectEcosystemInvitations(payload: { - acceptRejectInvitation: AcceptRejectEcosystemInvitationDto; + acceptRejectInvitation: AcceptRejectEcosystemInvitationDto, + userEmail: string }): Promise { - return this.ecosystemService.acceptRejectEcosystemInvitations(payload.acceptRejectInvitation); + return this.ecosystemService.acceptRejectEcosystemInvitations(payload.acceptRejectInvitation, payload.userEmail); } @MessagePattern({ cmd: 'get-sent-invitations-ecosystemId' }) diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index 225598d0e..a42b0e373 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -356,11 +356,12 @@ export class EcosystemRepository { * @returns Invitation details */ // eslint-disable-next-line camelcase - async getEcosystemInvitationById(id: string): Promise { + async getEcosystemInvitationById(id: string, email: string): Promise { try { return this.prisma.ecosystem_invitations.findUnique({ where: { - id + id, + email }, include: { ecosystem: true diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index eed9f4e52..a758ac941 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -425,7 +425,7 @@ export class EcosystemService { * @param userId * @returns Ecosystem invitation status */ - async acceptRejectEcosystemInvitations(acceptRejectInvitation: AcceptRejectEcosystemInvitationDto): Promise { + async acceptRejectEcosystemInvitations(acceptRejectInvitation: AcceptRejectEcosystemInvitationDto, email: string): Promise { try { const isMultiEcosystemEnabled = await this.ecosystemRepository.getSpecificEcosystemConfig( EcosystemConfigSettings.MULTI_ECOSYSTEM @@ -442,8 +442,7 @@ export class EcosystemService { } const { orgId, status, invitationId, orgName, orgDid, userId } = acceptRejectInvitation; - const invitation = await this.ecosystemRepository.getEcosystemInvitationById(invitationId); - + const invitation = await this.ecosystemRepository.getEcosystemInvitationById(invitationId, email); if (!invitation) { throw new NotFoundException(ResponseMessages.ecosystem.error.invitationNotFound); } From e246cb56d3e80c45aa36501778e27aeb76b5ea8e Mon Sep 17 00:00:00 2001 From: "rohit.shitre" Date: Thu, 29 Feb 2024 21:45:11 +0530 Subject: [PATCH 120/231] fix: shorten url changes for issue cred OOB on send email- WIP Signed-off-by: rohit.shitre --- apps/api-gateway/src/issuance/issuance.service.ts | 12 ++++++------ apps/issuance/src/issuance.service.ts | 14 +++++--------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index ef0cfdbe1..e97e26ad7 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -31,12 +31,12 @@ export class IssuanceService extends BaseService { }> { let payload; if (IssueCredentialType.INDY === issueCredentialDto.credentialType) { - payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl }; - } - if (IssueCredentialType.JSONLD === issueCredentialDto.credentialType) { - payload = { credential: issueCredentialDto.credential, options:issueCredentialDto.options, comment: issueCredentialDto.comment, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType }; - } - + payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl }; + } + if (IssueCredentialType.JSONLD === issueCredentialDto.credentialType) { + payload = { credential: issueCredentialDto.credential, options: issueCredentialDto.options, comment: issueCredentialDto.comment, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl }; + } + return this.sendNats(this.issuanceProxy, 'send-credential-create-offer-oob', payload); } diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index cc802e9b9..500a70e29 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -239,7 +239,6 @@ export class IssuanceService { comment: comment || '' }; } - const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); if (isShortenUrl) { const invitationUrl: string = credentialCreateOfferDetails.response?.invitationUrl; @@ -616,19 +615,16 @@ export class IssuanceService { return false; } - const invitationId = credentialCreateOfferDetails.response.invitation['@id']; + const invitationUrl: string = credentialCreateOfferDetails.response?.invitationUrl; + const shortenUrl: string = await this.storeIssuanceObjectReturnUrl(invitationUrl); - if (!invitationId) { + if (!invitationUrl) { errors.push(new NotFoundException(ResponseMessages.issuance.error.invitationNotFound)); return false; } - const agentEndPoint = agentDetails.tenantId - ? `${agentDetails.agentEndPoint}/multi-tenancy/url/${agentDetails.tenantId}/${invitationId}` - : `${agentDetails.agentEndPoint}/url/${invitationId}`; - const qrCodeOptions = { type: 'image/png' }; - const outOfBandIssuanceQrCode = await QRCode.toDataURL(agentEndPoint, qrCodeOptions); + const outOfBandIssuanceQrCode = await QRCode.toDataURL(shortenUrl, qrCodeOptions); const platformConfigData = await this.issuanceRepository.getPlatformConfigDetails(); if (!platformConfigData) { @@ -639,7 +635,7 @@ export class IssuanceService { this.emailData.emailFrom = platformConfigData.emailFrom; this.emailData.emailTo = emailId; this.emailData.emailSubject = `${process.env.PLATFORM_NAME} Platform: Issuance of Your Credential`; - this.emailData.emailHtml = this.outOfBandIssuance.outOfBandIssuance(emailId, organizationDetails.name, agentEndPoint); + this.emailData.emailHtml = this.outOfBandIssuance.outOfBandIssuance(emailId, organizationDetails.name, shortenUrl); this.emailData.emailAttachments = [ { filename: 'qrcode.png', From 10e83853cb257485ab0115636e706d07d9dfad66 Mon Sep 17 00:00:00 2001 From: "rohit.shitre" Date: Fri, 1 Mar 2024 11:43:19 +0530 Subject: [PATCH 121/231] fix:resolved isRequired issue Signed-off-by: rohit.shitre --- apps/issuance/src/issuance.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 500a70e29..171dfc1fc 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -567,12 +567,12 @@ export class IssuanceService { const iterationNo = index + 1; try { if (IssueCredentialType.INDY === credentialType) { - outOfBandIssuancePayload = { protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { - attributes: iterator.attributes || attributes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: (iterator.attributes).map(({ isRequired, ...rest }) => rest) || (attributes).map(({ isRequired, ...rest }) => rest), credentialDefinitionId } }, From 6f7ba0fc10d1b5b5950c176ba7d0749d43ec8d73 Mon Sep 17 00:00:00 2001 From: Nishad Date: Fri, 1 Mar 2024 11:50:17 +0530 Subject: [PATCH 122/231] refactor register user for org roles in DB Signed-off-by: Nishad --- apps/user/src/user.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index 5c1c77d96..0f03c13a8 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -284,6 +284,8 @@ export class UserService { ]; await this.clientRegistrationService.createUserHolderRole(token, keycloakDetails.keycloakUserId.toString(), payload); + const holderOrgRole = await this.orgRoleService.getRole(OrgRoles.HOLDER); + await this.userOrgRoleService.createUserOrgRole(userDetails.id, holderOrgRole.id, null, holderRoleData.id); return ResponseMessages.user.success.signUpUser; } catch (error) { From 19f9dcc1a7373a68c82ace88ae715f94ebc34770 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Fri, 1 Mar 2024 12:58:57 +0530 Subject: [PATCH 123/231] fix: ledger id validations Signed-off-by: bhavanakarwade --- apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts index 56d33e86c..4ee5d3388 100644 --- a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts @@ -1,7 +1,7 @@ import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { MaxLength, IsString, MinLength, Matches, IsNotEmpty, IsOptional, IsArray } from 'class-validator'; +import { MaxLength, IsString, MinLength, Matches, IsNotEmpty, IsOptional, IsArray, IsUUID } from 'class-validator'; const labelRegex = /^[a-zA-Z0-9 ]*$/; export class CreateTenantDto { @ApiProperty() @@ -28,6 +28,7 @@ export class CreateTenantDto { @ApiProperty({ example: ["b942473d-6fdd-4a38-b76e-a3314fca66b6"] }) @ApiPropertyOptional() @IsOptional() + @IsUUID(4, {each:true, message: "Please provide valid ledgerId"}) @IsArray({ message: 'ledgerId must be an array' }) @IsNotEmpty({ message: 'please provide valid ledgerId' }) @IsString({ each: true, message: 'Each ledgerId must be a string' }) From 2b2ae7251c8776144ef7e00f30cb5929091da403 Mon Sep 17 00:00:00 2001 From: Nishad Date: Fri, 1 Mar 2024 17:25:49 +0530 Subject: [PATCH 124/231] fixed the forgot password for old supabase user Signed-off-by: Nishad --- apps/user/src/user.service.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index f9e112b41..b9aefd058 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -470,11 +470,21 @@ export class UserService { } const decryptedPassword = await this.commonService.decryptPassword(password); - try { + try { + const authToken = await this.clientRegistrationService.getManagementToken(); userData.password = decryptedPassword; - await this.clientRegistrationService.resetPasswordOfUser(userData, process.env.KEYCLOAK_REALM, authToken); + if (userData.keycloakUserId) { + await this.clientRegistrationService.resetPasswordOfUser(userData, process.env.KEYCLOAK_REALM, authToken); + } else { + const keycloakDetails = await this.clientRegistrationService.createUser(userData, process.env.KEYCLOAK_REALM, authToken); + await this.userRepository.updateUserDetails(userData.id, + keycloakDetails.keycloakUserId.toString() + ); + } + await this.updateFidoVerifiedUser(email.toLowerCase(), userData.isFidoVerified, password); + } catch (error) { this.logger.error(`Error reseting the password`, error); throw new InternalServerErrorException('Error while reseting user password'); From e5f7289e408b02684cf525a87e69cfad306ae078 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 4 Mar 2024 13:15:45 +0530 Subject: [PATCH 125/231] fix: solved the bulk issuance flow and function separation in bulk issuance Signed-off-by: KulkarniShashank --- .../interfaces/issuance.interfaces.ts | 17 + apps/issuance/src/issuance.service.ts | 486 ++++++++++-------- 2 files changed, 284 insertions(+), 219 deletions(-) diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index fdb743271..5cb4caa75 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -240,3 +240,20 @@ export interface OrgAgent { orgAgentTypeId: string; tenantId: string; } + +export interface SendEmailCredentialOffer { + iterator: CredentialOffer; + emailId: string; + index: number; + credentialType: IssueCredentialType; + protocolVersion: string; + attributes: IAttributes[]; + credentialDefinitionId: string; + outOfBandCredential: OutOfBandCredentialOfferPayload; + comment: string; + organisation: organisation; + errors; + url: string; + apiKey: string; + organizationDetails: organisation; +} \ No newline at end of file diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 0b4ff8ddd..52d8bf709 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -9,7 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs'; // import { ClientDetails, FileUploadData, ICredentialAttributesInterface, ImportFileDetails, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; -import { FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces'; +import { CredentialOffer, FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails, SendEmailCredentialOffer } from '../interfaces/issuance.interfaces'; import { OrgAgentType } from '@credebl/enum/enum'; // import { platform_config } from '@prisma/client'; import * as QRCode from 'qrcode'; @@ -32,6 +32,7 @@ import { io } from 'socket.io-client'; import { IIssuedCredentialSearchParams, IssueCredentialType } from 'apps/api-gateway/src/issuance/interfaces'; import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto'; +import { organisation } from '@prisma/client'; @Injectable() @@ -425,259 +426,305 @@ export class IssuanceService { } } - - async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayload): Promise { - try { - const { - credentialOffer, - comment, - credentialDefinitionId, - orgId, - protocolVersion, - attributes, - emailId, - credentialType - } = outOfBandCredential; - +async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayload): Promise { + try { + const { + credentialOffer, + comment, + credentialDefinitionId, + orgId, + protocolVersion, + attributes, + emailId, + credentialType + } = outOfBandCredential; if (IssueCredentialType.INDY === credentialType) { + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); - const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( - credentialDefinitionId - ); - - let attributesArray:IAttributes[] = []; - if (schemaResponse?.attributes) { - - attributesArray = JSON.parse(schemaResponse.attributes); - } - - if (0 < attributes?.length) { -const attrError = []; - attributesArray.forEach((schemaAttribute, i) => { - if (schemaAttribute.isRequired) { + let attributesArray: IAttributes[] = []; + if (schemaResponse?.attributes) { + attributesArray = JSON.parse(schemaResponse.attributes); + } - const attribute = attributes.find(attribute => attribute.name === schemaAttribute.attributeName); - if (!attribute?.value) { - attrError.push( - `attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` - ); - } - - } - - }); - if (0 < attrError.length) { - throw new BadRequestException(attrError); + if (0 < attributes?.length) { + const attrError = []; + attributesArray.forEach((schemaAttribute, i) => { + if (schemaAttribute.isRequired) { + const attribute = attributes.find((attribute) => attribute.name === schemaAttribute.attributeName); + if (!attribute?.value) { + attrError.push(`attributes.${i}.Attribute ${schemaAttribute.attributeName} is required`); } + } + }); + if (0 < attrError.length) { + throw new BadRequestException(attrError); } - if (0 < credentialOffer?.length) { -const credefError = []; - credentialOffer.forEach((credentialAttribute, index) => { - - attributesArray.forEach((schemaAttribute, i) => { - - const attribute = credentialAttribute.attributes.find(attribute => attribute.name === schemaAttribute.attributeName); - - if (schemaAttribute.isRequired && !attribute?.value) { - credefError.push( - `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` - ); - } - - }); - }); - if (0 < credefError.length) { - throw new BadRequestException(credefError); + } + if (0 < credentialOffer?.length) { + const credefError = []; + credentialOffer.forEach((credentialAttribute, index) => { + attributesArray.forEach((schemaAttribute, i) => { + const attribute = credentialAttribute.attributes.find( + (attribute) => attribute.name === schemaAttribute.attributeName + ); + + if (schemaAttribute.isRequired && !attribute?.value) { + credefError.push( + `credentialOffer.${index}.attributes.${i}.Attribute ${schemaAttribute.attributeName} is required` + ); } + }); + }); + if (0 < credefError.length) { + throw new BadRequestException(credefError); } - } - - const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); - - const { organisation } = agentDetails; - if (!agentDetails) { - throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); - } - - const orgAgentType = await this.issuanceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); - - const issuanceMethodLabel = 'create-offer-oob'; - const url = await this.getAgentUrl(issuanceMethodLabel, orgAgentType, agentDetails.agentEndPoint, agentDetails.tenantId); - const organizationDetails = await this.issuanceRepository.getOrganization(orgId); - - if (!organizationDetails) { - throw new NotFoundException(ResponseMessages.issuance.error.organizationNotFound); } + } + const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } + const { organisation } = agentDetails; + if (!agentDetails) { + throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); + } - const errors = []; - const emailPromises = []; - let outOfBandIssuancePayload; - const sendEmailForCredentialOffer = async (iterator, emailId, index): Promise => { - const iterationNo = index + 1; - try { - if (IssueCredentialType.INDY === credentialType) { - - outOfBandIssuancePayload = { - protocolVersion: protocolVersion || 'v1', - credentialFormats: { - indy: { - attributes: iterator.attributes || attributes, - credentialDefinitionId - } - }, - autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', - comment, - goalCode: outOfBandCredential.goalCode || undefined, - parentThreadId: outOfBandCredential.parentThreadId || undefined, - willConfirm: outOfBandCredential.willConfirm || undefined, - label: outOfBandCredential.label || undefined, - imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl - }; - } + const orgAgentType = await this.issuanceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); - if (IssueCredentialType.JSONLD === credentialType) { - outOfBandIssuancePayload = { - protocolVersion:'v2', - credentialFormats: { - jsonld: { - credential: iterator.credential, - options: iterator.options - } - }, - autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', - comment, - goalCode: outOfBandCredential.goalCode || undefined, - parentThreadId: outOfBandCredential.parentThreadId || undefined, - willConfirm: outOfBandCredential.willConfirm || undefined, - label: outOfBandCredential.label || undefined, - imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl - }; - } - + const issuanceMethodLabel = 'create-offer-oob'; + const url = await this.getAgentUrl( + issuanceMethodLabel, + orgAgentType, + agentDetails.agentEndPoint, + agentDetails.tenantId + ); + const organizationDetails = await this.issuanceRepository.getOrganization(orgId); - this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); + if (!organizationDetails) { + throw new NotFoundException(ResponseMessages.issuance.error.organizationNotFound); + } - const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, apiKey); + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this._getOrgAgentApiKey(orgId); + } - if (!credentialCreateOfferDetails) { - errors.push(new NotFoundException(ResponseMessages.issuance.error.credentialOfferNotFound)); - return false; - } + const errors = []; + const emailPromises = []; + const sendEmailCredentialOffer: { + iterator: CredentialOffer; + emailId: string; + index: number; + credentialType: IssueCredentialType; + protocolVersion: string; + attributes: IAttributes[]; + credentialDefinitionId: string; + outOfBandCredential: OutOfBandCredentialOfferPayload; + comment: string; + organisation: organisation; + errors: string[]; + url: string; + apiKey: string; + organizationDetails: organisation; + } = { + credentialType, + protocolVersion, + attributes, + credentialDefinitionId, + outOfBandCredential, + comment, + organisation, + errors, + url, + apiKey, + organizationDetails, + iterator: undefined, + emailId: emailId || '', + index: 0 + }; - const invitationId = credentialCreateOfferDetails.response.invitation['@id']; + if (credentialOffer) { + for (let i = 0; i < credentialOffer.length; i += Number(process.env.OOB_BATCH_SIZE)) { + const batch = credentialOffer.slice(i, i + Number(process.env.OOB_BATCH_SIZE)); + // Process each batch in parallel + const batchPromises = batch.map(async (iterator, index) => { + + sendEmailCredentialOffer['iterator'] = iterator; + sendEmailCredentialOffer['emailId'] = iterator.emailId; + sendEmailCredentialOffer['index'] = index; - if (!invitationId) { - errors.push(new NotFoundException(ResponseMessages.issuance.error.invitationNotFound)); - return false; - } + return this.sendEmailForCredentialOffer(sendEmailCredentialOffer); + }); + emailPromises.push(Promise.all(batchPromises)); + } + } else { + emailPromises.push(this.sendEmailForCredentialOffer(sendEmailCredentialOffer)); + } - const agentEndPoint = agentDetails.tenantId - ? `${agentDetails.agentEndPoint}/multi-tenancy/url/${agentDetails.tenantId}/${invitationId}` - : `${agentDetails.agentEndPoint}/url/${invitationId}`; + const results = await Promise.all(emailPromises); - const qrCodeOptions = { type: 'image/png' }; - const outOfBandIssuanceQrCode = await QRCode.toDataURL(agentEndPoint, qrCodeOptions); - const platformConfigData = await this.issuanceRepository.getPlatformConfigDetails(); + // Flatten the results array + const flattenedResults = [].concat(...results); - if (!platformConfigData) { - errors.push(new NotFoundException(ResponseMessages.issuance.error.platformConfigNotFound)); - return false; - } + // Check if all emails were successfully sent + const allSuccessful = flattenedResults.every((result) => true === result); - this.emailData.emailFrom = platformConfigData.emailFrom; - this.emailData.emailTo = emailId; - this.emailData.emailSubject = `${process.env.PLATFORM_NAME} Platform: Issuance of Your Credential`; - this.emailData.emailHtml = this.outOfBandIssuance.outOfBandIssuance(emailId, organizationDetails.name, agentEndPoint); - this.emailData.emailAttachments = [ - { - filename: 'qrcode.png', - content: outOfBandIssuanceQrCode.split(';base64,')[1], - contentType: 'image/png', - disposition: 'attachment' - } - ]; + if (0 < errors.length) { + throw errors; + } - const isEmailSent = await sendEmail(this.emailData); - this.logger.log(`isEmailSent ::: ${JSON.stringify(isEmailSent)}`); + return allSuccessful; + } catch (error) { + this.logger.error( + `[outOfBoundCredentialOffer] - error in create out-of-band credentials: ${JSON.stringify(error)}` + ); + if (0 < error?.length) { + const errorStack = error?.map((item) => { + const { message, statusCode, error } = item?.error || item?.response || {}; + return { + message, + statusCode, + error + }; + }); + throw new RpcException({ + error: errorStack, + statusCode: error?.status?.code, + message: ResponseMessages.issuance.error.unableToCreateOOBOffer + }); + } else { + throw new RpcException(error.response ? error.response : error); + } + } +} - if (!isEmailSent) { - errors.push(new InternalServerErrorException(ResponseMessages.issuance.error.emailSend)); - return false; +async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialOffer): Promise { + const { + iterator, + emailId, + index, + credentialType, + protocolVersion, + attributes, + credentialDefinitionId, + outOfBandCredential, + comment, + organisation, + errors, + url, + apiKey, + organizationDetails + } = sendEmailCredentialOffer; + + const iterationNo = index + 1; + try { + let outOfBandIssuancePayload; + if (IssueCredentialType.INDY === credentialType) { + + outOfBandIssuancePayload = { + protocolVersion: protocolVersion || 'v1', + credentialFormats: { + indy: { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + attributes: attributes ? attributes : iterator.attributes.map(({ isRequired, ...rest }) => rest), + credentialDefinitionId } + }, + autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', + comment, + goalCode: outOfBandCredential.goalCode || undefined, + parentThreadId: outOfBandCredential.parentThreadId || undefined, + willConfirm: outOfBandCredential.willConfirm || undefined, + label: outOfBandCredential.label || undefined, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl + }; + } - return isEmailSent; - } catch (error) { - this.logger.error('[OUT-OF-BAND CREATE OFFER - SEND EMAIL]::', JSON.stringify(error)); - const errorStack = error?.status?.message; - if (errorStack) { - errors.push( - new RpcException({ - error: `${errorStack?.error?.message} at position ${iterationNo}`, - statusCode: errorStack?.statusCode, - message: `${ResponseMessages.issuance.error.walletError} at position ${iterationNo}` - })); - } else { - errors.push(new InternalServerErrorException(`${error.message} at position ${iterationNo}`)); + if (IssueCredentialType.JSONLD === credentialType) { + outOfBandIssuancePayload = { + protocolVersion: 'v2', + credentialFormats: { + jsonld: { + credential: iterator.credential, + options: iterator.options } - return false; - } + }, + autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', + comment, + goalCode: outOfBandCredential.goalCode || undefined, + parentThreadId: outOfBandCredential.parentThreadId || undefined, + willConfirm: outOfBandCredential.willConfirm || undefined, + label: outOfBandCredential.label || undefined, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl }; + } - if (credentialOffer) { - for (let i = 0; i < credentialOffer.length; i += Number(process.env.OOB_BATCH_SIZE)) { - const batch = credentialOffer.slice(i, i + Number(process.env.OOB_BATCH_SIZE)); - - // Process each batch in parallel - const batchPromises = batch.map((iterator, index) => sendEmailForCredentialOffer(iterator, iterator.emailId, index)); - emailPromises.push(Promise.all(batchPromises)); - } - } else { - emailPromises.push(sendEmailForCredentialOffer({}, emailId, 1)); - } - - const results = await Promise.all(emailPromises); + const agentDetails = await this.issuanceRepository.getAgentEndPoint(organisation.id); - // Flatten the results array - const flattenedResults = [].concat(...results); + this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); - // Check if all emails were successfully sent - const allSuccessful = flattenedResults.every((result) => true === result); + const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, apiKey); - if (0 < errors.length) { - throw errors; - } + if (!credentialCreateOfferDetails) { + errors.push(new NotFoundException(ResponseMessages.issuance.error.credentialOfferNotFound)); + return false; + } - return allSuccessful; - } catch (error) { - this.logger.error(`[outOfBoundCredentialOffer] - error in create out-of-band credentials: ${JSON.stringify(error)}`); - if (0 < error?.length) { - const errorStack = error?.map(item => { - const { message, statusCode, error } = item?.error || item?.response || {}; - return { - message, - statusCode, - error - }; - }); - throw new RpcException({ - error: errorStack, - statusCode: error?.status?.code, - message: ResponseMessages.issuance.error.unableToCreateOOBOffer - }); - } else { - throw new RpcException(error.response ? error.response : error); - } + const invitationId = credentialCreateOfferDetails.response.invitation['@id']; + if (!invitationId) { + errors.push(new NotFoundException(ResponseMessages.issuance.error.invitationNotFound)); + return false; + } + const agentEndPoint = agentDetails.tenantId + ? `${agentDetails.agentEndPoint}/multi-tenancy/url/${agentDetails.tenantId}/${invitationId}` + : `${agentDetails.agentEndPoint}/url/${invitationId}`; + const qrCodeOptions = { type: 'image/png' }; + const outOfBandIssuanceQrCode = await QRCode.toDataURL(agentEndPoint, qrCodeOptions); + const platformConfigData = await this.issuanceRepository.getPlatformConfigDetails(); + if (!platformConfigData) { + errors.push(new NotFoundException(ResponseMessages.issuance.error.platformConfigNotFound)); + return false; + } + this.emailData.emailFrom = platformConfigData.emailFrom; + this.emailData.emailTo = emailId; + this.emailData.emailSubject = `${process.env.PLATFORM_NAME} Platform: Issuance of Your Credential`; + this.emailData.emailHtml = this.outOfBandIssuance.outOfBandIssuance(emailId, organizationDetails.name, agentEndPoint); + this.emailData.emailAttachments = [ + { + filename: 'qrcode.png', + content: outOfBandIssuanceQrCode.split(';base64,')[1], + contentType: 'image/png', + disposition: 'attachment' + } + ]; + const isEmailSent = await sendEmail(this.emailData); + this.logger.log(`isEmailSent ::: ${JSON.stringify(isEmailSent)}`); + if (!isEmailSent) { + errors.push(new InternalServerErrorException(ResponseMessages.issuance.error.emailSend)); + return false; + } + return isEmailSent; + + } catch (error) { + this.logger.error('[OUT-OF-BAND CREATE OFFER - SEND EMAIL]::', JSON.stringify(error)); + const errorStack = error?.status?.message; + if (errorStack) { + errors.push( + new RpcException({ + error: `${errorStack?.error?.message} at position ${iterationNo}`, + statusCode: errorStack?.statusCode, + message: `${ResponseMessages.issuance.error.walletError} at position ${iterationNo}` + }) + ); + } else { + errors.push(new InternalServerErrorException(`${error.message} at position ${iterationNo}`)); } + return false; } +} - async _outOfBandCredentialOffer(outOfBandIssuancePayload: object, url: string, apiKey: string): Promise<{ response; }> { @@ -1161,7 +1208,8 @@ const credefError = []; orgId: jobDetails.orgId, label: organisation?.name, attributes: [], - emailId: jobDetails.data.email + emailId: jobDetails.data.email, + credentialType: IssueCredentialType.INDY }; for (const key in jobDetails.data) { From 3b04d614023a9ddf484bc129eeb3c9178a738dfe Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Mon, 4 Mar 2024 16:36:07 +0530 Subject: [PATCH 126/231] Added the bin bash in shell script Signed-off-by: KulkarniShashank --- apps/agent-provisioning/AFJ/scripts/start_agent.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh index fbb449988..87a595939 100755 --- a/apps/agent-provisioning/AFJ/scripts/start_agent.sh +++ b/apps/agent-provisioning/AFJ/scripts/start_agent.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash START_TIME=$(date +%s) From 2dc813cf809c303eb96b172bbda71b5c169d41ee Mon Sep 17 00:00:00 2001 From: pallavighule <61926403+pallavighule@users.noreply.github.com> Date: Mon, 4 Mar 2024 20:17:17 +0530 Subject: [PATCH 127/231] feat:added presentationExchange proof verification format (#564) Signed-off-by: pallavicoder --- .../src/verification/dto/request-proof.dto.ts | 113 +++++++++++++++++- .../verification/enum/verification.enum.ts | 5 + .../verification/verification.controller.ts | 10 +- .../src/interfaces/verification.interface.ts | 63 +++++++++- apps/verification/src/verification.service.ts | 47 ++++++-- 5 files changed, 226 insertions(+), 12 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 1a08db7e1..3f16e005a 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -156,6 +156,81 @@ export class OutOfBandRequestProof extends ProofPayload { autoAcceptProof: string; } +export class Fields { + @ApiProperty() + @IsArray() + @IsNotEmpty({ message: 'path is required.' }) + path: string[]; + } + +export class Constraints { + @ApiProperty({type: () => [Fields]}) + @IsOptional() + @IsNotEmpty({ message: 'Fields are required.' }) + @ValidateNested() + @Type(() => Fields) + fields: Fields[]; + } + + +export class Schema { + @ApiProperty() + @IsNotEmpty({ message: 'uri is required.' }) + @IsString() + uri:string; + +} +export class InputDescriptors { + @ApiProperty() + @IsNotEmpty({ message: 'id is required.' }) + @IsString() + id:string; + + @ApiProperty() + @IsString() + @IsOptional() + @IsNotEmpty({ message: 'name is required.' }) + name:string; + + @ApiProperty() + @IsString() + @IsOptional() + @IsNotEmpty({ message: 'purpose is required.' }) + purpose:string; + + + @ApiProperty({type: () => [Schema]}) + @IsNotEmpty({ message: 'schema is required.' }) + @ValidateNested() + @Type(() => Schema) + schema:Schema[]; + + + @ApiProperty({type: () => Constraints}) + @IsOptional() + @IsNotEmpty({ message: 'Constraints are required.' }) + @ValidateNested() + @Type(() => Constraints) + constraints:Constraints; + +} + +export class ProofRequestPresentationDefinition { + + @IsString() + @IsNotEmpty({ message: 'id is required.' }) + id: string; + @ApiProperty({type: () => [InputDescriptors]}) + @IsNotEmpty({ message: 'inputDescriptors is required.' }) + @IsArray({ message: 'inputDescriptors must be an array' }) + @IsObject({ each: true }) + @Type(() => InputDescriptors) + @ValidateNested() + + // eslint-disable-next-line camelcase + input_descriptors:InputDescriptors[]; +} + export class SendProofRequestPayload { @ApiPropertyOptional() @@ -194,7 +269,43 @@ export class SendProofRequestPayload { }) @IsObject({ each: true }) @IsNotEmpty({ message: 'please provide valid proofFormat' }) - proofFormats: IProofFormats; + @IsOptional() + proofFormats?: IProofFormats; + + @ApiProperty({ + 'example': + { + id: '32f54163-7166-48f1-93d8-ff217bdb0653', + inputDescriptors: [ + { + 'id': 'banking_input_1', + 'name': 'Bank Account Information', + 'schema': [ + { + 'uri': 'https://bank-schemas.org/1.0.0/accounts.json' + } + + ], + 'constraints': { + 'fields': [ + { + 'path': ['$.issuer'] + } + ] + } + } + ] + }, + type: () => [ProofRequestPresentationDefinition] + }) + @IsOptional() + @ValidateNested() + @IsObject({ message: 'presentationDefinition must be an object' }) + @IsNotEmpty({ message: 'presentationDefinition must not be empty' }) + @Type(() => ProofRequestPresentationDefinition) + presentationDefinition?:ProofRequestPresentationDefinition; + + type:string; @ApiPropertyOptional() @IsString({ message: 'auto accept proof must be in string' }) diff --git a/apps/api-gateway/src/verification/enum/verification.enum.ts b/apps/api-gateway/src/verification/enum/verification.enum.ts index bcb8ab6fc..87c8b88a2 100644 --- a/apps/api-gateway/src/verification/enum/verification.enum.ts +++ b/apps/api-gateway/src/verification/enum/verification.enum.ts @@ -3,4 +3,9 @@ export enum SortFields { STATUS = 'state', CONNECTION_ID = 'connectionId', PRESENTATION_ID = 'presentationId' +} + +export enum ProofRequestType { + INDY = 'indy', + PRESENTATIONEXCHANGE = 'presentationExchange' } \ No newline at end of file diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index b396b8b31..bed9126f0 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -32,7 +32,7 @@ import { ImageServiceService } from '@credebl/image-service'; import { User } from '../authz/decorators/user.decorator'; import { GetAllProofRequestsDto } from './dto/get-all-proof-requests.dto'; import { IProofRequestSearchCriteria } from './interfaces/verification.interface'; -import { SortFields } from './enum/verification.enum'; +import { ProofRequestType, SortFields } from './enum/verification.enum'; @UseFilters(CustomExceptionFilter) @Controller() @@ -247,6 +247,10 @@ export class VerificationController { @ApiUnauthorizedResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Unauthorized', type: UnauthorizedErrorDto }) @ApiForbiddenResponse({ status: HttpStatus.FORBIDDEN, description: 'Forbidden', type: ForbiddenErrorDto }) @ApiBody({ type: SendProofRequestPayload }) + @ApiQuery({ + name: 'requestType', + enum: ProofRequestType + }) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.VERIFIER) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @@ -254,9 +258,11 @@ export class VerificationController { @Res() res: Response, @User() user: IUserRequest, @Body() outOfBandRequestProof: SendProofRequestPayload, - @Param('orgId') orgId: string + @Param('orgId') orgId: string, + @Query('requestType') requestType:ProofRequestType = ProofRequestType.INDY ): Promise { user.orgId = orgId; + outOfBandRequestProof.type = requestType; const result = await this.verificationService.sendOutOfBandPresentationRequest(outOfBandRequestProof, user); const finalResponse: IResponseType = { statusCode: HttpStatus.CREATED, diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index d139576bd..4f7cc448d 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -88,18 +88,79 @@ interface IRequestedRestriction { issuer_did?: string; schema_version?: string; } +export interface ISchema { + uri:string; +} +export interface IFields { + path: string[]; + } +export interface IConstraints { + fields: IFields[]; + } + +export interface IInputDescriptors { + + id:string; + name?:string; + purpose?:string; + schema:ISchema[]; + constraints?:IConstraints; + +} + +export interface IProofRequestPresentationDefinition { + id:string; + input_descriptors:IInputDescriptors[]; +} + +export interface IPresentationExchange { + presentationDefinition:IProofRequestPresentationDefinition; + +} +export interface IPresentationExchangeProofFormats { + presentationExchange : IPresentationExchange; +} +export interface ISendPresentationExchangeProofRequestPayload { + protocolVersion: string; + comment: string; + proofFormats: IPresentationExchangeProofFormats; + autoAcceptProof: string; + label?: string; +} +export interface IPresentationExchangeProofRequestPayload { + url: string; + apiKey: string; + proofRequestPayload: ISendPresentationExchangeProofRequestPayload; +} export interface ISendProofRequestPayload { protocolVersion?: string; comment?: string; connectionId?: string; - proofFormats: IProofFormats; + proofFormats?: IProofFormats; + autoAcceptProof?: string; + label?: string; + goalCode?: string; + parentThreadId?: string; + willConfirm?: boolean; + imageUrl?: string; + type?:string; + presentationDefinition?:IProofRequestPresentationDefinition; +} + +export interface IWSendProofRequestPayload { + protocolVersion?: string; + comment?: string; + connectionId?: string; + proofFormats?: IProofFormats; autoAcceptProof?: string; label?: string; goalCode?: string; parentThreadId?: string; willConfirm?: boolean; imageUrl?: string; + type?:string; + presentationDefinition?:IProofRequestPresentationDefinition; } export interface IProofRequestPayload { diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 735c5f25e..024255043 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -2,7 +2,7 @@ import { BadRequestException, HttpException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs/operators'; -import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData } from './interfaces/verification.interface'; +import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IPresentationExchangeProofRequestPayload} from './interfaces/verification.interface'; import { VerificationRepository } from './repositories/verification.repository'; import { CommonConstants } from '@credebl/common/common.constant'; import { org_agents, organisation, presentations } from '@prisma/client'; @@ -16,6 +16,7 @@ import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { IProofPresentationDetails, IProofPresentationList } from '@credebl/common/interfaces/verification.interface'; +import { ProofRequestType } from 'apps/api-gateway/src/verification/enum/verification.enum'; @Injectable() export class VerificationService { @@ -339,7 +340,7 @@ export class VerificationService { outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); - + const [getAgentDetails, getOrganization] = await Promise.all([ this.verificationRepository.getAgentEndPoint(user.orgId), this.verificationRepository.getOrganization(user.orgId) @@ -350,7 +351,6 @@ export class VerificationService { outOfBandRequestProof['imageUrl'] = imageUrl; outOfBandRequestProof['label'] = label; - const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethodLabel = 'create-request-out-of-band'; @@ -358,13 +358,44 @@ export class VerificationService { if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(user.orgId); } - const payload: IProofRequestPayload - = { + + + let payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload; + + if (ProofRequestType.INDY === outOfBandRequestProof.type) { + const outOfBandIndyRequestPayload = { ...outOfBandRequestProof }; + delete outOfBandIndyRequestPayload.type; + + payload = { apiKey, url, - proofRequestPayload: outOfBandRequestProof + proofRequestPayload: outOfBandIndyRequestPayload }; + } + if (ProofRequestType.PRESENTATIONEXCHANGE === outOfBandRequestProof.type) { + + payload = { + apiKey, + url, + proofRequestPayload: { + protocolVersion:outOfBandRequestProof.protocolVersion || 'v1', + comment:outOfBandRequestProof.comment, + label, + proofFormats: { + presentationExchange: { + presentationDefinition: { + id: outOfBandRequestProof.presentationDefinition.id, + input_descriptors: [...outOfBandRequestProof.presentationDefinition.input_descriptors] + } + } + }, + autoAcceptProof:outOfBandRequestProof.autoAcceptProof || 'always' + } + }; + } + + const getProofPresentation = await this._sendOutOfBandProofRequest(payload); if (!getProofPresentation) { throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); @@ -489,11 +520,11 @@ export class VerificationService { * @param payload * @returns Get requested proof presentation details */ - async _sendOutOfBandProofRequest(payload: IProofRequestPayload): Promise<{ + async _sendOutOfBandProofRequest(payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload): Promise<{ response; }> { try { - + const pattern = { cmd: 'agent-send-out-of-band-proof-request' }; From 24897e290e211ef151f8795c51594f93d68b50c9 Mon Sep 17 00:00:00 2001 From: "rohit.shitre" Date: Mon, 4 Mar 2024 22:56:08 +0530 Subject: [PATCH 128/231] fix:removed multiple promise all from issuance flow. Signed-off-by: rohit.shitre --- apps/issuance/src/issuance.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 52d8bf709..f54a8031d 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -557,7 +557,7 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl return this.sendEmailForCredentialOffer(sendEmailCredentialOffer); }); - emailPromises.push(Promise.all(batchPromises)); + emailPromises.push(batchPromises); } } else { emailPromises.push(this.sendEmailForCredentialOffer(sendEmailCredentialOffer)); From 214dd2b3d9c24ec1deddbb7223690a0927215cb6 Mon Sep 17 00:00:00 2001 From: Nishad Date: Tue, 5 Mar 2024 16:23:06 +0530 Subject: [PATCH 129/231] worked on the client & user login credential message, worked on the error handling for conflict organization create Signed-off-by: Nishad --- .../repositories/organization.repository.ts | 14 ++++++++ apps/organization/src/organization.service.ts | 36 +++++++++++-------- apps/user/src/user.service.ts | 2 +- libs/common/src/response-messages/index.ts | 3 +- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index 930fe04d7..741d8bd09 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -42,6 +42,20 @@ export class OrganizationRepository { } } + + async checkOrganizationSlugExist(orgSlug: string): Promise { + try { + return this.prisma.organisation.findUnique({ + where: { + orgSlug + } + }); + } catch (error) { + this.logger.error(`error in checkOrganizationSlugExist: ${JSON.stringify(error)}`); + throw error; + } + } + /** * * @Body createOrgDtp diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index a82df7f25..0d3154c44 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -1,6 +1,6 @@ /* eslint-disable prefer-destructuring */ import { organisation, user } from '@prisma/client'; -import { Injectable, Logger, ConflictException, InternalServerErrorException, HttpException, BadRequestException, ForbiddenException } from '@nestjs/common'; +import { Injectable, Logger, ConflictException, InternalServerErrorException, HttpException, BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; import { CommonService } from '@credebl/common'; import { OrganizationRepository } from '../repositories/organization.repository'; @@ -63,6 +63,13 @@ export class OrganizationService { } const orgSlug = this.createOrgSlug(createOrgDto.name); + + const isOrgSlugExist = await this.organizationRepository.checkOrganizationSlugExist(orgSlug); + + if (isOrgSlugExist) { + throw new ConflictException(ResponseMessages.organisation.error.exists); + } + createOrgDto.orgSlug = orgSlug; createOrgDto.createdBy = userId; createOrgDto.lastChangedBy = userId; @@ -353,31 +360,32 @@ export class OrganizationService { } async clientLoginCredentails(clientCredentials: IClientCredentials): Promise { - - const {clientId, clientSecret} = clientCredentials; - return this.authenticateClientKeycloak(clientId, clientSecret); - } + const {clientId, clientSecret} = clientCredentials; + return this.authenticateClientKeycloak(clientId, clientSecret); +} async authenticateClientKeycloak(clientId: string, clientSecret: string): Promise { - + try { + const payload = new ClientCredentialTokenPayloadDto(); + // eslint-disable-next-line camelcase + payload.client_id = clientId; + // eslint-disable-next-line camelcase + payload.client_secret = clientSecret; + payload.scope = 'email profile'; - const payload = new ClientCredentialTokenPayloadDto(); - // eslint-disable-next-line camelcase - payload.client_id = clientId; - // eslint-disable-next-line camelcase - payload.client_secret = clientSecret; - payload.scope = 'email profile'; - + try { const mgmtTokenResponse = await this.clientRegistrationService.getToken(payload); return mgmtTokenResponse; + } catch (error) { + throw new UnauthorizedException(ResponseMessages.organisation.error.invalidClient); + } } catch (error) { this.logger.error(`Error in authenticateClientKeycloak : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } - } /** diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index f9e112b41..cfffa20a4 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -562,7 +562,7 @@ export class UserService { tokenResponse.isRegisteredToSupabase = false; return tokenResponse; } catch (error) { - throw new UnauthorizedException(error?.message); + throw new UnauthorizedException(ResponseMessages.user.error.invalidCredentials); } } else { diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 436d90506..a0bf8fd81 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -108,7 +108,8 @@ export const ResponseMessages = { invalidInvitationId:'Invalid format for invitation id', ecosystemIdIsRequired:'ecosystemId is required', roleNotMatch: 'User does not have access', - orgDoesNotMatch: 'Organization does not match' + orgDoesNotMatch: 'Organization does not match', + invalidClient: 'Invalid client credentials' } }, From 407b081e17c158d60f3277029869cbb80100644f Mon Sep 17 00:00:00 2001 From: Krishna Date: Tue, 5 Mar 2024 16:35:11 +0530 Subject: [PATCH 130/231] Initial changes to send email Signed-off-by: Krishna --- .../src/verification/dto/request-proof.dto.ts | 14 ++++- .../verification/verification.controller.ts | 2 + .../src/interfaces/verification.interface.ts | 1 + .../src/verification.controller.ts | 2 + apps/verification/src/verification.service.ts | 51 +++++++++++++------ 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 1a08db7e1..e2322cda9 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,4 +1,4 @@ -import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID } from 'class-validator'; +import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID, ArrayUnique } from 'class-validator'; import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; @@ -215,5 +215,17 @@ export class SendProofRequestPayload { @IsUUID() @IsNotEmpty({ message: 'please provide valid parentThreadId' }) parentThreadId: string; + + // @ApiProperty() + @ApiPropertyOptional() + @IsEmail({}, { each: true, message: 'Please provide a valid email' }) + @ArrayNotEmpty({ message: 'Email array must not be empty' }) + // @Transform(({ value }) => trim(value)) + // @Transform(({ value }) => value.map((email: string) => email.trim())) + @ArrayUnique({ message: 'Duplicate emails are not allowed' }) + @IsArray() + @IsString({ each: true, message: 'Each emailId in the array should be a string' }) + @IsOptional() + emailId: string[]; } diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index b396b8b31..8420077ea 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -256,6 +256,8 @@ export class VerificationController { @Body() outOfBandRequestProof: SendProofRequestPayload, @Param('orgId') orgId: string ): Promise { + // eslint-disable-next-line no-console + console.log('Received email in API-gateway-controller -> ', outOfBandRequestProof.emailId); user.orgId = orgId; const result = await this.verificationService.sendOutOfBandPresentationRequest(outOfBandRequestProof, user); const finalResponse: IResponseType = { diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index d139576bd..f592bc9e4 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -100,6 +100,7 @@ export interface ISendProofRequestPayload { parentThreadId?: string; willConfirm?: boolean; imageUrl?: string; + emailId?: string[] } export interface IProofRequestPayload { diff --git a/apps/verification/src/verification.controller.ts b/apps/verification/src/verification.controller.ts index 34e8bd9db..e0bbbe1fe 100644 --- a/apps/verification/src/verification.controller.ts +++ b/apps/verification/src/verification.controller.ts @@ -64,6 +64,8 @@ export class VerificationController { @MessagePattern({ cmd: 'send-out-of-band-proof-request' }) async sendOutOfBandPresentationRequest(payload: { outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest }): Promise { + // eslint-disable-next-line no-console + console.log('Stringify Received email in "Verification Controller"', JSON.stringify(payload.outOfBandRequestProof.emailId)); return this.verificationService.sendOutOfBandPresentationRequest(payload.outOfBandRequestProof, payload.user); } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 735c5f25e..f6baa665e 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -338,7 +338,11 @@ export class VerificationService { outOfBandRequestProof.protocolVersion = outOfBandRequestProof.protocolVersion || 'v1'; outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; + // eslint-disable-next-line no-console + console.log('Received outOfBandRequestProof in "sendOutOfBandPresentationRequest:::::"', JSON.stringify(outOfBandRequestProof)); // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); + // outOfBandRequestProof.proofFormats.indy.requested_attributes = requestedAttributes; + // outOfBandRequestProof.proofFormats.indy.requested_predicates = requestedPredicates; const [getAgentDetails, getOrganization] = await Promise.all([ this.verificationRepository.getAgentEndPoint(user.orgId), @@ -358,28 +362,39 @@ export class VerificationService { if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(user.orgId); } + + // Destructuring 'outOfBandRequestProof' to remove emailId, as it is not used while agent operation + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const {emailId, ...proofRequestPayload} = outOfBandRequestProof; + // eslint-disable-next-line no-console + console.log('This is the email in Verification-microservice->service::::::', emailId); const payload: IProofRequestPayload = { apiKey, url, - proofRequestPayload: outOfBandRequestProof + proofRequestPayload }; - const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - if (!getProofPresentation) { - throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); - } - return getProofPresentation.response; - + // const getProofPresentation = await this._sendOutOfBandProofRequest(payload); + // if (!getProofPresentation) { + // throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); + // } + // return getProofPresentation.response; // Unused code : to be segregated - // if (outOfBandRequestProof.emailId) { - // const batchSize = 100; // Define the batch size according to your needs - // const { emailId } = outOfBandRequestProof; // Assuming it's an array - // await this.sendEmailInBatches(payload, emailId, getAgentDetails, organizationDetails, batchSize); - // return true; - // } else { - // return this.generateOOBProofReq(payload, getAgentDetails); - // } + // eslint-disable-next-line no-console + console.log('This is "outOfBandRequestProof.emailId":::::', outOfBandRequestProof.emailId); + if (outOfBandRequestProof.emailId) { + const batchSize = 100; // Define the batch size according to your needs + const { emailId } = outOfBandRequestProof; // Assuming it's an array + const sentData = await this.sendEmailInBatches(payload, emailId, getAgentDetails, getOrganization, batchSize); + // eslint-disable-next-line no-console + console.log('Sent data is:::::::', sentData); + return true; + } else { + return this.generateOOBProofReq(payload, getAgentDetails); + // await this._sendOutOfBandProofRequest(payload); + } + // return getProofPresentation.response; } catch (error) { this.logger.error(`[sendOutOfBandPresentationRequest] - error in out of band proof request : ${error.message}`); this.verificationErrorHandling(error); @@ -412,6 +427,9 @@ export class VerificationService { const batch = emailIds.slice(i, i + batchSize); const emailPromises = batch.map(async email => { try { + await this.delay(5000); + // eslint-disable-next-line no-console + console.log(`Trying to send email to after 500 ms::::::::: ${email}`); await this.sendOutOfBandProofRequest(payload, email, getAgentDetails, organizationDetails); } catch (error) { accumulatedErrors.push(error); @@ -888,4 +906,7 @@ export class VerificationService { }); } + async delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } } \ No newline at end of file From d4189d3fa8f4f64122d587245d974a4d0c1ec35b Mon Sep 17 00:00:00 2001 From: Krishna Date: Tue, 5 Mar 2024 16:36:42 +0530 Subject: [PATCH 131/231] Initial changes to send email Signed-off-by: Krishna --- apps/agent-provisioning/AFJ/scripts/start_agent.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/agent-provisioning/AFJ/scripts/start_agent.sh b/apps/agent-provisioning/AFJ/scripts/start_agent.sh index fbb449988..87a595939 100755 --- a/apps/agent-provisioning/AFJ/scripts/start_agent.sh +++ b/apps/agent-provisioning/AFJ/scripts/start_agent.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash START_TIME=$(date +%s) From 8beae7ea601a11a75b51383b2190c10685f33c3a Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Tue, 5 Mar 2024 18:08:27 +0530 Subject: [PATCH 132/231] fix imageurl field bug Signed-off-by: bhavanakarwade --- apps/verification/src/verification.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 024255043..330b8d45d 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -349,7 +349,10 @@ export class VerificationService { const imageUrl = getOrganization?.logoUrl; const label = getOrganization?.name; - outOfBandRequestProof['imageUrl'] = imageUrl; + if (imageUrl) { + outOfBandRequestProof['imageUrl'] = imageUrl; + } + outOfBandRequestProof['label'] = label; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); From b85a5ad939d5c5da758c431a498c94777625449b Mon Sep 17 00:00:00 2001 From: Nishad Date: Tue, 5 Mar 2024 18:31:40 +0530 Subject: [PATCH 133/231] refactor API endpoint name Signed-off-by: Nishad --- apps/api-gateway/src/organization/organization.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index f894e091d..39e4e4047 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -389,7 +389,7 @@ export class OrganizationController { return res.status(HttpStatus.OK).json(finalResponse); } - @Post('/resiter-org-map-users') + @Post('/register-org-map-users') @ApiOperation({ summary: 'Register client and map users', description: 'Register client and map users' From ce256bc7ba7d9d5c086e7d243daa2431a1f24689 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Tue, 5 Mar 2024 18:39:20 +0530 Subject: [PATCH 134/231] resolved comments Signed-off-by: bhavanakarwade --- apps/verification/src/verification.service.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 330b8d45d..2f5f1afd5 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -346,11 +346,10 @@ export class VerificationService { this.verificationRepository.getOrganization(user.orgId) ]); - const imageUrl = getOrganization?.logoUrl; const label = getOrganization?.name; - if (imageUrl) { - outOfBandRequestProof['imageUrl'] = imageUrl; + if (getOrganization?.logoUrl) { + outOfBandRequestProof['imageUrl'] = getOrganization?.logoUrl; } outOfBandRequestProof['label'] = label; From 4cb0edf6f3db59575e1393d5a74ee64cc27c58ac Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 5 Mar 2024 19:09:23 +0530 Subject: [PATCH 135/231] refactor and fix the issuance return data on the OOB functionality Signed-off-by: KulkarniShashank --- apps/issuance/src/issuance.service.ts | 43 +++++++++++---------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index f54a8031d..e04c190bc 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -512,7 +512,8 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl } const errors = []; - const emailPromises = []; + let emailPromises; + const arrayEmailPromises = []; const sendEmailCredentialOffer: { iterator: CredentialOffer; emailId: string; @@ -546,36 +547,27 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl }; if (credentialOffer) { - for (let i = 0; i < credentialOffer.length; i += Number(process.env.OOB_BATCH_SIZE)) { - const batch = credentialOffer.slice(i, i + Number(process.env.OOB_BATCH_SIZE)); - // Process each batch in parallel - const batchPromises = batch.map(async (iterator, index) => { - + + for (const [index, iterator] of credentialOffer.entries()) { sendEmailCredentialOffer['iterator'] = iterator; sendEmailCredentialOffer['emailId'] = iterator.emailId; sendEmailCredentialOffer['index'] = index; - - return this.sendEmailForCredentialOffer(sendEmailCredentialOffer); - }); - emailPromises.push(batchPromises); + + await this.delay(500); // Wait for 0.5 seconds + const sendOobOffer = await this.sendEmailForCredentialOffer(sendEmailCredentialOffer); + + arrayEmailPromises.push(sendOobOffer); + } + if (0 < errors.length) { + throw errors; } + + return arrayEmailPromises.every((result) => true === result); } else { - emailPromises.push(this.sendEmailForCredentialOffer(sendEmailCredentialOffer)); + emailPromises = await this.sendEmailForCredentialOffer(sendEmailCredentialOffer); + return emailPromises; } - - const results = await Promise.all(emailPromises); - - // Flatten the results array - const flattenedResults = [].concat(...results); - - // Check if all emails were successfully sent - const allSuccessful = flattenedResults.every((result) => true === result); - - if (0 < errors.length) { - throw errors; - } - - return allSuccessful; + } catch (error) { this.logger.error( `[outOfBoundCredentialOffer] - error in create out-of-band credentials: ${JSON.stringify(error)}` @@ -705,6 +697,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO errors.push(new InternalServerErrorException(ResponseMessages.issuance.error.emailSend)); return false; } + return isEmailSent; } catch (error) { From 1a5b0908389765341a99baf81127ebe3ce403af2 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 5 Mar 2024 19:31:48 +0530 Subject: [PATCH 136/231] veriable name changes on the oob function Signed-off-by: KulkarniShashank --- apps/issuance/src/issuance.service.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index e04c190bc..64510b437 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -512,8 +512,8 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl } const errors = []; - let emailPromises; - const arrayEmailPromises = []; + let credentialOfferResponse; + const arraycredentialOfferResponse = []; const sendEmailCredentialOffer: { iterator: CredentialOffer; emailId: string; @@ -556,16 +556,16 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl await this.delay(500); // Wait for 0.5 seconds const sendOobOffer = await this.sendEmailForCredentialOffer(sendEmailCredentialOffer); - arrayEmailPromises.push(sendOobOffer); + arraycredentialOfferResponse.push(sendOobOffer); } if (0 < errors.length) { throw errors; } - return arrayEmailPromises.every((result) => true === result); + return arraycredentialOfferResponse.every((result) => true === result); } else { - emailPromises = await this.sendEmailForCredentialOffer(sendEmailCredentialOffer); - return emailPromises; + credentialOfferResponse = await this.sendEmailForCredentialOffer(sendEmailCredentialOffer); + return credentialOfferResponse; } } catch (error) { From cc687de3b2702f340dec74afd12720696734432c Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 5 Mar 2024 19:58:27 +0530 Subject: [PATCH 137/231] feat:env variable changes for platform white labeling task Signed-off-by: pranalidhanavade --- apps/ecosystem/src/ecosystem.service.ts | 2 +- .../ecosystem/templates/EcosystemInviteTemplate.ts | 14 +++++++------- .../templates/out-of-band-issuance.template.ts | 6 +++--- apps/organization/src/organization.service.ts | 2 +- .../templates/organization-invitation.template.ts | 12 ++++++------ apps/user/templates/arbiter-template.ts | 2 +- apps/user/templates/participant-template.ts | 2 +- apps/user/templates/reset-password-template.ts | 8 ++++---- apps/user/templates/user-email-template.ts | 8 ++++---- apps/user/templates/winner-template.ts | 2 +- apps/user/templates/world-record-template.ts | 2 +- .../templates/out-of-band-verification.template.ts | 6 +++--- 12 files changed, 33 insertions(+), 33 deletions(-) diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index a758ac941..ff92b5edc 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -593,7 +593,7 @@ export class EcosystemService { const emailData = new EmailDto(); emailData.emailFrom = platformConfigData[0].emailFrom; emailData.emailTo = email; - emailData.emailSubject = `Invitation to join an Ecosystem “${ecosystemName}” on CREDEBL`; + emailData.emailSubject = `Invitation to join an Ecosystem “${ecosystemName}” on ${process.env.PLATFORM_NAME}`; emailData.emailHtml = await urlEmailTemplate.sendInviteEmailTemplate( email, diff --git a/apps/ecosystem/templates/EcosystemInviteTemplate.ts b/apps/ecosystem/templates/EcosystemInviteTemplate.ts index 4403e1785..a4fe126b8 100644 --- a/apps/ecosystem/templates/EcosystemInviteTemplate.ts +++ b/apps/ecosystem/templates/EcosystemInviteTemplate.ts @@ -12,13 +12,13 @@ export class EcosystemInviteTemplate { const message = isUserExist ? `Please accept the invitation using the following link:` - : `To get started, kindly register on CREDEBL platform using this link:`; + : `To get started, kindly register on ${process.env.PLATFORM_NAME} platform using this link:`; const secondMessage = isUserExist - ? `After successful login into CREDEBL and click on "Accept Ecosystem Invitation" link on your dashboard to start participating in the digital trust ecosystem.` - : `After successful registration, you can log into CREDEBL and click on "Accept Ecosystem Invitation" link on your dashboard to start participating in the digital trust ecosystem.`; + ? `After successful login into ${process.env.PLATFORM_NAME} and click on "Accept Ecosystem Invitation" link on your dashboard to start participating in the digital trust ecosystem.` + : `After successful registration, you can log into ${process.env.PLATFORM_NAME} and click on "Accept Ecosystem Invitation" link on your dashboard to start participating in the digital trust ecosystem.`; - const Button = isUserExist ? 'Accept Ecosystem Invitation' : 'Register on CREDEBL'; + const Button = isUserExist ? `Accept Ecosystem Invitation` : `Register on ${process.env.PLATFORM_NAME}`; return ` @@ -33,7 +33,7 @@ export class EcosystemInviteTemplate {
- CREDEBL logo + ${process.env.PLATFORM_NAME} logo
- For any assistance or questions while accessing your account, please do not hesitate to contact the support team at support@blockster.global. Our team will ensure a seamless onboarding experience for you. + For any assistance or questions while accessing your account, please do not hesitate to contact the support team at ${process.env.PUBLIC_PLATFORM_SUPPORT_EMAIL}. Our team will ensure a seamless onboarding experience for you.

- © Blockster Labs Pvt. Ltd. + © ${process.env.POWERED_BY}.

diff --git a/apps/issuance/templates/out-of-band-issuance.template.ts b/apps/issuance/templates/out-of-band-issuance.template.ts index 9ac9b4ade..e97c04918 100644 --- a/apps/issuance/templates/out-of-band-issuance.template.ts +++ b/apps/issuance/templates/out-of-band-issuance.template.ts @@ -13,7 +13,7 @@ export class OutOfBandIssuance {
- CREDEBL logo + ${process.env.PLATFORM_NAME} logo
@@ -52,11 +52,11 @@ export class OutOfBandIssuance {
- For any assistance or questions while accessing your account, please do not hesitate to contact the support team at support@blockster.global. Our team will ensure a seamless onboarding experience for you. + For any assistance or questions while accessing your account, please do not hesitate to contact the support team at ${process.env.PUBLIC_PLATFORM_SUPPORT_EMAIL}. Our team will ensure a seamless onboarding experience for you.

- © Blockster Labs Pvt. Ltd. + © ${process.env.POWERED_BY}

diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 0d3154c44..15e8ddb7d 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -611,7 +611,7 @@ export class OrganizationService { const emailData = new EmailDto(); emailData.emailFrom = platformConfigData[0].emailFrom; emailData.emailTo = email; - emailData.emailSubject = `Invitation to join “${orgName}” on CREDEBL`; + emailData.emailSubject = `Invitation to join “${orgName}” on ${process.env.PLATFORM_NAME}`; emailData.emailHtml = await urlEmailTemplate.sendInviteEmailTemplate(email, orgName, orgRolesDetails, firstName, isUserExist); diff --git a/apps/organization/templates/organization-invitation.template.ts b/apps/organization/templates/organization-invitation.template.ts index 193886dc8..e3fb38f58 100644 --- a/apps/organization/templates/organization-invitation.template.ts +++ b/apps/organization/templates/organization-invitation.template.ts @@ -12,13 +12,13 @@ export class OrganizationInviteTemplate { const message = isUserExist ? `Please accept the invitation using the following link:` - : `To get started, kindly register on CREDEBL platform using this link:`; + : `To get started, kindly register on ${process.env.PLATFORM_NAME} platform using this link:`; const secondMessage = isUserExist - ? `After successful login into CREDEBL click on "Accept Organization Invitation" link on your dashboard.` + ? `After successful login into ${process.env.PLATFORM_NAME} click on "Accept Organization Invitation" link on your dashboard.` : `After successful registration, you can log in to the platform and click on “Accept Organization Invitation” on your dashboard.`; - const Button = isUserExist ? 'Accept Organization Invitation' : 'Register on CREDEBL'; + const Button = isUserExist ? `Accept Organization Invitation` : `Register on ${process.env.PLATFORM_NAME}`; return ` @@ -32,7 +32,7 @@ export class OrganizationInviteTemplate {
- CREDEBL logo + ${process.env.PLATFORM_NAME} logo
- For any assistance or questions while accessing your account, please do not hesitate to contact the support team at support@blockster.global. Our team will ensure a seamless onboarding experience for you. + For any assistance or questions while accessing your account, please do not hesitate to contact the support team at ${process.env.PUBLIC_PLATFORM_SUPPORT_EMAIL}. Our team will ensure a seamless onboarding experience for you.

- © Blockster Labs Pvt. Ltd. + © ${process.env.POWERED_BY}

diff --git a/apps/user/templates/arbiter-template.ts b/apps/user/templates/arbiter-template.ts index efeee0331..962c39710 100644 --- a/apps/user/templates/arbiter-template.ts +++ b/apps/user/templates/arbiter-template.ts @@ -86,7 +86,7 @@ export class ArbiterTemplate {

Date: 24, 25, 26 November 2023 | Place: Cidco Exhibition Centre, Navi Mumbai, India
-
Blockchain-based certificate issued using credebl.id, by Blockster Labs Pvt. Ltd.
+
Blockchain-based certificate issued using ${process.env.PLATFORM_WEB_URL}, by ${process.env.POWERED_BY}
diff --git a/apps/user/templates/participant-template.ts b/apps/user/templates/participant-template.ts index f2738fb23..c9250dd84 100644 --- a/apps/user/templates/participant-template.ts +++ b/apps/user/templates/participant-template.ts @@ -82,7 +82,7 @@ export class ParticipantTemplate {

exceptional memory skills demonstrated during the competition.

Date: 24, 25, 26 November 2023 | Place: Cidco Exhibition Centre, Navi Mumbai, India
-
Blockchain-based certificate issued using credebl.id, by Blockster Labs Pvt. Ltd.
+
Blockchain-based certificate issued using ${process.env.PLATFORM_WEB_URL}, by ${process.env.POWERED_BY}
diff --git a/apps/user/templates/reset-password-template.ts b/apps/user/templates/reset-password-template.ts index eb0019e68..ec05ecdde 100644 --- a/apps/user/templates/reset-password-template.ts +++ b/apps/user/templates/reset-password-template.ts @@ -23,7 +23,7 @@ export class URLUserResetPasswordTemplate {
- CREDEBL logo + ${process.env.PLATFORM_NAME} logo
@@ -46,11 +46,11 @@ export class URLUserResetPasswordTemplate {
- For any assistance or questions while accessing your account, please do not hesitate to contact the support team at support@blockster.global. Our team will ensure a seamless onboarding experience for you. + For any assistance or questions while accessing your account, please do not hesitate to contact the support team at ${process.env.PUBLIC_PLATFORM_SUPPORT_EMAIL}. Our team will ensure a seamless onboarding experience for you.

- © Blockster Labs Pvt. Ltd. + © ${process.env.POWERED_BY}

diff --git a/apps/user/templates/user-email-template.ts b/apps/user/templates/user-email-template.ts index 857a1bb7a..dd648c539 100644 --- a/apps/user/templates/user-email-template.ts +++ b/apps/user/templates/user-email-template.ts @@ -23,7 +23,7 @@ export class URLUserEmailTemplate {
- CREDEBL logo + ${process.env.PLATFORM_NAME} logo
@@ -48,11 +48,11 @@ export class URLUserEmailTemplate {
- For any assistance or questions while accessing your account, please do not hesitate to contact the support team at support@blockster.global. Our team will ensure a seamless onboarding experience for you. + For any assistance or questions while accessing your account, please do not hesitate to contact the support team at ${process.env.PUBLIC_PLATFORM_SUPPORT_EMAIL}. Our team will ensure a seamless onboarding experience for you.

- © Blockster Labs Pvt. Ltd. + © ${process.env.POWERED_BY}

diff --git a/apps/user/templates/winner-template.ts b/apps/user/templates/winner-template.ts index 72e3a3c30..aeb66b484 100644 --- a/apps/user/templates/winner-template.ts +++ b/apps/user/templates/winner-template.ts @@ -86,7 +86,7 @@ export class WinnerTemplate {

exceptional memory skills demonstrated during the competition.

Date: 24, 25, 26 November 2023 | Place: Cidco Exhibition Centre, Navi Mumbai, India
-
Blockchain-based certificate issued using credebl.id, by Blockster Labs Pvt. Ltd.
+
Blockchain-based certificate issued using ${process.env.PLATFORM_WEB_URL}, by ${process.env.POWERED_BY}
diff --git a/apps/user/templates/world-record-template.ts b/apps/user/templates/world-record-template.ts index 3eb30cf8c..27c9fbfda 100644 --- a/apps/user/templates/world-record-template.ts +++ b/apps/user/templates/world-record-template.ts @@ -84,7 +84,7 @@ export class WorldRecordTemplate {

exceptional memory skills demonstrated during the competition.

Date: 24, 25, 26 November 2023 | Place: Cidco Exhibition Centre, Navi Mumbai, India
-
Blockchain-based certificate issued using credebl.id, by Blockster Labs Pvt. Ltd.
+
Blockchain-based certificate issued using ${process.env.PLATFORM_WEB_URL}, by ${process.env.POWERED_BY}
diff --git a/apps/verification/templates/out-of-band-verification.template.ts b/apps/verification/templates/out-of-band-verification.template.ts index 858635ad2..a7a385775 100644 --- a/apps/verification/templates/out-of-band-verification.template.ts +++ b/apps/verification/templates/out-of-band-verification.template.ts @@ -14,7 +14,7 @@ export class OutOfBandVerification {
- CREDEBL logo + ${process.env.PLATFORM_NAME} logo
@@ -50,11 +50,11 @@ export class OutOfBandVerification {
- For any assistance or questions while accessing your account, please do not hesitate to contact the support team at support@blockster.global. Our team will ensure a seamless onboarding experience for you. + For any assistance or questions while accessing your account, please do not hesitate to contact the support team at ${process.env.PUBLIC_PLATFORM_SUPPORT_EMAIL}. Our team will ensure a seamless onboarding experience for you.

- © Blockster Labs Pvt. Ltd. + © ${process.env.POWERED_BY}

From 75b631df7829113d05d968f509b527ef2fbe35ae Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 5 Mar 2024 20:23:04 +0530 Subject: [PATCH 138/231] feat:env variable changes for platform white labeling task Signed-off-by: pranalidhanavade --- apps/api-gateway/src/main.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/api-gateway/src/main.ts b/apps/api-gateway/src/main.ts index 80d22c6ad..f8507b270 100644 --- a/apps/api-gateway/src/main.ts +++ b/apps/api-gateway/src/main.ts @@ -36,11 +36,11 @@ async function bootstrap(): Promise { .setDescription(`${process.env.PLATFORM_NAME} Platform APIs`) .setVersion('1.0') .addBearerAuth() - .addServer('http://localhost:5000') - .addServer('https://devapi.credebl.id') - .addServer('https://qa-api.credebl.id') - .addServer('https://api.credebl.id') - .addServer('https://sandboxapi.credebl.id') + .addServer(`${process.env.PUBLIC_DEV_API_URL}`) + .addServer(`${process.env.PUBLIC_LOCALHOST_URL}`) + .addServer(`${process.env.PUBLIC_QA_API_URL}`) + .addServer(`${process.env.PUBLIC_PRODUCTION_API_URL}`) + .addServer(`${process.env.PUBLIC_SANDBOX_API_URL}`) .addServer(`${process.env.API_GATEWAY_PROTOCOL}://${process.env.API_ENDPOINT}`) .addServer(`${process.env.API_GATEWAY_PROTOCOL}://${process.env.API_GATEWAY_HOST}`) .build(); From e39988162f7606c574aa939c3f325f02c8b95add Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Wed, 6 Mar 2024 12:45:51 +0530 Subject: [PATCH 139/231] develop to qa Signed-off-by: KulkarniShashank --- apps/issuance/src/issuance.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 64510b437..9806aca34 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -567,7 +567,6 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl credentialOfferResponse = await this.sendEmailForCredentialOffer(sendEmailCredentialOffer); return credentialOfferResponse; } - } catch (error) { this.logger.error( `[outOfBoundCredentialOffer] - error in create out-of-band credentials: ${JSON.stringify(error)}` From 9726f6ca0a9b6c141eecbd0a1433f42f22cff9af Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 6 Mar 2024 16:39:06 +0530 Subject: [PATCH 140/231] feat:changes in platform to get dynamic values from .env file Signed-off-by: pranalidhanavade --- apps/ecosystem/templates/EcosystemInviteTemplate.ts | 2 +- apps/organization/templates/organization-onboard.template.ts | 2 +- apps/organization/templates/organization-url-template.ts | 2 +- apps/user/templates/user-onboard.template.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/ecosystem/templates/EcosystemInviteTemplate.ts b/apps/ecosystem/templates/EcosystemInviteTemplate.ts index a4fe126b8..2d1166ea0 100644 --- a/apps/ecosystem/templates/EcosystemInviteTemplate.ts +++ b/apps/ecosystem/templates/EcosystemInviteTemplate.ts @@ -68,7 +68,7 @@ export class EcosystemInviteTemplate {

- © ${process.env.POWERED_BY}. + © ${process.env.POWERED_BY}

diff --git a/apps/organization/templates/organization-onboard.template.ts b/apps/organization/templates/organization-onboard.template.ts index 0750a742e..7f4c8b3be 100644 --- a/apps/organization/templates/organization-onboard.template.ts +++ b/apps/organization/templates/organization-onboard.template.ts @@ -25,7 +25,7 @@ export class OnBoardVerificationRequest {
Credebl Logo
- +
diff --git a/apps/user/templates/user-onboard.template.ts b/apps/user/templates/user-onboard.template.ts index 66a60b702..99faeb07d 100644 --- a/apps/user/templates/user-onboard.template.ts +++ b/apps/user/templates/user-onboard.template.ts @@ -25,7 +25,7 @@ export class OnBoardVerificationRequest {
Credebl Logo Date: Wed, 6 Mar 2024 16:45:50 +0530 Subject: [PATCH 141/231] feat: email functionality via oob email Signed-off-by: Krishna --- .../src/interfaces/verification.interface.ts | 2 +- apps/verification/src/verification.service.ts | 99 +++++++------------ libs/common/src/response-messages/index.ts | 1 + 3 files changed, 35 insertions(+), 67 deletions(-) diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index d516479aa..a5e1ce993 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -168,7 +168,7 @@ export interface IWSendProofRequestPayload { export interface IProofRequestPayload { url: string; apiKey: string; - proofRequestPayload: ISendProofRequestPayload; + proofRequestPayload: ISendProofRequestPayload | ISendPresentationExchangeProofRequestPayload; } interface IWebhookPresentationProof { diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index aa3cf499b..c8c93a2ce 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -2,7 +2,7 @@ import { BadRequestException, HttpException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs/operators'; -import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IPresentationExchangeProofRequestPayload} from './interfaces/verification.interface'; +import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData} from './interfaces/verification.interface'; import { VerificationRepository } from './repositories/verification.repository'; import { CommonConstants } from '@credebl/common/common.constant'; import { org_agents, organisation, presentations } from '@prisma/client'; @@ -339,12 +339,6 @@ export class VerificationService { outOfBandRequestProof.protocolVersion = outOfBandRequestProof.protocolVersion || 'v1'; outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; - // eslint-disable-next-line no-console - console.log('Received outOfBandRequestProof in "sendOutOfBandPresentationRequest:::::"', JSON.stringify(outOfBandRequestProof)); - // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); - // outOfBandRequestProof.proofFormats.indy.requested_attributes = requestedAttributes; - // outOfBandRequestProof.proofFormats.indy.requested_predicates = requestedPredicates; - const [getAgentDetails, getOrganization] = await Promise.all([ this.verificationRepository.getAgentEndPoint(user.orgId), this.verificationRepository.getOrganization(user.orgId) @@ -366,34 +360,20 @@ export class VerificationService { // Destructuring 'outOfBandRequestProof' to remove emailId, as it is not used while agent operation // eslint-disable-next-line @typescript-eslint/no-unused-vars - const {emailId, ...proofRequestPayload} = outOfBandRequestProof; - // eslint-disable-next-line no-console - console.log('This is the email in Verification-microservice->service::::::', emailId); - + const {emailId, type, ...proofRequestPayload} = outOfBandRequestProof; - let payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload; + + let payload: IProofRequestPayload; if (ProofRequestType.INDY === outOfBandRequestProof.type) { - // const outOfBandIndyRequestPayload = { ...outOfBandRequestProof }; - // delete outOfBandIndyRequestPayload.type; - payload = { apiKey, url, proofRequestPayload }; - - // const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - // if (!getProofPresentation) { - // throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); - // } - // return getProofPresentation.response; - // proofRequestPayload: outOfBandIndyRequestPayload - // }; } if (ProofRequestType.PRESENTATIONEXCHANGE === outOfBandRequestProof.type) { - payload = { apiKey, url, @@ -412,36 +392,23 @@ export class VerificationService { autoAcceptProof:outOfBandRequestProof.autoAcceptProof || 'always' } }; - } - - - const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - if (!getProofPresentation) { - throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); - } - return getProofPresentation.response; - - // Unused code : to be segregated - // eslint-disable-next-line no-console - console.log('This is "outOfBandRequestProof.emailId":::::', outOfBandRequestProof.emailId); - // if (outOfBandRequestProof.emailId) { - // const batchSize = 100; // Define the batch size according to your needs - // const { emailId } = outOfBandRequestProof; // Assuming it's an array - // const sentData = await this.sendEmailInBatches(payload, emailId, getAgentDetails, getOrganization, batchSize); - // // eslint-disable-next-line no-console - // console.log('Sent data is:::::::', sentData); - // return true; - // } else { - // return this.generateOOBProofReq(payload, getAgentDetails); - // // await this._sendOutOfBandProofRequest(payload); - // } - // return getProofPresentation.response; + }; + + if (outOfBandRequestProof.emailId) { + const batchSize = parseInt(process.env.OOB_BATCH_SIZE); // Batch size taken from env. It is same for issuance and verification + const { emailId } = outOfBandRequestProof; // Assuming it's an array + await this.sendEmailInBatches(payload, emailId, getAgentDetails, getOrganization, batchSize); + return true; + } else { + return await this.generateOOBProofReq(payload, getAgentDetails); + } } catch (error) { this.logger.error(`[sendOutOfBandPresentationRequest] - error in out of band proof request : ${error.message}`); this.verificationErrorHandling(error); } } + private async generateOOBProofReq(payload: IProofRequestPayload, getAgentDetails: org_agents): Promise { let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { @@ -458,37 +425,37 @@ export class VerificationService { } - async sendEmailInBatches(payload: IProofRequestPayload, emailIds: string[] | string, getAgentDetails: org_agents, organizationDetails: organisation, batchSize: number): Promise { + // Notes: Only accept array of string for emailIds + async sendEmailInBatches(payload: IProofRequestPayload, emailIds: string[], getAgentDetails: org_agents, organizationDetails: organisation, batchSize: number): Promise { + try { const accumulatedErrors = []; - if (Array.isArray(emailIds)) { - for (let i = 0; i < emailIds.length; i += batchSize) { const batch = emailIds.slice(i, i + batchSize); - const emailPromises = batch.map(async email => { + for (const email of batch) { try { - await this.delay(5000); - // eslint-disable-next-line no-console - console.log(`Trying to send email to after 500 ms::::::::: ${email}`); - await this.sendOutOfBandProofRequest(payload, email, getAgentDetails, organizationDetails); - } catch (error) { - accumulatedErrors.push(error); - } - }); - - await Promise.all(emailPromises); + await this.sendOutOfBandProofRequest(payload, email, getAgentDetails, organizationDetails); + await this.delay(500); + } catch (error) { + this.logger.error(`Error sending email to ${email}::::::`, error); + accumulatedErrors.push(error); + } + } } - } else { - await this.sendOutOfBandProofRequest(payload, emailIds, getAgentDetails, organizationDetails); - } if (0 < accumulatedErrors.length) { this.logger.error(accumulatedErrors); throw new Error(ResponseMessages.verification.error.emailSend); } + + } catch (error) { + this.logger.error('[sendEmailInBatches] - error in sending email in batches'); + throw new Error(ResponseMessages.verification.error.batchEmailSend); + } } + // This function is specifically for OOB verification using email async sendOutOfBandProofRequest(payload: IProofRequestPayload, email: string, getAgentDetails: org_agents, organizationDetails: organisation): Promise { let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { @@ -551,7 +518,7 @@ export class VerificationService { response; }> { try { - + const pattern = { cmd: 'agent-send-out-of-band-proof-request' }; diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index e94b566c3..532720e5d 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -294,6 +294,7 @@ export const ResponseMessages = { proofNotFound: 'Proof presentation not found', invitationNotFound: 'Invitation not found', platformConfigNotFound: 'Platform config not found', + batchEmailSend: 'Unable to send email in batches', emailSend: 'Unable to send email to the user' } }, From 3cc2e4afd2be83853529d38d86bd159d510b59b6 Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 6 Mar 2024 16:47:57 +0530 Subject: [PATCH 142/231] remove logs Signed-off-by: Krishna --- apps/api-gateway/src/verification/verification.controller.ts | 2 -- apps/verification/src/verification.controller.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index 7d9a186f3..bed9126f0 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -261,8 +261,6 @@ export class VerificationController { @Param('orgId') orgId: string, @Query('requestType') requestType:ProofRequestType = ProofRequestType.INDY ): Promise { - // eslint-disable-next-line no-console - console.log('Received email in API-gateway-controller -> ', outOfBandRequestProof.emailId); user.orgId = orgId; outOfBandRequestProof.type = requestType; const result = await this.verificationService.sendOutOfBandPresentationRequest(outOfBandRequestProof, user); diff --git a/apps/verification/src/verification.controller.ts b/apps/verification/src/verification.controller.ts index e0bbbe1fe..34e8bd9db 100644 --- a/apps/verification/src/verification.controller.ts +++ b/apps/verification/src/verification.controller.ts @@ -64,8 +64,6 @@ export class VerificationController { @MessagePattern({ cmd: 'send-out-of-band-proof-request' }) async sendOutOfBandPresentationRequest(payload: { outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest }): Promise { - // eslint-disable-next-line no-console - console.log('Stringify Received email in "Verification Controller"', JSON.stringify(payload.outOfBandRequestProof.emailId)); return this.verificationService.sendOutOfBandPresentationRequest(payload.outOfBandRequestProof, payload.user); } From 198103c3fe2e34ef14ab8112d33c9df315638950 Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 6 Mar 2024 17:15:49 +0530 Subject: [PATCH 143/231] fix: logging Signed-off-by: Krishna --- apps/api-gateway/src/verification/dto/request-proof.dto.ts | 3 --- apps/verification/src/verification.service.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 2a6c81a4f..869a9c0f5 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -327,12 +327,9 @@ export class SendProofRequestPayload { @IsNotEmpty({ message: 'please provide valid parentThreadId' }) parentThreadId: string; - // @ApiProperty() @ApiPropertyOptional() @IsEmail({}, { each: true, message: 'Please provide a valid email' }) @ArrayNotEmpty({ message: 'Email array must not be empty' }) - // @Transform(({ value }) => trim(value)) - // @Transform(({ value }) => value.map((email: string) => email.trim())) @ArrayUnique({ message: 'Duplicate emails are not allowed' }) @IsArray() @IsString({ each: true, message: 'Each emailId in the array should be a string' }) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index c8c93a2ce..6be759238 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -425,7 +425,7 @@ export class VerificationService { } - // Notes: Only accept array of string for emailIds + // Only accept array of string for emailIds async sendEmailInBatches(payload: IProofRequestPayload, emailIds: string[], getAgentDetails: org_agents, organizationDetails: organisation, batchSize: number): Promise { try { const accumulatedErrors = []; From 2689d80dbc71f4176e4e242b50e8dba03c57f3f0 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 6 Mar 2024 18:09:36 +0530 Subject: [PATCH 144/231] feat:changes in env sample file Signed-off-by: pranalidhanavade --- .env.sample | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index 17873880c..1baf4c3f6 100644 --- a/.env.sample +++ b/.env.sample @@ -9,7 +9,17 @@ API_GATEWAY_PROTOCOL=http API_GATEWAY_HOST='0.0.0.0' API_GATEWAY_PORT=5000 -PLATFORM_NAME=CREDEBL +## +PLATFORM_NAME= // Please specify your paltform name + +PUBLIC_PLATFORM_SUPPORT_EMAIL= // Please specify your support email + +PUBLIC_LOCALHOST_URL= // Please specify your localhost Url +PUBLIC_DEV_API_URL= // Please specify your DEV environment api Url +PUBLIC_QA_API_URL= // Please specify your your QA environment api Url +PUBLIC_PRODUCTION_API_URL= // Please specify your PRODUCTION environment api Url +PUBLIC_SANDBOX_API_URL= // Please specify your sandbox environment Url + AGENT_HOST=username@0.0.0.0 // Please specify your agent host VM and IP address AWS_ACCOUNT_ID=xxxxx // Please provide your AWS account Id From b666d97963e7779662a1024c8eef7d2a3e1cbd78 Mon Sep 17 00:00:00 2001 From: Nishad Date: Wed, 6 Mar 2024 18:38:26 +0530 Subject: [PATCH 145/231] renamed the variable in organization repository Signed-off-by: Nishad --- apps/organization/repositories/organization.repository.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index a267176e4..e2464aab6 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -494,7 +494,7 @@ export class OrganizationRepository { async getUnregisteredClientOrgs(): Promise { try { - const recordsWithNullValues = await this.prisma.organisation.findMany({ + const recordsWithNullIdpId = await this.prisma.organisation.findMany({ where: { idpId: null }, @@ -516,7 +516,7 @@ export class OrganizationRepository { } }); - return recordsWithNullValues; + return recordsWithNullIdpId; } catch (error) { this.logger.error(`error: ${JSON.stringify(error)}`); From 3a021f82d61b28426d97a5ab4aa4bc6b1576ea97 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 6 Mar 2024 20:10:29 +0530 Subject: [PATCH 146/231] feat:changes in common constant file for white labeling Signed-off-by: pranalidhanavade --- .env.sample | 17 ++++++++--------- .../user/dto/update-platform-settings.dto.ts | 2 +- libs/common/src/common.constant.ts | 8 -------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/.env.sample b/.env.sample index 1baf4c3f6..1e35a0246 100644 --- a/.env.sample +++ b/.env.sample @@ -1,7 +1,7 @@ MODE=DEV -SUPABASE_URL= // Please specify your Supabase Url +SUPABASE_URL= // Please specify your Supabase URL SUPABASE_KEY= // Please specify your Supabase Anon key SUPABASE_JWT_SECRET= // Please specify your Supabase jwt secret @@ -11,14 +11,13 @@ API_GATEWAY_PORT=5000 ## PLATFORM_NAME= // Please specify your paltform name - PUBLIC_PLATFORM_SUPPORT_EMAIL= // Please specify your support email -PUBLIC_LOCALHOST_URL= // Please specify your localhost Url -PUBLIC_DEV_API_URL= // Please specify your DEV environment api Url -PUBLIC_QA_API_URL= // Please specify your your QA environment api Url -PUBLIC_PRODUCTION_API_URL= // Please specify your PRODUCTION environment api Url -PUBLIC_SANDBOX_API_URL= // Please specify your sandbox environment Url +PUBLIC_LOCALHOST_URL= // Please specify your localhost URL +PUBLIC_DEV_API_URL= // Please specify your DEV environment api URL +PUBLIC_QA_API_URL= // Please specify your your QA environment api URL +PUBLIC_PRODUCTION_API_URL= // Please specify your PRODUCTION environment api URL +PUBLIC_SANDBOX_API_URL= // Please specify your sandbox environment URL AGENT_HOST=username@0.0.0.0 // Please specify your agent host VM and IP address @@ -57,8 +56,8 @@ PLATFORM_SEED= // The seed should consist of 32 characters. PLATFORM_ID= AFJ_AGENT_ENDPOINT_PATH=/apps/agent-provisioning/AFJ/endpoints/ -DATABASE_URL="postgresql://postgres:xxxxxx@localhost:5432/postgres?schema=public" #Provide supabase postgres url and Use the correct user/pwd, IP Address -POOL_DATABASE_URL="" #Provide pooler supabase postgres url +DATABASE_URL="postgresql://postgres:xxxxxx@localhost:5432/postgres?schema=public" #Provide supabase postgres URL and Use the correct user/pwd, IP Address +POOL_DATABASE_URL="" #Provide pooler supabase postgres URL CLUSTER_NAME="" # ecs cluster TESKDEFINITION_FAMILY="" # ecs task-definition AGENT_PROTOCOL=http diff --git a/apps/api-gateway/src/user/dto/update-platform-settings.dto.ts b/apps/api-gateway/src/user/dto/update-platform-settings.dto.ts index 79d4cdb76..d452e213a 100644 --- a/apps/api-gateway/src/user/dto/update-platform-settings.dto.ts +++ b/apps/api-gateway/src/user/dto/update-platform-settings.dto.ts @@ -23,7 +23,7 @@ export class UpdatePlatformSettingsDto { @IsString({ message: 'emailFrom should be string' }) emailFrom: string; - @ApiProperty({ example: 'dev.credebl.id' }) + @ApiProperty({ example: `${process.env.UPLOAD_LOGO_HOST}` }) @IsOptional() @IsString({ message: 'API endpoint should be string' }) apiEndPoint: string; diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index 2f03c0019..14f7ddc02 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -260,14 +260,6 @@ export enum CommonConstants { ACTIVE_NON_ADMIN_USER = 1, ALL_NON_ADMIN_USER = 3, - //passwordLess-login - PASSWORDLESS_LOGIN_SCHEMA_ORG = 1, - PASSWORDLESS_LOGIN_SCHEMA_NAME = 'CREDEBL-PLA', - PLATFORM_ADMIN_CRED_DEF_NAME = 'CREDEBL-PLA', - PLATFORM_ADMIN_SCHEMA_VERSION = '1.0', - - LOGIN_PASSWORDLESS = 'passwordless', - LOGIN_PASSWORD = 'password', // Platform admin Details PLATFORM_ADMIN_EMAIL='platform.admin@yopmail.com', From 82749cf23307d780f61852f80fb3aa09d02f9cff Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Thu, 7 Mar 2024 12:44:33 +0530 Subject: [PATCH 147/231] feat:change sin env sample file for white labelling changes Signed-off-by: pranalidhanavade --- .env.sample | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index 1e35a0246..608d2d608 100644 --- a/.env.sample +++ b/.env.sample @@ -1,6 +1,5 @@ MODE=DEV - SUPABASE_URL= // Please specify your Supabase URL SUPABASE_KEY= // Please specify your Supabase Anon key SUPABASE_JWT_SECRET= // Please specify your Supabase jwt secret @@ -12,6 +11,9 @@ API_GATEWAY_PORT=5000 ## PLATFORM_NAME= // Please specify your paltform name PUBLIC_PLATFORM_SUPPORT_EMAIL= // Please specify your support email +POWERED_BY= // Please specify your powered by org name +PLATFORM_WEB_URL= // Please specify your platform web URL +POWERED_BY_URL= // Please specify your support URL PUBLIC_LOCALHOST_URL= // Please specify your localhost URL PUBLIC_DEV_API_URL= // Please specify your DEV environment api URL From 661b3fd225c6caa5e751ec9041f467d3140fd090 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 7 Mar 2024 14:03:02 +0530 Subject: [PATCH 148/231] Minor changes to issuance and verification for shortening url Signed-off-by: Krishna --- apps/issuance/src/issuance.service.ts | 21 ++++++++----------- apps/verification/src/verification.service.ts | 12 ++--------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 13a9c7c50..88b0aec36 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -696,8 +696,6 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO }; } - const agentDetails = await this.issuanceRepository.getAgentEndPoint(organisation.id); - this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, apiKey); @@ -707,16 +705,15 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO return false; } - const invitationId = credentialCreateOfferDetails.response.invitation['@id']; - if (!invitationId) { - errors.push(new NotFoundException(ResponseMessages.issuance.error.invitationNotFound)); - return false; - } - const agentEndPoint = agentDetails.tenantId - ? `${agentDetails.agentEndPoint}/multi-tenancy/url/${agentDetails.tenantId}/${invitationId}` - : `${agentDetails.agentEndPoint}/url/${invitationId}`; + const invitationUrl: string = credentialCreateOfferDetails.response?.invitationUrl; + const shortenUrl: string = await this.storeIssuanceObjectReturnUrl(invitationUrl); + + if (!invitationUrl) { + errors.push(new NotFoundException(ResponseMessages.issuance.error.invitationNotFound)); + return false; + } const qrCodeOptions = { type: 'image/png' }; - const outOfBandIssuanceQrCode = await QRCode.toDataURL(agentEndPoint, qrCodeOptions); + const outOfBandIssuanceQrCode = await QRCode.toDataURL(shortenUrl, qrCodeOptions); const platformConfigData = await this.issuanceRepository.getPlatformConfigDetails(); if (!platformConfigData) { errors.push(new NotFoundException(ResponseMessages.issuance.error.platformConfigNotFound)); @@ -725,7 +722,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO this.emailData.emailFrom = platformConfigData.emailFrom; this.emailData.emailTo = emailId; this.emailData.emailSubject = `${process.env.PLATFORM_NAME} Platform: Issuance of Your Credential`; - this.emailData.emailHtml = this.outOfBandIssuance.outOfBandIssuance(emailId, organizationDetails.name, agentEndPoint); + this.emailData.emailHtml = this.outOfBandIssuance.outOfBandIssuance(emailId, organizationDetails.name, shortenUrl); this.emailData.emailAttachments = [ { filename: 'qrcode.png', diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 7bc74b92b..2d9d9ee42 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -359,25 +359,17 @@ export class VerificationService { apiKey = await this._getOrgAgentApiKey(user.orgId); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { isShortenUrl, ...updateOutOfBandRequestProof } = outOfBandRequestProof; - // const payload: IProofRequestPayload - // = { - // apiKey, - // url, - // proofRequestPayload: updateOutOfBandRequestProof - // }; + const { isShortenUrl, type, ...updateOutOfBandRequestProof } = outOfBandRequestProof; let payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload; if (ProofRequestType.INDY === outOfBandRequestProof.type) { - const outOfBandIndyRequestPayload = { ...outOfBandRequestProof }; - delete outOfBandIndyRequestPayload.type; payload = { apiKey, url, - proofRequestPayload: outOfBandIndyRequestPayload + proofRequestPayload: updateOutOfBandRequestProof }; } From 7f0f0e06ba86304e46662dfe5cb2df2f785455f8 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 7 Mar 2024 15:10:55 +0530 Subject: [PATCH 149/231] Change default version for w3c credential to v2 Signed-off-by: Krishna --- apps/verification/src/verification.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index b64800f79..27faf3528 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -381,7 +381,7 @@ export class VerificationService { apiKey, url, proofRequestPayload: { - protocolVersion:outOfBandRequestProof.protocolVersion || 'v1', + protocolVersion:outOfBandRequestProof.protocolVersion || 'v2', comment:outOfBandRequestProof.comment, label, proofFormats: { From 7f3458c487fdb080c07dfc757ed6ce25f6797b47 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 7 Mar 2024 15:20:17 +0530 Subject: [PATCH 150/231] fix: protocol versionining Signed-off-by: Krishna --- apps/verification/src/verification.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 27faf3528..a4cc4bde5 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -336,7 +336,6 @@ export class VerificationService { async sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { try { - outOfBandRequestProof.protocolVersion = outOfBandRequestProof.protocolVersion || 'v1'; outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); @@ -367,7 +366,7 @@ export class VerificationService { let payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload; if (ProofRequestType.INDY === outOfBandRequestProof.type) { - + updateOutOfBandRequestProof.protocolVersion = outOfBandRequestProof.protocolVersion || 'v1'; payload = { apiKey, url, From 28e6b62e99185e15b52dcfc196c11edde72d0382 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 7 Mar 2024 15:48:08 +0530 Subject: [PATCH 151/231] fix: linting issues Signed-off-by: Krishna --- apps/verification/src/verification.service.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index a4cc4bde5..dc21147f5 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -336,8 +336,6 @@ export class VerificationService { async sendOutOfBandPresentationRequest(outOfBandRequestProof: ISendProofRequestPayload, user: IUserRequest): Promise { try { - outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; - // const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(outOfBandRequestProof); const [getAgentDetails, getOrganization] = await Promise.all([ @@ -359,14 +357,14 @@ export class VerificationService { if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(user.orgId); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { isShortenUrl, type, ...updateOutOfBandRequestProof } = outOfBandRequestProof; + const { isShortenUrl, type, ...updateOutOfBandRequestProof } = outOfBandRequestProof; + outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; let payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload; - if (ProofRequestType.INDY === outOfBandRequestProof.type) { - updateOutOfBandRequestProof.protocolVersion = outOfBandRequestProof.protocolVersion || 'v1'; + if (ProofRequestType.INDY === type) { + updateOutOfBandRequestProof.protocolVersion = updateOutOfBandRequestProof.protocolVersion || 'v1'; payload = { apiKey, url, @@ -374,7 +372,7 @@ export class VerificationService { }; } - if (ProofRequestType.PRESENTATIONEXCHANGE === outOfBandRequestProof.type) { + if (ProofRequestType.PRESENTATIONEXCHANGE === type) { payload = { apiKey, From 0f1bced0d58bbb19786cdc14ccc32e6b10ed1916 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 7 Mar 2024 17:41:10 +0530 Subject: [PATCH 152/231] fix: solved the endosement credential definition Signed-off-by: KulkarniShashank --- .env.sample | 9 ++++++- apps/ecosystem/src/ecosystem.service.ts | 25 ++++++++++--------- .../src/client-registration.service.ts | 6 ----- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/.env.sample b/.env.sample index 1e35a0246..a8472ab00 100644 --- a/.env.sample +++ b/.env.sample @@ -77,4 +77,11 @@ export DEBUG="prisma:engine" export DEBUG="prisma:client" # enable both prisma-client- and engine-level debugging output -export DEBUG="prisma:client,prisma:engine" \ No newline at end of file +export DEBUG="prisma:client,prisma:engine" + +KEYCLOAK_DOMAIN=http://localhost:8080/ +KEYCLOAK_ADMIN_URL=http://localhost:8080 +KEYCLOAK_MASTER_REALM=xxxxxxx +KEYCLOAK_MANAGEMENT_CLIENT_ID=xxxxxxx +KEYCLOAK_MANAGEMENT_CLIENT_SECRET=xxxxxxx +KEYCLOAK_REALM=xxxxxxx \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index ff92b5edc..fa78b67c6 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -1326,7 +1326,6 @@ export class EcosystemService { endorsementTransactionStatus.SIGNED ); - if (!endorsementTransactionPayload) { throw new InternalServerErrorException(ResponseMessages.ecosystem.error.invalidTransaction); } @@ -1355,18 +1354,20 @@ export class EcosystemService { ecosystemLeadAgentDetails ); - const isSchemaExists = await this.ecosystemRepository.schemaExist( - payload.schema.name, - payload.schema.version - ); - - if (0 !== isSchemaExists.length) { - this.logger.error(ResponseMessages.ecosystem.error.schemaAlreadyExist); - throw new ConflictException( - ResponseMessages.ecosystem.error.schemaAlreadyExist, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } + if (endorsementTransactionPayload.type === endorsementTransactionType.SCHEMA) { + const isSchemaExists = await this.ecosystemRepository.schemaExist( + payload.schema.name, + payload.schema.version ); - } + + if (0 !== isSchemaExists.length) { + this.logger.error(ResponseMessages.ecosystem.error.schemaAlreadyExist); + throw new ConflictException( + ResponseMessages.ecosystem.error.schemaAlreadyExist, + { cause: new Error(), description: ResponseMessages.errorMessages.conflict } + ); + } + } let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index ea9fdbb78..a43fef46a 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -675,9 +675,6 @@ export class ClientRegistrationService { qs.stringify(payload) , config); - this.logger.debug( - `ClientRegistrationService token ${JSON.stringify(tokenResponse)}` - ); return tokenResponse; } catch (error) { throw error; @@ -817,9 +814,6 @@ export class ClientRegistrationService { qs.stringify(payload) , config); - this.logger.debug( - `ClientRegistrationService token ${JSON.stringify(tokenResponse)}` - ); return tokenResponse; } catch (error) { From 0a366e9d75a8dcebeef1650ddb0ac09f00b349b9 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 7 Mar 2024 17:44:35 +0530 Subject: [PATCH 153/231] fix: sonar lint issue of code repeatibility Signed-off-by: Krishna --- apps/issuance/src/issuance.service.ts | 64 ++++++++++++++------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 88b0aec36..8b0139d44 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -270,37 +270,39 @@ export class IssuanceService { const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; - try { - const message = await this.issuanceServiceProxy - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error( - `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( - error - )}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); - return message; - } catch (error) { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.status, - error: error.message - }, - error.status - ); - } + // try { + // const message = await this.issuanceServiceProxy + // // eslint-disable-next-line @typescript-eslint/no-explicit-any + // .send(pattern, payload) + // .toPromise() + // .catch((error) => { + // this.logger.error( + // `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( + // error + // )}` + // ); + // throw new HttpException( + // { + // status: error.statusCode, + // error: error.error?.message?.error ? error.error?.message?.error : error.error, + // message: error.message + // }, + // error.error + // ); + // }); + // return message; + // } catch (error) { + // this.logger.error(`catch: ${JSON.stringify(error)}`); + // throw new HttpException( + // { + // status: error.status, + // error: error.message + // }, + // error.status + // ); + // } + const message = await this.natsCall(pattern, payload); + return message.response; } // Created this function to avoid the impact of actual "natsCall" function for other operations From dd074f120954e92df17eec078fc673cbb992ee7b Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 7 Mar 2024 17:55:47 +0530 Subject: [PATCH 154/231] fix: sonar lint issue of code repeatibility in Issuance Signed-off-by: Krishna --- apps/verification/src/verification.service.ts | 65 ++++++++++--------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index dc21147f5..8555d5cf7 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -430,37 +430,40 @@ export class VerificationService { const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; - try { - const message = await this.verificationServiceProxy - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error( - `[storeVerificationObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( - error - )}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); - return message; - } catch (error) { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.status, - error: error.message - }, - error.status - ); - } + // try { + // const message = await this.verificationServiceProxy + // // eslint-disable-next-line @typescript-eslint/no-explicit-any + // .send(pattern, payload) + // .toPromise() + // .catch((error) => { + // this.logger.error( + // `[storeVerificationObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( + // error + // )}` + // ); + // throw new HttpException( + // { + // status: error.statusCode, + // error: error.error?.message?.error ? error.error?.message?.error : error.error, + // message: error.message + // }, + // error.error + // ); + // }); + // return message; + // } catch (error) { + // this.logger.error(`catch: ${JSON.stringify(error)}`); + // throw new HttpException( + // { + // status: error.status, + // error: error.message + // }, + // error.status + // ); + // } + + const message = await this.natsCall(pattern, payload); + return message.response; } From e240ecbb0c4698a676d4e34f910bc1b39eb19c14 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 7 Mar 2024 17:58:36 +0530 Subject: [PATCH 155/231] Replace the findFirst to findUnique query on the ecosystem Signed-off-by: KulkarniShashank --- apps/ecosystem/src/ecosystem.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index a42b0e373..01de77bf6 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -905,7 +905,7 @@ export class EcosystemRepository { // eslint-disable-next-line camelcase async getEndorsementTransactionById(endorsementId: string, status: endorsementTransactionStatus): Promise { try { - const ecosystemLeadDetails = await this.prisma.endorsement_transaction.findFirst({ + const ecosystemLeadDetails = await this.prisma.endorsement_transaction.findUnique({ where: { id: endorsementId, status From 878cb50b8e0a37400f4c4eab906876f752ede696 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 7 Mar 2024 18:43:03 +0530 Subject: [PATCH 156/231] fix: remove comments & unused code Signed-off-by: Krishna --- apps/issuance/src/issuance.service.ts | 32 ------------------ apps/verification/src/verification.service.ts | 33 ------------------- 2 files changed, 65 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 8b0139d44..5bd5725c7 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -269,38 +269,6 @@ export class IssuanceService { //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; - - // try { - // const message = await this.issuanceServiceProxy - // // eslint-disable-next-line @typescript-eslint/no-explicit-any - // .send(pattern, payload) - // .toPromise() - // .catch((error) => { - // this.logger.error( - // `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( - // error - // )}` - // ); - // throw new HttpException( - // { - // status: error.statusCode, - // error: error.error?.message?.error ? error.error?.message?.error : error.error, - // message: error.message - // }, - // error.error - // ); - // }); - // return message; - // } catch (error) { - // this.logger.error(`catch: ${JSON.stringify(error)}`); - // throw new HttpException( - // { - // status: error.status, - // error: error.message - // }, - // error.status - // ); - // } const message = await this.natsCall(pattern, payload); return message.response; } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 8555d5cf7..b00c7e2f1 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -429,39 +429,6 @@ export class VerificationService { //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; - - // try { - // const message = await this.verificationServiceProxy - // // eslint-disable-next-line @typescript-eslint/no-explicit-any - // .send(pattern, payload) - // .toPromise() - // .catch((error) => { - // this.logger.error( - // `[storeVerificationObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( - // error - // )}` - // ); - // throw new HttpException( - // { - // status: error.statusCode, - // error: error.error?.message?.error ? error.error?.message?.error : error.error, - // message: error.message - // }, - // error.error - // ); - // }); - // return message; - // } catch (error) { - // this.logger.error(`catch: ${JSON.stringify(error)}`); - // throw new HttpException( - // { - // status: error.status, - // error: error.message - // }, - // error.status - // ); - // } - const message = await this.natsCall(pattern, payload); return message.response; } From 67ef5991fdd85f1a5f84bd71465bdc8ef98387e6 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 7 Mar 2024 19:00:17 +0530 Subject: [PATCH 157/231] Solved the organization name updation on the email issuance Signed-off-by: KulkarniShashank --- apps/issuance/src/issuance.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 64510b437..0a7f7bc9d 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -629,7 +629,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO goalCode: outOfBandCredential.goalCode || undefined, parentThreadId: outOfBandCredential.parentThreadId || undefined, willConfirm: outOfBandCredential.willConfirm || undefined, - label: outOfBandCredential.label || undefined, + label: organisation?.name, imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl }; } @@ -648,7 +648,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO goalCode: outOfBandCredential.goalCode || undefined, parentThreadId: outOfBandCredential.parentThreadId || undefined, willConfirm: outOfBandCredential.willConfirm || undefined, - label: outOfBandCredential.label || undefined, + label: organisation?.name, imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl }; } From 13ed45bea76eefce821c68d6424564d1328dc213 Mon Sep 17 00:00:00 2001 From: Nishad Date: Thu, 7 Mar 2024 20:03:47 +0530 Subject: [PATCH 158/231] fixed the schema cred def for the user id Signed-off-by: Nishad --- apps/api-gateway/src/interfaces/IUserRequestInterface.ts | 1 + apps/api-gateway/src/schema/interfaces/index.ts | 1 + .../src/credential-definition/credential-definition.service.ts | 2 +- apps/ledger/src/credential-definition/interfaces/index.ts | 1 + apps/ledger/src/schema/interfaces/schema.interface.ts | 1 + apps/ledger/src/schema/schema.service.ts | 2 +- 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/api-gateway/src/interfaces/IUserRequestInterface.ts b/apps/api-gateway/src/interfaces/IUserRequestInterface.ts index 908cfe524..dbda90cec 100644 --- a/apps/api-gateway/src/interfaces/IUserRequestInterface.ts +++ b/apps/api-gateway/src/interfaces/IUserRequestInterface.ts @@ -1,6 +1,7 @@ import { UserRoleOrgPermsDto } from '../authz/dtos/user-role-org-perms.dto'; export interface IUserRequestInterface { + id: string; userId: string; email: string; orgId: string; diff --git a/apps/api-gateway/src/schema/interfaces/index.ts b/apps/api-gateway/src/schema/interfaces/index.ts index a0edf7816..ace0f7e67 100644 --- a/apps/api-gateway/src/schema/interfaces/index.ts +++ b/apps/api-gateway/src/schema/interfaces/index.ts @@ -1,6 +1,7 @@ import { UserRoleOrgPermsDto } from '../../dtos/user-role-org-perms.dto'; export interface IUserRequestInterface { + id: string; userId: string; email: string; orgId: string; diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 23d6f33f2..7f799e52c 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -41,7 +41,7 @@ export class CredentialDefinitionService extends BaseService { if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(credDef.orgId); } - const { userId } = user.selectedOrg; + const userId = user.id; credDef.tag = credDef.tag.trim(); const dbResult: credential_definition = await this.credentialDefinitionRepository.getByAttribute( credDef.schemaLedgerId, diff --git a/apps/ledger/src/credential-definition/interfaces/index.ts b/apps/ledger/src/credential-definition/interfaces/index.ts index b994e46a8..d4be794cd 100644 --- a/apps/ledger/src/credential-definition/interfaces/index.ts +++ b/apps/ledger/src/credential-definition/interfaces/index.ts @@ -1,6 +1,7 @@ import { UserRoleOrgPermsDto } from '../../schema/dtos/user-role-org-perms.dto'; export interface IUserRequestInterface { + id: string; userId: string; email: string; orgId: string; diff --git a/apps/ledger/src/schema/interfaces/schema.interface.ts b/apps/ledger/src/schema/interfaces/schema.interface.ts index a67d45a2b..a89044961 100644 --- a/apps/ledger/src/schema/interfaces/schema.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema.interface.ts @@ -1,6 +1,7 @@ import { UserRoleOrgPermsDto } from '../dtos/user-role-org-perms.dto'; export interface IUserRequestInterface { + id: string; userId: string; email: string; orgId: string; diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 34ffde1ca..7d253557b 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -42,7 +42,7 @@ export class SchemaService extends BaseService { if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(orgId); } - const { userId } = user.selectedOrg; + const userId = user.id; try { const schemaExists = await this.schemaRepository.schemaExists( From 74514487f28902be78d41be02aabba858755621f Mon Sep 17 00:00:00 2001 From: tipusinghaw <126460794+tipusinghaw@users.noreply.github.com> Date: Fri, 8 Mar 2024 09:44:02 +0530 Subject: [PATCH 159/231] fix: connection list from agent (#578) * feat: added connection list from agent Signed-off-by: tipusinghaw * feat: agent connection list API Signed-off-by: tipusinghaw --------- Signed-off-by: tipusinghaw --- .../src/agent-service.controller.ts | 4 +- .../src/agent-service.service.ts | 3 +- .../src/connection/connection.controller.ts | 37 ++- .../src/connection/connection.service.ts | 10 +- .../dtos/get-all-connections.dto.ts | 94 +++++--- .../interfaces/IConnectionSearch.interface.ts | 47 ++-- apps/connection/src/connection.controller.ts | 7 + apps/connection/src/connection.service.ts | 227 ++++++++++++------ .../src/interfaces/connection.interfaces.ts | 28 +++ .../src/interfaces/agent-service.interface.ts | 53 ++++ 10 files changed, 368 insertions(+), 142 deletions(-) create mode 100644 libs/common/src/interfaces/agent-service.interface.ts diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 55e6452d2..163c7edca 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -3,9 +3,9 @@ import { MessagePattern } from '@nestjs/microservices'; import { AgentServiceService } from './agent-service.service'; import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IProofPresentation, IAgentProofRequest, IPresentation } from './interface/agent-service.interface'; import { user } from '@prisma/client'; -import { ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IProofPresentationDetails } from '@credebl/common/interfaces/verification.interface'; +import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; @Controller() export class AgentServiceController { @@ -54,7 +54,7 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-create-connection-legacy-invitation' }) - async createLegacyConnectionInvitation(payload: { connectionPayload: IConnectionDetails, url: string, apiKey: string }): Promise { + async createLegacyConnectionInvitation(payload: { connectionPayload: IConnectionDetails, url: string, apiKey: string }): Promise { return this.agentServiceService.createLegacyConnectionInvitation(payload.connectionPayload, payload.url, payload.apiKey); } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 6dca30728..36552d004 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -36,6 +36,7 @@ import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IProofPresentationDetails } from '@credebl/common/interfaces/verification.interface'; import { ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; +import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; @Injectable() @WebSocketGateway() @@ -952,7 +953,7 @@ export class AgentServiceService { } } - async createLegacyConnectionInvitation(connectionPayload: IConnectionDetails, url: string, apiKey: string): Promise { + async createLegacyConnectionInvitation(connectionPayload: IConnectionDetails, url: string, apiKey: string): Promise { try { diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 686b97075..9fc45bfb7 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -15,7 +15,7 @@ import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler import { OrgRoles } from 'libs/org-roles/enums'; import { Roles } from '../authz/decorators/roles.decorator'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; -import { GetAllConnectionsDto } from './dtos/get-all-connections.dto'; +import { GetAllAgentConnectionsDto, GetAllConnectionsDto } from './dtos/get-all-connections.dto'; import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; import { SortFields } from 'apps/connection/src/enum/connection.enum'; @@ -108,7 +108,40 @@ export class ConnectionController { return res.status(HttpStatus.OK).json(finalResponse); } - + /** + * Description: Get all connections from agent + * @param user + * @param orgId + * + */ + @Get('/orgs/:orgId/agent/connections') + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @ApiOperation({ + summary: `Fetch all connections from agent by orgId`, + description: `Fetch all connections from agent by orgId` + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + async getConnectionListFromAgent( + @Query() getAllConnectionsDto: GetAllAgentConnectionsDto, + @Param('orgId') orgId: string, + @Res() res: Response + ): Promise { + + const connectionDetails = await this.connectionService.getConnectionListFromAgent( + getAllConnectionsDto, + orgId + ); + + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: ResponseMessages.connection.success.fetch, + data: connectionDetails + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + @Get('orgs/:orgId/question-answer/question') @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index c01321093..5b2998dc1 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -5,7 +5,7 @@ import { BaseService } from 'libs/service/base.service'; import { ConnectionDto, CreateConnectionDto, ReceiveInvitationDto, ReceiveInvitationUrlDto } from './dtos/connection.dto'; import { IReceiveInvitationRes, IUserRequestInterface } from './interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; -import { IConnectionDetailsById, IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; +import { AgentConnectionSearchCriteria, IConnectionDetailsById, IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; import { QuestionDto } from './dtos/question-answer.dto'; @Injectable() @@ -77,6 +77,14 @@ export class ConnectionService extends BaseService { return this.sendNatsMessage(this.connectionServiceProxy, 'get-all-connections', payload); } + getConnectionListFromAgent( + connectionSearchCriteria: AgentConnectionSearchCriteria, + orgId: string + ): Promise { + const payload = { connectionSearchCriteria, orgId }; + return this.sendNatsMessage(this.connectionServiceProxy, 'get-all-agent-connection-list', payload); + } + getConnectionsById( user: IUserRequest, connectionId: string, diff --git a/apps/api-gateway/src/connection/dtos/get-all-connections.dto.ts b/apps/api-gateway/src/connection/dtos/get-all-connections.dto.ts index 553a8a5a2..ac5ddb329 100644 --- a/apps/api-gateway/src/connection/dtos/get-all-connections.dto.ts +++ b/apps/api-gateway/src/connection/dtos/get-all-connections.dto.ts @@ -1,41 +1,61 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { Transform, Type } from "class-transformer"; -import { IsEnum, IsOptional } from "class-validator"; -import { SortValue } from "../../enum"; -import { trim } from "@credebl/common/cast.helper"; -import { SortFields } from "apps/connection/src/enum/connection.enum"; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform, Type } from 'class-transformer'; +import { IsEnum, IsOptional } from 'class-validator'; +import { SortValue } from '../../enum'; +import { trim } from '@credebl/common/cast.helper'; +import { SortFields } from 'apps/connection/src/enum/connection.enum'; export class GetAllConnectionsDto { - - @ApiProperty({ required: false, example: '1' }) - @IsOptional() - pageNumber: number = 1; - - @ApiProperty({ required: false, example: '10' }) - @IsOptional() - pageSize: number = 10; - - @ApiProperty({ required: false }) - @IsOptional() - @Transform(({ value }) => trim(value)) - @Type(() => String) - searchByText: string = ''; - - @ApiProperty({ - required: false - }) - @Transform(({ value }) => trim(value)) - @IsOptional() - @IsEnum(SortFields) - sortField: string = SortFields.CREATED_DATE_TIME; - - @ApiProperty({ - enum: [SortValue.DESC, SortValue.ASC], - required: false - }) - @Transform(({ value }) => trim(value)) - @IsOptional() - @IsEnum(SortValue) - sortBy: string = SortValue.DESC; + @ApiProperty({ required: false, example: '1' }) + @IsOptional() + pageNumber: number = 1; + + @ApiProperty({ required: false, example: '10' }) + @IsOptional() + pageSize: number = 10; + + @ApiProperty({ required: false }) + @IsOptional() + @Transform(({ value }) => trim(value)) + @Type(() => String) + searchByText: string = ''; + + @ApiProperty({ + required: false + }) + @Transform(({ value }) => trim(value)) + @IsOptional() + @IsEnum(SortFields) + sortField: string = SortFields.CREATED_DATE_TIME; + + @ApiProperty({ + enum: [SortValue.DESC, SortValue.ASC], + required: false + }) + @Transform(({ value }) => trim(value)) + @IsOptional() + @IsEnum(SortValue) + sortBy: string = SortValue.DESC; } +export class GetAllAgentConnectionsDto { + @ApiProperty({ required: false, example: 'e315f30d-9beb-4068-aea4-abb5fe5eecb1' }) + @IsOptional() + outOfBandId: string = ''; + + @ApiProperty({ required: false, example: 'Test' }) + @IsOptional() + alias: string = ''; + + @ApiProperty({ required: false, example: 'did:example:e315f30d-9beb-4068-aea4-abb5fe5eecb1' }) + @IsOptional() + myDid: string = ''; + + @ApiProperty({ required: false, example: 'did:example:e315f30d-9beb-4068-aea4-abb5fe5eecb1' }) + @IsOptional() + theirDid: string = ''; + + @ApiProperty({ required: false, example: 'Bob' }) + @IsOptional() + theirLabel: string = ''; +} diff --git a/apps/api-gateway/src/interfaces/IConnectionSearch.interface.ts b/apps/api-gateway/src/interfaces/IConnectionSearch.interface.ts index 35d79949d..a228ae636 100644 --- a/apps/api-gateway/src/interfaces/IConnectionSearch.interface.ts +++ b/apps/api-gateway/src/interfaces/IConnectionSearch.interface.ts @@ -1,25 +1,34 @@ import { IUserRequestInterface } from './IUserRequestInterface'; export interface IConnectionSearchCriteria { - pageNumber: number; - pageSize: number; - sortField: string; - sortBy: string; - searchByText: string; - user?: IUserRequestInterface + pageNumber: number; + pageSize: number; + sortField: string; + sortBy: string; + searchByText: string; + user?: IUserRequestInterface; } export interface IConnectionDetailsById { - id: string; - createdAt: string; - did: string; - theirDid: string; - theirLabel: string; - state: string; - role: string; - autoAcceptConnection: boolean; - threadId: string; - protocol: string; - outOfBandId: string; - updatedAt: string; - } + id: string; + createdAt: string; + did: string; + theirDid: string; + theirLabel: string; + state: string; + role: string; + autoAcceptConnection: boolean; + threadId: string; + protocol: string; + outOfBandId: string; + updatedAt: string; +} + +export interface AgentConnectionSearchCriteria { + outOfBandId?: string; + alias?: string; + state?: string; + myDid?: string; + theirDid?: string; + theirLabel?: string; +} diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index 923a15190..ca67a4149 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -2,6 +2,7 @@ import { Controller } from '@nestjs/common'; // Import the common service in the import { ConnectionService } from './connection.service'; // Import the common service in connection module import { MessagePattern } from '@nestjs/microservices'; // Import the nestjs microservices package import { + GetAllConnections, IConnection, ICreateConnection, IFetchConnectionById, @@ -54,6 +55,12 @@ export class ConnectionController { return this.connectionService.getConnections(user, orgId, connectionSearchCriteria); } + @MessagePattern({ cmd: 'get-all-agent-connection-list' }) + async getConnectionListFromAgent(payload: GetAllConnections): Promise { + const {orgId, connectionSearchCriteria } = payload; + return this.connectionService.getAllConnectionListFromAgent(orgId, connectionSearchCriteria); + } + /** * * @param connectionId diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 7dbb306f7..b7f1bc084 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -5,8 +5,9 @@ import { HttpException, Inject, Injectable, Logger, NotFoundException } from '@n import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs'; import { + ConnectionResponseDetail, + AgentConnectionSearchCriteria, IConnection, - IConnectionInvitation, IConnectionSearchCriteria, ICreateConnection, IReceiveInvitation, @@ -22,6 +23,7 @@ import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IQuestionPayload } from './interfaces/question-answer.interfaces'; +import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; @Injectable() export class ConnectionService { @@ -31,7 +33,7 @@ export class ConnectionService { private readonly connectionRepository: ConnectionRepository, private readonly logger: Logger, @Inject(CACHE_MANAGER) private cacheService: Cache - ) { } + ) {} /** * Create connection legacy invitation URL @@ -40,10 +42,18 @@ export class ConnectionService { * @returns Connection legacy invitation URL */ async createLegacyConnectionInvitation(payload: IConnection): Promise { - - const { orgId, multiUseInvitation, autoAcceptConnection, alias, imageUrl, goal, goalCode, handshake, handshakeProtocols } = payload; + const { + orgId, + multiUseInvitation, + autoAcceptConnection, + alias, + imageUrl, + goal, + goalCode, + handshake, + handshakeProtocols + } = payload; try { - const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const { agentEndPoint, id, organisation } = agentDetails; @@ -86,7 +96,20 @@ export class ConnectionService { agentId, orgId ); - return saveConnectionDetails; + + const connectionDetailRecords: ConnectionResponseDetail = { + id: saveConnectionDetails.id, + orgId: saveConnectionDetails.orgId, + agentId: saveConnectionDetails.agentId, + connectionInvitation: saveConnectionDetails.connectionInvitation, + multiUse: saveConnectionDetails.multiUse, + createDateTime: saveConnectionDetails.createDateTime, + createdBy: saveConnectionDetails.createdBy, + lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, + lastChangedBy: saveConnectionDetails.lastChangedBy, + recordId: createConnectionInvitation.message.outOfBandRecord.id + }; + return connectionDetailRecords; } catch (error) { this.logger.error(`[createLegacyConnectionInvitation] - error in connection invitation: ${error}`); if (error && error?.status && error?.status?.message && error?.status?.message?.error) { @@ -126,8 +149,7 @@ export class ConnectionService { connectionPayload: object, url: string, apiKey: string - ): Promise { - + ): Promise { //nats call in agent-service to create an invitation url const pattern = { cmd: 'agent-create-connection-legacy-invitation' }; const payload = { connectionPayload, url, apiKey }; @@ -222,10 +244,59 @@ export class ConnectionService { }; return connectionResponse; } catch (error) { + this.logger.error(`[getConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}`); - this.logger.error( - `[getConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}` - ); + throw new RpcException(error.response ? error.response : error); + } + } + + async getAllConnectionListFromAgent( + orgId: string, + connectionSearchCriteria: AgentConnectionSearchCriteria + ): Promise { + try { + const { alias, myDid, outOfBandId, state, theirDid, theirLabel } = connectionSearchCriteria; + const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); + const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + const { agentEndPoint } = agentDetails; + if (!agentDetails) { + throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); + } + + let url: string; + if (orgAgentType === OrgAgentType.DEDICATED) { + url = `${agentEndPoint}${CommonConstants.URL_CONN_GET_CONNECTIONS}`; + } else if (orgAgentType === OrgAgentType.SHARED) { + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREATEED_INVITATIONS}`.replace( + '#', + agentDetails.tenantId + ); + } else { + throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); + } + + //Create the dynamic URL for Search Criteria + const criteriaParams = []; + if (alias) { criteriaParams.push(`alias=${alias}`); } + if (myDid) { criteriaParams.push(`myDid=${myDid}`); } + if (outOfBandId) { criteriaParams.push(`outOfBandId=${outOfBandId}`); } + if (state) { criteriaParams.push(`state=${state}`); } + if (theirDid) { criteriaParams.push(`theirDid=${theirDid}`); } + if (theirLabel) { criteriaParams.push(`theirLabel=${theirLabel}`); } + + if (0 < criteriaParams.length) { + url += `?${criteriaParams.join('&')}`; + } + + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this._getOrgAgentApiKey(orgId); + } + + const connectionResponse = await this._getAllConnections(url, apiKey); + return connectionResponse.response; + } catch (error) { + this.logger.error(`[getConnectionsFromAgent] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } @@ -288,7 +359,6 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - // const apiKey = await this._getOrgAgentApiKey(orgId); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { @@ -296,8 +366,6 @@ export class ConnectionService { } const createConnectionInvitation = await this._getConnectionsByConnectionId(url, apiKey); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[getConnectionsById] - error in get connections : ${JSON.stringify(error)}`); @@ -324,15 +392,13 @@ export class ConnectionService { const label = 'get-question-answer-record'; const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId); - + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(orgId); } const record = await this._getQuestionAnswersRecord(url, apiKey); return record; - - } catch (error) { this.logger.error(`[sendQuestion] - error in get question answer record: ${error}`); if (error && error?.status && error?.status?.message && error?.status?.message?.error) { @@ -348,18 +414,14 @@ export class ConnectionService { } } - async _getConnectionsByConnectionId( - url: string, - apiKey: string - ): Promise { - + async _getConnectionsByConnectionId(url: string, apiKey: string): Promise { //nats call in agent service for fetch connection details const pattern = { cmd: 'agent-get-connection-details-by-connectionId' }; const payload = { url, apiKey }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { + .catch((error) => { this.logger.error( `[_getConnectionsByConnectionId] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` ); @@ -368,21 +430,19 @@ export class ConnectionService { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } - async _getQuestionAnswersRecord( - url: string, - apiKey: string - ): Promise { - + async _getQuestionAnswersRecord(url: string, apiKey: string): Promise { const pattern = { cmd: 'agent-get-question-answer-record' }; const payload = { url, apiKey }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { + .catch((error) => { this.logger.error( `[_getQuestionAnswersRecord] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` ); @@ -391,7 +451,9 @@ export class ConnectionService { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } @@ -425,20 +487,23 @@ export class ConnectionService { connectionId?: string ): Promise { try { - let url; switch (label) { case 'send-question': { - url = orgAgentType === OrgAgentType.DEDICATED + url = + orgAgentType === OrgAgentType.DEDICATED ? `${agentEndPoint}${CommonConstants.URL_SEND_QUESTION}`.replace('#', connectionId) : orgAgentType === OrgAgentType.SHARED - ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_SEND_QUESTION}`.replace('#', connectionId).replace('@', tenantId) + ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_SEND_QUESTION}` + .replace('#', connectionId) + .replace('@', tenantId) : null; break; } case 'get-question-answer-record': { - url = orgAgentType === OrgAgentType.DEDICATED + url = + orgAgentType === OrgAgentType.DEDICATED ? `${agentEndPoint}${CommonConstants.URL_QUESTION_ANSWER_RECORD}` : orgAgentType === OrgAgentType.SHARED ? `${agentEndPoint}${CommonConstants.URL_SHAGENT_QUESTION_ANSWER_RECORD}`.replace('#', tenantId) @@ -472,14 +537,21 @@ export class ConnectionService { return message; } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException({ - status: error.status, - error: error.message - }, error.status); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); } } - async receiveInvitationUrl(user: IUserRequest, receiveInvitationUrl: IReceiveInvitationUrl, orgId: string): Promise { + async receiveInvitationUrl( + user: IUserRequest, + receiveInvitationUrl: IReceiveInvitationUrl, + orgId: string + ): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -489,13 +561,14 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } - let url; if (orgAgentType === OrgAgentType.DEDICATED) { url = `${agentEndPoint}${CommonConstants.URL_RECEIVE_INVITATION_URL}`; } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION_URL}` - .replace('#', agentDetails.tenantId); + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION_URL}`.replace( + '#', + agentDetails.tenantId + ); } else { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } @@ -506,8 +579,6 @@ export class ConnectionService { } const createConnectionInvitation = await this._receiveInvitationUrl(url, apiKey, receiveInvitationUrl); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[receiveInvitationUrl] - error in receive invitation url : ${JSON.stringify(error)}`); @@ -528,13 +599,12 @@ export class ConnectionService { apiKey: string, receiveInvitationUrl: IReceiveInvitationUrl ): Promise { - const pattern = { cmd: 'agent-receive-invitation-url' }; const payload = { url, apiKey, receiveInvitationUrl }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { + .catch((error) => { this.logger.error( `[_receiveInvitationUrl] [NATS call]- error in receive invitation url : ${JSON.stringify(error)}` ); @@ -543,11 +613,17 @@ export class ConnectionService { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } - async receiveInvitation(user: IUserRequest, receiveInvitation: IReceiveInvitation, orgId: string): Promise { + async receiveInvitation( + user: IUserRequest, + receiveInvitation: IReceiveInvitation, + orgId: string + ): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -561,8 +637,7 @@ export class ConnectionService { if (orgAgentType === OrgAgentType.DEDICATED) { url = `${agentEndPoint}${CommonConstants.URL_RECEIVE_INVITATION}`; } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION}` - .replace('#', agentDetails.tenantId); + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION}`.replace('#', agentDetails.tenantId); } else { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } @@ -573,8 +648,6 @@ export class ConnectionService { } const createConnectionInvitation = await this._receiveInvitation(url, apiKey, receiveInvitation); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[receiveInvitation] - error in receive invitation : ${JSON.stringify(error)}`); @@ -595,61 +668,51 @@ export class ConnectionService { apiKey: string, receiveInvitation: IReceiveInvitation ): Promise { - const pattern = { cmd: 'agent-receive-invitation' }; const payload = { url, apiKey, receiveInvitation }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { - this.logger.error( - `[_receiveInvitation] [NATS call]- error in receive invitation : ${JSON.stringify(error)}` - ); + .catch((error) => { + this.logger.error(`[_receiveInvitation] [NATS call]- error in receive invitation : ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } - async _sendQuestion( - questionPayload: IQuestionPayload, - url: string, - apiKey: string - ): Promise { - + async _sendQuestion(questionPayload: IQuestionPayload, url: string, apiKey: string): Promise { const pattern = { cmd: 'agent-send-question' }; const payload = { questionPayload, url, apiKey }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { - this.logger.error( - `[_sendQuestion] [NATS call]- error in send question : ${JSON.stringify(error)}` - ); + .catch((error) => { + this.logger.error(`[_sendQuestion] [NATS call]- error in send question : ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); - - } async sendQuestion(payload: IQuestionPayload): Promise { - - const { detail, validResponses, question, orgId, connectionId} = payload; + const { detail, validResponses, question, orgId, connectionId } = payload; try { - const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); - const { agentEndPoint} = agentDetails; - + const { agentEndPoint } = agentDetails; + if (!agentDetails) { throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); } @@ -662,14 +725,19 @@ export class ConnectionService { const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); const label = 'send-question'; - const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId, connectionId); + const url = await this.getQuestionAnswerAgentUrl( + label, + orgAgentType, + agentEndPoint, + agentDetails?.tenantId, + connectionId + ); let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(orgId); } const createQuestion = await this._sendQuestion(questionPayload, url, apiKey); return createQuestion; - } catch (error) { this.logger.error(`[sendQuestion] - error in sending question: ${error}`); if (error && error?.status && error?.status?.message && error?.status?.message?.error) { @@ -685,4 +753,3 @@ export class ConnectionService { } } } - diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index f26a6fd2f..d7cc5da99 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -80,6 +80,12 @@ export interface IFetchConnections { orgId: string; } +export interface GetAllConnections { + connectionSearchCriteria: AgentConnectionSearchCriteria; + user: IUserRequest; + orgId: string; +} + export interface IFetchConnectionById { user: IUserRequest; connectionId: string; @@ -124,6 +130,15 @@ export interface IConnectionSearchCriteria { user: IUserRequestInterface } +export interface AgentConnectionSearchCriteria { + outOfBandId: string; + alias: string; + state: string; + myDid: string; + theirDid: string; + theirLabel: string; +} + export interface IReceiveInvitationByUrlOrg { user: IUserRequestInterface, receiveInvitationUrl: IReceiveInvitationUrl, @@ -236,3 +251,16 @@ export interface IReceiveInvitationResponse { outOfBandRecord: OutOfBandRecord; connectionRecord: ConnectionRecord; } + +export interface ConnectionResponseDetail { + id: string; + orgId: string; + agentId: string; + connectionInvitation: string; + multiUse: boolean; + createDateTime: Date; + createdBy: number; + lastChangedDateTime: Date; + lastChangedBy: number; + recordId: string; +} diff --git a/libs/common/src/interfaces/agent-service.interface.ts b/libs/common/src/interfaces/agent-service.interface.ts new file mode 100644 index 000000000..ea63de484 --- /dev/null +++ b/libs/common/src/interfaces/agent-service.interface.ts @@ -0,0 +1,53 @@ +export interface InvitationMessage { + message: { + invitationUrl: string; + invitation: { + '@type': string; + '@id': string; + label: string; + recipientKeys: string[]; + serviceEndpoint: string; + routingKeys: string[]; + }; + outOfBandRecord: OutOfBandRecord; + }; + } + + interface OutOfBandRecord { + _tags: Tags; + metadata?: { [key: string]: string }; + id: string; + createdAt: string; + outOfBandInvitation: OutOfBandInvitation; + role: string; + state: string; + autoAcceptConnection: boolean; + reusable: boolean; + updatedAt: string; + } + + interface Tags { + invitationId: string; + recipientKeyFingerprints: string[]; + role: string; + state: string; + threadId: string; + } + + interface OutOfBandInvitation { + '@type': string; + '@id': string; + label: string; + accept: string[]; + handshake_protocols: string[]; + services: OutOfBandInvitationService[]; + } + + interface OutOfBandInvitationService { + id: string; + serviceEndpoint: string; + type: string; + recipientKeys: string[]; + routingKeys: string[]; + } + \ No newline at end of file From e865efe2f5b7515f3b1a6b9e9ee777b7019b7fb6 Mon Sep 17 00:00:00 2001 From: Krishna Date: Fri, 8 Mar 2024 11:35:42 +0530 Subject: [PATCH 160/231] fix: spelling mistake Signed-off-by: Krishna --- apps/connection/src/connection.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index bace86364..dc030a186 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -83,9 +83,9 @@ export class ConnectionService { apiKey = await this._getOrgAgentApiKey(orgId); } const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); - const connectionInvitaionUrl: string = createConnectionInvitation?.message?.invitationUrl; + const connectionInvitationUrl: string = createConnectionInvitation?.message?.invitationUrl; const shortenedUrl: string = await this.storeConnectionObjectAndReturnUrl( - connectionInvitaionUrl, + connectionInvitationUrl, connectionPayload.multiUseInvitation ); From e36d3c7db0a083d38f56358e488aa8672f0c8ea1 Mon Sep 17 00:00:00 2001 From: Krishna Date: Fri, 8 Mar 2024 13:53:23 +0530 Subject: [PATCH 161/231] fix: error handling Signed-off-by: Krishna --- apps/issuance/src/issuance.service.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 11e836451..e79168379 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -264,6 +264,7 @@ export class IssuanceService { } async storeIssuanceObjectReturnUrl(storeObj: string): Promise { + try { // Set default to false, since currently our invitation are not multi-use const persistent: boolean = false; //nats call in agent-service to create an invitation url @@ -271,6 +272,10 @@ export class IssuanceService { const payload = { persistent, storeObj }; const message = await this.natsCall(pattern, payload); return message.response; + } catch (error) { + this.logger.error(`[storeIssuanceObjectReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify(error)}`); + throw error; + } } // Created this function to avoid the impact of actual "natsCall" function for other operations From d9388e065bf0b5beb039c211ba3ffec4614ea805 Mon Sep 17 00:00:00 2001 From: Krishna Date: Fri, 8 Mar 2024 16:38:47 +0530 Subject: [PATCH 162/231] fix: remove unnecessary logs Signed-off-by: Krishna --- apps/issuance/src/issuance.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index e79168379..a086d2bf5 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -671,8 +671,6 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO }; } - this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); - const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, apiKey); if (!credentialCreateOfferDetails) { From 4abc6010c3b6ccad583a51955c6979f3f641fbb9 Mon Sep 17 00:00:00 2001 From: Nishad Date: Fri, 8 Mar 2024 17:45:59 +0530 Subject: [PATCH 163/231] handled the resourse access condition in org role guard Signed-off-by: Nishad --- apps/api-gateway/src/authz/guards/org-roles.guard.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/org-roles.guard.ts b/apps/api-gateway/src/authz/guards/org-roles.guard.ts index b9018781b..d91443bbd 100644 --- a/apps/api-gateway/src/authz/guards/org-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/org-roles.guard.ts @@ -38,10 +38,11 @@ export class OrgRolesGuard implements CanActivate { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); } - const orgDetails = user.resource_access[orgId]; - if (orgDetails) { - const orgRoles: string[] = orgDetails.roles; + const orgData = user.hasOwnProperty('resource_access') ? user.resource_access[orgId] : null; + + if (orgData) { + const orgRoles: string[] = orgData.roles; const roleAccess = requiredRoles.some((role) => orgRoles.includes(role)); if (!roleAccess) { From b10cfcf16e88baf597c69cd88fe213aac7037117 Mon Sep 17 00:00:00 2001 From: Nishad Date: Fri, 8 Mar 2024 17:57:09 +0530 Subject: [PATCH 164/231] Included UserAccessGuard to create and get organizations Signed-off-by: Nishad --- apps/api-gateway/src/organization/organization.controller.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 39e4e4047..6884e5963 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -24,6 +24,7 @@ import { ImageServiceService } from '@credebl/image-service'; import { ClientCredentialsDto } from './dtos/client-credentials.dto'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; import { validate as isValidUUID } from 'uuid'; +import { UserAccessGuard } from '../authz/guards/user-access-guard'; @UseFilters(CustomExceptionFilter) @Controller('orgs') @@ -211,7 +212,7 @@ export class OrganizationController { @Get('/') @ApiOperation({ summary: 'Get all organizations', description: 'Get all organizations' }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard('jwt'), UserAccessGuard) @ApiBearerAuth() @ApiQuery({ name: 'pageNumber', @@ -324,7 +325,7 @@ export class OrganizationController { @Post('/') @ApiOperation({ summary: 'Create a new Organization', description: 'Create an organization' }) @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) - @UseGuards(AuthGuard('jwt')) + @UseGuards(AuthGuard('jwt'), UserAccessGuard) @ApiBearerAuth() async createOrganization(@Body() createOrgDto: CreateOrganizationDto, @Res() res: Response, @User() reqUser: user): Promise { From cd36a408c1d21acc50b98e7ff5dc00180b448b70 Mon Sep 17 00:00:00 2001 From: Nishad Date: Fri, 8 Mar 2024 18:07:43 +0530 Subject: [PATCH 165/231] cosmetic changes Signed-off-by: Nishad --- apps/api-gateway/src/authz/guards/org-roles.guard.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/api-gateway/src/authz/guards/org-roles.guard.ts b/apps/api-gateway/src/authz/guards/org-roles.guard.ts index d91443bbd..c109525fb 100644 --- a/apps/api-gateway/src/authz/guards/org-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/org-roles.guard.ts @@ -39,10 +39,8 @@ export class OrgRolesGuard implements CanActivate { } - const orgData = user.hasOwnProperty('resource_access') ? user.resource_access[orgId] : null; - - if (orgData) { - const orgRoles: string[] = orgData.roles; + if (user.hasOwnProperty('resource_access') && user.resource_access[orgId]) { + const orgRoles: string[] = user.resource_access[orgId].roles; const roleAccess = requiredRoles.some((role) => orgRoles.includes(role)); if (!roleAccess) { From 52af2759aa4fadf6bf3bd47ccb0830313123c6ab Mon Sep 17 00:00:00 2001 From: Nishad Date: Mon, 11 Mar 2024 13:46:15 +0530 Subject: [PATCH 166/231] refactor client scopes of keycloak Signed-off-by: Nishad --- libs/client-registration/src/client-registration.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index a43fef46a..5d578b520 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -177,7 +177,6 @@ export class ClientRegistrationService { const payload = new ClientCredentialTokenPayloadDto(); payload.client_id = process.env.KEYCLOAK_MANAGEMENT_CLIENT_ID; payload.client_secret = process.env.KEYCLOAK_MANAGEMENT_CLIENT_SECRET; - payload.scope = 'email profile'; const mgmtTokenResponse = await this.getToken(payload); return mgmtTokenResponse.access_token; } catch (error) { From afc2300c89726653516600f27da123e63874cb03 Mon Sep 17 00:00:00 2001 From: Nishad Date: Mon, 11 Mar 2024 14:01:27 +0530 Subject: [PATCH 167/231] refactor client scopes of keycloak Signed-off-by: Nishad --- apps/organization/src/organization.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 0976f33f6..bea77a66e 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -498,7 +498,6 @@ export class OrganizationService { payload.client_id = clientId; // eslint-disable-next-line camelcase payload.client_secret = clientSecret; - payload.scope = 'email profile'; try { const mgmtTokenResponse = await this.clientRegistrationService.getToken(payload); From 6172c30e501ec334c9ebeca6e45a7a979bf46381 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 12 Mar 2024 15:08:19 +0530 Subject: [PATCH 168/231] fix: handled csv file exception Signed-off-by: pranalidhanavade --- apps/issuance/src/issuance.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index a086d2bf5..8fa540114 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -1,7 +1,7 @@ /* eslint-disable no-useless-catch */ /* eslint-disable camelcase */ import { CommonService } from '@credebl/common'; -import { BadRequestException, HttpException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; +import { BadRequestException, ConflictException, HttpException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { IssuanceRepository } from './issuance.repository'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { CommonConstants } from '@credebl/common/common.constant'; @@ -1283,22 +1283,22 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO ): Promise { try { const fileSchemaHeader: string[] = fileHeader.slice(); - if ('email' === fileHeader[0]) { - fileSchemaHeader.splice(0, 1); + if ('email' === fileHeader[0]) { + fileSchemaHeader.splice(0, 1); } else { throw new BadRequestException(ResponseMessages.bulkIssuance.error.emailColumn ); } if (schemaAttributes.length !== fileSchemaHeader.length) { - throw new BadRequestException(ResponseMessages.bulkIssuance.error.attributeNumber + throw new ConflictException(ResponseMessages.bulkIssuance.error.attributeNumber ); } const mismatchedAttributes = fileSchemaHeader.filter(value => !schemaAttributes.includes(value)); if (0 < mismatchedAttributes.length) { - throw new BadRequestException(ResponseMessages.bulkIssuance.error.mismatchedAttributes); + throw new ConflictException(ResponseMessages.bulkIssuance.error.mismatchedAttributes); } } catch (error) { throw error; From 996808ebd83a1cc27a5e277f003ee8bdef6b0b7e Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 12 Mar 2024 17:58:56 +0530 Subject: [PATCH 169/231] refactor: agent token encryption Signed-off-by: KulkarniShashank --- .../src/agent-service.controller.ts | 81 ++++----- .../src/agent-service.service.ts | 170 +++++++++++------- .../src/interface/agent-service.interface.ts | 4 + apps/connection/src/connection.service.ts | 74 +++----- apps/ecosystem/src/ecosystem.service.ts | 36 ++-- .../interfaces/issuance.interfaces.ts | 5 +- apps/issuance/src/issuance.service.ts | 49 ++--- .../credential-definition.service.ts | 20 +-- .../credential-definition.interface.ts | 2 + apps/ledger/src/schema/schema.interface.ts | 2 + apps/ledger/src/schema/schema.service.ts | 20 +-- .../src/interfaces/verification.interface.ts | 15 +- apps/verification/src/verification.service.ts | 42 ++--- 13 files changed, 236 insertions(+), 284 deletions(-) diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 163c7edca..8046c80a9 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -54,17 +54,17 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-create-connection-legacy-invitation' }) - async createLegacyConnectionInvitation(payload: { connectionPayload: IConnectionDetails, url: string, apiKey: string }): Promise { - return this.agentServiceService.createLegacyConnectionInvitation(payload.connectionPayload, payload.url, payload.apiKey); + async createLegacyConnectionInvitation(payload: { connectionPayload: IConnectionDetails, url: string, orgId: string }): Promise { + return this.agentServiceService.createLegacyConnectionInvitation(payload.connectionPayload, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-send-credential-create-offer' }) async sendCredentialCreateOffer(payload: { issueData: IIssuanceCreateOffer; url: string; - apiKey: string; + orgId: string; }): Promise { - return this.agentServiceService.sendCredentialCreateOffer(payload.issueData, payload.url, payload.apiKey); + return this.agentServiceService.sendCredentialCreateOffer(payload.issueData, payload.url, payload.orgId); } //DONE @@ -75,8 +75,8 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-get-issued-credentials-by-credentialDefinitionId' }) - async getIssueCredentialsbyCredentialRecordId(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.getIssueCredentialsbyCredentialRecordId(payload.url, payload.apiKey); + async getIssueCredentialsbyCredentialRecordId(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.getIssueCredentialsbyCredentialRecordId(payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-get-proof-presentations' }) @@ -86,8 +86,8 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-get-proof-presentation-by-id' }) - async getProofPresentationById(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.getProofPresentationById(payload.url, payload.apiKey); + async getProofPresentationById(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.getProofPresentationById(payload.url, payload.orgId); } //DONE @@ -95,25 +95,25 @@ export class AgentServiceController { async sendProofRequest(payload: { proofRequestPayload: ISendProofRequestPayload; url: string; - apiKey: string; + orgId: string; }): Promise { - return this.agentServiceService.sendProofRequest(payload.proofRequestPayload, payload.url, payload.apiKey); + return this.agentServiceService.sendProofRequest(payload.proofRequestPayload, payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-verify-presentation' }) - async verifyPresentation(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.verifyPresentation(payload.url, payload.apiKey); + async verifyPresentation(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.verifyPresentation(payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-get-all-connections' }) - async getConnections(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.getConnections(payload.url, payload.apiKey); + async getConnections(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.getConnections(payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-get-connection-details-by-connectionId' }) - async getConnectionsByconnectionId(payload: { url: string, apiKey: string }): Promise { - return this.agentServiceService.getConnectionsByconnectionId(payload.url, payload.apiKey); + async getConnectionsByconnectionId(payload: { url: string, orgId: string }): Promise { + return this.agentServiceService.getConnectionsByconnectionId(payload.url, payload.orgId); } /** @@ -131,54 +131,54 @@ export class AgentServiceController { async sendOutOfBandProofRequest(payload: { proofRequestPayload: ISendProofRequestPayload; url: string; - apiKey: string; + orgId: string; }): Promise { - return this.agentServiceService.sendOutOfBandProofRequest(payload.proofRequestPayload, payload.url, payload.apiKey); + return this.agentServiceService.sendOutOfBandProofRequest(payload.proofRequestPayload, payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'get-agent-verified-proof-details' }) - async getVerifiedProofDetails(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.getVerifiedProofDetails(payload.url, payload.apiKey); + async getVerifiedProofDetails(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.getVerifiedProofDetails(payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-schema-endorsement-request' }) async schemaEndorsementRequest(payload: { url: string; - apiKey: string; + orgId: string; requestSchemaPayload: object; }): Promise { - return this.agentServiceService.schemaEndorsementRequest(payload.url, payload.apiKey, payload.requestSchemaPayload); + return this.agentServiceService.schemaEndorsementRequest(payload.url, payload.orgId, payload.requestSchemaPayload); } @MessagePattern({ cmd: 'agent-credDef-endorsement-request' }) async credDefEndorsementRequest(payload: { url: string; - apiKey: string; + orgId: string; requestSchemaPayload: object; }): Promise { return this.agentServiceService.credDefEndorsementRequest( payload.url, - payload.apiKey, + payload.orgId, payload.requestSchemaPayload ); } //DONE @MessagePattern({ cmd: 'agent-sign-transaction' }) - async signTransaction(payload: { url: string; apiKey: string; signEndorsementPayload: object }): Promise { - return this.agentServiceService.signTransaction(payload.url, payload.apiKey, payload.signEndorsementPayload); + async signTransaction(payload: { url: string; orgId: string; signEndorsementPayload: object }): Promise { + return this.agentServiceService.signTransaction(payload.url, payload.orgId, payload.signEndorsementPayload); } //DONE @MessagePattern({ cmd: 'agent-submit-transaction' }) - async submitTransaction(payload: { url: string; apiKey: string; submitEndorsementPayload: object }): Promise { - return this.agentServiceService.sumbitTransaction(payload.url, payload.apiKey, payload.submitEndorsementPayload); + async submitTransaction(payload: { url: string; orgId: string; submitEndorsementPayload: object }): Promise { + return this.agentServiceService.sumbitTransaction(payload.url, payload.orgId, payload.submitEndorsementPayload); } //DONE @MessagePattern({ cmd: 'agent-out-of-band-credential-offer' }) - async outOfBandCredentialOffer(payload: { outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, apiKey: string }): Promise { - return this.agentServiceService.outOfBandCredentialOffer(payload.outOfBandIssuancePayload, payload.url, payload.apiKey); + async outOfBandCredentialOffer(payload: { outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, orgId: string }): Promise { + return this.agentServiceService.outOfBandCredentialOffer(payload.outOfBandIssuancePayload, payload.url, payload.orgId); } @MessagePattern({ cmd: 'delete-wallet' }) @@ -186,42 +186,37 @@ export class AgentServiceController { return this.agentServiceService.deleteWallet(payload.url, payload.apiKey); } - @MessagePattern({ cmd: 'get-org-agent-api-key' }) - async getOrgAgentApiKey(payload: { orgId: string }): Promise { - return this.agentServiceService.getOrgAgentApiKey(payload.orgId); - } - @MessagePattern({ cmd: 'agent-receive-invitation-url' }) async receiveInvitationUrl(payload: { url, - apiKey, + orgId, receiveInvitationUrl }): Promise { - return this.agentServiceService.receiveInvitationUrl(payload.receiveInvitationUrl, payload.url, payload.apiKey); + return this.agentServiceService.receiveInvitationUrl(payload.receiveInvitationUrl, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-receive-invitation' }) async receiveInvitation(payload: { url, - apiKey, + orgId, receiveInvitation }): Promise { - return this.agentServiceService.receiveInvitation(payload.receiveInvitation, payload.url, payload.apiKey); + return this.agentServiceService.receiveInvitation(payload.receiveInvitation, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-send-question' }) async sendQuestion(payload: { url, - apiKey, + orgId, questionPayload }): Promise { - return this.agentServiceService.sendQuestion(payload.questionPayload, payload.url, payload.apiKey); + return this.agentServiceService.sendQuestion(payload.questionPayload, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-get-question-answer-record' }) - async getQuestionAnswersRecord(payload: { url: string, apiKey: string }): Promise { - return this.agentServiceService.getQuestionAnswersRecord(payload.url, payload.apiKey); + async getQuestionAnswersRecord(payload: { url: string, orgId: string }): Promise { + return this.agentServiceService.getQuestionAnswersRecord(payload.url, payload.orgId); } } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 36552d004..2186740f7 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -37,6 +37,7 @@ import { IProofPresentationDetails } from '@credebl/common/interfaces/verificati import { ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; +import * as CryptoJS from 'crypto-js'; @Injectable() @WebSocketGateway() @@ -168,7 +169,6 @@ export class AgentServiceService { // Create payload for the wallet create and store payload const walletProvisionPayload = await this.prepareWalletProvisionPayload(agentSpinupDto, externalIp, apiEndpoint, inboundEndpoint, ledgerDetails, orgData); - // Socket connection const socket: Socket = await this.initSocketConnection(`${process.env.SOCKET_HOST}`); this.emitAgentSpinupInitiatedEvent(agentSpinupDto, socket); @@ -365,10 +365,12 @@ export class AgentServiceService { socket.emit('invitation-url-creation-started', { clientId: agentSpinupDto.clientSocketId }); } + const encryptedToken = await this.tokenEncryption(agentDetails?.agentToken); + const agentPayload: IStoreOrgAgentDetails = { agentEndPoint, seed: agentSpinupDto.seed, - apiKey: agentDetails.agentToken, + apiKey: encryptedToken, agentsTypeId: agentSpinupDto?.agentType, orgId: orgData.id, walletName: agentSpinupDto.walletName, @@ -514,8 +516,9 @@ export class AgentServiceService { this.logger.error(`[_storeOrgAgentDetails] - Error in store agent details : ${JSON.stringify(error)}`); } - + async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, seed?: string, indyNamespace?: string, did?: string): Promise { + const getDcryptedToken = await this.commonService.decryptPassword(apiKey); const retryOptions = { retries: 10 }; @@ -523,9 +526,9 @@ export class AgentServiceService { try { return retry(async () => { if (agentApiState === 'write-did') { - return this.commonService.httpPost(agentUrl, { seed, method: indyNamespace, did }, { headers: { 'authorization': apiKey } }); + return this.commonService.httpPost(agentUrl, { seed, method: indyNamespace, did }, { headers: { 'authorization': getDcryptedToken } }); } else if (agentApiState === 'get-did-doc') { - return this.commonService.httpGet(agentUrl, { headers: { 'authorization': apiKey } }); + return this.commonService.httpGet(agentUrl, { headers: { 'authorization': getDcryptedToken } }); } }, retryOptions); } catch (error) { @@ -776,12 +779,12 @@ export class AgentServiceService { method: ledgerIds.indyNamespace }; - + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); // Invoke an API request from the agent to create multi-tenant agent const tenantDetails = await this.commonService.httpPost( `${platformAdminSpinnedUp.org_agents[0].agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, createTenantOptions, - { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } } + { headers: { 'authorization': getDcryptedToken } } ); return tenantDetails; @@ -815,6 +818,7 @@ export class AgentServiceService { async createSchema(payload: ITenantSchema): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(payload.orgId); let schemaResponse; if (OrgAgentType.DEDICATED === payload.agentType) { @@ -826,7 +830,7 @@ export class AgentServiceService { name: payload.name, issuerId: payload.issuerId }; - schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': payload.apiKey } }) + schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': getApiKey } }) .then(async (schema) => { return schema; }) @@ -846,7 +850,7 @@ export class AgentServiceService { name: payload.payload.name, issuerId: payload.payload.issuerId }; - schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': payload.apiKey } }) + schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': getApiKey } }) .then(async (schema) => { return schema; }) @@ -868,6 +872,7 @@ export class AgentServiceService { try { let schemaResponse; + const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_SCHEMA_BY_ID.replace('#', `${payload.schemaId}`)}`; schemaResponse = await this.commonService.httpGet(url, payload.schemaId) @@ -878,7 +883,7 @@ export class AgentServiceService { } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}`.replace('@', `${payload.payload.schemaId}`).replace('#', `${payload.tenantId}`); - schemaResponse = await this.commonService.httpGet(url, { headers: { 'authorization': payload.apiKey } }) + schemaResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async (schema) => { return schema; }); @@ -894,6 +899,7 @@ export class AgentServiceService { try { let credDefResponse; + const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === String(payload.agentType)) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_CREATE_CRED_DEF}`; @@ -903,7 +909,7 @@ export class AgentServiceService { issuerId: payload.issuerId }; - credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': payload.apiKey } }) + credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': getApiKey } }) .then(async (credDef) => { return credDef; }); @@ -915,7 +921,7 @@ export class AgentServiceService { schemaId: payload.payload.schemaId, issuerId: payload.payload.issuerId }; - credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': payload.apiKey } }) + credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': getApiKey } }) .then(async (credDef) => { return credDef; }); @@ -932,16 +938,17 @@ export class AgentServiceService { try { let credDefResponse; + const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_CRED_DEF_BY_ID.replace('#', `${payload.credentialDefinitionId}`)}`; - credDefResponse = await this.commonService.httpGet(url, payload.credentialDefinitionId) + credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async (credDef) => { return credDef; }); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CRED_DEF}`.replace('@', `${payload.payload.credentialDefinitionId}`).replace('#', `${payload.tenantId}`); - credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': payload.apiKey } }) + credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async (credDef) => { return credDef; }); @@ -953,12 +960,12 @@ export class AgentServiceService { } } - async createLegacyConnectionInvitation(connectionPayload: IConnectionDetails, url: string, apiKey: string): Promise { + async createLegacyConnectionInvitation(connectionPayload: IConnectionDetails, url: string, orgId: string): Promise { try { - + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpPost(url, connectionPayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, connectionPayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return data; @@ -968,10 +975,11 @@ export class AgentServiceService { } } - async sendCredentialCreateOffer(issueData: IIssuanceCreateOffer, url: string, apiKey: string): Promise { + async sendCredentialCreateOffer(issueData: IIssuanceCreateOffer, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpPost(url, issueData, { headers: { 'authorization': apiKey } }) + .httpPost(url, issueData, { headers: { 'authorization': getApiKey } }) .then(async response => response); return data; } catch (error) { @@ -1004,10 +1012,11 @@ export class AgentServiceService { } } - async getProofPresentationById(url: string, apiKey: string): Promise { + async getProofPresentationById(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const getProofPresentationById = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) + .httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async response => response) .catch(error => this.handleAgentSpinupStatusErrors(error)); @@ -1018,10 +1027,11 @@ export class AgentServiceService { } } - async getIssueCredentialsbyCredentialRecordId(url: string, apiKey: string): Promise { + async getIssueCredentialsbyCredentialRecordId(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) + .httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async response => response); return data; } catch (error) { @@ -1030,10 +1040,11 @@ export class AgentServiceService { } } - async sendProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, apiKey: string): Promise { + async sendProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const sendProofRequest = await this.commonService - .httpPost(url, proofRequestPayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, proofRequestPayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return sendProofRequest; } catch (error) { @@ -1042,10 +1053,11 @@ export class AgentServiceService { } } - async verifyPresentation(url: string, apiKey: string): Promise { + async verifyPresentation(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const verifyPresentation = await this.commonService - .httpPost(url, '', { headers: { 'authorization': apiKey } }) + .httpPost(url, '', { headers: { 'authorization': getApiKey } }) .then(async response => response) .catch(error => this.handleAgentSpinupStatusErrors(error)); return verifyPresentation; @@ -1055,10 +1067,11 @@ export class AgentServiceService { } } - async getConnections(url: string, apiKey: string): Promise { + async getConnections(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) + .httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async response => response); return data; } catch (error) { @@ -1067,11 +1080,12 @@ export class AgentServiceService { } } - async getConnectionsByconnectionId(url: string, apiKey: string): Promise { + async getConnectionsByconnectionId(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) + .httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async response => response) .catch(error => this.handleAgentSpinupStatusErrors(error)); @@ -1096,15 +1110,7 @@ export class AgentServiceService { let agentApiKey; if (orgAgentDetails) { - - agentApiKey = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { - agentApiKey = await this.getOrgAgentApiKey(orgId); - } - - if (agentApiKey === undefined || null) { agentApiKey = await this.getOrgAgentApiKey(orgId); - } } if (!orgAgentDetails) { @@ -1134,10 +1140,11 @@ export class AgentServiceService { } } - async sendOutOfBandProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, apiKey: string): Promise { + async sendOutOfBandProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const sendProofRequest = await this.commonService - .httpPost(url, proofRequestPayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, proofRequestPayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return sendProofRequest; } catch (error) { @@ -1146,10 +1153,11 @@ export class AgentServiceService { } } - async getVerifiedProofDetails(url: string, apiKey: string): Promise { + async getVerifiedProofDetails(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const getVerifiedProofData = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) + .httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async response => response) .catch(error => this.handleAgentSpinupStatusErrors(error)); @@ -1160,10 +1168,11 @@ export class AgentServiceService { } } - async schemaEndorsementRequest(url: string, apiKey: string, requestSchemaPayload: object): Promise { + async schemaEndorsementRequest(url: string, orgId: string, requestSchemaPayload: object): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const schemaRequest = await this.commonService - .httpPost(url, requestSchemaPayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, requestSchemaPayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return schemaRequest; } catch (error) { @@ -1172,10 +1181,11 @@ export class AgentServiceService { } } - async credDefEndorsementRequest(url: string, apiKey: string, requestSchemaPayload: object): Promise { + async credDefEndorsementRequest(url: string, orgId: string, requestSchemaPayload: object): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const credDefRequest = await this.commonService - .httpPost(url, requestSchemaPayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, requestSchemaPayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return credDefRequest; } catch (error) { @@ -1184,10 +1194,11 @@ export class AgentServiceService { } } - async signTransaction(url: string, apiKey: string, signEndorsementPayload: object): Promise { + async signTransaction(url: string, orgId: string, signEndorsementPayload: object): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const signEndorsementTransaction = await this.commonService - .httpPost(url, signEndorsementPayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, signEndorsementPayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return signEndorsementTransaction; @@ -1197,11 +1208,11 @@ export class AgentServiceService { } } - async sumbitTransaction(url: string, apiKey: string, submitEndorsementPayload: object): Promise { + async sumbitTransaction(url: string, orgId: string, submitEndorsementPayload: object): Promise { try { - + const getApiKey = await this.getOrgAgentApiKey(orgId); const signEndorsementTransaction = await this.commonService - .httpPost(url, submitEndorsementPayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, submitEndorsementPayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return signEndorsementTransaction; @@ -1211,10 +1222,11 @@ export class AgentServiceService { } } - async outOfBandCredentialOffer(outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, apiKey: string): Promise { + async outOfBandCredentialOffer(outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const sendOutOfbandCredentialOffer = await this.commonService - .httpPost(url, outOfBandIssuancePayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, outOfBandIssuancePayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return sendOutOfbandCredentialOffer; } catch (error) { @@ -1238,10 +1250,11 @@ export class AgentServiceService { } } - async receiveInvitationUrl(receiveInvitationUrl: IReceiveInvitationUrl, url: string, apiKey: string): Promise { + async receiveInvitationUrl(receiveInvitationUrl: IReceiveInvitationUrl, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const receiveInvitationUrlRes = await this.commonService - .httpPost(url, receiveInvitationUrl, { headers: { 'authorization': apiKey } }) + .httpPost(url, receiveInvitationUrl, { headers: { 'authorization': getApiKey } }) .then(async response => response); return receiveInvitationUrlRes; } catch (error) { @@ -1250,10 +1263,11 @@ export class AgentServiceService { } } - async receiveInvitation(receiveInvitation: IReceiveInvitation, url: string, apiKey: string): Promise { + async receiveInvitation(receiveInvitation: IReceiveInvitation, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const receiveInvitationRes = await this.commonService - .httpPost(url, receiveInvitation, { headers: { 'authorization': apiKey } }) + .httpPost(url, receiveInvitation, { headers: { 'authorization': getApiKey } }) .then(async response => response); return receiveInvitationRes; } catch (error) { @@ -1262,16 +1276,22 @@ export class AgentServiceService { } } - async getOrgAgentApiKey(orgId: string): Promise { + private async getOrgAgentApiKey(orgId: string): Promise { try { let agentApiKey; const orgAgentApiKey = await this.agentServiceRepository.getAgentApiKey(orgId); + const apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + + if (apiKey) { + const getDcryptedToken = await this.commonService.decryptPassword(apiKey); + return getDcryptedToken; + } const orgAgentId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); if (orgAgentApiKey?.orgAgentTypeId === orgAgentId) { const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); - + const [orgAgentData] = platformAdminSpinnedUp.org_agents; const { apiKey } = orgAgentData; if (!platformAdminSpinnedUp) { @@ -1287,8 +1307,11 @@ export class AgentServiceService { if (!agentApiKey) { throw new NotFoundException(ResponseMessages.agent.error.apiKeyNotExist); } + await this.cacheService.set(CommonConstants.CACHE_APIKEY_KEY, agentApiKey, CommonConstants.CACHE_TTL_SECONDS); - return agentApiKey; + const getDcryptedToken = await this.commonService.decryptPassword(agentApiKey); + + return getDcryptedToken; } catch (error) { this.logger.error(`Agent api key details : ${JSON.stringify(error)}`); @@ -1307,10 +1330,11 @@ export class AgentServiceService { } } - async sendQuestion(questionPayload: IQuestionPayload, url: string, apiKey: string): Promise { + async sendQuestion(questionPayload: IQuestionPayload, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const sendQuestionRes = await this.commonService - .httpPost(url, questionPayload, { headers: { 'authorization': apiKey } }) + .httpPost(url, questionPayload, { headers: { 'authorization': getApiKey } }) .then(async response => response); return sendQuestionRes; } catch (error) { @@ -1319,11 +1343,13 @@ export class AgentServiceService { } } - async getQuestionAnswersRecord(url: string, apiKey: string): Promise { + async getQuestionAnswersRecord(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); + const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) + .httpGet(url, { headers: { 'authorization': getApiKey } }) .then(async response => response) .catch(error => this.handleAgentSpinupStatusErrors(error)); @@ -1335,5 +1361,19 @@ export class AgentServiceService { } + private async tokenEncryption(token: string): Promise { + try { + const encryptedToken = CryptoJS.AES.encrypt( + JSON.stringify(token), + process.env.CRYPTO_PRIVATE_KEY + ).toString(); + + return encryptedToken; + + } catch (error) { + throw error; + } + } + } diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index f52152eda..79a565979 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -53,6 +53,7 @@ export interface ITenantSchema { agentType?: string; apiKey?: string; agentEndPoint?: string; + orgId?: string; } export interface ITenantSchemaDto { @@ -70,6 +71,7 @@ export interface IGetSchemaAgentRedirection { agentEndPoint?: string; agentType?: string; method?: string; + orgId?: string; } export interface IGetSchemaFromTenantPayload { @@ -86,6 +88,7 @@ export interface ITenantCredDef { agentType?: string; apiKey?: string; agentEndPoint?: string; + orgId?: string; } export interface IWalletProvision { @@ -202,6 +205,7 @@ export interface IGetCredDefAgentRedirection { agentEndPoint?: string; agentType?: string; method?: string; + orgId?: string; } export interface IGetCredDefFromTenantPayload { diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index dc030a186..69823b1e5 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -77,12 +77,7 @@ export class ConnectionService { const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); const url = await this.getAgentUrl(orgAgentType, agentEndPoint, agentDetails?.tenantId); - - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); + const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, orgId); const connectionInvitationUrl: string = createConnectionInvitation?.message?.invitationUrl; const shortenedUrl: string = await this.storeConnectionObjectAndReturnUrl( connectionInvitationUrl, @@ -146,11 +141,11 @@ export class ConnectionService { async _createConnectionInvitation( connectionPayload: object, url: string, - apiKey: string + orgId: string ): Promise { //nats call in agent-service to create an invitation url const pattern = { cmd: 'agent-create-connection-legacy-invitation' }; - const payload = { connectionPayload, url, apiKey }; + const payload = { connectionPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -286,12 +281,7 @@ export class ConnectionService { url += `?${criteriaParams.join('&')}`; } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - - const connectionResponse = await this._getAllConnections(url, apiKey); + const connectionResponse = await this._getAllConnections(url, orgId); return connectionResponse.response; } catch (error) { this.logger.error(`[getConnectionsFromAgent] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}`); @@ -302,13 +292,13 @@ export class ConnectionService { async _getAllConnections( url: string, - apiKey: string + orgId: string ): Promise<{ response: string; }> { try { const pattern = { cmd: 'agent-get-all-connections' }; - const payload = { url, apiKey }; + const payload = { url, orgId }; return this.connectionServiceProxy .send(pattern, payload) .pipe( @@ -357,12 +347,7 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - // const apiKey = await this._getOrgAgentApiKey(orgId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._getConnectionsByConnectionId(url, apiKey); + const createConnectionInvitation = await this._getConnectionsByConnectionId(url, orgId); return createConnectionInvitation; } catch (error) { this.logger.error(`[getConnectionsById] - error in get connections : ${JSON.stringify(error)}`); @@ -391,11 +376,7 @@ export class ConnectionService { const label = 'get-question-answer-record'; const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const record = await this._getQuestionAnswersRecord(url, apiKey); + const record = await this._getQuestionAnswersRecord(url, orgId); return record; } catch (error) { this.logger.error(`[sendQuestion] - error in get question answer record: ${error}`); @@ -412,10 +393,10 @@ export class ConnectionService { } } - async _getConnectionsByConnectionId(url: string, apiKey: string): Promise { + async _getConnectionsByConnectionId(url: string, orgId: string): Promise { //nats call in agent service for fetch connection details const pattern = { cmd: 'agent-get-connection-details-by-connectionId' }; - const payload = { url, apiKey }; + const payload = { url, orgId }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() @@ -434,9 +415,9 @@ export class ConnectionService { }); } - async _getQuestionAnswersRecord(url: string, apiKey: string): Promise { + async _getQuestionAnswersRecord(url: string, orgId: string): Promise { const pattern = { cmd: 'agent-get-question-answer-record' }; - const payload = { url, apiKey }; + const payload = { url, orgId }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() @@ -572,11 +553,7 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._receiveInvitationUrl(url, apiKey, receiveInvitationUrl); + const createConnectionInvitation = await this._receiveInvitationUrl(url, orgId, receiveInvitationUrl); return createConnectionInvitation; } catch (error) { this.logger.error(`[receiveInvitationUrl] - error in receive invitation url : ${JSON.stringify(error)}`); @@ -595,11 +572,11 @@ export class ConnectionService { async _receiveInvitationUrl( url: string, - apiKey: string, + orgId: string, receiveInvitationUrl: IReceiveInvitationUrl ): Promise { const pattern = { cmd: 'agent-receive-invitation-url' }; - const payload = { url, apiKey, receiveInvitationUrl }; + const payload = { url, orgId, receiveInvitationUrl }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() @@ -641,11 +618,7 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._receiveInvitation(url, apiKey, receiveInvitation); + const createConnectionInvitation = await this._receiveInvitation(url, orgId, receiveInvitation); return createConnectionInvitation; } catch (error) { this.logger.error(`[receiveInvitation] - error in receive invitation : ${JSON.stringify(error)}`); @@ -664,11 +637,11 @@ export class ConnectionService { async _receiveInvitation( url: string, - apiKey: string, + orgId: string, receiveInvitation: IReceiveInvitation ): Promise { const pattern = { cmd: 'agent-receive-invitation' }; - const payload = { url, apiKey, receiveInvitation }; + const payload = { url, orgId, receiveInvitation }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() @@ -685,9 +658,9 @@ export class ConnectionService { }); } - async _sendQuestion(questionPayload: IQuestionPayload, url: string, apiKey: string): Promise { + async _sendQuestion(questionPayload: IQuestionPayload, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-send-question' }; - const payload = { questionPayload, url, apiKey }; + const payload = { questionPayload, url, orgId }; return this.connectionServiceProxy .send(pattern, payload) @@ -731,11 +704,8 @@ export class ConnectionService { agentDetails?.tenantId, connectionId ); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createQuestion = await this._sendQuestion(questionPayload, url, apiKey); + + const createQuestion = await this._sendQuestion(questionPayload, url, orgId); return createQuestion; } catch (error) { this.logger.error(`[sendQuestion] - error in sending question: ${error}`); diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index fa78b67c6..a67d4917e 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -754,7 +754,6 @@ export class EcosystemService { endorsementTransactionType.SCHEMA, ecosystemMemberDetails.tenantId ); - const apiKey = await this._getOrgAgentApiKey(orgId); const attributeArray = requestSchemaPayload.attributes.map((item) => item.attributeName); const schemaTransactionPayload = { @@ -769,7 +768,7 @@ export class EcosystemService { const schemaTransactionRequest: SchemaMessage = await this._requestSchemaEndorsement( schemaTransactionPayload, url, - apiKey + orgId ); const schemaTransactionResponse = { @@ -916,7 +915,6 @@ export class EcosystemService { endorsementTransactionType.CREDENTIAL_DEFINITION, ecosystemMemberDetails.tenantId ); - const apiKey = await this._getOrgAgentApiKey(orgId); const credDefTransactionPayload = { endorserDid: ecosystemLeadAgentDetails.orgDid, endorse: requestCredDefPayload.endorse, @@ -928,7 +926,7 @@ export class EcosystemService { const credDefTransactionRequest: CredDefMessage = await this._requestCredDeffEndorsement( credDefTransactionPayload, url, - apiKey + orgId ); if ('failed' === credDefTransactionRequest.message.credentialDefinitionState.state) { @@ -992,9 +990,9 @@ export class EcosystemService { } } - async _requestSchemaEndorsement(requestSchemaPayload: object, url: string, apiKey: string): Promise { + async _requestSchemaEndorsement(requestSchemaPayload: object, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-schema-endorsement-request' }; - const payload = { requestSchemaPayload, url, apiKey }; + const payload = { requestSchemaPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1012,9 +1010,9 @@ export class EcosystemService { } } - async _requestCredDeffEndorsement(requestSchemaPayload: object, url: string, apiKey: string): Promise { + async _requestCredDeffEndorsement(requestSchemaPayload: object, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-credDef-endorsement-request' }; - const payload = { requestSchemaPayload, url, apiKey }; + const payload = { requestSchemaPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1065,17 +1063,14 @@ export class EcosystemService { endorsementTransactionType.SIGN, ecosystemLeadAgentDetails?.tenantId ); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(ecosystemLeadDetails.orgId); - } + const jsonString = endorsementTransactionPayload.requestPayload.toString(); const payload = { transaction: jsonString, endorserDid: endorsementTransactionPayload.endorserDid }; - const schemaTransactionRequest: SignedTransactionMessage = await this._signTransaction(payload, url, apiKey); + const schemaTransactionRequest: SignedTransactionMessage = await this._signTransaction(payload, url, ecosystemLeadDetails.orgId); if (!schemaTransactionRequest) { throw new InternalServerErrorException(ResponseMessages.ecosystem.error.signRequestError); @@ -1180,9 +1175,9 @@ export class EcosystemService { * @param url * @returns sign message */ - async _signTransaction(signEndorsementPayload: object, url: string, apiKey: string): Promise { + async _signTransaction(signEndorsementPayload: object, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-sign-transaction' }; - const payload = { signEndorsementPayload, url, apiKey }; + const payload = { signEndorsementPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1369,12 +1364,7 @@ export class EcosystemService { } } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - - const submitTransactionRequest = await this._submitTransaction(payload, url, apiKey); + const submitTransactionRequest = await this._submitTransaction(payload, url, orgId); if ('failed' === submitTransactionRequest['message'].state) { throw new InternalServerErrorException(ResponseMessages.ecosystem.error.sumbitTransaction); @@ -1448,9 +1438,9 @@ export class EcosystemService { * @param url * @returns sign message */ - async _submitTransaction(submitEndorsementPayload: object, url: string, apiKey: string): Promise { + async _submitTransaction(submitEndorsementPayload: object, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-submit-transaction' }; - const payload = { submitEndorsementPayload, url, apiKey }; + const payload = { submitEndorsementPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 5cb4caa75..4bca2ffb7 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -85,7 +85,8 @@ export interface IPattern { export interface ISendOfferNatsPayload { issueData: IIssueData, url: string, - apiKey: string; + apiKey?: string; + orgId?: string; } export interface IIssueCredentialsDefinitions { @@ -254,6 +255,6 @@ export interface SendEmailCredentialOffer { organisation: organisation; errors; url: string; - apiKey: string; + orgId: string; organizationDetails: organisation; } \ No newline at end of file diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index a086d2bf5..9b27765d5 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -101,11 +101,6 @@ export class IssuanceService { const issuanceMethodLabel = 'create-offer'; const url = await this.getAgentUrl(issuanceMethodLabel, orgAgentType, agentEndPoint, agentDetails?.tenantId); - let apiKey; - apiKey = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } const issueData: IIssueData = { protocolVersion: 'v1', connectionId, @@ -121,7 +116,7 @@ export class IssuanceService { comment }; - const credentialCreateOfferDetails: ICreateOfferResponse = await this._sendCredentialCreateOffer(issueData, url, apiKey); + const credentialCreateOfferDetails: ICreateOfferResponse = await this._sendCredentialCreateOffer(issueData, url, orgId); if (credentialCreateOfferDetails && 0 < Object.keys(credentialCreateOfferDetails).length) { delete credentialCreateOfferDetails._tags; @@ -194,11 +189,7 @@ export class IssuanceService { const issuanceMethodLabel = 'create-offer-oob'; const url = await this.getAgentUrl(issuanceMethodLabel, orgAgentType, agentEndPoint, agentDetails?.tenantId); - let apiKey; - apiKey = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } + let issueData; if (credentialType === IssueCredentialType.INDY) { @@ -240,7 +231,7 @@ export class IssuanceService { comment: comment || '' }; } - const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); + const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, orgId); if (isShortenUrl) { const invitationUrl: string = credentialCreateOfferDetails.response?.invitationUrl; const url: string = await this.storeIssuanceObjectReturnUrl(invitationUrl); @@ -326,10 +317,10 @@ export class IssuanceService { } } - async _sendCredentialCreateOffer(issueData: IIssueData, url: string, apiKey: string): Promise { + async _sendCredentialCreateOffer(issueData: IIssueData, url: string, orgId: string): Promise { try { const pattern = { cmd: 'agent-send-credential-create-offer' }; - const payload: ISendOfferNatsPayload = { issueData, url, apiKey }; + const payload: ISendOfferNatsPayload = { issueData, url, orgId }; return await this.natsCallAgent(pattern, payload); } catch (error) { this.logger.error(`[_sendCredentialCreateOffer] [NATS call]- error in create credentials : ${JSON.stringify(error)}`); @@ -398,13 +389,8 @@ export class IssuanceService { const issuanceMethodLabel = 'get-issue-credential-by-credential-id'; const url = await this.getAgentUrl(issuanceMethodLabel, orgAgentType, agentEndPoint, agentDetails?.tenantId, credentialRecordId); - // const apiKey = platformConfig?.sgApiKey; - // const apiKey = await this._getOrgAgentApiKey(orgId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._getIssueCredentialsbyCredentialRecordId(url, apiKey); + + const createConnectionInvitation = await this._getIssueCredentialsbyCredentialRecordId(url, orgId); return createConnectionInvitation?.response; } catch (error) { this.logger.error(`[getIssueCredentialsbyCredentialRecordId] - error in get credentials : ${JSON.stringify(error)}`); @@ -430,12 +416,12 @@ export class IssuanceService { } } - async _getIssueCredentialsbyCredentialRecordId(url: string, apiKey: string): Promise<{ + async _getIssueCredentialsbyCredentialRecordId(url: string, orgId: string): Promise<{ response: string; }> { try { const pattern = { cmd: 'agent-get-issued-credentials-by-credentialDefinitionId' }; - const payload = { url, apiKey }; + const payload = { url, orgId }; return await this.natsCall(pattern, payload); } catch (error) { @@ -524,11 +510,6 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl throw new NotFoundException(ResponseMessages.issuance.error.organizationNotFound); } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const errors = []; let credentialOfferResponse; const arraycredentialOfferResponse = []; @@ -545,7 +526,7 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl organisation: organisation; errors: string[]; url: string; - apiKey: string; + orgId: string; organizationDetails: organisation; } = { credentialType, @@ -557,7 +538,7 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl organisation, errors, url, - apiKey, + orgId, organizationDetails, iterator: undefined, emailId: emailId || '', @@ -624,7 +605,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO organisation, errors, url, - apiKey, + orgId, organizationDetails } = sendEmailCredentialOffer; @@ -671,7 +652,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO }; } - const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, apiKey); + const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, orgId); if (!credentialCreateOfferDetails) { errors.push(new NotFoundException(ResponseMessages.issuance.error.credentialOfferNotFound)); @@ -731,12 +712,12 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO } } - async _outOfBandCredentialOffer(outOfBandIssuancePayload: object, url: string, apiKey: string): Promise<{ + async _outOfBandCredentialOffer(outOfBandIssuancePayload: object, url: string, orgId: string): Promise<{ response; }> { try { const pattern = { cmd: 'agent-out-of-band-credential-offer' }; - const payload = { outOfBandIssuancePayload, url, apiKey }; + const payload = { outOfBandIssuancePayload, url, orgId }; return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`[_outOfBandCredentialOffer] [NATS call]- error in out of band : ${JSON.stringify(error)}`); diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 7f799e52c..1d19e5041 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -17,7 +17,6 @@ import { map } from 'rxjs/operators'; import { OrgAgentType } from '@credebl/enum/enum'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { CommonConstants } from '@credebl/common/common.constant'; @Injectable() export class CredentialDefinitionService extends BaseService { constructor( @@ -36,11 +35,7 @@ export class CredentialDefinitionService extends BaseService { // eslint-disable-next-line yoda const did = credDef.orgDid?.split(':').length >= 4 ? credDef.orgDid : orgDid; const getAgentDetails = await this.credentialDefinitionRepository.getAgentType(credDef.orgId); - // const apiKey = await this._getOrgAgentApiKey(credDef.orgId); - let apiKey:string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(credDef.orgId); - } + const userId = user.id; credDef.tag = credDef.tag.trim(); const dbResult: credential_definition = await this.credentialDefinitionRepository.getByAttribute( @@ -60,7 +55,7 @@ export class CredentialDefinitionService extends BaseService { schemaId: credDef.schemaLedgerId, issuerId: did, agentEndPoint, - apiKey, + orgId: credDef.orgId, agentType: OrgAgentType.DEDICATED }; @@ -78,7 +73,7 @@ export class CredentialDefinitionService extends BaseService { issuerId: did }, agentEndPoint, - apiKey, + orgId: credDef.orgId, agentType: OrgAgentType.SHARED }; credDefResponseFromAgentService = await this._createCredentialDefinition(CredDefPayload); @@ -181,17 +176,12 @@ export class CredentialDefinitionService extends BaseService { const { agentEndPoint } = await this.credentialDefinitionRepository.getAgentDetailsByOrgId(String(orgId)); const getAgentDetails = await this.credentialDefinitionRepository.getAgentType(String(orgId)); const orgAgentType = await this.credentialDefinitionRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); - // const apiKey = await this._getOrgAgentApiKey(String(orgId)); - let apiKey:string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(String(orgId)); - } + let credDefResponse; if (OrgAgentType.DEDICATED === orgAgentType) { const getSchemaPayload = { credentialDefinitionId, - apiKey, + orgId, agentEndPoint, agentType: OrgAgentType.DEDICATED }; diff --git a/apps/ledger/src/credential-definition/interfaces/credential-definition.interface.ts b/apps/ledger/src/credential-definition/interfaces/credential-definition.interface.ts index b290bce9a..3fcd058e1 100644 --- a/apps/ledger/src/credential-definition/interfaces/credential-definition.interface.ts +++ b/apps/ledger/src/credential-definition/interfaces/credential-definition.interface.ts @@ -8,6 +8,7 @@ export interface CreateCredDefAgentRedirection { agentType?: string; apiKey?: string; agentEndPoint?: string; + orgId?: string; } export interface ITenantCredDef { @@ -24,6 +25,7 @@ export interface GetCredDefAgentRedirection { agentEndPoint?: string; agentType?: string; method?: string; + orgId?: string; } export interface GetCredDefFromTenantPayload { diff --git a/apps/ledger/src/schema/schema.interface.ts b/apps/ledger/src/schema/schema.interface.ts index ad4f58ed3..3ce52edbb 100644 --- a/apps/ledger/src/schema/schema.interface.ts +++ b/apps/ledger/src/schema/schema.interface.ts @@ -23,6 +23,7 @@ export interface CreateSchemaAgentRedirection { agentType?: string; apiKey?: string; agentEndPoint?: string; + orgId?: string; } export interface ITenantSchemaDto { @@ -40,6 +41,7 @@ export interface GetSchemaAgentRedirection { agentEndPoint?: string; agentType?: string; method?: string; + orgId?: string; } export interface GetSchemaFromTenantPayload { diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 7d253557b..3d674470c 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -20,7 +20,6 @@ import { OrgAgentType } from '@credebl/enum/enum'; import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { CommonConstants } from '@credebl/common/common.constant'; @Injectable() export class SchemaService extends BaseService { @@ -38,10 +37,6 @@ export class SchemaService extends BaseService { orgId: string ): Promise { - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } const userId = user.id; try { @@ -144,7 +139,7 @@ export class SchemaService extends BaseService { name: schema.schemaName, issuerId, agentEndPoint, - apiKey, + orgId, agentType: OrgAgentType.DEDICATED }; schemaResponseFromAgentService = await this._createSchema(schemaPayload); @@ -162,7 +157,7 @@ export class SchemaService extends BaseService { issuerId: did }, agentEndPoint, - apiKey, + orgId, agentType: OrgAgentType.SHARED }; schemaResponseFromAgentService = await this._createSchema(schemaPayload); @@ -278,20 +273,13 @@ export class SchemaService extends BaseService { const { agentEndPoint } = await this.schemaRepository.getAgentDetailsByOrgId(orgId); const getAgentDetails = await this.schemaRepository.getAgentType(orgId); const orgAgentType = await this.schemaRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); - // const apiKey = ''; - - let apiKey; - apiKey = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } let schemaResponse; if (OrgAgentType.DEDICATED === orgAgentType) { const getSchemaPayload = { schemaId, - apiKey, + orgId, agentEndPoint, agentType: OrgAgentType.DEDICATED }; @@ -304,7 +292,7 @@ export class SchemaService extends BaseService { payload: { schemaId }, agentType: OrgAgentType.SHARED, agentEndPoint, - apiKey + orgId }; schemaResponse = await this._getSchemaById(getSchemaPayload); } diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 2a3397514..b3b22c77a 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -31,17 +31,20 @@ export interface IGetAllProofPresentations { export interface IGetProofPresentationById { url: string; - apiKey: string; + apiKey?: string; + orgId?: string; } export interface IVerifyPresentation { url: string; - apiKey: string; + apiKey?: string; + orgId?: string; } export interface IVerifiedProofData { url: string; - apiKey: string; + apiKey?: string; + orgId?: string } export interface IProofPresentationData { @@ -129,8 +132,9 @@ export interface ISendPresentationExchangeProofRequestPayload { } export interface IPresentationExchangeProofRequestPayload { url: string; - apiKey: string; + apiKey?: string; proofRequestPayload: ISendPresentationExchangeProofRequestPayload; + orgId?: string; } export interface ISendProofRequestPayload { @@ -166,7 +170,8 @@ export interface IWSendProofRequestPayload { export interface IProofRequestPayload { url: string; - apiKey: string; + apiKey?: string; + orgId?: string proofRequestPayload: ISendProofRequestPayload; } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index b00c7e2f1..20a449863 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -121,12 +121,9 @@ export class VerificationService { const verificationMethodLabel = 'get-proof-presentation-by-id'; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId, '', proofId); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const payload = { apiKey, url }; + + const payload = { orgId, url }; const getProofPresentationById = await this._getProofPresentationById(payload); return getProofPresentationById?.response; @@ -222,11 +219,8 @@ export class VerificationService { const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); const verificationMethodLabel = 'request-proof'; const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(requestProof.orgId); - } - const payload = { apiKey, url, proofRequestPayload }; + + const payload = { orgId: requestProof.orgId, url, proofRequestPayload }; const getProofPresentationById = await this._sendProofRequest(payload); return getProofPresentationById?.response; @@ -268,15 +262,12 @@ export class VerificationService { try { const getAgentData = await this.verificationRepository.getAgentEndPoint(orgId); const orgAgentTypeData = await this.verificationRepository.getOrgAgentType(getAgentData?.orgAgentTypeId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - + const verificationMethod = 'accept-presentation'; - + const url = await this.getAgentUrl(verificationMethod, orgAgentTypeData, getAgentData?.agentEndPoint, getAgentData?.tenantId, '', proofId); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const payload = { apiKey, url }; + + const payload = { orgId, url }; const getProofPresentationById = await this._verifyPresentation(payload); return getProofPresentationById?.response; } catch (error) { @@ -351,12 +342,9 @@ export class VerificationService { outOfBandRequestProof['label'] = label; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethodLabel = 'create-request-out-of-band'; const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(user.orgId); - } + const { isShortenUrl, type, ...updateOutOfBandRequestProof } = outOfBandRequestProof; outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; @@ -366,7 +354,7 @@ export class VerificationService { if (ProofRequestType.INDY === type) { updateOutOfBandRequestProof.protocolVersion = updateOutOfBandRequestProof.protocolVersion || 'v1'; payload = { - apiKey, + orgId: user.orgId, url, proofRequestPayload: updateOutOfBandRequestProof }; @@ -375,7 +363,7 @@ export class VerificationService { if (ProofRequestType.PRESENTATIONEXCHANGE === type) { payload = { - apiKey, + orgId: user.orgId, url, proofRequestPayload: { protocolVersion:outOfBandRequestProof.protocolVersion || 'v2', @@ -713,12 +701,8 @@ export class VerificationService { let schemaId; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId, '', proofId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const payload = { apiKey, url }; + + const payload = { orgId, url }; const getProofPresentationById = await this._getVerifiedProofDetails(payload); From adbb71d58f4ab6972f5b761f6c6aee4bffcb1fb6 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Tue, 12 Mar 2024 18:01:09 +0530 Subject: [PATCH 170/231] feat: support multiple did methods (#589) * feat: support multiple did methods Signed-off-by: bhavanakarwade * refactor: removed the commented code Signed-off-by: tipusinghaw * fix: removed commented code Signed-off-by: tipusinghaw * fix: removed commented code agent-service Signed-off-by: tipusinghaw * fix: removed commented code agent-service Signed-off-by: tipusinghaw --------- Signed-off-by: bhavanakarwade Signed-off-by: tipusinghaw Co-authored-by: tipusinghaw --- .../src/agent-service.controller.ts | 26 +- .../src/agent-service.service.ts | 456 ++++++++++++------ .../src/interface/agent-service.interface.ts | 81 +++- .../repositories/agent-service.repository.ts | 36 +- .../agent-service/agent-service.controller.ts | 125 ++++- .../agent-service/agent-service.service.ts | 33 +- .../src/agent-service/dto/create-did.dto.ts | 70 +++ .../agent-service/dto/create-tenant.dto.ts | 34 +- .../agent-service/dto/create-wallet.dto.ts | 28 ++ .../interface/agent-service.interface.ts | 18 + libs/common/src/cast.helper.ts | 11 + libs/common/src/common.constant.ts | 13 +- libs/common/src/did.validator.ts | 27 ++ libs/common/src/interfaces/did.interface.ts | 13 + libs/common/src/response-messages/index.ts | 7 +- libs/enum/src/enum.ts | 21 +- .../prisma/data/credebl-master-table.json | 48 ++ .../migration.sql | 13 + libs/prisma-service/prisma/schema.prisma | 10 + libs/prisma-service/prisma/seed.ts | 62 ++- 20 files changed, 928 insertions(+), 204 deletions(-) create mode 100644 apps/api-gateway/src/agent-service/dto/create-did.dto.ts create mode 100644 apps/api-gateway/src/agent-service/dto/create-wallet.dto.ts create mode 100644 libs/common/src/did.validator.ts create mode 100644 libs/common/src/interfaces/did.interface.ts create mode 100644 libs/prisma-service/prisma/migrations/20240223140032_ledger_config/migration.sql diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 163c7edca..57228fad8 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -1,7 +1,7 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { AgentServiceService } from './agent-service.service'; -import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IProofPresentation, IAgentProofRequest, IPresentation } from './interface/agent-service.interface'; +import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IProofPresentation, IAgentProofRequest, IPresentation, IDidCreate, IWallet, ITenantRecord } from './interface/agent-service.interface'; import { user } from '@prisma/client'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IProofPresentationDetails } from '@credebl/common/interfaces/verification.interface'; @@ -28,6 +28,19 @@ export class AgentServiceController { return this.agentServiceService.createTenant(payload.createTenantDto, payload.user); } + /** + * @returns did + */ + @MessagePattern({ cmd: 'create-did' }) + async createDid(payload: { createDidDto: IDidCreate, orgId: string, user: IUserRequestInterface }): Promise { + return this.agentServiceService.createDid(payload.createDidDto, payload.orgId, payload.user); + } + + @MessagePattern({ cmd: 'create-wallet' }) + async createWallet(payload: { createWalletDto: IWallet, user: IUserRequestInterface}): Promise { + return this.agentServiceService.createWallet(payload.createWalletDto); + } + //DONE @MessagePattern({ cmd: 'agent-create-schema' }) async createSchema(payload: ITenantSchema): Promise { @@ -126,6 +139,11 @@ export class AgentServiceController { return this.agentServiceService.getAgentHealthDetails(payload.orgId); } + @MessagePattern({ cmd: 'get-ledger-config' }) + async getLedgerConfig(payload: { user: IUserRequestInterface }): Promise { + return this.agentServiceService.getLedgerConfigDetails(payload.user); + } + //DONE @MessagePattern({ cmd: 'agent-send-out-of-band-proof-request' }) async sendOutOfBandProofRequest(payload: { @@ -224,4 +242,10 @@ export class AgentServiceController { return this.agentServiceService.getQuestionAnswersRecord(payload.url, payload.apiKey); } + @MessagePattern({ cmd: 'polygon-create-keys' }) + async createSecp256k1KeyPair(payload: {orgId: string}): Promise { + return this.agentServiceService.createSecp256k1KeyPair(payload.orgId); + } + } + diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 36552d004..063d9ed0a 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -19,8 +19,8 @@ import * as dotenv from 'dotenv'; import * as fs from 'fs'; import { map } from 'rxjs/operators'; dotenv.config(); -import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation, IQuestionPayload } from './interface/agent-service.interface'; -import { AgentSpinUpStatus, AgentType, Ledgers, OrgAgentType } from '@credebl/enum/enum'; +import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation, IQuestionPayload, IDidCreate, IWallet, ITenantRecord, IPlatformAgent, LedgerListResponse, IOrgLedgers, IStoreOrgAgent } from './interface/agent-service.interface'; +import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; import { ledgers, org_agents, organisation, platform_config } from '@prisma/client'; import { CommonConstants } from '@credebl/common/common.constant'; @@ -34,10 +34,11 @@ import * as retry from 'async-retry'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IProofPresentationDetails } from '@credebl/common/interfaces/verification.interface'; -import { ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; +import { ledgerName } from '@credebl/common/cast.helper'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; + @Injectable() @WebSocketGateway() export class AgentServiceService { @@ -382,7 +383,6 @@ export class AgentServiceService { * Store organization agent details */ const storeAgentDetails = await this._storeOrgAgentDetails(agentPayload); - if (storeAgentDetails) { const filePath = `${process.cwd()}${process.env.AFJ_AGENT_TOKEN_PATH}${orgData.id}_${orgData.name.split(' ').join('_')}.json`; @@ -404,7 +404,6 @@ export class AgentServiceService { const getOrganization = await this.agentServiceRepository.getOrgDetails(orgData?.id); await this._createLegacyConnectionInvitation(orgData?.id, user, getOrganization.name); - if (agentSpinupDto.clientSocketId) { socket.emit('invitation-url-creation-success', { clientId: agentSpinupDto.clientSocketId }); } @@ -440,22 +439,18 @@ export class AgentServiceService { this._getAgentDid(payload), this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.DEDICATED) ]); - /** * Get DID method by agent */ const getDidMethod = await this._getDidMethod(payload, agentDid); - /** * Organization storage data */ const storeOrgAgentData = await this._buildStoreOrgAgentData(payload, getDidMethod, orgAgentTypeId); - /** * Store org agent details */ const storeAgentDid = await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); - return storeAgentDid; } catch (error) { await this._handleError(payload, error); @@ -465,17 +460,33 @@ export class AgentServiceService { private async _getAgentDid(payload: IStoreOrgAgentDetails): Promise { - const { agentEndPoint, apiKey, seed, ledgerId, did } = payload; + const { agentEndPoint, apiKey, ledgerId } = payload; + + //we take this values as static because of latest changes in afj controller to up agent of platform + const platformAgent: IPlatformAgent = { + seed: `${CommonConstants.SEED}`, + keyType: `${CommonConstants.KEYTYPE}`, + method: `${CommonConstants.METHOD}`, + network: `${CommonConstants.NETWORK}`, + role: `${CommonConstants.ROLE}` + }; const writeDid = 'write-did'; const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(ledgerId); const agentDidWriteUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; - return this._retryAgentSpinup(agentDidWriteUrl, apiKey, writeDid, seed, ledgerDetails[0].indyNamespace, did); + return this._retryAgentSpinup(agentDidWriteUrl, apiKey, writeDid, platformAgent); } private async _getDidMethod(payload: IStoreOrgAgentDetails, agentDid: object): Promise { const getDidDic = 'get-did-doc'; + const platformAgent: IPlatformAgent = { + seed: `${CommonConstants.SEED}`, + keyType: `${CommonConstants.KEYTYPE}`, + method: `${CommonConstants.METHOD}`, + network: `${CommonConstants.NETWORK}`, + role: `${CommonConstants.ROLE}` + }; const getDidMethodUrl = `${payload.agentEndPoint}${CommonConstants.URL_AGENT_GET_DID}`.replace('#', agentDid['did']); - return this._retryAgentSpinup(getDidMethodUrl, payload.apiKey, getDidDic); + return this._retryAgentSpinup(getDidMethodUrl, payload.apiKey, getDidDic, platformAgent); } private _buildStoreOrgAgentData(payload: IStoreOrgAgentDetails, getDidMethod: object, orgAgentTypeId: string): IStoreOrgAgentDetails { @@ -509,21 +520,24 @@ export class AgentServiceService { } if (payload && payload?.id) { + this.agentServiceRepository.removeOrgAgent(payload?.id); } this.logger.error(`[_storeOrgAgentDetails] - Error in store agent details : ${JSON.stringify(error)}`); } - async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, seed?: string, indyNamespace?: string, did?: string): Promise { + + async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, payload: IPlatformAgent): Promise { + + const { seed, keyType, method, network, role} = payload; const retryOptions = { retries: 10 }; - try { return retry(async () => { if (agentApiState === 'write-did') { - return this.commonService.httpPost(agentUrl, { seed, method: indyNamespace, did }, { headers: { 'authorization': apiKey } }); + return this.commonService.httpPost(agentUrl, { seed, keyType, method, network, role}, { headers: { 'authorization': apiKey } }); } else if (agentApiState === 'get-did-doc') { return this.commonService.httpGet(agentUrl, { headers: { 'authorization': apiKey } }); } @@ -563,6 +577,35 @@ export class AgentServiceService { } } + async _getALlLedgerDetails(): Promise<{ + response; + }> { + try { + const pattern = { + cmd: 'get-all-ledgers' + }; + const payload = {}; + return this.agentServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ( + { + response + })) + ).toPromise() + .catch(error => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.statusCode, + error: error.message + }, error.error); + }); + } catch (error) { + this.logger.error(`error in while fetching all the ledger details : ${JSON.stringify(error)}`); + } + } + async _walletProvision(payload: IWalletProvision): Promise<{ response; @@ -639,102 +682,185 @@ export class AgentServiceService { * @param user * @returns Get agent status */ - async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { - let agentProcess; + async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { + let agentProcess; + let ledgerIdData = []; + try { + if (payload.method !== DidMethod.KEY && payload.method !== DidMethod.WEB) { + + const { network } = payload; + const ledger = await ledgerName(network); + const ledgerList = await this._getALlLedgerDetails() as unknown as LedgerListResponse; + const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); + if (!isLedgerExist) { + throw new BadRequestException( + ResponseMessages.agent.error.invalidLedger, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } + + ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); + } - try { + const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; - // Get orgaization agent details by orgId - const getOrgAgent = await this.agentServiceRepository.getAgentDetails(payload.orgId); + // Create and stored agent details + agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); - if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { - this.logger.error(`Your wallet has already been created.`); - throw new BadRequestException( - ResponseMessages.agent.error.walletAlreadyCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); - } + // Get platform admin details + const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); + + payload.endpoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + // Create tenant wallet and DID + const tenantDetails = await this.createTenantAndNotify(payload, platformAdminSpinnedUp); + if (!tenantDetails?.walletResponseDetails?.id || !tenantDetails?.DIDCreationOption?.did) { + this.logger.error(`Error in getting wallet id and wallet did`); + throw new NotFoundException( + ResponseMessages.agent.error.notAbleToSpinUpAgent, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } - if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { - this.logger.error(`Your wallet has already processing.`); - throw new BadRequestException( - ResponseMessages.agent.error.walletAlreadyProcessing, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { + this.logger.error(`Platform-admin agent is not spun-up`); + throw new NotFoundException( + ResponseMessages.agent.error.platformAdminNotAbleToSpinp, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } + // Get shared agent type + const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); + // Get agent type details + const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); + + const storeOrgAgentData: IStoreOrgAgentDetails = { + did: tenantDetails.DIDCreationOption.did, + isDidPublic: true, + agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, + agentsTypeId: agentTypeId, + orgId: payload.orgId, + agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, + orgAgentTypeId, + tenantId: tenantDetails.walletResponseDetails['id'], + walletName: payload.label, + ledgerId: ledgerIdData ? ledgerIdData.map(item => item.id) : null, + id: agentProcess?.id + }; + + // Get organization data + const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); + + this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); + + await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); + + this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); + + // Create the legacy connection invitation + await this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); + + this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); + + } catch (error) { + this.handleError(error, payload.clientSocketId); + + if (agentProcess && agentProcess?.id) { + this.agentServiceRepository.removeOrgAgent(agentProcess?.id); + } + throw error; } + } - // Get ledgers details - const ledgerIdData = await this.agentServiceRepository.getLedgerDetails(Ledgers.Indicio_Demonet); - const ledgerIds = ledgerIdData.map(ledger => ledger?.id); + /** + * Create wallet + * @param payload + * @returns wallet details + */ + async createWallet(payload: IWallet): Promise { + try { + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); - payload.ledgerId = !payload.ledgerId || 0 === payload.ledgerId?.length ? ledgerIds : payload.ledgerId; - const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; + const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - // Create and stored agent details - agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); + const { label } = payload; + const createTenantOptions = { + config: { label } + }; - // Get platform admin details - const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); + const tenantDetails = await this.commonService.httpPost( + `${getPlatformAgentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, + createTenantOptions, + { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } } + ); - // Get genesis URLs by ledger Id - const ledgerDetails: ledgers[] = await this.agentServiceRepository.getGenesisUrl(payload.ledgerId); + return tenantDetails; - for (const iterator of ledgerDetails) { + } catch (error) { + this.logger.error(`error in create wallet : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } - // Create tenant in agent controller - const tenantDetails = await this.createTenantAndNotify(payload, iterator, platformAdminSpinnedUp); + /** + * Create did + * @param payload + * @returns did and didDocument + */ + async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { + try { + const agentDetails = await this.agentServiceRepository.getOrgAgentDetails(orgId); + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this.getOrgAgentApiKey(orgId); + } + const getOrgAgentType = await this.agentServiceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + let url; - if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { - this.logger.error(`Platform-admin agent is not spun-up`); - throw new NotFoundException( - ResponseMessages.agent.error.platformAdminNotAbleToSpinp, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } + if (getOrgAgentType.agent === OrgAgentType.DEDICATED) { + url = `${agentDetails.agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; + } else if (getOrgAgentType.agent === OrgAgentType.SHARED) { + url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`; + } + const didDetails = await this.commonService.httpPost(url, payload, + { headers: { 'authorization': apiKey } } + ); + return didDetails; - // Get org agent type details by shared agent - const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); - - // Get agent type details by AFJ agent - const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); - - const storeOrgAgentData: IStoreOrgAgentDetails = { - did: tenantDetails['did'], - verkey: tenantDetails['verkey'], - isDidPublic: true, - agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, - agentsTypeId: agentTypeId, - orgId: payload.orgId, - agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, - orgAgentTypeId, - tenantId: tenantDetails['tenantRecord']['id'], - walletName: payload.label, - ledgerId: payload.ledgerId, - id: agentProcess?.id - }; + } catch (error) { + this.logger.error(`error in create did : ${JSON.stringify(error)}`); - // Get organization data - const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); + if (error?.response?.error?.message) { + throw new RpcException({ + statusCode: error?.response?.status, + error: error?.response?.error?.message + }); + } else { + throw new RpcException(error.response ? error.response : error); + } - this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); - await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); + } +} - this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); + /** + * @returns Secp256k1 key pair for polygon DID + */ + async createSecp256k1KeyPair(orgId:string): Promise { + try { + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( + CommonConstants.PLATFORM_ADMIN_ORG + ); - // Create the legacy connection invitation - this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); + const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); - } - } catch (error) { - this.handleError(error, payload.clientSocketId); + const url = `${getPlatformAgentEndPoint}${CommonConstants.CREATE_POLYGON_SECP256k1_KEY}`; - if (agentProcess && agentProcess?.id) { - this.agentServiceRepository.removeOrgAgent(agentProcess?.id); - } - throw error; - } - } + const createKeyPairResponse = await this.commonService.httpPost(url, {}, { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } }); + return createKeyPairResponse; + } catch (error) { + this.logger.error(`error in createSecp256k1KeyPair : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } private async getPlatformAdminAndNotify(clientSocketId: string | undefined): Promise { const socket = await this.createSocketInstance(); @@ -762,31 +888,79 @@ export class AgentServiceService { * @param platformAdminSpinnedUp * @returns Get tanant status */ - private async createTenantAndNotify(payload: ITenantDto, ledgerIds: ledgers, platformAdminSpinnedUp: IOrgAgentsResponse): Promise { + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async createTenantAndNotify(payload: ITenantDto, platformAdminSpinnedUp: IOrgAgentsResponse): Promise { + const WalletSetupPayload = {...payload}; const socket = await this.createSocketInstance(); - if (payload.clientSocketId) { - socket.emit('agent-spinup-process-initiated', { clientId: payload.clientSocketId }); + if (WalletSetupPayload.clientSocketId) { + socket.emit('agent-spinup-process-initiated', { clientId: WalletSetupPayload.clientSocketId }); } - - const { label, seed, did } = payload; - const createTenantOptions = { - config: { label }, - seed, - did: did ? did : undefined, - method: ledgerIds.indyNamespace + const walletLabel = WalletSetupPayload.label; + + delete WalletSetupPayload.label; + delete WalletSetupPayload.clientSocketId; + delete WalletSetupPayload.orgId; + delete WalletSetupPayload.ledgerId; + + + const walletResponseDetails = await this._createTenantWallet(walletLabel, platformAdminSpinnedUp.org_agents[0].agentEndPoint, platformAdminSpinnedUp.org_agents[0].apiKey); + if (!walletResponseDetails && !walletResponseDetails.id) { + throw new InternalServerErrorException('Error while creating the wallet'); + } + const didCreateOption = { + didPayload: WalletSetupPayload, + agentEndpoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, + apiKey: platformAdminSpinnedUp.org_agents[0].apiKey, + tenantId: walletResponseDetails.id }; + const DIDCreationOption = await this._createDID(didCreateOption); + if (!DIDCreationOption) { + throw new InternalServerErrorException('Error while creating the wallet'); + } + + return {walletResponseDetails, DIDCreationOption}; + } +// + /** + * Create tenant wallet on the agent + * @param createTenantWalletPayload + * @returns Get tanant status + */ - // Invoke an API request from the agent to create multi-tenant agent - const tenantDetails = await this.commonService.httpPost( - `${platformAdminSpinnedUp.org_agents[0].agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, - createTenantOptions, - { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } } - ); - - return tenantDetails; - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async _createTenantWallet(label, endpoint, agentApiKey): Promise { //remove any + + + const createTenantOptions = { + config: { label } + }; + // Invoke an API request from the agent to create multi-tenant agent + const tenantDetails = await this.commonService.httpPost( + `${endpoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, + createTenantOptions, + { headers: { authorization: agentApiKey } } + ); + return tenantDetails; +} + /** + * Create tenant wallet on the agent + * @param _createDID + * @returns Get tanant status + */ + private async _createDID(didCreateOption): Promise { + + const {didPayload, agentEndpoint, apiKey, tenantId} = didCreateOption; + // Invoke an API request from the agent to create multi-tenant agent + const didDetails = await this.commonService.httpPost( + `${agentEndpoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${tenantId}`, + didPayload, + { headers: { authorization: apiKey } } + ); + return didDetails; +} private async createSocketInstance(): Promise { return io(`${process.env.SOCKET_HOST}`, { reconnection: true, @@ -827,9 +1001,9 @@ export class AgentServiceService { issuerId: payload.issuerId }; schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }) + .then(async (schema) => { + return schema; + }) .catch(error => { throw new InternalServerErrorException( ResponseMessages.agent.error.agentDown, @@ -847,10 +1021,10 @@ export class AgentServiceService { issuerId: payload.payload.issuerId }; schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }) - .catch(error => { + .then(async (schema) => { + return schema; + }) + .catch(error => { throw new InternalServerErrorException( ResponseMessages.agent.error.agentDown, { cause: new Error(), description: ResponseMessages.errorMessages.serverError } @@ -871,17 +1045,17 @@ export class AgentServiceService { if (OrgAgentType.DEDICATED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_SCHEMA_BY_ID.replace('#', `${payload.schemaId}`)}`; schemaResponse = await this.commonService.httpGet(url, payload.schemaId) - .then(async (schema) => { - return schema; - }); + .then(async (schema) => { + return schema; + }); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}`.replace('@', `${payload.payload.schemaId}`).replace('#', `${payload.tenantId}`); schemaResponse = await this.commonService.httpGet(url, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }); + .then(async (schema) => { + return schema; + }); } return schemaResponse; } catch (error) { @@ -904,9 +1078,9 @@ export class AgentServiceService { }; credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => { + return credDef; + }); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_CRED_DEF}`.replace('#', `${payload.tenantId}`); @@ -916,9 +1090,9 @@ export class AgentServiceService { issuerId: payload.payload.issuerId }; credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => { + return credDef; + }); } return credDefResponse; @@ -935,16 +1109,16 @@ export class AgentServiceService { if (OrgAgentType.DEDICATED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_CRED_DEF_BY_ID.replace('#', `${payload.credentialDefinitionId}`)}`; credDefResponse = await this.commonService.httpGet(url, payload.credentialDefinitionId) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => { + return credDef; + }); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CRED_DEF}`.replace('@', `${payload.payload.credentialDefinitionId}`).replace('#', `${payload.tenantId}`); credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => { + return credDef; + }); } return credDefResponse; } catch (error) { @@ -1134,6 +1308,17 @@ export class AgentServiceService { } } + async getLedgerConfigDetails(user: IUserRequestInterface): Promise { + try { + const getLedgerConfigData = await this.agentServiceRepository.getLedgerConfigByOrgId(); + return getLedgerConfigData; + + } catch (error) { + this.logger.error(`Error in send out of band proof request in agent service : ${JSON.stringify(error)}`); + throw error; + } + } + async sendOutOfBandProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, apiKey: string): Promise { try { const sendProofRequest = await this.commonService @@ -1336,4 +1521,3 @@ export class AgentServiceService { } } - diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index f52152eda..57070b9ea 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -33,15 +33,42 @@ export interface IOutOfBandCredentialOffer { export interface ITenantDto { label: string; - seed: string; - ledgerId?: string[]; + seed?: string; + keyType: string; + ledgerId: string[]; + domain?: string; + privatekey?: string; + endpoint?: string; + role?: string; + network?: string + endorserDid?: string method: string; orgId: string; did?: string; tenantId?: string; + didDocument?: string; + clientSocketId?: string; +} + +export interface IWallet { + label: string; + orgId: string; + did?: string; clientSocketId?: string; } +export interface IDidCreate { + keyType: KeyType; + seed: string; + domain?: string; + network?: string; + privatekey?: string; + endpoint?: string; + method: string; + did?: string; + role?: string; + endorserDid?: string; +} export interface ITenantSchema { tenantId?: string; attributes: string[]; @@ -141,6 +168,26 @@ export interface IStoreOrgAgentDetails { agentType?: string; } +export interface IStoreOrgAgent { + id?: string; + clientSocketId?: string; + agentEndPoint?: string; + apiKey?: string; + seed?: string; + did?: string; + verkey?: string; + isDidPublic?: boolean; + agentSpinUpStatus?: number; + walletName?: string; + agentsTypeId?: string; + orgId?: string; + agentId?: string; + orgAgentTypeId?: string; + tenantId?: string; + ledgerId?: unknown; + agentType?: string; +} + export interface IConnectionDetails { multiUseInvitation?: boolean; @@ -178,6 +225,14 @@ export interface IOrganizationAgentInterface { } +export interface IPlatformAgent { + seed: string; + keyType: string; + method: string; + network: string; + role: string; + } + export interface IOrgAgentInterface { orgDid: string; verkey: string; @@ -296,8 +351,7 @@ interface IConfig { label: string; walletConfig: IWalletConfig; } - -interface ITenantRecord { +export interface ITenantRecord { _tags: string; metadata: string; id: string; @@ -312,6 +366,7 @@ export interface ICreateTenant { verkey: string; } + export interface IOrgAgent { agentSpinUpStatus: number; } @@ -426,4 +481,22 @@ export interface IValidResponses { orgId?: string; connectionId: string; tenantId: string; + } +interface Ledger { + id: string; + createDateTime: string; + lastChangedDateTime: string; + name: string; + networkType: string; + poolConfig: string; + isActive: boolean; + networkString: string; + registerDIDEndpoint: string; + registerDIDPayload: string; + indyNamespace: string; + networkUrl: string | null; + } + + export interface LedgerListResponse { + response: Ledger[]; } \ No newline at end of file diff --git a/apps/agent-service/src/repositories/agent-service.repository.ts b/apps/agent-service/src/repositories/agent-service.repository.ts index 78133e7a2..096eaae29 100644 --- a/apps/agent-service/src/repositories/agent-service.repository.ts +++ b/apps/agent-service/src/repositories/agent-service.repository.ts @@ -1,8 +1,8 @@ import { PrismaService } from '@credebl/prisma-service'; import { Injectable, Logger } from '@nestjs/common'; // eslint-disable-next-line camelcase -import { ledgers, org_agents, organisation, platform_config, user } from '@prisma/client'; -import { ICreateOrgAgent, IStoreOrgAgentDetails, IOrgAgent, IOrgAgentsResponse, IOrgLedgers, IStoreAgent } from '../interface/agent-service.interface'; +import { ledgerConfig, ledgers, org_agents, org_agents_type, organisation, platform_config, user } from '@prisma/client'; +import { ICreateOrgAgent, IOrgAgent, IOrgAgentsResponse, IOrgLedgers, IStoreAgent, IStoreOrgAgentDetails } from '../interface/agent-service.interface'; import { AgentType } from '@credebl/enum/enum'; @Injectable() @@ -26,6 +26,15 @@ export class AgentServiceRepository { } } + async getLedgerConfigByOrgId(): Promise { + try { + const ledgerConfigData = await this.prisma.ledgerConfig.findMany(); + return ledgerConfigData; + } catch (error) { + this.logger.error(`[getGenesisUrl] - get genesis URL: ${JSON.stringify(error)}`); + throw error; + } + } /** * Get genesis url * @param id @@ -107,7 +116,7 @@ export class AgentServiceRepository { } - /** + /** * Store agent details * @param storeAgentDetails * @returns @@ -116,7 +125,7 @@ export class AgentServiceRepository { async storeOrgAgentDetails(storeOrgAgentDetails: IStoreOrgAgentDetails): Promise { try { - return this.prisma.org_agents.update({ + return await this.prisma.org_agents.update({ where: { id: storeOrgAgentDetails.id }, @@ -313,6 +322,23 @@ export class AgentServiceRepository { } } + // eslint-disable-next-line camelcase + async getOrgAgentType(orgAgentId: string): Promise { + try { + const orgAgent = await this.prisma.org_agents_type.findUnique({ + where: { + id: orgAgentId + } + }); + + return orgAgent; + + } catch (error) { + this.logger.error(`[getOrgAgentType] - error: ${JSON.stringify(error)}`); + throw error; + } + } + async getPlatfomAdminUser(platformAdminUserEmail: string): Promise { try { const platformAdminUser = await this.prisma.user.findUnique({ @@ -344,4 +370,4 @@ export class AgentServiceRepository { throw error; } } -} +} \ No newline at end of file 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 52c39c16e..ebd4fe8d2 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -32,6 +32,9 @@ 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 { CreateDidDto } from './dto/create-did.dto'; +import { validateDid } from '@credebl/common/did.validator'; +import { CreateWalletDto } from './dto/create-wallet.dto'; const seedLength = 32; @UseFilters(CustomExceptionFilter) @@ -75,6 +78,29 @@ export class AgentController { } + @Get('/orgs/agents/ledgerConfig') + @ApiOperation({ + summary: 'Get the ledger config details', + description: 'Get the ledger config details' + }) + @UseGuards(AuthGuard('jwt')) + async getLedgerDetails( + @User() reqUser: user, + @Res() res: Response + ): Promise { + + const ledgerConfigData = await this.agentService.getLedgerConfig(reqUser); + + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: ResponseMessages.agent.success.ledgerConfig, + data: ledgerConfigData + }; + + return res.status(HttpStatus.OK).json(finalResponse); + + } + /** * Spinup the agent by organization * @param agentSpinupDto @@ -154,14 +180,6 @@ export class AgentController { createTenantDto.orgId = orgId; - if (seedLength !== createTenantDto.seed.length) { - this.logger.error(`seed must be at most 32 characters`); - throw new BadRequestException( - ResponseMessages.agent.error.seedCharCount, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); - } - const tenantDetails = await this.agentService.createTenant(createTenantDto, user); const finalResponse: IResponse = { @@ -172,4 +190,95 @@ export class AgentController { return res.status(HttpStatus.CREATED).json(finalResponse); } + + /** + * Create wallet + * @param orgId + * @returns wallet + */ + @Post('/orgs/:orgId/agents/createWallet') + @ApiOperation({ + summary: 'Create wallet', + description: 'Create wallet' + }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createWallet( + @Param('orgId') orgId: string, + @Body() createWalletDto: CreateWalletDto, + @User() user: user, + @Res() res: Response + ): Promise { + + createWalletDto.orgId = orgId; + const walletDetails = await this.agentService.createWallet(createWalletDto, user); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.agent.success.createWallet, + data: walletDetails + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + // This function will be used after multiple did method implementation in create wallet + /** + * Create did + * @param orgId + * @returns did + */ + @Post('/orgs/:orgId/agents/createDid') + @ApiOperation({ + summary: 'Create did', + description: 'Create did' + }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createDid( + @Param('orgId') orgId: string, + @Body() createDidDto: CreateDidDto, + @User() user: user, + @Res() res: Response + ): Promise { + + validateDid(createDidDto); + + const didDetails = await this.agentService.createDid(createDidDto, orgId, user); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.agent.success.createDid, + data: didDetails + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + /** + * Create Secp256k1 key pair for polygon DID + * @param orgId + * @returns Secp256k1 key pair for polygon DID + */ + @Post('/orgs/:orgId/agents/polygon/create-keys') + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.PLATFORM_ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createSecp256k1KeyPair( + @Param('orgId') orgId: string, + @Res() res: Response + ): Promise { + + const didDetails = await this.agentService.createSecp256k1KeyPair(orgId); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.agent.success.createKeys, + data: didDetails + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } } \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/agent-service.service.ts b/apps/api-gateway/src/agent-service/agent-service.service.ts index 417512bed..48cead6c9 100644 --- a/apps/api-gateway/src/agent-service/agent-service.service.ts +++ b/apps/api-gateway/src/agent-service/agent-service.service.ts @@ -4,8 +4,10 @@ import { user } from '@prisma/client'; import { BaseService } from 'libs/service/base.service'; import { AgentSpinupDto } from './dto/agent-service.dto'; import { CreateTenantDto } from './dto/create-tenant.dto'; -import { AgentSpinUpSatus } from './interface/agent-service.interface'; +import { AgentSpinUpSatus, IWalletRecord } from './interface/agent-service.interface'; import { AgentStatus } from './interface/agent-service.interface'; +import { CreateDidDto } from './dto/create-did.dto'; +import { CreateWalletDto } from './dto/create-wallet.dto'; @Injectable() export class AgentService extends BaseService { @@ -35,6 +37,19 @@ export class AgentService extends BaseService { return this.sendNatsMessage(this.agentServiceProxy, 'create-tenant', payload); } + async createDid(createDidDto: CreateDidDto, orgId:string, user: user): Promise { + const payload = { createDidDto, orgId, user }; + + // NATS call + return this.sendNatsMessage(this.agentServiceProxy, 'create-did', payload); + } + + async createWallet(createWalletDto: CreateWalletDto, user: user): Promise { + const payload = { createWalletDto, user }; + // NATS call + return this.sendNatsMessage(this.agentServiceProxy, 'create-wallet', payload); + } + async getAgentHealth(user: user, orgId:string): Promise { const payload = { user, orgId }; @@ -43,4 +58,18 @@ export class AgentService extends BaseService { } -} + async getLedgerConfig(user: user): Promise { + const payload = { user }; + + // NATS call + return this.sendNatsMessage(this.agentServiceProxy, 'get-ledger-config', payload); + } + + async createSecp256k1KeyPair(orgId:string): Promise { + const payload = {orgId}; + // NATS call + + return this.sendNatsMessage(this.agentServiceProxy, 'polygon-create-keys', payload); + } + +} \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts new file mode 100644 index 000000000..7f75116ec --- /dev/null +++ b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts @@ -0,0 +1,70 @@ +import { trim } from '@credebl/common/cast.helper'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { MaxLength, IsString, Matches, IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreateDidDto { + + @ApiProperty({ example: '000000000000000000000000000Seed1' }) + @MaxLength(32, { message: 'seed must be at most 32 characters.' }) + @Transform(({ value }) => trim(value)) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'seed must be in string format.' }) + @Matches(/^\S*$/, { + message: 'Spaces are not allowed in seed' + }) + seed?: string; + + @ApiProperty({ example: 'ed25519'}) + @IsNotEmpty({ message: 'key type is required' }) + @IsString({ message: 'key type be in string format.' }) + keyType: string; + + @ApiProperty({ example: 'indy'}) + @IsNotEmpty({ message: 'seed is required' }) + @IsString({ message: 'did must be in string format.' }) + method: string; + + @ApiProperty({example: 'bcovrin:testnet'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'network must be in string format.' }) + network?: string; + + @ApiProperty({example: 'www.github.com'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'domain must be in string format.' }) + domain?: string; + + @ApiProperty({example: 'endorser'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'role must be in string format.' }) + role?: string; + + @ApiProperty({example: ''}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'private key must be in string format.' }) + privatekey?: string; + + @ApiProperty({example: 'http://localhost:6006/docs'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'endpoint must be in string format.' }) + endpoint?: string; + + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'did must be in string format.' }) + did?: string; + + @ApiProperty({example: 'did:indy:bcovrin:testnet:UEeW111G1tYo1nEkPwMcF'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'endorser did must be in string format.' }) + endorserDid?: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts index 4ee5d3388..72fb459ea 100644 --- a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts @@ -1,9 +1,10 @@ import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { MaxLength, IsString, MinLength, Matches, IsNotEmpty, IsOptional, IsArray, IsUUID } from 'class-validator'; +import { MaxLength, IsString, MinLength, Matches, IsOptional } from 'class-validator'; +import { CreateDidDto } from './create-did.dto'; const labelRegex = /^[a-zA-Z0-9 ]*$/; -export class CreateTenantDto { +export class CreateTenantDto extends CreateDidDto { @ApiProperty() @MaxLength(25, { message: 'Maximum length for label must be 25 characters.' }) @IsString({ message: 'label must be in string format.' }) @@ -15,37 +16,18 @@ export class CreateTenantDto { }) label: string; - @ApiProperty({ example: 'dfuhgfklskmjngrjekjfgjjfkoekfdad' }) - @MaxLength(32, { message: 'seed must be at most 32 characters.' }) - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'seed is required' }) - @IsString({ message: 'seed must be in string format.' }) - @Matches(/^\S*$/, { - message: 'Spaces are not allowed in seed' - }) - seed: string; - - @ApiProperty({ example: ["b942473d-6fdd-4a38-b76e-a3314fca66b6"] }) - @ApiPropertyOptional() - @IsOptional() - @IsUUID(4, {each:true, message: "Please provide valid ledgerId"}) - @IsArray({ message: 'ledgerId must be an array' }) - @IsNotEmpty({ message: 'please provide valid ledgerId' }) - @IsString({ each: true, message: 'Each ledgerId must be a string' }) - @MaxLength(36, { each: true, message: 'ledgerId must be at most 36 characters.' }) - ledgerId?: string[]; - - @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) @IsOptional() @ApiPropertyOptional() @IsString({ message: 'did must be in string format.' }) - did?: string; + clientSocketId?: string; - @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) @IsOptional() @ApiPropertyOptional() @IsString({ message: 'did must be in string format.' }) - clientSocketId?: string; + did?: string; orgId: string; + } \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/dto/create-wallet.dto.ts b/apps/api-gateway/src/agent-service/dto/create-wallet.dto.ts new file mode 100644 index 000000000..eec4ac54c --- /dev/null +++ b/apps/api-gateway/src/agent-service/dto/create-wallet.dto.ts @@ -0,0 +1,28 @@ +import { trim } from '@credebl/common/cast.helper'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { MaxLength, IsString, MinLength, IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreateWalletDto { + @ApiProperty() + @MaxLength(25, { message: 'Maximum length for label must be 25 characters.' }) + @IsString({ message: 'label must be in string format.' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'label is required' }) + @MinLength(2, { message: 'Minimum length for label must be 2 characters.' }) + label: string; + + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'did must be in string format.' }) + did?: string; + + @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'did must be in string format.' }) + clientSocketId?: string; + + orgId: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts b/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts index 6ced1ed42..25511321e 100644 --- a/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts +++ b/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts @@ -5,4 +5,22 @@ export interface AgentStatus { label: string; endpoints: string[]; isInitialized: boolean; +} +interface IWalletConfig { + id: string; + key: string; + keyDerivationMethod: string; +} + +interface IConfig { + label: string; + walletConfig: IWalletConfig; +} +export interface IWalletRecord { + _tags: string; + metadata: string; + id: string; + createdAt: string; + config: IConfig; + updatedAt: string; } \ No newline at end of file diff --git a/libs/common/src/cast.helper.ts b/libs/common/src/cast.helper.ts index 4b886c1ef..d92cb2a59 100644 --- a/libs/common/src/cast.helper.ts +++ b/libs/common/src/cast.helper.ts @@ -44,4 +44,15 @@ export function toNumber(value: string, opts: ToNumberOptions = {}): number { } return newValue; +} + +export function ledgerName(value: string): string { + let network; + network = value.replace(":", " "); + network = network.charAt(0).toUpperCase() + network.slice(1); + const words = network.split(" "); + network = `${words[0]} ${words[1].charAt(0).toUpperCase()}${words[1].slice(1)}`; + + return network; + } \ No newline at end of file diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index 14f7ddc02..1ce7017a4 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -86,6 +86,7 @@ export enum CommonConstants { // SHARED AGENT URL_SHAGENT_CREATE_TENANT = '/multi-tenancy/create-tenant', + URL_SHAGENT_CREATE_DID = '/multi-tenancy/create-did/', URL_SHAGENT_WITH_TENANT_AGENT = '/multi-tenancy/with-tenant-agent', URL_SHAGENT_CREATE_SCHEMA = '/multi-tenancy/schema/#', URL_SHAGENT_GET_SCHEMA = '/multi-tenancy/schema/@/#', @@ -126,6 +127,10 @@ export enum CommonConstants { URL_AGENT_GET_DIDS = '/dids', URL_AGENT_GET_ENDPOINT = '/agent', + // CREATE KEYS + CREATE_POLYGON_SECP256k1_KEY = '/polygon/create-keys', + + // ENTITY NAMES ENTITY_NAME_TEMPLATE = 'templates', ENTITY_NAME_CRED_DEF = 'credential_definition', @@ -301,6 +306,12 @@ export enum CommonConstants { TRANSACTION_MULTITENANT_SIGN = '/multi-tenancy/transactions/endorse/#', TRANSACTION_MULTITENANT_SUMBIT = '/multi-tenancy/transactions/write/#', + // Static values to up platform Agent + SEED = '101111110111101100111100000Seed1', + KEYTYPE = 'ed25519', + METHOD = 'indy', + NETWORK = 'bcovrin:testnet', + ROLE = 'endorser', //CacheInfo CACHE_APIKEY_KEY = "apiKey", @@ -762,4 +773,4 @@ export const DISALLOWED_EMAIL_DOMAIN = [ 'zapto.org', 'ze.cx', 'zeroe.ml' -]; +]; \ No newline at end of file diff --git a/libs/common/src/did.validator.ts b/libs/common/src/did.validator.ts new file mode 100644 index 000000000..2537cb48a --- /dev/null +++ b/libs/common/src/did.validator.ts @@ -0,0 +1,27 @@ +import { DidMethod, KeyType } from "@credebl/enum/enum"; +import { IDidCreate } from "./interfaces/did.interface"; + +export function validateDid(createDid: IDidCreate): string[] { + const errors: string[] = []; + + if (DidMethod.WEB && !createDid.domain) { + errors.push('domain is required for Web method'); + } else if (DidMethod.INDY && !createDid.network) { + errors.push('network is required for Indy method'); + } else if (DidMethod.INDY && createDid.keyType !== KeyType.Ed25519) { + errors.push('Only ed25519 key type is supported for Indy method'); + } else if ((createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && + (createDid.keyType !== KeyType.Ed25519 && createDid.keyType !== KeyType.Bls12381g2)) { + errors.push('Only ed25519 and bls12381g2 key type is supported'); + } else if (!createDid.role) { + errors.push('role or endorserDid is required'); + } else if (DidMethod.POLYGON && !createDid.privatekey) { + errors.push('privateKey is required for polygon method'); + } else if (DidMethod.POLYGON && !createDid.endpoint) { + errors.push('endpoint is required for polygon method'); + } else if ((DidMethod.INDY || DidMethod.KEY || DidMethod.WEB) && (!createDid.seed)) { + errors.push('seed is required'); + } + + return errors; +} \ No newline at end of file diff --git a/libs/common/src/interfaces/did.interface.ts b/libs/common/src/interfaces/did.interface.ts new file mode 100644 index 000000000..8299f06d7 --- /dev/null +++ b/libs/common/src/interfaces/did.interface.ts @@ -0,0 +1,13 @@ +export interface IDidCreate { + keyType: string; + seed?: string; + domain?: string; + network?: string; + privatekey?: string; + endpoint?: string; + method: string; + did?: string; + role?: string; + endorserDid?: string; + didDocument?: object; +} \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index c75109bd6..5bf6ac00f 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -187,9 +187,13 @@ export const ResponseMessages = { agent: { success: { create: 'Agent process initiated successfully. Please wait', + createWallet: 'Wallet created successfully', + createDid: 'Did created successfully', health: 'Agent health details retrieved successfully.', + ledgerConfig: 'Ledger config details fetched successfully.', webhookUrlRegister:'Webhook Url registered successfully', - getWebhookUrl:'Webhook Url fetched successfully' + getWebhookUrl:'Webhook Url fetched successfully', + createKeys:'Key-pair created successfully' }, error: { exists: 'An agent name is already exist', @@ -214,6 +218,7 @@ export const ResponseMessages = { walletAlreadyProcessing: 'Your wallet is already processing', notAbleToSpinp: 'Agent not able to spun up', platformAdminNotAbleToSpinp: 'Platform admin agent is not spun up', + invalidLedger: 'Invalid ledger name', seedCharCount: 'seed must be at most 32 characters', nullTenantId:'TenantId must not be null', tenantIdNotFound:'TenantId not found', diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 3536f0bbc..047256879 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -8,6 +8,25 @@ export enum AgentType { ACAPY = 'ACAPY' } +export declare enum KeyType { + Ed25519 = 'ed25519', + Bls12381g1g2 = 'bls12381g1g2', + Bls12381g1 = 'bls12381g1', + Bls12381g2 = 'bls12381g2', + X25519 = 'x25519', + P256 = 'p256', + P384 = 'p384', + P521 = 'p521', + K256 = 'k256' +} + +export enum DidMethod { + INDY = 'indy', + KEY = 'key', + WEB = 'web', + POLYGON = 'polygon' +} + export enum Ledgers { Bcovrin_Testnet = 'Bcovrin Testnet', Indicio_Testnet = 'Indicio Testnet', @@ -83,4 +102,4 @@ const transitionMap: { [key in Invitation]: Invitation[] } = { [Invitation.REJECTED]: [] }; -export const transition = (currentStatus: Invitation, nextStatus: Invitation): boolean => (transitionMap[currentStatus].includes(nextStatus)); +export const transition = (currentStatus: Invitation, nextStatus: Invitation): boolean => (transitionMap[currentStatus].includes(nextStatus)); \ No newline at end of file diff --git a/libs/prisma-service/prisma/data/credebl-master-table.json b/libs/prisma-service/prisma/data/credebl-master-table.json index 028c6cfd9..41f135270 100644 --- a/libs/prisma-service/prisma/data/credebl-master-table.json +++ b/libs/prisma-service/prisma/data/credebl-master-table.json @@ -129,6 +129,26 @@ "registerDIDEndpoint": "https://selfserve.indiciotech.io/nym", "registerDIDPayload": "", "indyNamespace": "indicio:mainnet" + }, + { + "name": "Polygon Testnet", + "networkType": "testnet", + "poolConfig": "", + "isActive": true, + "networkString": "testnet", + "registerDIDEndpoint": "", + "registerDIDPayload": "", + "indyNamespace": "polygon:testnet" + }, + { + "name": "Polygon Mainnet", + "networkType": "mainnet", + "poolConfig": "", + "isActive": true, + "networkString": "mainnet", + "registerDIDEndpoint": "", + "registerDIDPayload": "", + "indyNamespace": "polygon:mainnet" } ], "endorseData": [ @@ -186,5 +206,33 @@ "key": "multiEcosystemSupport", "value": "false" } + ], + "ledgerConfig": [ + { + "name": "indy", + "details": { + "bcovrin": { + "testnet":"did:indy:bcovrin:testnet" + }, + "indicio": { + "testnet":"did:indy:indicio:testnet", + "demonet":"did:indy:indicio:demonet", + "mainnet":"did:indy:indicio:mainnet" + } + } + }, + { + "name": "polygon", + "details": { + "mainnet": "did:polygon:mainnet", + "testnet": "did:polygon:testnet" + } + }, + { + "key": "did:key" + }, + { + "key": "did:web" + } ] } \ No newline at end of file diff --git a/libs/prisma-service/prisma/migrations/20240223140032_ledger_config/migration.sql b/libs/prisma-service/prisma/migrations/20240223140032_ledger_config/migration.sql new file mode 100644 index 000000000..ed5a71d72 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240223140032_ledger_config/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "ledgerConfig" ( + "id" UUID NOT NULL, + "name" TEXT NOT NULL, + "details" JSONB NOT NULL, + "createDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdBy" TEXT NOT NULL DEFAULT '1', + "lastChangedDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "lastChangedBy" TEXT NOT NULL DEFAULT '1', + "deletedAt" TIMESTAMP(6), + + CONSTRAINT "ledgerConfig_pkey" PRIMARY KEY ("id") +); diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 28dcfbf44..baac39f5d 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -486,3 +486,13 @@ model notification { lastChangedBy String @default("1") deletedAt DateTime? @db.Timestamp(6) } +model ledgerConfig { + id String @id @default(uuid()) @db.Uuid + name String + details Json + createDateTime DateTime @default(now()) @db.Timestamptz(6) + createdBy String @default("1") + lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) + lastChangedBy String @default("1") + deletedAt DateTime? @db.Timestamp(6) +} \ No newline at end of file diff --git a/libs/prisma-service/prisma/seed.ts b/libs/prisma-service/prisma/seed.ts index 7c2eea86c..fc4bf8814 100644 --- a/libs/prisma-service/prisma/seed.ts +++ b/libs/prisma-service/prisma/seed.ts @@ -13,7 +13,7 @@ const createPlatformConfig = async (): Promise => { try { const existPlatformAdmin = await prisma.platform_config.findMany(); - if (existPlatformAdmin.length === 0) { + if (0 === existPlatformAdmin.length) { const { platformConfigData } = JSON.parse(configData); const platformConfig = await prisma.platform_config.create({ data: platformConfigData @@ -38,9 +38,9 @@ const createOrgRoles = async (): Promise => { in: roleNames } } - }) + }); - if (existOrgRole.length === 0) { + if (0 === existOrgRole.length) { const orgRoles = await prisma.org_roles.createMany({ data: orgRoleData }); @@ -66,9 +66,9 @@ const createAgentTypes = async (): Promise => { in: agentType } } - }) + }); - if (existAgentType.length === 0) { + if (0 === existAgentType.length) { const agentTypes = await prisma.agents_type.createMany({ data: agentTypeData }); @@ -94,9 +94,9 @@ const createOrgAgentTypes = async (): Promise => { in: orgAgentType } } - }) + }); - if (existAgentType.length === 0) { + if (0 === existAgentType.length) { const orgAgentTypes = await prisma.org_agents_type.createMany({ data: orgAgentTypeData }); @@ -122,9 +122,9 @@ const createPlatformUser = async (): Promise => { where: { email: platformAdminData.email } - }) + }); - if (existPlatformAdminUser.length === 0) { + if (0 === existPlatformAdminUser.length) { const platformUser = await prisma.user.create({ data: platformAdminData }); @@ -152,9 +152,9 @@ const createPlatformOrganization = async (): Promise => { where: { name: platformAdminOrganizationData.name } - }) + }); - if (existPlatformAdminUser.length === 0) { + if (0 === existPlatformAdminUser.length) { const platformOrganization = await prisma.organisation.create({ data: platformAdminOrganizationData }); @@ -220,9 +220,9 @@ const createLedger = async (): Promise => { in: ledgerIndyNamespace } } - }) + }); - if (existLedgerIndyNameSpace.length === 0) { + if (0 === existLedgerIndyNameSpace.length) { const createLedger = await prisma.ledgers.createMany({ data: ledgerData @@ -250,9 +250,9 @@ const createEcosystemRoles = async (): Promise => { in: ecosystemRoleDetails } } - }) + }); - if (existEcosystemRole.length === 0) { + if (0 === existEcosystemRole.length) { const ecosystemRoles = await prisma.ecosystem_roles.createMany({ data: ecosystemRoleData }); @@ -279,10 +279,10 @@ const createEcosystemConfig = async (): Promise => { in: ecosystemConfigKey } } - }) + }); - if (existEcosystemConfig.length === 0) { + if (0 === existEcosystemConfig.length) { const configDetails = await prisma.ecosystem_config.createMany({ data: ecosystemConfigData }); @@ -298,6 +298,30 @@ const createEcosystemConfig = async (): Promise => { } }; +const createLedgerConfig = async (): Promise => { + try { + const { ledgerConfig } = JSON.parse(configData); + + const ledgerConfigList = await prisma.ledgerConfig.findMany(); + + + if (0 === ledgerConfigList.length && ledgerConfig) { + const configDetails = await prisma.ledgerConfig.createMany({ + data: ledgerConfig + }); + + logger.log(configDetails); + } else { + logger.log('Already seeding in ledger config'); + } + + + } catch (e) { + logger.error('An error occurred ecosystem config:', e); + } +}; + + async function main(): Promise { await createPlatformConfig(); @@ -310,7 +334,7 @@ async function main(): Promise { await createLedger(); await createEcosystemRoles(); await createEcosystemConfig(); - + await createLedgerConfig(); } @@ -322,4 +346,4 @@ main() logger.error(`In prisma seed initialize`, e); await prisma.$disconnect(); process.exit(1); - }); + }); \ No newline at end of file From 8e8e1ccc8f0aa6d656e7be3e48c426ce5201b2e1 Mon Sep 17 00:00:00 2001 From: Krishna Date: Tue, 12 Mar 2024 20:55:09 +0530 Subject: [PATCH 171/231] Add shortening url for email verification Signed-off-by: Krishna --- .../src/verification/dto/request-proof.dto.ts | 3 +- .../src/interfaces/verification.interface.ts | 5 +- apps/verification/src/verification.service.ts | 63 +++++++------------ 3 files changed, 30 insertions(+), 41 deletions(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 97e4a39ce..32e160c89 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,4 +1,4 @@ -import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID, ArrayUnique } from 'class-validator'; +import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID, ArrayUnique, ArrayMaxSize } from 'class-validator'; import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; @@ -336,6 +336,7 @@ export class SendProofRequestPayload { @IsEmail({}, { each: true, message: 'Please provide a valid email' }) @ArrayNotEmpty({ message: 'Email array must not be empty' }) @ArrayUnique({ message: 'Duplicate emails are not allowed' }) + @ArrayMaxSize(10, { message: 'Max 10 emails can be sent' }) @IsArray() @IsString({ each: true, message: 'Each emailId in the array should be a string' }) @IsOptional() diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index f3e10e03b..632e71bb2 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -161,7 +161,7 @@ export interface IWSendProofRequestPayload { parentThreadId?: string; willConfirm?: boolean; imageUrl?: string; - emailId?: string[] + emailId?: string[]; type?:string; presentationDefinition?:IProofRequestPresentationDefinition; } @@ -213,3 +213,6 @@ export interface IProofRequestSearchCriteria { searchByText: string; } +export interface IInvitation{ + invitationUrl?: string; +} \ No newline at end of file diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index f33693427..5e7ae9848 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -2,7 +2,7 @@ import { BadRequestException, HttpException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs/operators'; -import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData} from './interfaces/verification.interface'; +import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IInvitation} from './interfaces/verification.interface'; import { VerificationRepository } from './repositories/verification.repository'; import { CommonConstants } from '@credebl/common/common.constant'; import { org_agents, organisation, presentations } from '@prisma/client'; @@ -360,7 +360,6 @@ export class VerificationService { } // Destructuring 'outOfBandRequestProof' to remove emailId, as it is not used while agent operation - // eslint-disable-next-line @typescript-eslint/no-unused-vars const { isShortenUrl, emailId, type, ...updateOutOfBandRequestProof } = outOfBandRequestProof; outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; @@ -393,33 +392,28 @@ export class VerificationService { } } }, - autoAcceptProof:outOfBandRequestProof.autoAcceptProof || 'always' + autoAcceptProof:outOfBandRequestProof.autoAcceptProof } }; } - - const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - //apply presentation shorting URL - if (isShortenUrl) { - const proofRequestInvitationUrl: string = getProofPresentation?.response?.invitationUrl; - const shortenedUrl: string = await this.storeVerificationObjectAndReturnUrl(proofRequestInvitationUrl, false); - this.logger.log('shortenedUrl', shortenedUrl); - if (shortenedUrl) { - getProofPresentation.response.invitationUrl = shortenedUrl; - } - } - if (!getProofPresentation) { - throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); - } - - if (outOfBandRequestProof.emailId) { - const batchSize = parseInt(process.env.OOB_BATCH_SIZE); // Batch size taken from env. It is same for issuance and verification - const { emailId } = outOfBandRequestProof; // Assuming it's an array - await this.sendEmailInBatches(payload, emailId, getAgentDetails, getOrganization, batchSize); + if (emailId) { + await this.sendEmailInBatches(payload, emailId, getAgentDetails, getOrganization); return true; } else { - return await this.generateOOBProofReq(payload, getAgentDetails); + const presentationProof: IInvitation = await this.generateOOBProofReq(payload, getAgentDetails); + const proofRequestInvitationUrl: string = presentationProof.invitationUrl; + if (isShortenUrl) { + const shortenedUrl: string = await this.storeVerificationObjectAndReturnUrl(proofRequestInvitationUrl, false); + this.logger.log('shortenedUrl', shortenedUrl); + if (shortenedUrl) { + presentationProof.invitationUrl = shortenedUrl; + } + } + if (!presentationProof) { + throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); + } + return presentationProof; } } catch (error) { this.logger.error(`[sendOutOfBandPresentationRequest] - error in out of band proof request : ${error.message}`); @@ -452,14 +446,12 @@ export class VerificationService { } - // Only accept array of string for emailIds - async sendEmailInBatches(payload: IProofRequestPayload, emailIds: string[], getAgentDetails: org_agents, organizationDetails: organisation, batchSize: number): Promise { + // Currently batch size is not used, as length of emails sent is restricted to '10' + async sendEmailInBatches(payload: IProofRequestPayload, emailIds: string[], getAgentDetails: org_agents, organizationDetails: organisation): Promise { try { const accumulatedErrors = []; - for (let i = 0; i < emailIds.length; i += batchSize) { - const batch = emailIds.slice(i, i + batchSize); - for (const email of batch) { + for (const email of emailIds) { try { await this.sendOutOfBandProofRequest(payload, email, getAgentDetails, organizationDetails); await this.delay(500); @@ -468,7 +460,6 @@ export class VerificationService { accumulatedErrors.push(error); } } - } if (0 < accumulatedErrors.length) { this.logger.error(accumulatedErrors); @@ -495,16 +486,10 @@ export class VerificationService { throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); } - const invitationId = getProofPresentation?.response?.invitation['@id']; - - if (!invitationId) { - throw new Error(ResponseMessages.verification.error.invitationNotFound); - } - - const shortenedUrl = getAgentDetails?.tenantId - ? `${getAgentDetails?.agentEndPoint}/multi-tenancy/url/${getAgentDetails?.tenantId}/${invitationId}` - : `${getAgentDetails?.agentEndPoint}/url/${invitationId}`; - + const invitationUrl = getProofPresentation?.response?.invitationUrl; + // Currently have shortenedUrl to store only for 30 days + const persist: boolean = false; + const shortenedUrl = await this.storeVerificationObjectAndReturnUrl(invitationUrl, persist); const qrCodeOptions: QRCode.QRCodeToDataURLOptions = { type: 'image/png' }; const outOfBandVerificationQrCode = await QRCode.toDataURL(shortenedUrl, qrCodeOptions); From 65fff221143b89225d174d47ed49649bc861a906 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Wed, 13 Mar 2024 15:59:43 +0530 Subject: [PATCH 172/231] fix: refactored tables Signed-off-by: bhavanakarwade --- .../prisma/data/credebl-master-table.json | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/libs/prisma-service/prisma/data/credebl-master-table.json b/libs/prisma-service/prisma/data/credebl-master-table.json index 41f135270..fb55cb3ad 100644 --- a/libs/prisma-service/prisma/data/credebl-master-table.json +++ b/libs/prisma-service/prisma/data/credebl-master-table.json @@ -139,16 +139,6 @@ "registerDIDEndpoint": "", "registerDIDPayload": "", "indyNamespace": "polygon:testnet" - }, - { - "name": "Polygon Mainnet", - "networkType": "mainnet", - "poolConfig": "", - "isActive": true, - "networkString": "mainnet", - "registerDIDEndpoint": "", - "registerDIDPayload": "", - "indyNamespace": "polygon:mainnet" } ], "endorseData": [ @@ -229,10 +219,16 @@ } }, { + "name": "key", + "details": { "key": "did:key" + } }, { + "name": "web", + "details": { "key": "did:web" + } } ] } \ No newline at end of file From c0c0b34829e7c815671daca63c6f7a1cb82de36b Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Wed, 13 Mar 2024 16:08:05 +0530 Subject: [PATCH 173/231] fix: reafctored json data Signed-off-by: bhavanakarwade --- libs/prisma-service/prisma/data/credebl-master-table.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/prisma-service/prisma/data/credebl-master-table.json b/libs/prisma-service/prisma/data/credebl-master-table.json index fb55cb3ad..94c5c465c 100644 --- a/libs/prisma-service/prisma/data/credebl-master-table.json +++ b/libs/prisma-service/prisma/data/credebl-master-table.json @@ -227,7 +227,7 @@ { "name": "web", "details": { - "key": "did:web" + "web": "did:web" } } ] From 0bc4525f64c4598006b3699c9ecd6bad22343b59 Mon Sep 17 00:00:00 2001 From: pallavicoder Date: Wed, 13 Mar 2024 16:32:09 +0530 Subject: [PATCH 174/231] feat:add reuse connection Signed-off-by: pallavicoder --- .../src/connection/connection.service.ts | 3 ++- .../src/connection/dtos/connection.dto.ts | 6 ++++++ .../src/issuance/dtos/issuance.dto.ts | 6 ++++++ .../src/issuance/issuance.service.ts | 4 ++-- .../src/verification/dto/request-proof.dto.ts | 7 +++++++ apps/connection/src/connection.repository.ts | 6 ++++-- apps/connection/src/connection.service.ts | 16 +++++++++------ .../src/interfaces/connection.interfaces.ts | 2 ++ apps/issuance/src/issuance.repository.ts | 18 +++++++++++++++++ apps/issuance/src/issuance.service.ts | 20 +++++++++++++------ .../src/interfaces/verification.interface.ts | 6 ++++-- .../repositories/verification.repository.ts | 19 +++++++++++++++++- apps/verification/src/verification.service.ts | 16 ++++++++++++--- .../src/interfaces/agent-service.interface.ts | 1 + .../src/interfaces/connection.interface.ts | 1 + .../migration.sql | 2 ++ libs/prisma-service/prisma/schema.prisma | 1 + 17 files changed, 111 insertions(+), 23 deletions(-) create mode 100644 libs/prisma-service/prisma/migrations/20240312064123_agent_invitation_add_recipient_key/migration.sql diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index 5b2998dc1..8938e62b4 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -40,7 +40,8 @@ export class ConnectionService extends BaseService { goal: connectionDto.goal, handshake: connectionDto.handshake, handshakeProtocols: connectionDto.handshakeProtocols, - user + user, + recipientKey:connectionDto.recipientKey }; return this.sendNatsMessage(this.connectionServiceProxy, 'create-connection', connectionDetails); diff --git a/apps/api-gateway/src/connection/dtos/connection.dto.ts b/apps/api-gateway/src/connection/dtos/connection.dto.ts index 6ac0f90fc..63afc50ee 100644 --- a/apps/api-gateway/src/connection/dtos/connection.dto.ts +++ b/apps/api-gateway/src/connection/dtos/connection.dto.ts @@ -60,6 +60,12 @@ export class CreateConnectionDto { handshakeProtocols: string[]; orgId: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + @IsNotEmpty({ message: 'Please provide recipientKey' }) + recipientKey: string; } export class ConnectionDto { diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 390d402fc..e2cfcc948 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -191,6 +191,12 @@ class CredentialsIssuanceDto { @IsOptional() credentialType:IssueCredentialType; + @ApiPropertyOptional({ default: true }) + @IsOptional() + @IsNotEmpty({ message: 'please provide valid reuseConnection' }) + @IsBoolean({ message: 'reuseConnection must be a boolean' }) + reuseConnection?: boolean; + orgId: string; } diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index e97e26ad7..934cc80c0 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -31,10 +31,10 @@ export class IssuanceService extends BaseService { }> { let payload; if (IssueCredentialType.INDY === issueCredentialDto.credentialType) { - payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl }; + payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl, reuseConnection : issueCredentialDto.reuseConnection }; } if (IssueCredentialType.JSONLD === issueCredentialDto.credentialType) { - payload = { credential: issueCredentialDto.credential, options: issueCredentialDto.options, comment: issueCredentialDto.comment, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl }; + payload = { credential: issueCredentialDto.credential, options: issueCredentialDto.options, comment: issueCredentialDto.comment, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl, reuseConnection : issueCredentialDto.reuseConnection }; } return this.sendNats(this.issuanceProxy, 'send-credential-create-offer-oob', payload); diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 576d05854..c5e684126 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -332,5 +332,12 @@ export class SendProofRequestPayload { @IsOptional() @IsNotEmpty({message:'Please provide the flag for shorten url.'}) isShortenUrl?: boolean; + + @ApiPropertyOptional({ default: true }) + @IsOptional() + @IsNotEmpty({ message: 'please provide valid reuseConnection' }) + @IsBoolean({ message: 'reuseConnection must be a string' }) + reuseConnection?: boolean; + } diff --git a/apps/connection/src/connection.repository.ts b/apps/connection/src/connection.repository.ts index d28b6faf3..2b7034878 100644 --- a/apps/connection/src/connection.repository.ts +++ b/apps/connection/src/connection.repository.ts @@ -48,7 +48,8 @@ export class ConnectionRepository { async saveAgentConnectionInvitations( connectionInvitation: string, agentId: string, - orgId: string + orgId: string, + recipientKey: string // eslint-disable-next-line camelcase ): Promise { try { @@ -57,7 +58,8 @@ export class ConnectionRepository { orgId: String(orgId), agentId, connectionInvitation, - multiUse: true + multiUse: true, + recipientKey } }); return agentDetails; diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index dc030a186..7bf0a43e1 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -51,7 +51,8 @@ export class ConnectionService { goal, goalCode, handshake, - handshakeProtocols + handshakeProtocols, + recipientKey } = payload; try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); @@ -72,7 +73,8 @@ export class ConnectionService { goal: goal || undefined, goalCode: goalCode || undefined, handshake: handshake || undefined, - handshakeProtocols: handshakeProtocols || undefined + handshakeProtocols: handshakeProtocols || undefined, + recipientKey:recipientKey || undefined }; const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -83,18 +85,19 @@ export class ConnectionService { apiKey = await this._getOrgAgentApiKey(orgId); } const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); + const connectionInvitationUrl: string = createConnectionInvitation?.message?.invitationUrl; const shortenedUrl: string = await this.storeConnectionObjectAndReturnUrl( connectionInvitationUrl, connectionPayload.multiUseInvitation ); - + const recipientsKey = createConnectionInvitation?.message?.recipientKey || recipientKey; const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, agentId, - orgId + orgId, + recipientsKey ); - const connectionDetailRecords: ConnectionResponseDetail = { id: saveConnectionDetails.id, orgId: saveConnectionDetails.orgId, @@ -105,7 +108,8 @@ export class ConnectionService { createdBy: saveConnectionDetails.createdBy, lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, lastChangedBy: saveConnectionDetails.lastChangedBy, - recordId: createConnectionInvitation.message.outOfBandRecord.id + recordId: createConnectionInvitation.message.outOfBandRecord.id, + recipientKey:saveConnectionDetails.recipientKey }; return connectionDetailRecords; } catch (error) { diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index c47ede3a5..b49d6ea9e 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -15,6 +15,7 @@ export interface IConnection { handshake: string; handshakeProtocols: string[]; orgId: string; + recipientKey?: string; } export interface IUserRequestInterface { userId: string; @@ -264,4 +265,5 @@ export interface ConnectionResponseDetail { lastChangedDateTime: Date; lastChangedBy: number; recordId: string; + recipientKey:string; } diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 3b8307f22..050327509 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -3,6 +3,7 @@ import { Injectable, InternalServerErrorException, Logger, NotFoundException } f import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase import { + agent_invitations, credentials, file_data, file_upload, @@ -70,6 +71,23 @@ export class IssuanceRepository { } } + + async getRecipientKeyByOrgId(orgId: string): Promise { + try { + return this.prisma.agent_invitations.findMany({ + where: { + orgId + }, + orderBy: { + createDateTime: 'asc' + } + }); + } catch (error) { + this.logger.error(`Error in getRecipientKey in issuance repository: ${error.message}`); + throw error; + } + } + async getAllIssuedCredentials( user: IUserRequest, orgId: string, diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index a086d2bf5..9f35cdc00 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -32,7 +32,7 @@ import { io } from 'socket.io-client'; import { IIssuedCredentialSearchParams, IssueCredentialType } from 'apps/api-gateway/src/issuance/interfaces'; import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto'; -import { organisation } from '@prisma/client'; +import { agent_invitations, organisation } from '@prisma/client'; @Injectable() @@ -150,7 +150,7 @@ export class IssuanceService { async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { - const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, credential, options, credentialType, isShortenUrl } = payload; + const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, credential, options, credentialType, isShortenUrl, reuseConnection } = payload; if (credentialType === IssueCredentialType.INDY) { const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( credentialDefinitionId @@ -181,8 +181,14 @@ export class IssuanceService { } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); - // eslint-disable-next-line camelcase - + let recipientKey: string | undefined; + if (true === reuseConnection) { + const data: agent_invitations[] = await this.issuanceRepository.getRecipientKeyByOrgId(orgId); + if (data && 0 < data.length) { + const [firstElement] = data; + recipientKey = firstElement?.recipientKey ?? undefined; + } + } const { agentEndPoint, organisation } = agentDetails; if (!agentDetails) { @@ -217,7 +223,8 @@ export class IssuanceService { willConfirm: payload.willConfirm || undefined, imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, label: organisation?.name, - comment: comment || '' + comment: comment || '', + recipientKey:recipientKey || undefined }; } @@ -237,7 +244,8 @@ export class IssuanceService { willConfirm: payload.willConfirm || undefined, imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, label: organisation?.name, - comment: comment || '' + comment: comment || '', + recipientKey:recipientKey || undefined }; } const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 2a3397514..f883e49c2 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -145,8 +145,10 @@ export interface ISendProofRequestPayload { willConfirm?: boolean; imageUrl?: string; isShortenUrl?: boolean; - type?:string; - presentationDefinition?:IProofRequestPresentationDefinition; + type?:string; + presentationDefinition?:IProofRequestPresentationDefinition; + reuseConnection?: boolean; + recipientKey?:string; } export interface IWSendProofRequestPayload { diff --git a/apps/verification/src/repositories/verification.repository.ts b/apps/verification/src/repositories/verification.repository.ts index 31b32be28..b64b24145 100644 --- a/apps/verification/src/repositories/verification.repository.ts +++ b/apps/verification/src/repositories/verification.repository.ts @@ -2,7 +2,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { PrismaService } from '@credebl/prisma-service'; import { Injectable, Logger, NotFoundException } from '@nestjs/common'; // eslint-disable-next-line camelcase -import { org_agents, organisation, platform_config, presentations } from '@prisma/client'; +import { agent_invitations, org_agents, organisation, platform_config, presentations } from '@prisma/client'; import { IProofPresentation } from '../interfaces/verification.interface'; import { IProofRequestSearchCriteria } from '../interfaces/verification.interface'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; @@ -185,4 +185,21 @@ export class VerificationRepository { throw error; } } + + // eslint-disable-next-line camelcase + async getRecipientKeyByOrgId(orgId: string): Promise { + try { + return this.prisma.agent_invitations.findMany({ + where: { + orgId + }, + orderBy: { + createDateTime: 'asc' // or 'desc' for descending order + } + }); + } catch (error) { + this.logger.error(`Error in getRecipientKey in verification repository: ${error.message}`); + throw error; + } + } } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index b00c7e2f1..5b5b0e222 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -5,7 +5,7 @@ import { map } from 'rxjs/operators'; import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IPresentationExchangeProofRequestPayload} from './interfaces/verification.interface'; import { VerificationRepository } from './repositories/verification.repository'; import { CommonConstants } from '@credebl/common/common.constant'; -import { org_agents, organisation, presentations } from '@prisma/client'; +import { agent_invitations, org_agents, organisation, presentations } from '@prisma/client'; import { OrgAgentType } from '@credebl/enum/enum'; import { ResponseMessages } from '@credebl/common/response-messages'; import * as QRCode from 'qrcode'; @@ -358,13 +358,22 @@ export class VerificationService { apiKey = await this._getOrgAgentApiKey(user.orgId); } - const { isShortenUrl, type, ...updateOutOfBandRequestProof } = outOfBandRequestProof; + const { isShortenUrl, type, reuseConnection, ...updateOutOfBandRequestProof } = outOfBandRequestProof; + let recipientKey: string | undefined; + if (true === reuseConnection) { + const data: agent_invitations[] = await this.verificationRepository.getRecipientKeyByOrgId(user.orgId); + if (data && 0 < data.length) { + const [firstElement] = data; + recipientKey = firstElement?.recipientKey ?? undefined; + } + } outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; let payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload; if (ProofRequestType.INDY === type) { updateOutOfBandRequestProof.protocolVersion = updateOutOfBandRequestProof.protocolVersion || 'v1'; + updateOutOfBandRequestProof.recipientKey = recipientKey || undefined; payload = { apiKey, url, @@ -389,7 +398,8 @@ export class VerificationService { } } }, - autoAcceptProof:outOfBandRequestProof.autoAcceptProof || 'always' + autoAcceptProof:outOfBandRequestProof.autoAcceptProof || 'always', + recipientKey:recipientKey || undefined } }; } diff --git a/libs/common/src/interfaces/agent-service.interface.ts b/libs/common/src/interfaces/agent-service.interface.ts index ea63de484..235e00210 100644 --- a/libs/common/src/interfaces/agent-service.interface.ts +++ b/libs/common/src/interfaces/agent-service.interface.ts @@ -10,6 +10,7 @@ export interface InvitationMessage { routingKeys: string[]; }; outOfBandRecord: OutOfBandRecord; + recipientKey?:string }; } diff --git a/libs/common/src/interfaces/connection.interface.ts b/libs/common/src/interfaces/connection.interface.ts index 0d4a34ca5..9ff7c4785 100644 --- a/libs/common/src/interfaces/connection.interface.ts +++ b/libs/common/src/interfaces/connection.interface.ts @@ -30,5 +30,6 @@ export interface IConnectionsListCount { createdBy: number; lastChangedDateTime: Date; lastChangedBy: number; + recipientKey?:string; } \ No newline at end of file diff --git a/libs/prisma-service/prisma/migrations/20240312064123_agent_invitation_add_recipient_key/migration.sql b/libs/prisma-service/prisma/migrations/20240312064123_agent_invitation_add_recipient_key/migration.sql new file mode 100644 index 000000000..e74b69d59 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240312064123_agent_invitation_add_recipient_key/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "agent_invitations" ADD COLUMN "recipientKey" TEXT; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 28dcfbf44..c8d7bb164 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -293,6 +293,7 @@ model agent_invitations { lastChangedBy Int @default(1) org_agents org_agents @relation(fields: [agentId], references: [id]) organisation organisation @relation(fields: [orgId], references: [id]) + recipientKey String? } model connections { From fc60add5e088c5135ec252e3f5458d00248bfbfd Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:58:15 +0530 Subject: [PATCH 175/231] fix: refactored tables (#592) * fix: refactored tables Signed-off-by: bhavanakarwade * fix: reafctored json data Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- .../prisma/data/credebl-master-table.json | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/libs/prisma-service/prisma/data/credebl-master-table.json b/libs/prisma-service/prisma/data/credebl-master-table.json index 41f135270..94c5c465c 100644 --- a/libs/prisma-service/prisma/data/credebl-master-table.json +++ b/libs/prisma-service/prisma/data/credebl-master-table.json @@ -139,16 +139,6 @@ "registerDIDEndpoint": "", "registerDIDPayload": "", "indyNamespace": "polygon:testnet" - }, - { - "name": "Polygon Mainnet", - "networkType": "mainnet", - "poolConfig": "", - "isActive": true, - "networkString": "mainnet", - "registerDIDEndpoint": "", - "registerDIDPayload": "", - "indyNamespace": "polygon:mainnet" } ], "endorseData": [ @@ -229,10 +219,16 @@ } }, { + "name": "key", + "details": { "key": "did:key" + } }, { - "key": "did:web" + "name": "web", + "details": { + "web": "did:web" + } } ] } \ No newline at end of file From 9d7074e237e307387e02f9929f92a7c3b58b2459 Mon Sep 17 00:00:00 2001 From: pallavicoder Date: Wed, 13 Mar 2024 17:17:04 +0530 Subject: [PATCH 176/231] fix:changed validation message Signed-off-by: pallavicoder --- apps/api-gateway/src/issuance/dtos/issuance.dto.ts | 2 +- apps/api-gateway/src/verification/dto/request-proof.dto.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index e2cfcc948..20c1ba466 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -193,7 +193,7 @@ class CredentialsIssuanceDto { @ApiPropertyOptional({ default: true }) @IsOptional() - @IsNotEmpty({ message: 'please provide valid reuseConnection' }) + @IsNotEmpty({ message: 'please provide valid value for reuseConnection' }) @IsBoolean({ message: 'reuseConnection must be a boolean' }) reuseConnection?: boolean; diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index c5e684126..ac7f8d705 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -335,8 +335,8 @@ export class SendProofRequestPayload { @ApiPropertyOptional({ default: true }) @IsOptional() - @IsNotEmpty({ message: 'please provide valid reuseConnection' }) - @IsBoolean({ message: 'reuseConnection must be a string' }) + @IsNotEmpty({ message: 'please provide valid value for reuseConnection' }) + @IsBoolean({ message: 'reuseConnection must be a boolean' }) reuseConnection?: boolean; } From 573cb71270d7c61565f27d86718618581483ce93 Mon Sep 17 00:00:00 2001 From: Nishad Date: Wed, 13 Mar 2024 18:21:40 +0530 Subject: [PATCH 177/231] worked on the POST API of refresh token Signed-off-by: Nishad --- .../api-gateway/src/authz/authz.controller.ts | 20 +++++++++++++++++++ apps/api-gateway/src/authz/authz.service.ts | 4 ++++ .../src/authz/dtos/refresh-token.dto.ts | 14 +++++++++++++ apps/user/src/user.controller.ts | 5 +++++ apps/user/src/user.service.ts | 17 ++++++++++++++++ .../src/client-registration.service.ts | 12 ++++------- libs/common/src/response-messages/index.ts | 6 ++++-- 7 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 apps/api-gateway/src/authz/dtos/refresh-token.dto.ts diff --git a/apps/api-gateway/src/authz/authz.controller.ts b/apps/api-gateway/src/authz/authz.controller.ts index 36d2fda25..f93d61af4 100644 --- a/apps/api-gateway/src/authz/authz.controller.ts +++ b/apps/api-gateway/src/authz/authz.controller.ts @@ -27,6 +27,7 @@ import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler import { ResetPasswordDto } from './dtos/reset-password.dto'; import { ForgotPasswordDto } from './dtos/forgot-password.dto'; import { ResetTokenPasswordDto } from './dtos/reset-token-password'; +import { RefreshTokenDto } from './dtos/refresh-token.dto'; @Controller('auth') @@ -176,5 +177,24 @@ export class AuthzController { } + @Post('/refresh-token') + @ApiOperation({ + summary: 'Token from refresh token', + description: 'Get a new token from a refresh token' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + async refreshToken( + @Body() refreshTokenDto: RefreshTokenDto, + @Res() res: Response): Promise { + const tokenData = await this.authzService.refreshToken(refreshTokenDto.refreshToken); + const finalResponse: IResponseType = { + statusCode: HttpStatus.OK, + message: ResponseMessages.user.success.refreshToken, + data: tokenData + }; + + return res.status(HttpStatus.OK).json(finalResponse); + + } } \ No newline at end of file diff --git a/apps/api-gateway/src/authz/authz.service.ts b/apps/api-gateway/src/authz/authz.service.ts index 8cb6b7d01..7c58d0f7a 100644 --- a/apps/api-gateway/src/authz/authz.service.ts +++ b/apps/api-gateway/src/authz/authz.service.ts @@ -58,6 +58,10 @@ export class AuthzService extends BaseService { return this.sendNatsMessage(this.authServiceProxy, 'user-set-token-password', resetTokenPasswordDto); } + async refreshToken(refreshToken: string): Promise { + return this.sendNatsMessage(this.authServiceProxy, 'refresh-token-details', refreshToken); + } + async addUserDetails(userInfo: AddUserDetailsDto): Promise { const payload = { userInfo }; return this.sendNatsMessage(this.authServiceProxy, 'add-user', payload); diff --git a/apps/api-gateway/src/authz/dtos/refresh-token.dto.ts b/apps/api-gateway/src/authz/dtos/refresh-token.dto.ts new file mode 100644 index 000000000..e9ad0f6fa --- /dev/null +++ b/apps/api-gateway/src/authz/dtos/refresh-token.dto.ts @@ -0,0 +1,14 @@ +import { IsNotEmpty } from 'class-validator'; + +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { trim } from '@credebl/common/cast.helper'; + +export class RefreshTokenDto { + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'refreshToken is required.' }) + refreshToken: string; + +} \ No newline at end of file diff --git a/apps/user/src/user.controller.ts b/apps/user/src/user.controller.ts index 802b7b150..76f995980 100644 --- a/apps/user/src/user.controller.ts +++ b/apps/user/src/user.controller.ts @@ -44,6 +44,11 @@ export class UserController { return loginRes; } + @MessagePattern({ cmd: 'refresh-token-details' }) + async refreshTokenDetails(refreshToken: string): Promise { + return this.userService.refreshTokenDetails(refreshToken); + } + @MessagePattern({ cmd: 'user-reset-password' }) async resetPassword(payload: IUserResetPassword): Promise { return this.userService.resetPassword(payload); diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index dbbac2214..c4a3e589b 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -374,6 +374,23 @@ export class UserService { } } + async refreshTokenDetails(refreshToken: string): Promise { + + try { + try { + const tokenResponse = await this.clientRegistrationService.getAccessToken(refreshToken); + return tokenResponse; + } catch (error) { + throw new BadRequestException(ResponseMessages.user.error.invalidRefreshToken); + } + + } catch (error) { + this.logger.error(`In refreshTokenDetails : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + + } + } + async updateFidoVerifiedUser(email: string, isFidoVerified: boolean, password: string): Promise { if (isFidoVerified) { await this.userRepository.addUserPassword(email.toLowerCase(), password); diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index a43fef46a..01a3e609a 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -787,10 +787,6 @@ export class ClientRegistrationService { payload.refresh_token = refreshToken; payload.client_secret = process.env.KEYCLOAK_MANAGEMENT_CLIENT_SECRET; - - this.logger.log(`access Token for platform Payload: ${JSON.stringify(payload)}`); - - if ( 'refresh_token' !== payload.grant_type || !payload.client_id || @@ -801,8 +797,6 @@ export class ClientRegistrationService { throw new Error('Invalid inputs while getting token.'); } - const strURL = await this.keycloakUrlService.GetSATURL('credebl-platform'); - this.logger.log(`getToken URL: ${strURL}`); const config = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' @@ -810,14 +804,16 @@ export class ClientRegistrationService { }; const tokenResponse = await this.commonService.httpPost( - await this.keycloakUrlService.GetSATURL('credebl-platform'), + await this.keycloakUrlService.GetSATURL(process.env.KEYCLOAK_REALM), qs.stringify(payload) , config); return tokenResponse; } catch (error) { - + this.logger.error( + `Error in getAccessToken ${JSON.stringify(error)}` + ); throw error; } } diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index c75109bd6..1ce824217 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -23,7 +23,8 @@ export const ResponseMessages = { updateUserProfile:'User profile updated successfully', resetPassword: 'Password reset successfully', degreeCertificate: 'Degree Certificate shared successfully', - resetPasswordLink: 'Reset password link has been sent to your mail' + resetPasswordLink: 'Reset password link has been sent to your mail', + refreshToken: 'Token details fetched successfully' }, error: { exists: 'User already exists', @@ -60,7 +61,8 @@ export const ResponseMessages = { resetSamePassword: 'New password should not be the current password', resetPasswordLink: 'Unable to create reset password token', invalidResetLink: 'Invalid or expired reset password link', - invalidAccessToken: 'Authentication failed' + invalidAccessToken: 'Authentication failed', + invalidRefreshToken: 'Invalid refreshToken provided' } }, organisation: { From 8f106caa241c0d8e5dddeaf1fc66a93c3944bebf Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 13 Mar 2024 18:38:41 +0530 Subject: [PATCH 178/231] fix: an incorrect message appears when delete the ecosystem invitation which is already deleted Signed-off-by: pranalidhanavade --- apps/ecosystem/src/ecosystem.repository.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index 01de77bf6..06f0d1017 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -868,6 +868,18 @@ export class EcosystemRepository { // eslint-disable-next-line camelcase async deleteInvitations(invitationId: string): Promise { try { + + const findInvitation = await this.prisma.ecosystem_invitations.findUnique({ + where: { + id: invitationId, + status: EcosystemInvitationStatus.PENDING + } + }); + + if (!findInvitation) { + throw new NotFoundException('Ecosystem Invitation not found'); + } + const deletedInvitation = await this.prisma.ecosystem_invitations.delete({ where: { id: invitationId, From d2f29ebf279bfe0cbfc83a97a13296f2425f44af Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 14 Mar 2024 15:17:01 +0530 Subject: [PATCH 179/231] fix: added the token validation with encryption on the wallet and DID Signed-off-by: KulkarniShashank --- .../src/agent-service.service.ts | 56 +++++++------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index e899cbf8f..e2c860cd1 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -784,6 +784,7 @@ export class AgentServiceService { const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); const { label } = payload; const createTenantOptions = { @@ -793,7 +794,7 @@ export class AgentServiceService { const tenantDetails = await this.commonService.httpPost( `${getPlatformAgentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, createTenantOptions, - { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } } + { headers: { 'authorization': getDcryptedToken } } ); return tenantDetails; @@ -812,20 +813,17 @@ export class AgentServiceService { async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { try { const agentDetails = await this.agentServiceRepository.getOrgAgentDetails(orgId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this.getOrgAgentApiKey(orgId); - } + + const getApiKey = await this.getOrgAgentApiKey(orgId); const getOrgAgentType = await this.agentServiceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); let url; - if (getOrgAgentType.agent === OrgAgentType.DEDICATED) { url = `${agentDetails.agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; } else if (getOrgAgentType.agent === OrgAgentType.SHARED) { url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`; } const didDetails = await this.commonService.httpPost(url, payload, - { headers: { 'authorization': apiKey } } + { headers: { 'authorization': getApiKey } } ); return didDetails; @@ -854,10 +852,11 @@ export class AgentServiceService { ); const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); const url = `${getPlatformAgentEndPoint}${CommonConstants.CREATE_POLYGON_SECP256k1_KEY}`; - const createKeyPairResponse = await this.commonService.httpPost(url, {}, { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } }); + const createKeyPairResponse = await this.commonService.httpPost(url, {}, { headers: { 'authorization': getDcryptedToken } }); return createKeyPairResponse; } catch (error) { this.logger.error(`error in createSecp256k1KeyPair : ${JSON.stringify(error)}`); @@ -907,8 +906,8 @@ export class AgentServiceService { delete WalletSetupPayload.ledgerId; - const walletResponseDetails = await this._createTenantWallet(walletLabel, platformAdminSpinnedUp.org_agents[0].agentEndPoint, platformAdminSpinnedUp.org_agents[0].apiKey); - const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); + const walletResponseDetails = await this._createTenantWallet(walletLabel, platformAdminSpinnedUp.org_agents[0].agentEndPoint, getDcryptedToken); if (!walletResponseDetails && !walletResponseDetails.id) { throw new InternalServerErrorException('Error while creating the wallet'); } @@ -1006,9 +1005,7 @@ export class AgentServiceService { issuerId: payload.issuerId }; schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': getApiKey } }) - .then(async (schema) => { - return schema; - }) + .then(async (schema) => schema) .catch(error => { throw new InternalServerErrorException( ResponseMessages.agent.error.agentDown, @@ -1026,9 +1023,7 @@ export class AgentServiceService { issuerId: payload.payload.issuerId }; schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': getApiKey } }) - .then(async (schema) => { - return schema; - }) + .then(async (schema) => schema) .catch(error => { throw new InternalServerErrorException( ResponseMessages.agent.error.agentDown, @@ -1051,17 +1046,13 @@ export class AgentServiceService { if (OrgAgentType.DEDICATED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_SCHEMA_BY_ID.replace('#', `${payload.schemaId}`)}`; schemaResponse = await this.commonService.httpGet(url, payload.schemaId) - .then(async (schema) => { - return schema; - }); + .then(async (schema) => schema); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}`.replace('@', `${payload.payload.schemaId}`).replace('#', `${payload.tenantId}`); schemaResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async (schema) => { - return schema; - }); + .then(async (schema) => schema); } return schemaResponse; } catch (error) { @@ -1085,9 +1076,7 @@ export class AgentServiceService { }; credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': getApiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => credDef); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_CRED_DEF}`.replace('#', `${payload.tenantId}`); @@ -1097,9 +1086,7 @@ export class AgentServiceService { issuerId: payload.payload.issuerId }; credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': getApiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => credDef); } return credDefResponse; @@ -1117,16 +1104,12 @@ export class AgentServiceService { if (OrgAgentType.DEDICATED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_CRED_DEF_BY_ID.replace('#', `${payload.credentialDefinitionId}`)}`; credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => credDef); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CRED_DEF}`.replace('@', `${payload.payload.credentialDefinitionId}`).replace('#', `${payload.tenantId}`); credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => credDef); } return credDefResponse; } catch (error) { @@ -1462,13 +1445,12 @@ export class AgentServiceService { } } - private async getOrgAgentApiKey(orgId: string): Promise { + async getOrgAgentApiKey(orgId: string): Promise { try { let agentApiKey; const orgAgentApiKey = await this.agentServiceRepository.getAgentApiKey(orgId); const apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (apiKey) { const getDcryptedToken = await this.commonService.decryptPassword(apiKey); return getDcryptedToken; @@ -1494,7 +1476,7 @@ export class AgentServiceService { throw new NotFoundException(ResponseMessages.agent.error.apiKeyNotExist); } - await this.cacheService.set(CommonConstants.CACHE_APIKEY_KEY, agentApiKey, CommonConstants.CACHE_TTL_SECONDS); + await this.cacheService.set(CommonConstants.CACHE_APIKEY_KEY, agentApiKey, 0); const getDcryptedToken = await this.commonService.decryptPassword(agentApiKey); return getDcryptedToken; From b0c16347313143d5baa21a4b2db5582cb2b3a275 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Thu, 14 Mar 2024 15:27:45 +0530 Subject: [PATCH 180/231] fix: schema id validations Signed-off-by: bhavanakarwade --- apps/agent-service/src/agent-service.service.ts | 4 ++-- .../credential-definition.service.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 063d9ed0a..89a2dc570 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -141,9 +141,9 @@ export class AgentServiceService { } if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { - throw new BadRequestException( + throw new ConflictException( ResponseMessages.agent.error.walletAlreadyCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } + { cause: new Error(), description: ResponseMessages.errorMessages.conflict } ); } diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 7f799e52c..2bf12a082 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -1,5 +1,6 @@ /* eslint-disable camelcase */ import { + BadRequestException, ConflictException, HttpException, Inject, @@ -48,6 +49,8 @@ export class CredentialDefinitionService extends BaseService { credDef.tag ); + credDef.schemaLedgerId = await this.validateSchemaId(credDef.schemaLedgerId); + if (dbResult) { throw new ConflictException(ResponseMessages.credentialDefinition.error.Conflict); } @@ -144,6 +147,15 @@ export class CredentialDefinitionService extends BaseService { } } + validateSchemaId(schemaLedgerId: string): string { + const pattern = /^[\w\d]+:\d+:[\w\d\s]+:\d+\.\d+\.\d+$/; + if (!pattern.test(schemaLedgerId)) { + throw new BadRequestException('Invalid schema id format'); + } + return schemaLedgerId; + } + + async _createCredentialDefinition(payload: CreateCredDefAgentRedirection): Promise<{ response: string; }> { From c3e1215c215de5d8e6a98848f5c9aa6a86b9211f Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Fri, 15 Mar 2024 12:45:33 +0530 Subject: [PATCH 181/231] feat: dedicated agent spin up Signed-off-by: bhavanakarwade --- .../src/agent-service.service.ts | 67 ++++++++++--------- .../src/interface/agent-service.interface.ts | 47 ++++++++----- apps/agent-service/src/main.ts | 8 ++- .../agent-service/agent-service.controller.ts | 17 ++--- .../agent-service/dto/agent-service.dto.ts | 33 ++------- libs/common/src/common.constant.ts | 3 +- 6 files changed, 86 insertions(+), 89 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 89a2dc570..d25f5a714 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -96,7 +96,7 @@ export class AgentServiceService { this.agentServiceRepository.getAgentTypeDetails(), this.agentServiceRepository.getLedgerDetails(agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]) ]); - + let orgData; if (!user?.userId && agentSpinupDto?.platformAdminEmail) { @@ -158,7 +158,6 @@ export class AgentServiceService { agentSpinupDto.tenant = agentSpinupDto.tenant || false; agentSpinupDto.ledgerName = agentSpinupDto.ledgerName?.length ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]; - // Invoke function for validate platform configuration this.validatePlatformConfig(platformConfig); @@ -338,13 +337,28 @@ export class AgentServiceService { } async _agentSpinup(walletProvisionPayload: IWalletProvision, agentSpinupDto: IAgentSpinupDto, orgApiKey: string, orgData: organisation, user: IUserRequestInterface, socket: Socket, ledgerId: string[], agentProcess: ICreateOrgAgent): Promise { - try { + let ledgerIdData = []; + try { + if (agentSpinupDto.method !== DidMethod.KEY && agentSpinupDto.method !== DidMethod.WEB) { + + const { network } = agentSpinupDto; + const ledger = await ledgerName(network); + const ledgerList = await this._getALlLedgerDetails() as unknown as LedgerListResponse; + const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); + if (!isLedgerExist) { + throw new BadRequestException( + ResponseMessages.agent.error.invalidLedger, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } + + ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); + } /** * Invoke wallet create and provision with agent */ const walletProvision = await this._walletProvision(walletProvisionPayload); - if (!walletProvision?.response) { this.logger.error(`Agent not able to spin-up`); throw new BadRequestException( @@ -354,7 +368,6 @@ export class AgentServiceService { } const agentDetails = walletProvision.response; const agentEndPoint = `${process.env.API_GATEWAY_PROTOCOL}://${agentDetails.agentEndPoint}`; - /** * Socket connection */ @@ -374,11 +387,14 @@ export class AgentServiceService { orgId: orgData.id, walletName: agentSpinupDto.walletName, clientSocketId: agentSpinupDto.clientSocketId, - ledgerId, + method: agentSpinupDto.method, + role: agentSpinupDto.role, + network: agentSpinupDto.network, + keyType: agentSpinupDto.keyType, + ledgerId: ledgerIdData ? ledgerIdData.map(item => item.id) : null, did: agentSpinupDto.did, id: agentProcess?.id }; - /** * Store organization agent details */ @@ -446,7 +462,7 @@ export class AgentServiceService { /** * Organization storage data */ - const storeOrgAgentData = await this._buildStoreOrgAgentData(payload, getDidMethod, orgAgentTypeId); + const storeOrgAgentData = await this._buildStoreOrgAgentData(payload, getDidMethod, `${orgAgentTypeId}`); /** * Store org agent details */ @@ -460,33 +476,19 @@ export class AgentServiceService { private async _getAgentDid(payload: IStoreOrgAgentDetails): Promise { - const { agentEndPoint, apiKey, ledgerId } = payload; - - //we take this values as static because of latest changes in afj controller to up agent of platform - const platformAgent: IPlatformAgent = { - seed: `${CommonConstants.SEED}`, - keyType: `${CommonConstants.KEYTYPE}`, - method: `${CommonConstants.METHOD}`, - network: `${CommonConstants.NETWORK}`, - role: `${CommonConstants.ROLE}` - }; + const { agentEndPoint, apiKey, ledgerId, seed, keyType, method, network, role, did } = payload; const writeDid = 'write-did'; const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(ledgerId); const agentDidWriteUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; - return this._retryAgentSpinup(agentDidWriteUrl, apiKey, writeDid, platformAgent); + return this._retryAgentSpinup(agentDidWriteUrl, apiKey, writeDid, seed, keyType, method, network, role, did); + } private async _getDidMethod(payload: IStoreOrgAgentDetails, agentDid: object): Promise { - const getDidDic = 'get-did-doc'; - const platformAgent: IPlatformAgent = { - seed: `${CommonConstants.SEED}`, - keyType: `${CommonConstants.KEYTYPE}`, - method: `${CommonConstants.METHOD}`, - network: `${CommonConstants.NETWORK}`, - role: `${CommonConstants.ROLE}` - }; - const getDidMethodUrl = `${payload.agentEndPoint}${CommonConstants.URL_AGENT_GET_DID}`.replace('#', agentDid['did']); - return this._retryAgentSpinup(getDidMethodUrl, payload.apiKey, getDidDic, platformAgent); + const { agentEndPoint, apiKey, seed, keyType, method, network, role } = payload; + const getDidDoc = 'get-did-doc'; + const getDidMethodUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_GET_DID}/${agentDid['did']}`; + return this._retryAgentSpinup(getDidMethodUrl, apiKey, getDidDoc, seed, keyType, method, network, role, `${agentDid['did']}`); } private _buildStoreOrgAgentData(payload: IStoreOrgAgentDetails, getDidMethod: object, orgAgentTypeId: string): IStoreOrgAgentDetails { @@ -528,20 +530,19 @@ export class AgentServiceService { } - async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, payload: IPlatformAgent): Promise { - - const { seed, keyType, method, network, role} = payload; + async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, seed: string, keyType: string, method: string, network: string, role: string, did: string): Promise { const retryOptions = { retries: 10 }; try { return retry(async () => { if (agentApiState === 'write-did') { - return this.commonService.httpPost(agentUrl, { seed, keyType, method, network, role}, { headers: { 'authorization': apiKey } }); + return this.commonService.httpPost(agentUrl, { seed, keyType, method, network, role, did}, { headers: { 'authorization': apiKey } }); } else if (agentApiState === 'get-did-doc') { return this.commonService.httpGet(agentUrl, { headers: { 'authorization': apiKey } }); } }, retryOptions); + } catch (error) { throw error; } diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 57070b9ea..31a649be9 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -1,13 +1,20 @@ import { UserRoleOrgPermsDto } from 'apps/api-gateway/src/dtos/user-role-org-perms.dto'; export interface IAgentSpinupDto { - walletName: string; walletPassword: string; seed: string; orgId?: string; orgName?: string; ledgerId?: string[]; + keyType: string; + domain?: string; + privatekey?: string; + endpoint?: string; + role?: string; + network?: string + endorserDid?: string + method: string; did?: string; agentType?: string; transactionApproval?: boolean; @@ -149,23 +156,27 @@ export interface IPlatformConfigDto { } export interface IStoreOrgAgentDetails { - id?: string; - clientSocketId?: string; - agentEndPoint?: string; - apiKey?: string; - seed?: string; - did?: string; - verkey?: string; - isDidPublic?: boolean; - agentSpinUpStatus?: number; - walletName?: string; - agentsTypeId?: string; - orgId?: string; - agentId?: string; - orgAgentTypeId?: string; - tenantId?: string; - ledgerId?: string[]; - agentType?: string; + id?: string; + clientSocketId?: string; + agentEndPoint?: string; + apiKey?: string; + seed?: string; + keyType?: string; + method?: string; + network?: string; + role?: string; + did?: string; + verkey?: string; + isDidPublic?: boolean; + agentSpinUpStatus?: number; + walletName?: string; + agentsTypeId?: string; + orgId?: string; + agentId?: string; + orgAgentTypeId?: string; + tenantId?: string; + ledgerId?: string[]; + agentType?: string; } export interface IStoreOrgAgent { diff --git a/apps/agent-service/src/main.ts b/apps/agent-service/src/main.ts index 15e462947..efafbfbc4 100644 --- a/apps/agent-service/src/main.ts +++ b/apps/agent-service/src/main.ts @@ -32,8 +32,12 @@ async function bootstrap(): Promise { orgName: `${CommonConstants.PLATFORM_ADMIN_ORG}`, platformAdminEmail: process.env.PLATFORM_ADMIN_EMAIL, tenant: true, - ledgerName: [Ledgers.Bcovrin_Testnet, Ledgers.Indicio_Demonet, Ledgers.Indicio_Mainnet, Ledgers.Indicio_Testnet] - }; + ledgerName: [Ledgers.Bcovrin_Testnet, Ledgers.Indicio_Demonet, Ledgers.Indicio_Mainnet, Ledgers.Indicio_Testnet], + keyType: `${CommonConstants.KEYTYPE}`, + method: `${CommonConstants.METHOD}`, + network: `${CommonConstants.NETWORK}`, + role: `${CommonConstants.ROLE}` +}; const agentService = app.get(AgentServiceService); await agentService.walletProvision(agentSpinupPayload, user); 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 ebd4fe8d2..05d28a583 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -37,6 +37,7 @@ import { validateDid } from '@credebl/common/did.validator'; import { CreateWalletDto } from './dto/create-wallet.dto'; const seedLength = 32; + @UseFilters(CustomExceptionFilter) @Controller() @ApiTags('agents') @@ -122,14 +123,6 @@ export class AgentController { @Res() res: Response ): Promise { - if (seedLength !== agentSpinupDto.seed.length) { - this.logger.error(`seed must be at most 32 characters.`); - throw new BadRequestException( - ResponseMessages.agent.error.seedChar, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); - } - const regex = new RegExp('^[a-zA-Z0-9]+$'); if (!regex.test(agentSpinupDto.walletName)) { @@ -246,6 +239,14 @@ export class AgentController { validateDid(createDidDto); + if (seedLength !== createDidDto.seed.length) { + this.logger.error(`seed must be at most 32 characters.`); + throw new BadRequestException( + ResponseMessages.agent.error.seedChar, + { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } + ); + } + const didDetails = await this.agentService.createDid(createDidDto, orgId, user); const finalResponse: IResponse = { diff --git a/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts b/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts index a668a0025..b0cc851d2 100644 --- a/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts @@ -1,19 +1,19 @@ import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsBoolean, IsNotEmpty, IsOptional, IsString, Matches, MaxLength, MinLength, IsArray } from 'class-validator'; +import { IsBoolean, IsNotEmpty, IsOptional, IsString, Matches, MaxLength, MinLength } from 'class-validator'; +import { CreateDidDto } from './create-did.dto'; const regex = /^[a-zA-Z0-9 ]*$/; -export class AgentSpinupDto { +export class AgentSpinupDto extends CreateDidDto { @ApiProperty() + @MaxLength(25, { message: 'Maximum length for wallet must be 25 characters.' }) + @IsString({ message: 'label must be in string format.' }) @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'walletName is required' }) - @MinLength(2, { message: 'walletName must be at least 2 characters.' }) - @MaxLength(50, { message: 'walletName must be at most 50 characters.' }) - @IsString({ message: 'walletName must be in string format.' }) + @MinLength(2, { message: 'Minimum length for wallet name must be 2 characters.' }) @Matches(regex, { message: 'Wallet name must not contain special characters.' }) @Matches(/^\S*$/, { - message: 'Spaces are not allowed in wallet name' + message: 'Spaces are not allowed in label' }) walletName: string; @@ -23,31 +23,12 @@ export class AgentSpinupDto { @IsNotEmpty({ message: 'Password is required.' }) walletPassword: string; - @ApiProperty({ example: 'dfuhgfklskmjngrjekjfgjjfkoekfdad' }) - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'seed is required' }) - @MaxLength(32, { message: 'seed must be at most 32 characters.' }) - @IsString({ message: 'seed must be in string format.' }) - @Matches(/^\S*$/, { - message: 'Spaces are not allowed in seed' - }) - seed: string; - @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) @IsOptional() @ApiPropertyOptional() @IsString({ message: 'did must be in string format.' }) did?: string; - @ApiProperty({ example: ['6ba7b810-9dad-11d1-80b4-00c04fd430c8'] }) - @IsOptional() - @ApiPropertyOptional() - @IsArray({ message: 'ledgerId must be an array' }) - @IsString({ each: true, message: 'Each ledgerId must be a string' }) - @MaxLength(36, { each: true, message: 'ledgerId must be at most 36 characters.' }) - @IsNotEmpty({ message: 'please provide valid ledgerId' }) - ledgerId?: string[]; - @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) @IsOptional() @ApiPropertyOptional() diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index 1ce7017a4..b93f0a0d7 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -123,8 +123,7 @@ export enum CommonConstants { // server or agent URL_SERVER_STATUS = '/status', URL_AGENT_WRITE_DID = '/dids/write', - URL_AGENT_GET_DID = '/dids/#', - URL_AGENT_GET_DIDS = '/dids', + URL_AGENT_GET_DID = '/dids', URL_AGENT_GET_ENDPOINT = '/agent', // CREATE KEYS From be68d5072c250c06f0a8150c6b6cfe4772eca1b2 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Fri, 15 Mar 2024 14:27:23 +0530 Subject: [PATCH 182/231] fix: solved the platform-admin agent spin-up Signed-off-by: KulkarniShashank --- .../src/agent-service.service.ts | 1036 +++++++++-------- libs/common/src/common.constant.ts | 3 +- 2 files changed, 565 insertions(+), 474 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index fa1ef4037..b814a34a0 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -19,7 +19,38 @@ import * as dotenv from 'dotenv'; import * as fs from 'fs'; import { map } from 'rxjs/operators'; dotenv.config(); -import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation, IQuestionPayload, IDidCreate, IWallet, ITenantRecord, IPlatformAgent, LedgerListResponse, IOrgLedgers, IStoreOrgAgent } from './interface/agent-service.interface'; +import { + IGetCredDefAgentRedirection, + IConnectionDetails, + IUserRequestInterface, + IAgentSpinupDto, + IStoreOrgAgentDetails, + ITenantCredDef, + ITenantDto, + ITenantSchema, + IWalletProvision, + ISendProofRequestPayload, + IIssuanceCreateOffer, + IOutOfBandCredentialOffer, + IAgentSpinUpSatus, + ICreateTenant, + IAgentStatus, + ICreateOrgAgent, + IOrgAgentsResponse, + IProofPresentation, + IAgentProofRequest, + IPresentation, + IReceiveInvitationUrl, + IReceiveInvitation, + IQuestionPayload, + IDidCreate, + IWallet, + ITenantRecord, + IPlatformAgent, + LedgerListResponse, + IOrgLedgers, + IStoreOrgAgent +} from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; import { ledgers, org_agents, organisation, platform_config } from '@prisma/client'; @@ -39,11 +70,9 @@ import { ledgerName } from '@credebl/common/cast.helper'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; import * as CryptoJS from 'crypto-js'; - @Injectable() @WebSocketGateway() export class AgentServiceService { - private readonly logger = new Logger('WalletService'); constructor( @@ -52,24 +81,21 @@ export class AgentServiceService { private readonly connectionService: ConnectionService, @Inject('NATS_CLIENT') private readonly agentServiceProxy: ClientProxy, @Inject(CACHE_MANAGER) private cacheService: Cache - ) { } + ) {} async ReplaceAt(input, search, replace, start, end): Promise { - return input.slice(0, start) - + input.slice(start, end).replace(search, replace) - + input.slice(end); + return input.slice(0, start) + input.slice(start, end).replace(search, replace) + input.slice(end); } /** * Spinup the agent by organization - * @param agentSpinupDto - * @param user + * @param agentSpinupDto + * @param user * @returns Get agent status */ async walletProvision(agentSpinupDto: IAgentSpinupDto, user: IUserRequestInterface): Promise { let agentProcess: ICreateOrgAgent; try { - // Invoke an internal function to create wallet await this.processWalletProvision(agentSpinupDto, user); const agentStatusResponse = { @@ -78,7 +104,6 @@ export class AgentServiceService { return agentStatusResponse; } catch (error) { - // Invoke an internal function to handle error to create wallet this.handleErrorOnWalletProvision(agentSpinupDto, error, agentProcess); throw new RpcException(error.response ? error.response : error); @@ -91,17 +116,16 @@ export class AgentServiceService { let agentProcess: ICreateOrgAgent; let getOrgAgent; try { - const [platformConfig, getAgentType, ledgerIdData] = await Promise.all([ this.agentServiceRepository.getPlatformConfigDetails(), this.agentServiceRepository.getAgentTypeDetails(), - this.agentServiceRepository.getLedgerDetails(agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]) + this.agentServiceRepository.getLedgerDetails( + agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet] + ) ]); - + let orgData; if (!user?.userId && agentSpinupDto?.platformAdminEmail) { - - // Get Platform admin user by platform admin email platformAdminUser = await this.agentServiceRepository.getPlatfomAdminUser(agentSpinupDto?.platformAdminEmail); @@ -114,14 +138,12 @@ export class AgentServiceService { const platformAdminOrgDetails = await this.agentServiceRepository.getPlatfomOrg(agentSpinupDto?.orgName); if (agentSpinupDto.orgId) { - // Get organization details getOrgAgent = await this.agentServiceRepository.getAgentDetails(agentSpinupDto.orgId); // Get organization data by orgId orgData = await this.agentServiceRepository.getOrgDetails(agentSpinupDto.orgId); } else { - // Get platform organization details getOrgAgent = await this.agentServiceRepository.getAgentDetails(platformAdminOrgDetails); @@ -129,27 +151,28 @@ export class AgentServiceService { orgData = await this.agentServiceRepository.getOrgDetails(platformAdminOrgDetails); } - agentSpinupDto.ledgerId = agentSpinupDto.ledgerId?.length ? agentSpinupDto.ledgerId : ledgerIdData.map(ledger => ledger?.id); + agentSpinupDto.ledgerId = agentSpinupDto.ledgerId?.length + ? agentSpinupDto.ledgerId + : ledgerIdData.map((ledger) => ledger?.id); // Get genesis URL and ledger details const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(agentSpinupDto.ledgerId); if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { - throw new BadRequestException( - ResponseMessages.agent.error.walletAlreadyProcessing, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.walletAlreadyProcessing, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { - throw new ConflictException( - ResponseMessages.agent.error.walletAlreadyCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + throw new ConflictException(ResponseMessages.agent.error.walletAlreadyCreated, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } if (!agentSpinupDto.orgId) { - if (platformAdminOrgDetails) { agentSpinupDto.orgId = platformAdminOrgDetails; } @@ -157,7 +180,9 @@ export class AgentServiceService { agentSpinupDto.agentType = agentSpinupDto.agentType || getAgentType; agentSpinupDto.tenant = agentSpinupDto.tenant || false; - agentSpinupDto.ledgerName = agentSpinupDto.ledgerName?.length ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]; + agentSpinupDto.ledgerName = agentSpinupDto.ledgerName?.length + ? agentSpinupDto.ledgerName + : [Ledgers.Indicio_Demonet]; // Invoke function for validate platform configuration this.validatePlatformConfig(platformConfig); @@ -167,7 +192,14 @@ export class AgentServiceService { const apiEndpoint = platformConfig?.apiEndpoint; // Create payload for the wallet create and store payload - const walletProvisionPayload = await this.prepareWalletProvisionPayload(agentSpinupDto, externalIp, apiEndpoint, inboundEndpoint, ledgerDetails, orgData); + const walletProvisionPayload = await this.prepareWalletProvisionPayload( + agentSpinupDto, + externalIp, + apiEndpoint, + inboundEndpoint, + ledgerDetails, + orgData + ); // Socket connection const socket: Socket = await this.initSocketConnection(`${process.env.SOCKET_HOST}`); @@ -177,8 +209,16 @@ export class AgentServiceService { agentProcess = await this.createOrgAgent(agentSpinUpStatus, userId); // AFJ agent spin-up - this._agentSpinup(walletProvisionPayload, agentSpinupDto, platformConfig?.sgApiKey, orgData, user, socket, agentSpinupDto.ledgerId, agentProcess); - + this._agentSpinup( + walletProvisionPayload, + agentSpinupDto, + platformConfig?.sgApiKey, + orgData, + user, + socket, + agentSpinupDto.ledgerId, + agentProcess + ); } catch (error) { this.handleErrorOnWalletProvision(agentSpinupDto, error, agentProcess); throw error; @@ -188,34 +228,34 @@ export class AgentServiceService { validatePlatformConfig(platformConfig: platform_config): void { if (!platformConfig) { this.logger.error(`Platform configuration is missing or invalid`); - throw new BadRequestException( - ResponseMessages.agent.error.platformConfiguration, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.platformConfiguration, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } if (!platformConfig.apiEndpoint) { this.logger.error(`API endpoint is missing in the platform configuration`); - throw new BadRequestException( - ResponseMessages.agent.error.apiEndpoint, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.apiEndpoint, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } if (!platformConfig.externalIp) { this.logger.error(`External IP is missing in the platform configuration`); - throw new BadRequestException( - ResponseMessages.agent.error.externalIp, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.externalIp, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } if (typeof platformConfig.externalIp !== 'string') { this.logger.error(`External IP must be a string`); - throw new BadRequestException( - ResponseMessages.agent.error.externalIp, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.externalIp, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } } @@ -223,10 +263,10 @@ export class AgentServiceService { try { if (!agentProcess) { this.logger.error(`Agent process is invalid or not in a completed state`); - throw new BadRequestException( - ResponseMessages.agent.error.externalIp, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.externalIp, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } } catch (error) { this.logger.error(`Error validating agent process: ${error.message}`); @@ -237,12 +277,10 @@ export class AgentServiceService { emitAgentSpinupInitiatedEvent(agentSpinupDto: IAgentSpinupDto, socket: Socket): void { try { if (agentSpinupDto.clientSocketId) { - socket.emit('agent-spinup-process-initiated', { clientId: agentSpinupDto.clientSocketId }); // Log or perform other actions after emitting the event this.logger.log(`Agent spinup initiated event emitted for orgId ${agentSpinupDto.orgId}`); } - } catch (error) { this.logger.error(`Error emitting agent-spinup-initiated event: ${error.message}`); throw error; @@ -257,7 +295,7 @@ export class AgentServiceService { ledgerDetails: ledgers[], orgData: organisation ): Promise { - const ledgerArray = ledgerDetails.map(ledger => ({ + const ledgerArray = ledgerDetails.map((ledger) => ({ genesisTransactions: ledger.poolConfig, indyNamespace: ledger.indyNamespace })); @@ -307,13 +345,16 @@ export class AgentServiceService { this.logger.log(`Organization agent created with status: ${agentSpinUpStatus}`); return agentProcess; } catch (error) { - this.logger.error(`Error creating organization agent: ${error.message}`); throw error; } } - private async handleErrorOnWalletProvision(agentSpinupDto: IAgentSpinupDto, error: Error, agentProcess: ICreateOrgAgent): Promise { + private async handleErrorOnWalletProvision( + agentSpinupDto: IAgentSpinupDto, + error: Error, + agentProcess: ICreateOrgAgent + ): Promise { if (agentProcess) { const socket = await this.initSocketConnection(`${process.env.SOCKET_HOST}`); @@ -329,42 +370,49 @@ export class AgentServiceService { try { socket.emit('error-in-wallet-creation-process', { clientId: agentSpinupDto.clientSocketId, error }); this.logger.error(`Error in wallet creation process emitted for orgId ${agentSpinupDto.orgId}: ${error.message}`); - } catch (emitError) { this.logger.error(`Error emitting error-in-wallet-creation-process event: ${emitError.message}`); throw emitError; } } - async _agentSpinup(walletProvisionPayload: IWalletProvision, agentSpinupDto: IAgentSpinupDto, orgApiKey: string, orgData: organisation, user: IUserRequestInterface, socket: Socket, ledgerId: string[], agentProcess: ICreateOrgAgent): Promise { + async _agentSpinup( + walletProvisionPayload: IWalletProvision, + agentSpinupDto: IAgentSpinupDto, + orgApiKey: string, + orgData: organisation, + user: IUserRequestInterface, + socket: Socket, + ledgerId: string[], + agentProcess: ICreateOrgAgent + ): Promise { let ledgerIdData = []; try { if (agentSpinupDto.method !== DidMethod.KEY && agentSpinupDto.method !== DidMethod.WEB) { + const { network } = agentSpinupDto; + const ledger = await ledgerName(network); + const ledgerList = (await this._getALlLedgerDetails()) as unknown as LedgerListResponse; + const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); + if (!isLedgerExist) { + throw new BadRequestException(ResponseMessages.agent.error.invalidLedger, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); + } - const { network } = agentSpinupDto; - const ledger = await ledgerName(network); - const ledgerList = await this._getALlLedgerDetails() as unknown as LedgerListResponse; - const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); - if (!isLedgerExist) { - throw new BadRequestException( - ResponseMessages.agent.error.invalidLedger, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); + ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); } - - ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); - } /** - * Invoke wallet create and provision with agent + * Invoke wallet create and provision with agent */ const walletProvision = await this._walletProvision(walletProvisionPayload); if (!walletProvision?.response) { this.logger.error(`Agent not able to spin-up`); - throw new BadRequestException( - ResponseMessages.agent.error.notAbleToSpinup, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.notAbleToSpinup, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } const agentDetails = walletProvision.response; const agentEndPoint = `${process.env.API_GATEWAY_PROTOCOL}://${agentDetails.agentEndPoint}`; @@ -393,17 +441,18 @@ export class AgentServiceService { role: agentSpinupDto.role, network: agentSpinupDto.network, keyType: agentSpinupDto.keyType, - ledgerId: ledgerIdData ? ledgerIdData.map(item => item.id) : null, + ledgerId: ledgerIdData ? ledgerIdData.map((item) => item.id) : null, did: agentSpinupDto.did, id: agentProcess?.id }; /** - * Store organization agent details + * Store organization agent details */ const storeAgentDetails = await this._storeOrgAgentDetails(agentPayload); if (storeAgentDetails) { - - const filePath = `${process.cwd()}${process.env.AFJ_AGENT_TOKEN_PATH}${orgData.id}_${orgData.name.split(' ').join('_')}.json`; + const filePath = `${process.cwd()}${process.env.AFJ_AGENT_TOKEN_PATH}${orgData.id}_${orgData.name + .split(' ') + .join('_')}.json`; if (agentDetails?.agentToken) { fs.unlink(filePath, (err) => { if (err) { @@ -427,10 +476,10 @@ export class AgentServiceService { } } else { this.logger.error(`Agent not able to spin-up`); - throw new BadRequestException( - ResponseMessages.agent.error.notAbleToSpinup, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.notAbleToSpinup, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } } catch (error) { if (agentSpinupDto.clientSocketId) { @@ -439,7 +488,7 @@ export class AgentServiceService { if (agentProcess && agentProcess?.id) { /** - * If getting error remove organization agent + * If getting error remove organization agent */ await this.agentServiceRepository.removeOrgAgent(agentProcess?.id); } @@ -449,7 +498,6 @@ export class AgentServiceService { async _storeOrgAgentDetails(payload: IStoreOrgAgentDetails): Promise { try { - /** * Get orgaization agent type and agent details */ @@ -476,24 +524,36 @@ export class AgentServiceService { } } - private async _getAgentDid(payload: IStoreOrgAgentDetails): Promise { - const { agentEndPoint, apiKey, ledgerId, seed, keyType, method, network, role, did } = payload; + const { agentEndPoint, apiKey, ledgerId, seed, keyType, method, network, role, did } = payload; const writeDid = 'write-did'; const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(ledgerId); const agentDidWriteUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; return this._retryAgentSpinup(agentDidWriteUrl, apiKey, writeDid, seed, keyType, method, network, role, did); - } private async _getDidMethod(payload: IStoreOrgAgentDetails, agentDid: object): Promise { - const { agentEndPoint, apiKey, seed, keyType, method, network, role } = payload; + const { agentEndPoint, apiKey, seed, keyType, method, network, role } = payload; const getDidDoc = 'get-did-doc'; const getDidMethodUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_GET_DID}/${agentDid['did']}`; - return this._retryAgentSpinup(getDidMethodUrl, apiKey, getDidDoc, seed, keyType, method, network, role, `${agentDid['did']}`); + return this._retryAgentSpinup( + getDidMethodUrl, + apiKey, + getDidDoc, + seed, + keyType, + method, + network, + role, + `${agentDid['did']}` + ); } - private _buildStoreOrgAgentData(payload: IStoreOrgAgentDetails, getDidMethod: object, orgAgentTypeId: string): IStoreOrgAgentDetails { + private _buildStoreOrgAgentData( + payload: IStoreOrgAgentDetails, + getDidMethod: object, + orgAgentTypeId: string + ): IStoreOrgAgentDetails { return { did: getDidMethod['didDocument']?.id, verkey: getDidMethod['didDocument']?.verificationMethod[0]?.publicKeyBase58, @@ -524,35 +584,49 @@ export class AgentServiceService { } if (payload && payload?.id) { - this.agentServiceRepository.removeOrgAgent(payload?.id); } this.logger.error(`[_storeOrgAgentDetails] - Error in store agent details : ${JSON.stringify(error)}`); } - - async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, seed: string, keyType: string, method: string, network: string, role: string, did: string): Promise { + async _retryAgentSpinup( + agentUrl: string, + apiKey: string, + agentApiState: string, + seed: string, + keyType: string, + method: string, + network: string, + role: string, + did: string + ): Promise { const retryOptions = { retries: 10 }; try { - const getDcryptedToken = await this.commonService.decryptPassword(apiKey); + const getDcryptedToken = await this.commonService.decryptPassword(apiKey); return retry(async () => { if (agentApiState === 'write-did') { - return this.commonService.httpPost(agentUrl, { seed, keyType, method, network, role, did}, { headers: { 'authorization': getDcryptedToken } }); + return this.commonService.httpPost( + agentUrl, + { seed, keyType, method, network, role, did }, + { headers: { authorization: getDcryptedToken } } + ); } else if (agentApiState === 'get-did-doc') { - return this.commonService.httpGet(agentUrl, { headers: { 'authorization': getDcryptedToken } }); + return this.commonService.httpGet(agentUrl, { headers: { authorization: getDcryptedToken } }); } }, retryOptions); - } catch (error) { throw error; } } - - async _createLegacyConnectionInvitation(orgId: string, user: IUserRequestInterface, label: string): Promise<{ + async _createLegacyConnectionInvitation( + orgId: string, + user: IUserRequestInterface, + label: string + ): Promise<{ response; }> { try { @@ -563,18 +637,20 @@ export class AgentServiceService { return this.agentServiceProxy .send(pattern, payload) .pipe( - map((response) => ( - { - response - })) - ).toPromise() - .catch(error => { + map((response) => ({ + response + })) + ) + .toPromise() + .catch((error) => { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, error: error.message - }, error.error); + }, + error.error + ); }); } catch (error) { this.logger.error(`error in create-connection in wallet provision : ${JSON.stringify(error)}`); @@ -592,25 +668,26 @@ export class AgentServiceService { return this.agentServiceProxy .send(pattern, payload) .pipe( - map((response) => ( - { - response - })) - ).toPromise() - .catch(error => { + map((response) => ({ + response + })) + ) + .toPromise() + .catch((error) => { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, error: error.message - }, error.error); + }, + error.error + ); }); } catch (error) { this.logger.error(`error in while fetching all the ledger details : ${JSON.stringify(error)}`); } } - async _walletProvision(payload: IWalletProvision): Promise<{ response; }> { @@ -621,18 +698,20 @@ export class AgentServiceService { return this.agentServiceProxy .send(pattern, payload) .pipe( - map((response) => ( - { - response - })) - ).toPromise() - .catch(error => { + map((response) => ({ + response + })) + ) + .toPromise() + .catch((error) => { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, error: error.message - }, error.error); + }, + error.error + ); }); } catch (error) { this.logger.error(`error in wallet provision : ${JSON.stringify(error)}`); @@ -642,13 +721,12 @@ export class AgentServiceService { /** * Create tenant (Shared agent) - * @param payload - * @param user + * @param payload + * @param user * @returns Get agent status */ async createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { try { - const agentStatusResponse = { agentSpinupStatus: AgentSpinUpStatus.PROCESSED }; @@ -657,18 +735,18 @@ export class AgentServiceService { if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { this.logger.error(`Your wallet is already been created.`); - throw new ConflictException( - ResponseMessages.agent.error.walletAlreadyCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + throw new ConflictException(ResponseMessages.agent.error.walletAlreadyCreated, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { this.logger.error(`Your wallet is already processing.`); - throw new ConflictException( - ResponseMessages.agent.error.walletAlreadyProcessing, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + throw new ConflictException(ResponseMessages.agent.error.walletAlreadyProcessing, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } // Create tenant @@ -682,124 +760,123 @@ export class AgentServiceService { /** * Create tenant (Shared agent) - * @param payload - * @param user + * @param payload + * @param user * @returns Get agent status */ - async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { - let agentProcess; - let ledgerIdData = []; - try { - if (payload.method !== DidMethod.KEY && payload.method !== DidMethod.WEB) { - - const { network } = payload; - const ledger = await ledgerName(network); - const ledgerList = await this._getALlLedgerDetails() as unknown as LedgerListResponse; - const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); - if (!isLedgerExist) { - throw new BadRequestException( - ResponseMessages.agent.error.invalidLedger, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } - - ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); + async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { + let agentProcess; + let ledgerIdData = []; + try { + if (payload.method !== DidMethod.KEY && payload.method !== DidMethod.WEB) { + const { network } = payload; + const ledger = await ledgerName(network); + const ledgerList = (await this._getALlLedgerDetails()) as unknown as LedgerListResponse; + const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); + if (!isLedgerExist) { + throw new BadRequestException(ResponseMessages.agent.error.invalidLedger, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } - const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; - - // Create and stored agent details - agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); - - // Get platform admin details - const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); - - payload.endpoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - // Create tenant wallet and DID - const tenantDetails = await this.createTenantAndNotify(payload, platformAdminSpinnedUp); - if (!tenantDetails?.walletResponseDetails?.id || !tenantDetails?.DIDCreationOption?.did) { - this.logger.error(`Error in getting wallet id and wallet did`); - throw new NotFoundException( - ResponseMessages.agent.error.notAbleToSpinUpAgent, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } + ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); + } - if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { - this.logger.error(`Platform-admin agent is not spun-up`); - throw new NotFoundException( - ResponseMessages.agent.error.platformAdminNotAbleToSpinp, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } - // Get shared agent type - const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); - // Get agent type details - const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); - - const storeOrgAgentData: IStoreOrgAgentDetails = { - did: tenantDetails.DIDCreationOption.did, - isDidPublic: true, - agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, - agentsTypeId: agentTypeId, - orgId: payload.orgId, - agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, - orgAgentTypeId, - tenantId: tenantDetails.walletResponseDetails['id'], - walletName: payload.label, - ledgerId: ledgerIdData ? ledgerIdData.map(item => item.id) : null, - id: agentProcess?.id - }; - - // Get organization data - const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); - - this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); - - await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); - - this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); - - // Create the legacy connection invitation - await this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); - - this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); - - } catch (error) { - this.handleError(error, payload.clientSocketId); - - if (agentProcess && agentProcess?.id) { - this.agentServiceRepository.removeOrgAgent(agentProcess?.id); - } - throw error; + const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; + + // Create and stored agent details + agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); + + // Get platform admin details + const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); + + payload.endpoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + // Create tenant wallet and DID + const tenantDetails = await this.createTenantAndNotify(payload, platformAdminSpinnedUp); + if (!tenantDetails?.walletResponseDetails?.id || !tenantDetails?.DIDCreationOption?.did) { + this.logger.error(`Error in getting wallet id and wallet did`); + throw new NotFoundException(ResponseMessages.agent.error.notAbleToSpinUpAgent, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } + + if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { + this.logger.error(`Platform-admin agent is not spun-up`); + throw new NotFoundException(ResponseMessages.agent.error.platformAdminNotAbleToSpinp, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); + } + // Get shared agent type + const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); + // Get agent type details + const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); + + const storeOrgAgentData: IStoreOrgAgentDetails = { + did: tenantDetails.DIDCreationOption.did, + isDidPublic: true, + agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, + agentsTypeId: agentTypeId, + orgId: payload.orgId, + agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, + orgAgentTypeId, + tenantId: tenantDetails.walletResponseDetails['id'], + walletName: payload.label, + ledgerId: ledgerIdData ? ledgerIdData.map((item) => item.id) : null, + id: agentProcess?.id + }; + + // Get organization data + const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); + + this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); + + await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); + + this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); + + // Create the legacy connection invitation + await this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); + + this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); + } catch (error) { + this.handleError(error, payload.clientSocketId); + + if (agentProcess && agentProcess?.id) { + this.agentServiceRepository.removeOrgAgent(agentProcess?.id); + } + throw error; } + } /** * Create wallet - * @param payload + * @param payload * @returns wallet details */ async createWallet(payload: IWallet): Promise { try { - const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( + CommonConstants.PLATFORM_ADMIN_ORG + ); - const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); + const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); - const { label } = payload; - const createTenantOptions = { - config: { label } - }; - - const tenantDetails = await this.commonService.httpPost( - `${getPlatformAgentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, - createTenantOptions, - { headers: { 'authorization': getDcryptedToken } } - ); + const { label } = payload; + const createTenantOptions = { + config: { label } + }; - return tenantDetails; + const tenantDetails = await this.commonService.httpPost( + `${getPlatformAgentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, + createTenantOptions, + { headers: { authorization: getDcryptedToken } } + ); + return tenantDetails; } catch (error) { this.logger.error(`error in create wallet : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -808,62 +885,62 @@ export class AgentServiceService { /** * Create did - * @param payload + * @param payload * @returns did and didDocument */ - async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { - try { + async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { + try { const agentDetails = await this.agentServiceRepository.getOrgAgentDetails(orgId); - + const getApiKey = await this.getOrgAgentApiKey(orgId); const getOrgAgentType = await this.agentServiceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); let url; if (getOrgAgentType.agent === OrgAgentType.DEDICATED) { - url = `${agentDetails.agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; + url = `${agentDetails.agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; } else if (getOrgAgentType.agent === OrgAgentType.SHARED) { - url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`; + url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`; } - const didDetails = await this.commonService.httpPost(url, payload, - { headers: { 'authorization': getApiKey } } - ); + const didDetails = await this.commonService.httpPost(url, payload, { headers: { authorization: getApiKey } }); return didDetails; - } catch (error) { - this.logger.error(`error in create did : ${JSON.stringify(error)}`); + this.logger.error(`error in create did : ${JSON.stringify(error)}`); - if (error?.response?.error?.message) { - throw new RpcException({ - statusCode: error?.response?.status, - error: error?.response?.error?.message - }); - } else { - throw new RpcException(error.response ? error.response : error); + if (error?.response?.error?.message) { + throw new RpcException({ + statusCode: error?.response?.status, + error: error?.response?.error?.message + }); + } else { + throw new RpcException(error.response ? error.response : error); + } } - } -} - /** + /** * @returns Secp256k1 key pair for polygon DID */ - async createSecp256k1KeyPair(orgId:string): Promise { - try { - const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( - CommonConstants.PLATFORM_ADMIN_ORG - ); + async createSecp256k1KeyPair(orgId: string): Promise { + try { + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( + CommonConstants.PLATFORM_ADMIN_ORG + ); - const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); + const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); - const url = `${getPlatformAgentEndPoint}${CommonConstants.CREATE_POLYGON_SECP256k1_KEY}`; + const url = `${getPlatformAgentEndPoint}${CommonConstants.CREATE_POLYGON_SECP256k1_KEY}`; - const createKeyPairResponse = await this.commonService.httpPost(url, {}, { headers: { 'authorization': getDcryptedToken } }); - return createKeyPairResponse; - } catch (error) { - this.logger.error(`error in createSecp256k1KeyPair : ${JSON.stringify(error)}`); - throw new RpcException(error.response ? error.response : error); - } - } + const createKeyPairResponse = await this.commonService.httpPost( + url, + {}, + { headers: { authorization: getDcryptedToken } } + ); + return createKeyPairResponse; + } catch (error) { + this.logger.error(`error in createSecp256k1KeyPair : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } private async getPlatformAdminAndNotify(clientSocketId: string | undefined): Promise { const socket = await this.createSocketInstance(); @@ -871,14 +948,16 @@ export class AgentServiceService { socket.emit('agent-spinup-process-initiated', { clientId: clientSocketId }); } - const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( + CommonConstants.PLATFORM_ADMIN_ORG + ); if (!platformAdminSpinnedUp) { this.logger.error(`Agent not able to spin-up`); - throw new BadRequestException( - ResponseMessages.agent.error.notAbleToSpinp, - { cause: new Error(), description: ResponseMessages.errorMessages.serverError } - ); + throw new BadRequestException(ResponseMessages.agent.error.notAbleToSpinp, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); } return platformAdminSpinnedUp; @@ -886,15 +965,15 @@ export class AgentServiceService { /** * Create tenant on the agent - * @param payload - * @param ledgerIds - * @param platformAdminSpinnedUp + * @param payload + * @param ledgerIds + * @param platformAdminSpinnedUp * @returns Get tanant status */ - + // eslint-disable-next-line @typescript-eslint/no-explicit-any private async createTenantAndNotify(payload: ITenantDto, platformAdminSpinnedUp: IOrgAgentsResponse): Promise { - const WalletSetupPayload = {...payload}; + const WalletSetupPayload = { ...payload }; const socket = await this.createSocketInstance(); if (WalletSetupPayload.clientSocketId) { socket.emit('agent-spinup-process-initiated', { clientId: WalletSetupPayload.clientSocketId }); @@ -906,12 +985,15 @@ export class AgentServiceService { delete WalletSetupPayload.orgId; delete WalletSetupPayload.ledgerId; - const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); - const walletResponseDetails = await this._createTenantWallet(walletLabel, platformAdminSpinnedUp.org_agents[0].agentEndPoint, getDcryptedToken); - if (!walletResponseDetails && !walletResponseDetails.id) { - throw new InternalServerErrorException('Error while creating the wallet'); - } + const walletResponseDetails = await this._createTenantWallet( + walletLabel, + platformAdminSpinnedUp.org_agents[0].agentEndPoint, + getDcryptedToken + ); + if (!walletResponseDetails && !walletResponseDetails.id) { + throw new InternalServerErrorException('Error while creating the wallet'); + } const didCreateOption = { didPayload: WalletSetupPayload, agentEndpoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, @@ -921,50 +1003,49 @@ export class AgentServiceService { const DIDCreationOption = await this._createDID(didCreateOption); if (!DIDCreationOption) { throw new InternalServerErrorException('Error while creating the wallet'); - } - - return {walletResponseDetails, DIDCreationOption}; + } + + return { walletResponseDetails, DIDCreationOption }; } -// + // - /** + /** * Create tenant wallet on the agent * @param createTenantWalletPayload * @returns Get tanant status */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private async _createTenantWallet(label, endpoint, agentApiKey): Promise { //remove any - - - const createTenantOptions = { - config: { label } - }; - // Invoke an API request from the agent to create multi-tenant agent - const tenantDetails = await this.commonService.httpPost( - `${endpoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, - createTenantOptions, - { headers: { authorization: agentApiKey } } - ); - return tenantDetails; -} + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async _createTenantWallet(label, endpoint, agentApiKey): Promise { + //remove any + + const createTenantOptions = { + config: { label } + }; + // Invoke an API request from the agent to create multi-tenant agent + const tenantDetails = await this.commonService.httpPost( + `${endpoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, + createTenantOptions, + { headers: { authorization: agentApiKey } } + ); + return tenantDetails; + } - /** + /** * Create tenant wallet on the agent * @param _createDID * @returns Get tanant status */ - private async _createDID(didCreateOption): Promise { - - const {didPayload, agentEndpoint, apiKey, tenantId} = didCreateOption; - // Invoke an API request from the agent to create multi-tenant agent - const didDetails = await this.commonService.httpPost( - `${agentEndpoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${tenantId}`, - didPayload, - { headers: { authorization: apiKey } } - ); - return didDetails; -} + private async _createDID(didCreateOption): Promise { + const { didPayload, agentEndpoint, apiKey, tenantId } = didCreateOption; + // Invoke an API request from the agent to create multi-tenant agent + const didDetails = await this.commonService.httpPost( + `${agentEndpoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${tenantId}`, + didPayload, + { headers: { authorization: apiKey } } + ); + return didDetails; + } private async createSocketInstance(): Promise { return io(`${process.env.SOCKET_HOST}`, { reconnection: true, @@ -997,7 +1078,6 @@ export class AgentServiceService { let schemaResponse; if (OrgAgentType.DEDICATED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_CREATE_SCHEMA}`; const schemaPayload = { attributes: payload.attributes, @@ -1005,31 +1085,34 @@ export class AgentServiceService { name: payload.name, issuerId: payload.issuerId }; - schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': getApiKey } }) + schemaResponse = await this.commonService + .httpPost(url, schemaPayload, { headers: { authorization: getApiKey } }) .then(async (schema) => schema) - .catch(error => { - throw new InternalServerErrorException( - ResponseMessages.agent.error.agentDown, - { cause: new Error(), description: ResponseMessages.errorMessages.serverError } - ); + .catch((error) => { + throw new InternalServerErrorException(ResponseMessages.agent.error.agentDown, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); }); - } else if (OrgAgentType.SHARED === payload.agentType) { - - const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_SCHEMA}`.replace('#', `${payload.tenantId}`); + const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_SCHEMA}`.replace( + '#', + `${payload.tenantId}` + ); const schemaPayload = { attributes: payload.payload.attributes, version: payload.payload.version, name: payload.payload.name, issuerId: payload.payload.issuerId }; - schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': getApiKey } }) + schemaResponse = await this.commonService + .httpPost(url, schemaPayload, { headers: { authorization: getApiKey } }) .then(async (schema) => schema) - .catch(error => { - throw new InternalServerErrorException( - ResponseMessages.agent.error.agentDown, - { cause: new Error(), description: ResponseMessages.errorMessages.serverError } - ); + .catch((error) => { + throw new InternalServerErrorException(ResponseMessages.agent.error.agentDown, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); }); } return schemaResponse; @@ -1045,14 +1128,18 @@ export class AgentServiceService { const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_SCHEMA_BY_ID.replace('#', `${payload.schemaId}`)}`; - schemaResponse = await this.commonService.httpGet(url, payload.schemaId) - .then(async (schema) => schema); - + const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_SCHEMA_BY_ID.replace( + '#', + `${payload.schemaId}` + )}`; + schemaResponse = await this.commonService.httpGet(url, payload.schemaId).then(async (schema) => schema); } else if (OrgAgentType.SHARED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}`.replace('@', `${payload.payload.schemaId}`).replace('#', `${payload.tenantId}`); + const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}` + .replace('@', `${payload.payload.schemaId}`) + .replace('#', `${payload.tenantId}`); - schemaResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) + schemaResponse = await this.commonService + .httpGet(url, { headers: { authorization: getApiKey } }) .then(async (schema) => schema); } return schemaResponse; @@ -1068,7 +1155,6 @@ export class AgentServiceService { const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === String(payload.agentType)) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_CREATE_CRED_DEF}`; const credDefPayload = { tag: payload.tag, @@ -1076,17 +1162,21 @@ export class AgentServiceService { issuerId: payload.issuerId }; - credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': getApiKey } }) + credDefResponse = await this.commonService + .httpPost(url, credDefPayload, { headers: { authorization: getApiKey } }) .then(async (credDef) => credDef); - } else if (OrgAgentType.SHARED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_CRED_DEF}`.replace('#', `${payload.tenantId}`); + const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_CRED_DEF}`.replace( + '#', + `${payload.tenantId}` + ); const credDefPayload = { tag: payload.payload.tag, schemaId: payload.payload.schemaId, issuerId: payload.payload.issuerId }; - credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': getApiKey } }) + credDefResponse = await this.commonService + .httpPost(url, credDefPayload, { headers: { authorization: getApiKey } }) .then(async (credDef) => credDef); } @@ -1103,13 +1193,19 @@ export class AgentServiceService { const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_CRED_DEF_BY_ID.replace('#', `${payload.credentialDefinitionId}`)}`; - credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) + const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_CRED_DEF_BY_ID.replace( + '#', + `${payload.credentialDefinitionId}` + )}`; + credDefResponse = await this.commonService + .httpGet(url, { headers: { authorization: getApiKey } }) .then(async (credDef) => credDef); - } else if (OrgAgentType.SHARED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CRED_DEF}`.replace('@', `${payload.payload.credentialDefinitionId}`).replace('#', `${payload.tenantId}`); - credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': getApiKey } }) + const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CRED_DEF}` + .replace('@', `${payload.payload.credentialDefinitionId}`) + .replace('#', `${payload.tenantId}`); + credDefResponse = await this.commonService + .httpGet(url, { headers: { authorization: getApiKey } }) .then(async (credDef) => credDef); } return credDefResponse; @@ -1119,13 +1215,16 @@ export class AgentServiceService { } } - async createLegacyConnectionInvitation(connectionPayload: IConnectionDetails, url: string, orgId: string): Promise { + async createLegacyConnectionInvitation( + connectionPayload: IConnectionDetails, + url: string, + orgId: string + ): Promise { try { - const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpPost(url, connectionPayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, connectionPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return data; } catch (error) { @@ -1138,8 +1237,8 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpPost(url, issueData, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, issueData, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return data; } catch (error) { this.logger.error(`Error in sendCredentialCreateOffer in agent service : ${JSON.stringify(error)}`); @@ -1149,8 +1248,8 @@ export class AgentServiceService { async getProofPresentations(url: string, apiKey: string): Promise { try { const getProofPresentationsData = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpGet(url, { headers: { authorization: apiKey } }) + .then(async (response) => response); return getProofPresentationsData; } catch (error) { @@ -1162,8 +1261,8 @@ export class AgentServiceService { async getIssueCredentials(url: string, apiKey: string): Promise { try { const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpGet(url, { headers: { authorization: apiKey } }) + .then(async (response) => response); return data; } catch (error) { this.logger.error(`Error in getIssueCredentials in agent service : ${JSON.stringify(error)}`); @@ -1175,9 +1274,9 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const getProofPresentationById = await this.commonService - .httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response) + .catch((error) => this.handleAgentSpinupStatusErrors(error)); return getProofPresentationById; } catch (error) { @@ -1190,8 +1289,8 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return data; } catch (error) { this.logger.error(`Error in getIssueCredentialsbyCredentialRecordId in agent service : ${JSON.stringify(error)}`); @@ -1199,12 +1298,16 @@ export class AgentServiceService { } } - async sendProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, orgId: string): Promise { + async sendProofRequest( + proofRequestPayload: ISendProofRequestPayload, + url: string, + orgId: string + ): Promise { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const sendProofRequest = await this.commonService - .httpPost(url, proofRequestPayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, proofRequestPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return sendProofRequest; } catch (error) { this.logger.error(`Error in send proof request in agent service : ${JSON.stringify(error)}`); @@ -1216,9 +1319,9 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const verifyPresentation = await this.commonService - .httpPost(url, '', { headers: { 'authorization': getApiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); + .httpPost(url, '', { headers: { authorization: getApiKey } }) + .then(async (response) => response) + .catch((error) => this.handleAgentSpinupStatusErrors(error)); return verifyPresentation; } catch (error) { this.logger.error(`Error in verify proof presentation in agent service : ${JSON.stringify(error)}`); @@ -1230,8 +1333,8 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return data; } catch (error) { this.logger.error(`Error in getConnections in agent service : ${JSON.stringify(error)}`); @@ -1240,59 +1343,57 @@ export class AgentServiceService { } async getConnectionsByconnectionId(url: string, orgId: string): Promise { - try { const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response) + .catch((error) => this.handleAgentSpinupStatusErrors(error)); return data; } catch (error) { this.logger.error(`Error in getConnectionsByconnectionId in agent service : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } - } /** * Get agent health - * @param orgId + * @param orgId * @returns agent status */ async getAgentHealthDetails(orgId: string): Promise { try { - // Get organization agent details const orgAgentDetails: org_agents = await this.agentServiceRepository.getOrgAgentDetails(orgId); let agentApiKey; if (orgAgentDetails) { - agentApiKey = await this.getOrgAgentApiKey(orgId); + agentApiKey = await this.getOrgAgentApiKey(orgId); } if (!orgAgentDetails) { - throw new NotFoundException( - ResponseMessages.agent.error.agentNotExists, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); + throw new NotFoundException(ResponseMessages.agent.error.agentNotExists, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } if (!orgAgentDetails?.agentEndPoint) { - throw new NotFoundException( - ResponseMessages.agent.error.agentUrl, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); + throw new NotFoundException(ResponseMessages.agent.error.agentUrl, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } // Invoke an API request from the agent to assess its current status const agentHealthData = await this.commonService - .httpGet(`${orgAgentDetails.agentEndPoint}${CommonConstants.URL_AGENT_STATUS}`, { headers: { 'authorization': agentApiKey } }) - .then(async response => response); + .httpGet(`${orgAgentDetails.agentEndPoint}${CommonConstants.URL_AGENT_STATUS}`, { + headers: { authorization: agentApiKey } + }) + .then(async (response) => response); return agentHealthData; - } catch (error) { this.logger.error(`Agent health details : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -1303,19 +1404,22 @@ export class AgentServiceService { try { const getLedgerConfigData = await this.agentServiceRepository.getLedgerConfigByOrgId(); return getLedgerConfigData; - } catch (error) { this.logger.error(`Error in send out of band proof request in agent service : ${JSON.stringify(error)}`); throw error; } } - async sendOutOfBandProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, orgId: string): Promise { + async sendOutOfBandProofRequest( + proofRequestPayload: ISendProofRequestPayload, + url: string, + orgId: string + ): Promise { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const sendProofRequest = await this.commonService - .httpPost(url, proofRequestPayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, proofRequestPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return sendProofRequest; } catch (error) { this.logger.error(`Error in send out of band proof request in agent service : ${JSON.stringify(error)}`); @@ -1327,9 +1431,9 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const getVerifiedProofData = await this.commonService - .httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response) + .catch((error) => this.handleAgentSpinupStatusErrors(error)); return getVerifiedProofData; } catch (error) { @@ -1342,8 +1446,8 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const schemaRequest = await this.commonService - .httpPost(url, requestSchemaPayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, requestSchemaPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return schemaRequest; } catch (error) { this.logger.error(`Error in schema endorsement request in agent service : ${JSON.stringify(error)}`); @@ -1355,11 +1459,13 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const credDefRequest = await this.commonService - .httpPost(url, requestSchemaPayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, requestSchemaPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return credDefRequest; } catch (error) { - this.logger.error(`Error in credential-definition endorsement request in agent service : ${JSON.stringify(error)}`); + this.logger.error( + `Error in credential-definition endorsement request in agent service : ${JSON.stringify(error)}` + ); throw error; } } @@ -1368,8 +1474,8 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const signEndorsementTransaction = await this.commonService - .httpPost(url, signEndorsementPayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, signEndorsementPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return signEndorsementTransaction; } catch (error) { @@ -1382,8 +1488,8 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const signEndorsementTransaction = await this.commonService - .httpPost(url, submitEndorsementPayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, submitEndorsementPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return signEndorsementTransaction; } catch (error) { @@ -1392,12 +1498,16 @@ export class AgentServiceService { } } - async outOfBandCredentialOffer(outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, orgId: string): Promise { + async outOfBandCredentialOffer( + outOfBandIssuancePayload: IOutOfBandCredentialOffer, + url: string, + orgId: string + ): Promise { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const sendOutOfbandCredentialOffer = await this.commonService - .httpPost(url, outOfBandIssuancePayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, outOfBandIssuancePayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return sendOutOfbandCredentialOffer; } catch (error) { this.logger.error(`Error in out-of-band credential in agent service : ${JSON.stringify(error)}`); @@ -1405,14 +1515,11 @@ export class AgentServiceService { } } - async deleteWallet( - url: string, - apiKey: string - ): Promise { + async deleteWallet(url: string, apiKey: string): Promise { try { const deleteWallet = await this.commonService - .httpDelete(url, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpDelete(url, { headers: { authorization: apiKey } }) + .then(async (response) => response); return deleteWallet; } catch (error) { this.logger.error(`Error in delete wallet in agent service : ${JSON.stringify(error)}`); @@ -1424,8 +1531,8 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const receiveInvitationUrlRes = await this.commonService - .httpPost(url, receiveInvitationUrl, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, receiveInvitationUrl, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return receiveInvitationUrlRes; } catch (error) { this.logger.error(`Error in receive invitation in agent service : ${JSON.stringify(error)}`); @@ -1437,8 +1544,8 @@ export class AgentServiceService { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const receiveInvitationRes = await this.commonService - .httpPost(url, receiveInvitation, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, receiveInvitation, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return receiveInvitationRes; } catch (error) { this.logger.error(`Error in receive invitation in agent service : ${JSON.stringify(error)}`); @@ -1448,63 +1555,53 @@ export class AgentServiceService { async getOrgAgentApiKey(orgId: string): Promise { try { - let agentApiKey; - const orgAgentApiKey = await this.agentServiceRepository.getAgentApiKey(orgId); - - const apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (apiKey) { - const getDcryptedToken = await this.commonService.decryptPassword(apiKey); - return getDcryptedToken; - } - - const orgAgentId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); - if (orgAgentApiKey?.orgAgentTypeId === orgAgentId) { - const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); - - const [orgAgentData] = platformAdminSpinnedUp.org_agents; - const { apiKey } = orgAgentData; - if (!platformAdminSpinnedUp) { - throw new InternalServerErrorException('Agent not able to spin-up'); + const orgAgentApiKey = await this.agentServiceRepository.getAgentApiKey(orgId); + const orgAgentId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); + const cacheKey = orgAgentApiKey?.orgAgentTypeId === orgAgentId ? CommonConstants.CACHE_SHARED_APIKEY_KEY : CommonConstants.CACHE_APIKEY_KEY; + + let apiKey = await this.cacheService.get(cacheKey); + if (!apiKey) { + if (orgAgentApiKey?.orgAgentTypeId === orgAgentId) { + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); + if (!platformAdminSpinnedUp) { + throw new InternalServerErrorException('Agent not able to spin-up'); + } + apiKey = platformAdminSpinnedUp.org_agents[0]?.apiKey; + } else { + apiKey = orgAgentApiKey?.apiKey; + } + if (!apiKey) { + throw new NotFoundException(ResponseMessages.agent.error.apiKeyNotExist); + } + await this.cacheService.set(cacheKey, apiKey, 0); } - agentApiKey = apiKey; - - } else { - agentApiKey = orgAgentApiKey?.apiKey; - } - - if (!agentApiKey) { - throw new NotFoundException(ResponseMessages.agent.error.apiKeyNotExist); - } - - await this.cacheService.set(CommonConstants.CACHE_APIKEY_KEY, agentApiKey, 0); - const getDcryptedToken = await this.commonService.decryptPassword(agentApiKey); - - return getDcryptedToken; - + const decryptedToken = await this.commonService.decryptPassword(apiKey); + return decryptedToken; } catch (error) { - this.logger.error(`Agent api key details : ${JSON.stringify(error)}`); - throw error; + this.logger.error(`Agent api key details : ${JSON.stringify(error)}`); + throw error; } - } +} + async handleAgentSpinupStatusErrors(error: string): Promise { if (error && Object.keys(error).length === 0) { - throw new InternalServerErrorException( - ResponseMessages.agent.error.agentDown, - { cause: new Error(), description: ResponseMessages.errorMessages.serverError } - ); + throw new InternalServerErrorException(ResponseMessages.agent.error.agentDown, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); } else { throw error; } - } + } async sendQuestion(questionPayload: IQuestionPayload, url: string, orgId: string): Promise { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const sendQuestionRes = await this.commonService - .httpPost(url, questionPayload, { headers: { 'authorization': getApiKey } }) - .then(async response => response); + .httpPost(url, questionPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return sendQuestionRes; } catch (error) { this.logger.error(`Error in send question in agent service : ${JSON.stringify(error)}`); @@ -1513,35 +1610,28 @@ export class AgentServiceService { } async getQuestionAnswersRecord(url: string, orgId: string): Promise { - try { const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': getApiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response) + .catch((error) => this.handleAgentSpinupStatusErrors(error)); return data; } catch (error) { this.logger.error(`Error in getQuestionAnswersRecord in agent service : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } - } private async tokenEncryption(token: string): Promise { try { - const encryptedToken = CryptoJS.AES.encrypt( - JSON.stringify(token), - process.env.CRYPTO_PRIVATE_KEY - ).toString(); + const encryptedToken = CryptoJS.AES.encrypt(JSON.stringify(token), process.env.CRYPTO_PRIVATE_KEY).toString(); return encryptedToken; - } catch (error) { throw error; } } - } diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index b93f0a0d7..83a0e78ee 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -313,7 +313,8 @@ export enum CommonConstants { ROLE = 'endorser', //CacheInfo -CACHE_APIKEY_KEY = "apiKey", +CACHE_SHARED_APIKEY_KEY = "dedicatedApiKey", +CACHE_APIKEY_KEY = "sharedApiKey", CACHE_TTL_SECONDS = 604800 } From 7ecb19be25912c499b674158f69beed86f27d65a Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Fri, 15 Mar 2024 20:58:30 +0530 Subject: [PATCH 183/231] fix: schema validation Signed-off-by: bhavanakarwade --- .../src/credential-definition/credential-definition.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 2bf12a082..861f4fa7c 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -148,7 +148,7 @@ export class CredentialDefinitionService extends BaseService { } validateSchemaId(schemaLedgerId: string): string { - const pattern = /^[\w\d]+:\d+:[\w\d\s]+:\d+\.\d+\.\d+$/; + const pattern = /^[\w\d]+:\d+:[\w\d\s]+:\d+(\.\d+(\.\d+)?)?$/; if (!pattern.test(schemaLedgerId)) { throw new BadRequestException('Invalid schema id format'); } From ecfcfce69ecd774353583be99cc9feb1a9b16f26 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Fri, 15 Mar 2024 21:10:06 +0530 Subject: [PATCH 184/231] fix: schema validations Signed-off-by: bhavanakarwade --- .../credential-definition.service.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 861f4fa7c..94c34f666 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -49,8 +49,6 @@ export class CredentialDefinitionService extends BaseService { credDef.tag ); - credDef.schemaLedgerId = await this.validateSchemaId(credDef.schemaLedgerId); - if (dbResult) { throw new ConflictException(ResponseMessages.credentialDefinition.error.Conflict); } @@ -147,15 +145,6 @@ export class CredentialDefinitionService extends BaseService { } } - validateSchemaId(schemaLedgerId: string): string { - const pattern = /^[\w\d]+:\d+:[\w\d\s]+:\d+(\.\d+(\.\d+)?)?$/; - if (!pattern.test(schemaLedgerId)) { - throw new BadRequestException('Invalid schema id format'); - } - return schemaLedgerId; - } - - async _createCredentialDefinition(payload: CreateCredDefAgentRedirection): Promise<{ response: string; }> { From 97daf1a12d814df58ca46dfaae2cc88e20a38ba3 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Mon, 18 Mar 2024 17:12:23 +0530 Subject: [PATCH 185/231] feat: added w3c schema Signed-off-by: tipusinghaw --- .../src/agent-service.controller.ts | 7 ++ .../src/agent-service.service.ts | 13 ++- .../api-gateway/src/dtos/create-schema.dto.ts | 19 ++++ .../src/schema/schema.controller.ts | 22 ++++- apps/api-gateway/src/schema/schema.service.ts | 5 ++ .../interfaces/schema-payload.interface.ts | 7 ++ apps/ledger/src/schema/schema.controller.ts | 8 +- apps/ledger/src/schema/schema.service.ts | 88 ++++++++++++++++++- libs/common/src/common.constant.ts | 4 + 9 files changed, 168 insertions(+), 5 deletions(-) diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 163c7edca..6d035f798 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -34,6 +34,13 @@ export class AgentServiceController { return this.agentServiceService.createSchema(payload); } + //DONE + @MessagePattern({ cmd: 'agent-create-w3c-schema' }) + async createWC3Schema(payload: {url, apiKey, schemaRequestPayload}): Promise { + + return this.agentServiceService.createWC3Schema(payload.url, payload.apiKey, payload.schemaRequestPayload); + } + //DONE @MessagePattern({ cmd: 'agent-get-schema' }) async getSchemaById(payload: IGetSchemaAgentRedirection): Promise { diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 36552d004..4188c7184 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -1267,7 +1267,6 @@ export class AgentServiceService { let agentApiKey; const orgAgentApiKey = await this.agentServiceRepository.getAgentApiKey(orgId); - const orgAgentId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); if (orgAgentApiKey?.orgAgentTypeId === orgAgentId) { const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); @@ -1335,5 +1334,17 @@ export class AgentServiceService { } + async createWC3Schema(url: string, apiKey: string, schemaRequestPayload): Promise { + try { + const schemaRequest = await this.commonService + .httpPost(url, schemaRequestPayload, { headers: { 'authorization': apiKey } }) + .then(async response => response); + return schemaRequest; + } catch (error) { + this.logger.error(`Error in schema endorsement request in agent service : ${JSON.stringify(error)}`); + throw error; + } + } + } diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index 5ba17afe9..a2f84530c 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -70,3 +70,22 @@ export class CreateSchemaDto { @IsString({ message: 'orgDid must be a string' }) orgDid: string; } + +export class CreateW3CSchemaDto { + @ApiProperty() + @IsNotEmpty({ message: 'schemaObject is required' }) + schema: object; + + @ApiProperty() + @IsString({ message: 'schemaName must be a string' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'schemaName is required' }) + schemaName: string; + + @ApiProperty() + @IsString({ message: 'schemaName must be a string' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'schemaName is required' }) + did: string; + +} diff --git a/apps/api-gateway/src/schema/schema.controller.ts b/apps/api-gateway/src/schema/schema.controller.ts index 596be7c4d..f1c25aab6 100644 --- a/apps/api-gateway/src/schema/schema.controller.ts +++ b/apps/api-gateway/src/schema/schema.controller.ts @@ -17,7 +17,7 @@ import { OrgRoles } from 'libs/org-roles/enums'; import { Roles } from '../authz/decorators/roles.decorator'; import { IUserRequestInterface } from './interfaces'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; -import { CreateSchemaDto } from '../dtos/create-schema.dto'; +import { CreateSchemaDto, CreateW3CSchemaDto } from '../dtos/create-schema.dto'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { CredDefSortFields, SortFields } from 'apps/ledger/src/schema/enum/schema.enum'; @@ -133,6 +133,26 @@ export class SchemaController { return res.status(HttpStatus.OK).json(finalResponse); } + @Post('/:orgId/polygon-w3c/schemas') + @ApiOperation({ + summary: 'Create and sends a schema to the ledger.', + description: 'Create and sends a schema to the ledger.' + }) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createW3CSchema(@Res() res: Response, @Body() schemaPayload: CreateW3CSchemaDto, @Param('orgId') orgId: string, @User() user: IUserRequestInterface): Promise { + + const schemaDetails = await this.appService.createW3CSchema(schemaPayload, orgId); + + const finalResponse: IResponseType = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.schema.success.create, + data: schemaDetails + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + @Post('/:orgId/schemas') @ApiOperation({ summary: 'Create and sends a schema to the ledger.', diff --git a/apps/api-gateway/src/schema/schema.service.ts b/apps/api-gateway/src/schema/schema.service.ts index a1d0863a3..7432f3c3a 100644 --- a/apps/api-gateway/src/schema/schema.service.ts +++ b/apps/api-gateway/src/schema/schema.service.ts @@ -19,6 +19,11 @@ export class SchemaService extends BaseService { return this.sendNatsMessage(this.schemaServiceProxy, 'create-schema', payload); } + createW3CSchema(schemaPayload: object, orgId: string): Promise { + const payload = { schemaPayload, orgId }; + return this.sendNatsMessage(this.schemaServiceProxy, 'create-w3c-schema', payload); + } + getSchemaById(schemaId: string, orgId: string): Promise<{ response: object; }> { diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index fb5a3f37e..42d2d477b 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -72,3 +72,10 @@ export interface ISchemaExist { version: string; } +export interface SchemaPayload { + schema: object; + schemaName: string; + did: string; + orgId: string; + } + diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 68003ac49..723241091 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -5,7 +5,8 @@ import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, - ISchemaSearchPayload + ISchemaSearchPayload, + SchemaPayload } from './interfaces/schema-payload.interface'; import { schema } from '@prisma/client'; import { @@ -24,6 +25,11 @@ export class SchemaController { return this.schemaService.createSchema(schema, user, orgId); } + @MessagePattern({ cmd: 'create-w3c-schema' }) + async createW3CSchema(payload: SchemaPayload): Promise { + return this.schemaService.createW3CSchema(payload); + } + @MessagePattern({ cmd: 'get-schema-by-id' }) async getSchemaById(payload: ISchema): Promise { const { schemaId, orgId } = payload; diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 7d253557b..f02d0092d 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -11,7 +11,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { SchemaRepository } from './repositories/schema.repository'; import { schema } from '@prisma/client'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria } from './interfaces/schema-payload.interface'; +import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria, SchemaPayload } from './interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; @@ -246,6 +246,64 @@ export class SchemaService extends BaseService { } } + async createW3CSchema( + schemaRequestPayload: SchemaPayload + ): Promise { + + const { orgId } = schemaRequestPayload; + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this._getOrgAgentApiKey(orgId); + } + + try { + const agentDetails = await this.schemaRepository.getAgentDetailsByOrgId(orgId); + if (!agentDetails) { + throw new NotFoundException( + ResponseMessages.schema.error.agentDetailsNotFound, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } + const { agentEndPoint } = agentDetails; + + const getAgentDetails = await this.schemaRepository.getAgentType(orgId); + + let url; + const orgAgentType = await this.schemaRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); + if (OrgAgentType.DEDICATED === orgAgentType) { + url = `${agentEndPoint}${CommonConstants.DEDICATED_CREATE_POLYGON_W3C_SCHEMA}`; + const schemaPayload = { + url, + apiKey, + schemaRequestPayload + }; + + return this._createW3CSchema(schemaPayload); + } + if (OrgAgentType.SHARED === orgAgentType) { + const { tenantId } = await this.schemaRepository.getAgentDetailsByOrgId(orgId); + + url = `${agentEndPoint}${CommonConstants.SHARED_CREATE_POLYGON_W3C_SCHEMA}${tenantId}`; + const schemaPayload = { + url, + apiKey, + schemaRequestPayload + }; + + return this._createW3CSchema(schemaPayload); + + } + + + } catch (error) { + this.logger.error( + `[createSchema] - outer Error: ${JSON.stringify(error)}` + ); + + throw new RpcException(error.response ? error.response : error); + } + } + async _createSchema(payload: CreateSchemaAgentRedirection): Promise<{ response: string; }> { @@ -272,6 +330,32 @@ export class SchemaService extends BaseService { return schemaResponse; } + async _createW3CSchema(payload: object): Promise<{ + response: string; + }> { + const pattern = { + cmd: 'agent-create-w3c-schema' + }; + const schemaResponse = await this.schemaServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ( + { + response + })) + ).toPromise() + .catch(error => { + this.logger.error(`Error in creating WC3 schema : ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.statusCode, + error: error.error, + message: error.message + }, error.error); + }); + return schemaResponse; + } + async getSchemaById(schemaId: string, orgId: string): Promise { try { @@ -467,7 +551,7 @@ export class SchemaService extends BaseService { async _getOrgAgentApiKey(orgId: string): Promise { const pattern = { cmd: 'get-org-agent-api-key' }; const payload = { orgId }; - + try { // eslint-disable-next-line @typescript-eslint/no-explicit-any const message = await this.schemaServiceProxy.send(pattern, payload).toPromise(); diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index 14f7ddc02..4c4dbdc2e 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -84,6 +84,10 @@ export enum CommonConstants { URL_SCHM_GET_CRED_DEF_BY_ID = '/credential-definitions/#', URL_SCHM_GET_CRED_DEF_BY_ATTRB = '/credential-definitions/created', + // POLYGON BASED W3C SCHEMAS + DEDICATED_CREATE_POLYGON_W3C_SCHEMA = '/polygon/create-schema', + SHARED_CREATE_POLYGON_W3C_SCHEMA = '/multi-tenancy/polygon-wc3/schema/', + // SHARED AGENT URL_SHAGENT_CREATE_TENANT = '/multi-tenancy/create-tenant', URL_SHAGENT_WITH_TENANT_AGENT = '/multi-tenancy/with-tenant-agent', From 143e8d9714cfdf33d5f2b0d265ab48d5bd77bdd2 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Mon, 18 Mar 2024 18:10:49 +0530 Subject: [PATCH 186/231] feat: added interface Signed-off-by: tipusinghaw --- .../src/interfaces/ISchemaSearch.interface.ts | 6 ++ apps/api-gateway/src/schema/schema.service.ts | 4 +- .../interfaces/schema-payload.interface.ts | 23 +++++--- apps/ledger/src/schema/schema.controller.ts | 4 +- apps/ledger/src/schema/schema.service.ts | 57 +++++++------------ 5 files changed, 45 insertions(+), 49 deletions(-) diff --git a/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts b/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts index 2f83d26cb..6266a4e37 100644 --- a/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts +++ b/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts @@ -10,3 +10,9 @@ export interface ISchemaSearchPayload { user?: IUserRequestInterface } + +export interface W3CSchemaPayload { + schema: object; + schemaName: string; + did: string; + } diff --git a/apps/api-gateway/src/schema/schema.service.ts b/apps/api-gateway/src/schema/schema.service.ts index 7432f3c3a..5a5ade975 100644 --- a/apps/api-gateway/src/schema/schema.service.ts +++ b/apps/api-gateway/src/schema/schema.service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from '../../../../libs/service/base.service'; import { CreateSchemaDto } from '../dtos/create-schema.dto'; -import { ISchemaSearchPayload } from '../interfaces/ISchemaSearch.interface'; +import { ISchemaSearchPayload, W3CSchemaPayload } from '../interfaces/ISchemaSearch.interface'; import { IUserRequestInterface } from './interfaces'; import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { GetCredentialDefinitionBySchemaIdDto } from './dtos/get-all-schema.dto'; @@ -19,7 +19,7 @@ export class SchemaService extends BaseService { return this.sendNatsMessage(this.schemaServiceProxy, 'create-schema', payload); } - createW3CSchema(schemaPayload: object, orgId: string): Promise { + createW3CSchema(schemaPayload: W3CSchemaPayload, orgId: string): Promise { const payload = { schemaPayload, orgId }; return this.sendNatsMessage(this.schemaServiceProxy, 'create-w3c-schema', payload); } diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index 42d2d477b..5393bdddf 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -72,10 +72,19 @@ export interface ISchemaExist { version: string; } -export interface SchemaPayload { - schema: object; - schemaName: string; - did: string; - orgId: string; - } - +interface SchemaPayload { + schema: object, + schemaName: string, + did: string + } + +export interface W3CSchemaPayload { + schemaPayload: SchemaPayload, + orgId: string + } + +export interface W3CCreateSchema { + url: string, + apiKey: string, + schemaRequestPayload: SchemaPayload +} diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 723241091..dafc53a89 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -6,7 +6,7 @@ import { ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaSearchPayload, - SchemaPayload + W3CSchemaPayload } from './interfaces/schema-payload.interface'; import { schema } from '@prisma/client'; import { @@ -26,7 +26,7 @@ export class SchemaController { } @MessagePattern({ cmd: 'create-w3c-schema' }) - async createW3CSchema(payload: SchemaPayload): Promise { + async createW3CSchema(payload: W3CSchemaPayload): Promise { return this.schemaService.createW3CSchema(payload); } diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index f02d0092d..9f1ae6f1e 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -11,7 +11,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { SchemaRepository } from './repositories/schema.repository'; import { schema } from '@prisma/client'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria, SchemaPayload } from './interfaces/schema-payload.interface'; +import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria, W3CCreateSchema, W3CSchemaPayload } from './interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; @@ -247,16 +247,10 @@ export class SchemaService extends BaseService { } async createW3CSchema( - schemaRequestPayload: SchemaPayload - ): Promise { - - const { orgId } = schemaRequestPayload; - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - + schemaRequestPayload: W3CSchemaPayload + ): Promise { try { + const { orgId } = schemaRequestPayload; const agentDetails = await this.schemaRepository.getAgentDetailsByOrgId(orgId); if (!agentDetails) { throw new NotFoundException( @@ -265,44 +259,31 @@ export class SchemaService extends BaseService { ); } const { agentEndPoint } = agentDetails; - + const apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY) || await this._getOrgAgentApiKey(orgId); const getAgentDetails = await this.schemaRepository.getAgentType(orgId); - - let url; const orgAgentType = await this.schemaRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); + let url; if (OrgAgentType.DEDICATED === orgAgentType) { - url = `${agentEndPoint}${CommonConstants.DEDICATED_CREATE_POLYGON_W3C_SCHEMA}`; - const schemaPayload = { - url, - apiKey, - schemaRequestPayload - }; - - return this._createW3CSchema(schemaPayload); - } - if (OrgAgentType.SHARED === orgAgentType) { + url = `${agentEndPoint}${CommonConstants.DEDICATED_CREATE_POLYGON_W3C_SCHEMA}`; + } else if (OrgAgentType.SHARED === orgAgentType) { const { tenantId } = await this.schemaRepository.getAgentDetailsByOrgId(orgId); - - url = `${agentEndPoint}${CommonConstants.SHARED_CREATE_POLYGON_W3C_SCHEMA}${tenantId}`; - const schemaPayload = { - url, - apiKey, - schemaRequestPayload - }; - - return this._createW3CSchema(schemaPayload); - + url = `${agentEndPoint}${CommonConstants.SHARED_CREATE_POLYGON_W3C_SCHEMA}${tenantId}`; } - - + const schemaPayload = { + url, + apiKey, + schemaRequestPayload: schemaRequestPayload.schemaPayload + }; + return this._createW3CSchema(schemaPayload); } catch (error) { this.logger.error( `[createSchema] - outer Error: ${JSON.stringify(error)}` ); - - throw new RpcException(error.response ? error.response : error); + //need to discuss the multiple error message + throw new RpcException(error.error ? error.error.message : error.message); } } + async _createSchema(payload: CreateSchemaAgentRedirection): Promise<{ response: string; @@ -330,7 +311,7 @@ export class SchemaService extends BaseService { return schemaResponse; } - async _createW3CSchema(payload: object): Promise<{ + async _createW3CSchema(payload: W3CCreateSchema): Promise<{ response: string; }> { const pattern = { From a495d9ae41823331227e9248266a9da10ff5c731 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Mon, 18 Mar 2024 18:13:56 +0530 Subject: [PATCH 187/231] fix: schema dto Signed-off-by: tipusinghaw --- apps/api-gateway/src/dtos/create-schema.dto.ts | 4 ++-- apps/api-gateway/src/schema/schema.controller.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index a2f84530c..65b109a5c 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -83,9 +83,9 @@ export class CreateW3CSchemaDto { schemaName: string; @ApiProperty() - @IsString({ message: 'schemaName must be a string' }) + @IsString({ message: 'did must be a string' }) @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'schemaName is required' }) + @IsNotEmpty({ message: 'did is required' }) did: string; } diff --git a/apps/api-gateway/src/schema/schema.controller.ts b/apps/api-gateway/src/schema/schema.controller.ts index f1c25aab6..6ca121d2f 100644 --- a/apps/api-gateway/src/schema/schema.controller.ts +++ b/apps/api-gateway/src/schema/schema.controller.ts @@ -135,8 +135,8 @@ export class SchemaController { @Post('/:orgId/polygon-w3c/schemas') @ApiOperation({ - summary: 'Create and sends a schema to the ledger.', - description: 'Create and sends a schema to the ledger.' + summary: 'Create and sends a W3C-schema to the ledger.', + description: 'Create and sends a W3C-schema to the ledger.' }) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) From 5340e31283b51f2594285c37ee7af9bb14645190 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Mon, 18 Mar 2024 18:33:39 +0530 Subject: [PATCH 188/231] fix: sonar cloud issue Signed-off-by: tipusinghaw --- apps/ledger/src/schema/schema.service.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 9f1ae6f1e..394797427 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -269,12 +269,12 @@ export class SchemaService extends BaseService { const { tenantId } = await this.schemaRepository.getAgentDetailsByOrgId(orgId); url = `${agentEndPoint}${CommonConstants.SHARED_CREATE_POLYGON_W3C_SCHEMA}${tenantId}`; } - const schemaPayload = { + const W3cSchemaPayload = { url, apiKey, schemaRequestPayload: schemaRequestPayload.schemaPayload }; - return this._createW3CSchema(schemaPayload); + return this._createW3CSchema(W3cSchemaPayload); } catch (error) { this.logger.error( `[createSchema] - outer Error: ${JSON.stringify(error)}` @@ -314,11 +314,11 @@ export class SchemaService extends BaseService { async _createW3CSchema(payload: W3CCreateSchema): Promise<{ response: string; }> { - const pattern = { + const natsPattern = { cmd: 'agent-create-w3c-schema' }; - const schemaResponse = await this.schemaServiceProxy - .send(pattern, payload) + const W3CSchemaResponse = await this.schemaServiceProxy + .send(natsPattern, payload) .pipe( map((response) => ( { @@ -334,7 +334,7 @@ export class SchemaService extends BaseService { message: error.message }, error.error); }); - return schemaResponse; + return W3CSchemaResponse; } From aa377f7c512fe91e0834be9c2b2f3fbf49c16182 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Mon, 18 Mar 2024 19:34:03 +0530 Subject: [PATCH 189/231] fix: changed the interface of create schema Signed-off-by: tipusinghaw --- apps/api-gateway/src/schema/schema.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-gateway/src/schema/schema.service.ts b/apps/api-gateway/src/schema/schema.service.ts index 5a5ade975..f0655d307 100644 --- a/apps/api-gateway/src/schema/schema.service.ts +++ b/apps/api-gateway/src/schema/schema.service.ts @@ -19,7 +19,7 @@ export class SchemaService extends BaseService { return this.sendNatsMessage(this.schemaServiceProxy, 'create-schema', payload); } - createW3CSchema(schemaPayload: W3CSchemaPayload, orgId: string): Promise { + createW3CSchema(schemaPayload: W3CSchemaPayload, orgId: string): Promise { const payload = { schemaPayload, orgId }; return this.sendNatsMessage(this.schemaServiceProxy, 'create-w3c-schema', payload); } From 101a38b068414bac44ed6ec2b1df77ff8851c65a Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Tue, 19 Mar 2024 13:03:14 +0530 Subject: [PATCH 190/231] fix: multiple error messages Signed-off-by: tipusinghaw --- apps/ledger/src/schema/schema.service.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 394797427..4ecf1796d 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -279,7 +279,6 @@ export class SchemaService extends BaseService { this.logger.error( `[createSchema] - outer Error: ${JSON.stringify(error)}` ); - //need to discuss the multiple error message throw new RpcException(error.error ? error.error.message : error.message); } } @@ -327,12 +326,7 @@ export class SchemaService extends BaseService { ).toPromise() .catch(error => { this.logger.error(`Error in creating WC3 schema : ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.error, - message: error.message - }, error.error); + throw new RpcException(error.error ? error.error.message : error.message); }); return W3CSchemaResponse; } From fa4b3166697d1995ba7df8c1f06a40363be0d957 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 19 Mar 2024 13:33:15 +0530 Subject: [PATCH 191/231] fix: solved the duplication code Signed-off-by: KulkarniShashank --- .../src/agent-service.service.ts | 83 +++++++------------ apps/connection/src/connection.service.ts | 35 ++++---- 2 files changed, 44 insertions(+), 74 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index b814a34a0..4675dd32c 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -634,24 +634,7 @@ export class AgentServiceService { cmd: 'create-connection' }; const payload = { orgId, user, label }; - return this.agentServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ({ - response - })) - ) - .toPromise() - .catch((error) => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in create-connection in wallet provision : ${JSON.stringify(error)}`); } @@ -665,24 +648,7 @@ export class AgentServiceService { cmd: 'get-all-ledgers' }; const payload = {}; - return this.agentServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ({ - response - })) - ) - .toPromise() - .catch((error) => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in while fetching all the ledger details : ${JSON.stringify(error)}`); } @@ -695,24 +661,7 @@ export class AgentServiceService { const pattern = { cmd: 'wallet-provisioning' }; - return this.agentServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ({ - response - })) - ) - .toPromise() - .catch((error) => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in wallet provision : ${JSON.stringify(error)}`); throw error; @@ -1625,6 +1574,32 @@ export class AgentServiceService { } } + async natsCall(pattern: object, payload: object): Promise<{ + response: string; + }> { + try { + return this.agentServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ( + { + response + })) + ).toPromise() + .catch(error => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.statusCode, + error: error.message + }, error.error); + }); + } catch (error) { + this.logger.error(`[natsCall] - error in nats call : ${JSON.stringify(error)}`); + throw error; + } + } + private async tokenEncryption(token: string): Promise { try { const encryptedToken = CryptoJS.AES.encrypt(JSON.stringify(token), process.env.CRYPTO_PRIVATE_KEY).toString(); diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 18f72b391..712f5fed5 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -108,16 +108,7 @@ export class ConnectionService { return connectionDetailRecords; } catch (error) { this.logger.error(`[createLegacyConnectionInvitation] - error in connection invitation: ${error}`); - if (error && error?.status && error?.status?.message && error?.status?.message?.error) { - throw new RpcException({ - message: error?.status?.message?.error?.reason - ? error?.status?.message?.error?.reason - : error?.status?.message?.error, - statusCode: error?.status?.code - }); - } else { - throw new RpcException(error.response ? error.response : error); - } + this.handleError(error); } } @@ -383,16 +374,7 @@ export class ConnectionService { return record; } catch (error) { this.logger.error(`[sendQuestion] - error in get question answer record: ${error}`); - if (error && error?.status && error?.status?.message && error?.status?.message?.error) { - throw new RpcException({ - message: error?.status?.message?.error?.reason - ? error?.status?.message?.error?.reason - : error?.status?.message?.error, - statusCode: error?.status?.code - }); - } else { - throw new RpcException(error.response ? error.response : error); - } + this.handleError(error); } } @@ -763,4 +745,17 @@ export class ConnectionService { ); } } + + handleError(error): Promise { + if (error && error?.status && error?.status?.message && error?.status?.message?.error) { + throw new RpcException({ + message: error?.status?.message?.error?.reason + ? error?.status?.message?.error?.reason + : error?.status?.message?.error, + statusCode: error?.status?.code + }); + } else { + throw new RpcException(error.response ? error.response : error); + } + } } From 0fd36cd03385a854398cd71b8dfe1770f87ecb96 Mon Sep 17 00:00:00 2001 From: Nishad Shirsat <103021375+nishad-ayanworks@users.noreply.github.com> Date: Tue, 19 Mar 2024 16:02:56 +0530 Subject: [PATCH 192/231] Develop To QA (#602) * feat: support multiple did methods (#589) * feat: support multiple did methods Signed-off-by: bhavanakarwade * refactor: removed the commented code Signed-off-by: tipusinghaw * fix: removed commented code Signed-off-by: tipusinghaw * fix: removed commented code agent-service Signed-off-by: tipusinghaw * fix: removed commented code agent-service Signed-off-by: tipusinghaw --------- Signed-off-by: bhavanakarwade Signed-off-by: tipusinghaw Co-authored-by: tipusinghaw * fix: refactored tables Signed-off-by: bhavanakarwade * fix: reafctored json data Signed-off-by: bhavanakarwade * feat:add reuse connection Signed-off-by: pallavicoder * fix: refactored tables (#592) * fix: refactored tables Signed-off-by: bhavanakarwade * fix: reafctored json data Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade * fix:changed validation message Signed-off-by: pallavicoder * worked on the POST API of refresh token Signed-off-by: Nishad * fix: an incorrect message appears when delete the ecosystem invitation which is already deleted Signed-off-by: pranalidhanavade * fix: schema id validations Signed-off-by: bhavanakarwade * feat: dedicated agent spin up Signed-off-by: bhavanakarwade * fix: schema validation Signed-off-by: bhavanakarwade * fix: schema validations Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade Signed-off-by: tipusinghaw Signed-off-by: pallavicoder Signed-off-by: Nishad Signed-off-by: pranalidhanavade Co-authored-by: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Co-authored-by: tipusinghaw Co-authored-by: bhavanakarwade Co-authored-by: pallavicoder Co-authored-by: pallavighule <61926403+pallavighule@users.noreply.github.com> Co-authored-by: pranalidhanavade Co-authored-by: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Co-authored-by: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> --- .../src/agent-service.controller.ts | 26 +- .../src/agent-service.service.ts | 481 ++++++++++++------ .../src/interface/agent-service.interface.ts | 96 +++- apps/agent-service/src/main.ts | 8 +- .../repositories/agent-service.repository.ts | 36 +- .../agent-service/agent-service.controller.ts | 142 +++++- .../agent-service/agent-service.service.ts | 33 +- .../agent-service/dto/agent-service.dto.ts | 33 +- .../src/agent-service/dto/create-did.dto.ts | 70 +++ .../agent-service/dto/create-tenant.dto.ts | 34 +- .../agent-service/dto/create-wallet.dto.ts | 28 + .../interface/agent-service.interface.ts | 18 + .../api-gateway/src/authz/authz.controller.ts | 20 + apps/api-gateway/src/authz/authz.service.ts | 4 + .../src/authz/dtos/refresh-token.dto.ts | 14 + .../src/connection/connection.service.ts | 3 +- .../src/connection/dtos/connection.dto.ts | 6 + .../src/issuance/dtos/issuance.dto.ts | 6 + .../src/issuance/issuance.service.ts | 4 +- .../src/verification/dto/request-proof.dto.ts | 7 + apps/connection/src/connection.repository.ts | 6 +- apps/connection/src/connection.service.ts | 16 +- .../src/interfaces/connection.interfaces.ts | 2 + apps/ecosystem/src/ecosystem.repository.ts | 12 + apps/issuance/src/issuance.repository.ts | 18 + apps/issuance/src/issuance.service.ts | 20 +- .../credential-definition.service.ts | 1 + apps/user/src/user.controller.ts | 5 + apps/user/src/user.service.ts | 17 + .../src/interfaces/verification.interface.ts | 6 +- .../repositories/verification.repository.ts | 19 +- apps/verification/src/verification.service.ts | 16 +- .../src/client-registration.service.ts | 12 +- libs/common/src/cast.helper.ts | 11 + libs/common/src/common.constant.ts | 16 +- libs/common/src/did.validator.ts | 27 + .../src/interfaces/agent-service.interface.ts | 1 + .../src/interfaces/connection.interface.ts | 1 + libs/common/src/interfaces/did.interface.ts | 13 + libs/common/src/response-messages/index.ts | 13 +- libs/enum/src/enum.ts | 21 +- .../prisma/data/credebl-master-table.json | 44 ++ .../migration.sql | 13 + .../migration.sql | 2 + libs/prisma-service/prisma/schema.prisma | 11 + libs/prisma-service/prisma/seed.ts | 62 ++- 46 files changed, 1165 insertions(+), 289 deletions(-) create mode 100644 apps/api-gateway/src/agent-service/dto/create-did.dto.ts create mode 100644 apps/api-gateway/src/agent-service/dto/create-wallet.dto.ts create mode 100644 apps/api-gateway/src/authz/dtos/refresh-token.dto.ts create mode 100644 libs/common/src/did.validator.ts create mode 100644 libs/common/src/interfaces/did.interface.ts create mode 100644 libs/prisma-service/prisma/migrations/20240223140032_ledger_config/migration.sql create mode 100644 libs/prisma-service/prisma/migrations/20240312064123_agent_invitation_add_recipient_key/migration.sql diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 163c7edca..57228fad8 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -1,7 +1,7 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { AgentServiceService } from './agent-service.service'; -import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IProofPresentation, IAgentProofRequest, IPresentation } from './interface/agent-service.interface'; +import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IProofPresentation, IAgentProofRequest, IPresentation, IDidCreate, IWallet, ITenantRecord } from './interface/agent-service.interface'; import { user } from '@prisma/client'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IProofPresentationDetails } from '@credebl/common/interfaces/verification.interface'; @@ -28,6 +28,19 @@ export class AgentServiceController { return this.agentServiceService.createTenant(payload.createTenantDto, payload.user); } + /** + * @returns did + */ + @MessagePattern({ cmd: 'create-did' }) + async createDid(payload: { createDidDto: IDidCreate, orgId: string, user: IUserRequestInterface }): Promise { + return this.agentServiceService.createDid(payload.createDidDto, payload.orgId, payload.user); + } + + @MessagePattern({ cmd: 'create-wallet' }) + async createWallet(payload: { createWalletDto: IWallet, user: IUserRequestInterface}): Promise { + return this.agentServiceService.createWallet(payload.createWalletDto); + } + //DONE @MessagePattern({ cmd: 'agent-create-schema' }) async createSchema(payload: ITenantSchema): Promise { @@ -126,6 +139,11 @@ export class AgentServiceController { return this.agentServiceService.getAgentHealthDetails(payload.orgId); } + @MessagePattern({ cmd: 'get-ledger-config' }) + async getLedgerConfig(payload: { user: IUserRequestInterface }): Promise { + return this.agentServiceService.getLedgerConfigDetails(payload.user); + } + //DONE @MessagePattern({ cmd: 'agent-send-out-of-band-proof-request' }) async sendOutOfBandProofRequest(payload: { @@ -224,4 +242,10 @@ export class AgentServiceController { return this.agentServiceService.getQuestionAnswersRecord(payload.url, payload.apiKey); } + @MessagePattern({ cmd: 'polygon-create-keys' }) + async createSecp256k1KeyPair(payload: {orgId: string}): Promise { + return this.agentServiceService.createSecp256k1KeyPair(payload.orgId); + } + } + diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 36552d004..d25f5a714 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -19,8 +19,8 @@ import * as dotenv from 'dotenv'; import * as fs from 'fs'; import { map } from 'rxjs/operators'; dotenv.config(); -import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation, IQuestionPayload } from './interface/agent-service.interface'; -import { AgentSpinUpStatus, AgentType, Ledgers, OrgAgentType } from '@credebl/enum/enum'; +import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation, IQuestionPayload, IDidCreate, IWallet, ITenantRecord, IPlatformAgent, LedgerListResponse, IOrgLedgers, IStoreOrgAgent } from './interface/agent-service.interface'; +import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; import { ledgers, org_agents, organisation, platform_config } from '@prisma/client'; import { CommonConstants } from '@credebl/common/common.constant'; @@ -34,10 +34,11 @@ import * as retry from 'async-retry'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IProofPresentationDetails } from '@credebl/common/interfaces/verification.interface'; -import { ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; +import { ledgerName } from '@credebl/common/cast.helper'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; + @Injectable() @WebSocketGateway() export class AgentServiceService { @@ -95,7 +96,7 @@ export class AgentServiceService { this.agentServiceRepository.getAgentTypeDetails(), this.agentServiceRepository.getLedgerDetails(agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]) ]); - + let orgData; if (!user?.userId && agentSpinupDto?.platformAdminEmail) { @@ -140,9 +141,9 @@ export class AgentServiceService { } if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { - throw new BadRequestException( + throw new ConflictException( ResponseMessages.agent.error.walletAlreadyCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } + { cause: new Error(), description: ResponseMessages.errorMessages.conflict } ); } @@ -157,7 +158,6 @@ export class AgentServiceService { agentSpinupDto.tenant = agentSpinupDto.tenant || false; agentSpinupDto.ledgerName = agentSpinupDto.ledgerName?.length ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]; - // Invoke function for validate platform configuration this.validatePlatformConfig(platformConfig); @@ -337,13 +337,28 @@ export class AgentServiceService { } async _agentSpinup(walletProvisionPayload: IWalletProvision, agentSpinupDto: IAgentSpinupDto, orgApiKey: string, orgData: organisation, user: IUserRequestInterface, socket: Socket, ledgerId: string[], agentProcess: ICreateOrgAgent): Promise { - try { + let ledgerIdData = []; + try { + if (agentSpinupDto.method !== DidMethod.KEY && agentSpinupDto.method !== DidMethod.WEB) { + + const { network } = agentSpinupDto; + const ledger = await ledgerName(network); + const ledgerList = await this._getALlLedgerDetails() as unknown as LedgerListResponse; + const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); + if (!isLedgerExist) { + throw new BadRequestException( + ResponseMessages.agent.error.invalidLedger, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } + + ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); + } /** * Invoke wallet create and provision with agent */ const walletProvision = await this._walletProvision(walletProvisionPayload); - if (!walletProvision?.response) { this.logger.error(`Agent not able to spin-up`); throw new BadRequestException( @@ -353,7 +368,6 @@ export class AgentServiceService { } const agentDetails = walletProvision.response; const agentEndPoint = `${process.env.API_GATEWAY_PROTOCOL}://${agentDetails.agentEndPoint}`; - /** * Socket connection */ @@ -373,16 +387,18 @@ export class AgentServiceService { orgId: orgData.id, walletName: agentSpinupDto.walletName, clientSocketId: agentSpinupDto.clientSocketId, - ledgerId, + method: agentSpinupDto.method, + role: agentSpinupDto.role, + network: agentSpinupDto.network, + keyType: agentSpinupDto.keyType, + ledgerId: ledgerIdData ? ledgerIdData.map(item => item.id) : null, did: agentSpinupDto.did, id: agentProcess?.id }; - /** * Store organization agent details */ const storeAgentDetails = await this._storeOrgAgentDetails(agentPayload); - if (storeAgentDetails) { const filePath = `${process.cwd()}${process.env.AFJ_AGENT_TOKEN_PATH}${orgData.id}_${orgData.name.split(' ').join('_')}.json`; @@ -404,7 +420,6 @@ export class AgentServiceService { const getOrganization = await this.agentServiceRepository.getOrgDetails(orgData?.id); await this._createLegacyConnectionInvitation(orgData?.id, user, getOrganization.name); - if (agentSpinupDto.clientSocketId) { socket.emit('invitation-url-creation-success', { clientId: agentSpinupDto.clientSocketId }); } @@ -440,22 +455,18 @@ export class AgentServiceService { this._getAgentDid(payload), this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.DEDICATED) ]); - /** * Get DID method by agent */ const getDidMethod = await this._getDidMethod(payload, agentDid); - /** * Organization storage data */ - const storeOrgAgentData = await this._buildStoreOrgAgentData(payload, getDidMethod, orgAgentTypeId); - + const storeOrgAgentData = await this._buildStoreOrgAgentData(payload, getDidMethod, `${orgAgentTypeId}`); /** * Store org agent details */ const storeAgentDid = await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); - return storeAgentDid; } catch (error) { await this._handleError(payload, error); @@ -465,17 +476,19 @@ export class AgentServiceService { private async _getAgentDid(payload: IStoreOrgAgentDetails): Promise { - const { agentEndPoint, apiKey, seed, ledgerId, did } = payload; + const { agentEndPoint, apiKey, ledgerId, seed, keyType, method, network, role, did } = payload; const writeDid = 'write-did'; const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(ledgerId); const agentDidWriteUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; - return this._retryAgentSpinup(agentDidWriteUrl, apiKey, writeDid, seed, ledgerDetails[0].indyNamespace, did); + return this._retryAgentSpinup(agentDidWriteUrl, apiKey, writeDid, seed, keyType, method, network, role, did); + } private async _getDidMethod(payload: IStoreOrgAgentDetails, agentDid: object): Promise { - const getDidDic = 'get-did-doc'; - const getDidMethodUrl = `${payload.agentEndPoint}${CommonConstants.URL_AGENT_GET_DID}`.replace('#', agentDid['did']); - return this._retryAgentSpinup(getDidMethodUrl, payload.apiKey, getDidDic); + const { agentEndPoint, apiKey, seed, keyType, method, network, role } = payload; + const getDidDoc = 'get-did-doc'; + const getDidMethodUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_GET_DID}/${agentDid['did']}`; + return this._retryAgentSpinup(getDidMethodUrl, apiKey, getDidDoc, seed, keyType, method, network, role, `${agentDid['did']}`); } private _buildStoreOrgAgentData(payload: IStoreOrgAgentDetails, getDidMethod: object, orgAgentTypeId: string): IStoreOrgAgentDetails { @@ -509,25 +522,27 @@ export class AgentServiceService { } if (payload && payload?.id) { + this.agentServiceRepository.removeOrgAgent(payload?.id); } this.logger.error(`[_storeOrgAgentDetails] - Error in store agent details : ${JSON.stringify(error)}`); } - async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, seed?: string, indyNamespace?: string, did?: string): Promise { + + async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, seed: string, keyType: string, method: string, network: string, role: string, did: string): Promise { const retryOptions = { retries: 10 }; - try { return retry(async () => { if (agentApiState === 'write-did') { - return this.commonService.httpPost(agentUrl, { seed, method: indyNamespace, did }, { headers: { 'authorization': apiKey } }); + return this.commonService.httpPost(agentUrl, { seed, keyType, method, network, role, did}, { headers: { 'authorization': apiKey } }); } else if (agentApiState === 'get-did-doc') { return this.commonService.httpGet(agentUrl, { headers: { 'authorization': apiKey } }); } }, retryOptions); + } catch (error) { throw error; } @@ -563,6 +578,35 @@ export class AgentServiceService { } } + async _getALlLedgerDetails(): Promise<{ + response; + }> { + try { + const pattern = { + cmd: 'get-all-ledgers' + }; + const payload = {}; + return this.agentServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ( + { + response + })) + ).toPromise() + .catch(error => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.statusCode, + error: error.message + }, error.error); + }); + } catch (error) { + this.logger.error(`error in while fetching all the ledger details : ${JSON.stringify(error)}`); + } + } + async _walletProvision(payload: IWalletProvision): Promise<{ response; @@ -639,102 +683,185 @@ export class AgentServiceService { * @param user * @returns Get agent status */ - async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { - let agentProcess; + async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { + let agentProcess; + let ledgerIdData = []; + try { + if (payload.method !== DidMethod.KEY && payload.method !== DidMethod.WEB) { + + const { network } = payload; + const ledger = await ledgerName(network); + const ledgerList = await this._getALlLedgerDetails() as unknown as LedgerListResponse; + const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); + if (!isLedgerExist) { + throw new BadRequestException( + ResponseMessages.agent.error.invalidLedger, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } + + ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); + } - try { + const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; - // Get orgaization agent details by orgId - const getOrgAgent = await this.agentServiceRepository.getAgentDetails(payload.orgId); + // Create and stored agent details + agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); - if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { - this.logger.error(`Your wallet has already been created.`); - throw new BadRequestException( - ResponseMessages.agent.error.walletAlreadyCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); - } + // Get platform admin details + const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); + + payload.endpoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + // Create tenant wallet and DID + const tenantDetails = await this.createTenantAndNotify(payload, platformAdminSpinnedUp); + if (!tenantDetails?.walletResponseDetails?.id || !tenantDetails?.DIDCreationOption?.did) { + this.logger.error(`Error in getting wallet id and wallet did`); + throw new NotFoundException( + ResponseMessages.agent.error.notAbleToSpinUpAgent, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } - if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { - this.logger.error(`Your wallet has already processing.`); - throw new BadRequestException( - ResponseMessages.agent.error.walletAlreadyProcessing, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { + this.logger.error(`Platform-admin agent is not spun-up`); + throw new NotFoundException( + ResponseMessages.agent.error.platformAdminNotAbleToSpinp, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } + // Get shared agent type + const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); + // Get agent type details + const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); + + const storeOrgAgentData: IStoreOrgAgentDetails = { + did: tenantDetails.DIDCreationOption.did, + isDidPublic: true, + agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, + agentsTypeId: agentTypeId, + orgId: payload.orgId, + agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, + orgAgentTypeId, + tenantId: tenantDetails.walletResponseDetails['id'], + walletName: payload.label, + ledgerId: ledgerIdData ? ledgerIdData.map(item => item.id) : null, + id: agentProcess?.id + }; + + // Get organization data + const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); + + this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); + + await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); + + this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); + + // Create the legacy connection invitation + await this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); + + this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); + + } catch (error) { + this.handleError(error, payload.clientSocketId); + + if (agentProcess && agentProcess?.id) { + this.agentServiceRepository.removeOrgAgent(agentProcess?.id); + } + throw error; } + } - // Get ledgers details - const ledgerIdData = await this.agentServiceRepository.getLedgerDetails(Ledgers.Indicio_Demonet); - const ledgerIds = ledgerIdData.map(ledger => ledger?.id); + /** + * Create wallet + * @param payload + * @returns wallet details + */ + async createWallet(payload: IWallet): Promise { + try { + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); - payload.ledgerId = !payload.ledgerId || 0 === payload.ledgerId?.length ? ledgerIds : payload.ledgerId; - const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; + const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - // Create and stored agent details - agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); + const { label } = payload; + const createTenantOptions = { + config: { label } + }; - // Get platform admin details - const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); + const tenantDetails = await this.commonService.httpPost( + `${getPlatformAgentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, + createTenantOptions, + { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } } + ); - // Get genesis URLs by ledger Id - const ledgerDetails: ledgers[] = await this.agentServiceRepository.getGenesisUrl(payload.ledgerId); + return tenantDetails; - for (const iterator of ledgerDetails) { + } catch (error) { + this.logger.error(`error in create wallet : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } - // Create tenant in agent controller - const tenantDetails = await this.createTenantAndNotify(payload, iterator, platformAdminSpinnedUp); + /** + * Create did + * @param payload + * @returns did and didDocument + */ + async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { + try { + const agentDetails = await this.agentServiceRepository.getOrgAgentDetails(orgId); + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this.getOrgAgentApiKey(orgId); + } + const getOrgAgentType = await this.agentServiceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + let url; - if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { - this.logger.error(`Platform-admin agent is not spun-up`); - throw new NotFoundException( - ResponseMessages.agent.error.platformAdminNotAbleToSpinp, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } + if (getOrgAgentType.agent === OrgAgentType.DEDICATED) { + url = `${agentDetails.agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; + } else if (getOrgAgentType.agent === OrgAgentType.SHARED) { + url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`; + } + const didDetails = await this.commonService.httpPost(url, payload, + { headers: { 'authorization': apiKey } } + ); + return didDetails; - // Get org agent type details by shared agent - const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); - - // Get agent type details by AFJ agent - const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); - - const storeOrgAgentData: IStoreOrgAgentDetails = { - did: tenantDetails['did'], - verkey: tenantDetails['verkey'], - isDidPublic: true, - agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, - agentsTypeId: agentTypeId, - orgId: payload.orgId, - agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, - orgAgentTypeId, - tenantId: tenantDetails['tenantRecord']['id'], - walletName: payload.label, - ledgerId: payload.ledgerId, - id: agentProcess?.id - }; + } catch (error) { + this.logger.error(`error in create did : ${JSON.stringify(error)}`); - // Get organization data - const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); + if (error?.response?.error?.message) { + throw new RpcException({ + statusCode: error?.response?.status, + error: error?.response?.error?.message + }); + } else { + throw new RpcException(error.response ? error.response : error); + } - this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); - await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); + } +} - this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); + /** + * @returns Secp256k1 key pair for polygon DID + */ + async createSecp256k1KeyPair(orgId:string): Promise { + try { + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( + CommonConstants.PLATFORM_ADMIN_ORG + ); - // Create the legacy connection invitation - this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); + const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); - } - } catch (error) { - this.handleError(error, payload.clientSocketId); + const url = `${getPlatformAgentEndPoint}${CommonConstants.CREATE_POLYGON_SECP256k1_KEY}`; - if (agentProcess && agentProcess?.id) { - this.agentServiceRepository.removeOrgAgent(agentProcess?.id); - } - throw error; - } - } + const createKeyPairResponse = await this.commonService.httpPost(url, {}, { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } }); + return createKeyPairResponse; + } catch (error) { + this.logger.error(`error in createSecp256k1KeyPair : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } private async getPlatformAdminAndNotify(clientSocketId: string | undefined): Promise { const socket = await this.createSocketInstance(); @@ -762,31 +889,79 @@ export class AgentServiceService { * @param platformAdminSpinnedUp * @returns Get tanant status */ - private async createTenantAndNotify(payload: ITenantDto, ledgerIds: ledgers, platformAdminSpinnedUp: IOrgAgentsResponse): Promise { + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async createTenantAndNotify(payload: ITenantDto, platformAdminSpinnedUp: IOrgAgentsResponse): Promise { + const WalletSetupPayload = {...payload}; const socket = await this.createSocketInstance(); - if (payload.clientSocketId) { - socket.emit('agent-spinup-process-initiated', { clientId: payload.clientSocketId }); + if (WalletSetupPayload.clientSocketId) { + socket.emit('agent-spinup-process-initiated', { clientId: WalletSetupPayload.clientSocketId }); } - - const { label, seed, did } = payload; - const createTenantOptions = { - config: { label }, - seed, - did: did ? did : undefined, - method: ledgerIds.indyNamespace + const walletLabel = WalletSetupPayload.label; + + delete WalletSetupPayload.label; + delete WalletSetupPayload.clientSocketId; + delete WalletSetupPayload.orgId; + delete WalletSetupPayload.ledgerId; + + + const walletResponseDetails = await this._createTenantWallet(walletLabel, platformAdminSpinnedUp.org_agents[0].agentEndPoint, platformAdminSpinnedUp.org_agents[0].apiKey); + if (!walletResponseDetails && !walletResponseDetails.id) { + throw new InternalServerErrorException('Error while creating the wallet'); + } + const didCreateOption = { + didPayload: WalletSetupPayload, + agentEndpoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, + apiKey: platformAdminSpinnedUp.org_agents[0].apiKey, + tenantId: walletResponseDetails.id }; + const DIDCreationOption = await this._createDID(didCreateOption); + if (!DIDCreationOption) { + throw new InternalServerErrorException('Error while creating the wallet'); + } + + return {walletResponseDetails, DIDCreationOption}; + } +// + /** + * Create tenant wallet on the agent + * @param createTenantWalletPayload + * @returns Get tanant status + */ - // Invoke an API request from the agent to create multi-tenant agent - const tenantDetails = await this.commonService.httpPost( - `${platformAdminSpinnedUp.org_agents[0].agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, - createTenantOptions, - { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } } - ); - - return tenantDetails; - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async _createTenantWallet(label, endpoint, agentApiKey): Promise { //remove any + + + const createTenantOptions = { + config: { label } + }; + // Invoke an API request from the agent to create multi-tenant agent + const tenantDetails = await this.commonService.httpPost( + `${endpoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, + createTenantOptions, + { headers: { authorization: agentApiKey } } + ); + return tenantDetails; +} + /** + * Create tenant wallet on the agent + * @param _createDID + * @returns Get tanant status + */ + private async _createDID(didCreateOption): Promise { + + const {didPayload, agentEndpoint, apiKey, tenantId} = didCreateOption; + // Invoke an API request from the agent to create multi-tenant agent + const didDetails = await this.commonService.httpPost( + `${agentEndpoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${tenantId}`, + didPayload, + { headers: { authorization: apiKey } } + ); + return didDetails; +} private async createSocketInstance(): Promise { return io(`${process.env.SOCKET_HOST}`, { reconnection: true, @@ -827,9 +1002,9 @@ export class AgentServiceService { issuerId: payload.issuerId }; schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }) + .then(async (schema) => { + return schema; + }) .catch(error => { throw new InternalServerErrorException( ResponseMessages.agent.error.agentDown, @@ -847,10 +1022,10 @@ export class AgentServiceService { issuerId: payload.payload.issuerId }; schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }) - .catch(error => { + .then(async (schema) => { + return schema; + }) + .catch(error => { throw new InternalServerErrorException( ResponseMessages.agent.error.agentDown, { cause: new Error(), description: ResponseMessages.errorMessages.serverError } @@ -871,17 +1046,17 @@ export class AgentServiceService { if (OrgAgentType.DEDICATED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_SCHEMA_BY_ID.replace('#', `${payload.schemaId}`)}`; schemaResponse = await this.commonService.httpGet(url, payload.schemaId) - .then(async (schema) => { - return schema; - }); + .then(async (schema) => { + return schema; + }); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}`.replace('@', `${payload.payload.schemaId}`).replace('#', `${payload.tenantId}`); schemaResponse = await this.commonService.httpGet(url, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }); + .then(async (schema) => { + return schema; + }); } return schemaResponse; } catch (error) { @@ -904,9 +1079,9 @@ export class AgentServiceService { }; credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => { + return credDef; + }); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_CRED_DEF}`.replace('#', `${payload.tenantId}`); @@ -916,9 +1091,9 @@ export class AgentServiceService { issuerId: payload.payload.issuerId }; credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => { + return credDef; + }); } return credDefResponse; @@ -935,16 +1110,16 @@ export class AgentServiceService { if (OrgAgentType.DEDICATED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_CRED_DEF_BY_ID.replace('#', `${payload.credentialDefinitionId}`)}`; credDefResponse = await this.commonService.httpGet(url, payload.credentialDefinitionId) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => { + return credDef; + }); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CRED_DEF}`.replace('@', `${payload.payload.credentialDefinitionId}`).replace('#', `${payload.tenantId}`); credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); + .then(async (credDef) => { + return credDef; + }); } return credDefResponse; } catch (error) { @@ -1134,6 +1309,17 @@ export class AgentServiceService { } } + async getLedgerConfigDetails(user: IUserRequestInterface): Promise { + try { + const getLedgerConfigData = await this.agentServiceRepository.getLedgerConfigByOrgId(); + return getLedgerConfigData; + + } catch (error) { + this.logger.error(`Error in send out of band proof request in agent service : ${JSON.stringify(error)}`); + throw error; + } + } + async sendOutOfBandProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, apiKey: string): Promise { try { const sendProofRequest = await this.commonService @@ -1336,4 +1522,3 @@ export class AgentServiceService { } } - diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index f52152eda..31a649be9 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -1,13 +1,20 @@ import { UserRoleOrgPermsDto } from 'apps/api-gateway/src/dtos/user-role-org-perms.dto'; export interface IAgentSpinupDto { - walletName: string; walletPassword: string; seed: string; orgId?: string; orgName?: string; ledgerId?: string[]; + keyType: string; + domain?: string; + privatekey?: string; + endpoint?: string; + role?: string; + network?: string + endorserDid?: string + method: string; did?: string; agentType?: string; transactionApproval?: boolean; @@ -33,15 +40,42 @@ export interface IOutOfBandCredentialOffer { export interface ITenantDto { label: string; - seed: string; - ledgerId?: string[]; + seed?: string; + keyType: string; + ledgerId: string[]; + domain?: string; + privatekey?: string; + endpoint?: string; + role?: string; + network?: string + endorserDid?: string method: string; orgId: string; did?: string; tenantId?: string; + didDocument?: string; clientSocketId?: string; } +export interface IWallet { + label: string; + orgId: string; + did?: string; + clientSocketId?: string; +} + +export interface IDidCreate { + keyType: KeyType; + seed: string; + domain?: string; + network?: string; + privatekey?: string; + endpoint?: string; + method: string; + did?: string; + role?: string; + endorserDid?: string; +} export interface ITenantSchema { tenantId?: string; attributes: string[]; @@ -122,6 +156,30 @@ export interface IPlatformConfigDto { } export interface IStoreOrgAgentDetails { + id?: string; + clientSocketId?: string; + agentEndPoint?: string; + apiKey?: string; + seed?: string; + keyType?: string; + method?: string; + network?: string; + role?: string; + did?: string; + verkey?: string; + isDidPublic?: boolean; + agentSpinUpStatus?: number; + walletName?: string; + agentsTypeId?: string; + orgId?: string; + agentId?: string; + orgAgentTypeId?: string; + tenantId?: string; + ledgerId?: string[]; + agentType?: string; +} + +export interface IStoreOrgAgent { id?: string; clientSocketId?: string; agentEndPoint?: string; @@ -137,7 +195,7 @@ export interface IStoreOrgAgentDetails { agentId?: string; orgAgentTypeId?: string; tenantId?: string; - ledgerId?: string[]; + ledgerId?: unknown; agentType?: string; } @@ -178,6 +236,14 @@ export interface IOrganizationAgentInterface { } +export interface IPlatformAgent { + seed: string; + keyType: string; + method: string; + network: string; + role: string; + } + export interface IOrgAgentInterface { orgDid: string; verkey: string; @@ -296,8 +362,7 @@ interface IConfig { label: string; walletConfig: IWalletConfig; } - -interface ITenantRecord { +export interface ITenantRecord { _tags: string; metadata: string; id: string; @@ -312,6 +377,7 @@ export interface ICreateTenant { verkey: string; } + export interface IOrgAgent { agentSpinUpStatus: number; } @@ -426,4 +492,22 @@ export interface IValidResponses { orgId?: string; connectionId: string; tenantId: string; + } +interface Ledger { + id: string; + createDateTime: string; + lastChangedDateTime: string; + name: string; + networkType: string; + poolConfig: string; + isActive: boolean; + networkString: string; + registerDIDEndpoint: string; + registerDIDPayload: string; + indyNamespace: string; + networkUrl: string | null; + } + + export interface LedgerListResponse { + response: Ledger[]; } \ No newline at end of file diff --git a/apps/agent-service/src/main.ts b/apps/agent-service/src/main.ts index 15e462947..efafbfbc4 100644 --- a/apps/agent-service/src/main.ts +++ b/apps/agent-service/src/main.ts @@ -32,8 +32,12 @@ async function bootstrap(): Promise { orgName: `${CommonConstants.PLATFORM_ADMIN_ORG}`, platformAdminEmail: process.env.PLATFORM_ADMIN_EMAIL, tenant: true, - ledgerName: [Ledgers.Bcovrin_Testnet, Ledgers.Indicio_Demonet, Ledgers.Indicio_Mainnet, Ledgers.Indicio_Testnet] - }; + ledgerName: [Ledgers.Bcovrin_Testnet, Ledgers.Indicio_Demonet, Ledgers.Indicio_Mainnet, Ledgers.Indicio_Testnet], + keyType: `${CommonConstants.KEYTYPE}`, + method: `${CommonConstants.METHOD}`, + network: `${CommonConstants.NETWORK}`, + role: `${CommonConstants.ROLE}` +}; const agentService = app.get(AgentServiceService); await agentService.walletProvision(agentSpinupPayload, user); diff --git a/apps/agent-service/src/repositories/agent-service.repository.ts b/apps/agent-service/src/repositories/agent-service.repository.ts index 78133e7a2..096eaae29 100644 --- a/apps/agent-service/src/repositories/agent-service.repository.ts +++ b/apps/agent-service/src/repositories/agent-service.repository.ts @@ -1,8 +1,8 @@ import { PrismaService } from '@credebl/prisma-service'; import { Injectable, Logger } from '@nestjs/common'; // eslint-disable-next-line camelcase -import { ledgers, org_agents, organisation, platform_config, user } from '@prisma/client'; -import { ICreateOrgAgent, IStoreOrgAgentDetails, IOrgAgent, IOrgAgentsResponse, IOrgLedgers, IStoreAgent } from '../interface/agent-service.interface'; +import { ledgerConfig, ledgers, org_agents, org_agents_type, organisation, platform_config, user } from '@prisma/client'; +import { ICreateOrgAgent, IOrgAgent, IOrgAgentsResponse, IOrgLedgers, IStoreAgent, IStoreOrgAgentDetails } from '../interface/agent-service.interface'; import { AgentType } from '@credebl/enum/enum'; @Injectable() @@ -26,6 +26,15 @@ export class AgentServiceRepository { } } + async getLedgerConfigByOrgId(): Promise { + try { + const ledgerConfigData = await this.prisma.ledgerConfig.findMany(); + return ledgerConfigData; + } catch (error) { + this.logger.error(`[getGenesisUrl] - get genesis URL: ${JSON.stringify(error)}`); + throw error; + } + } /** * Get genesis url * @param id @@ -107,7 +116,7 @@ export class AgentServiceRepository { } - /** + /** * Store agent details * @param storeAgentDetails * @returns @@ -116,7 +125,7 @@ export class AgentServiceRepository { async storeOrgAgentDetails(storeOrgAgentDetails: IStoreOrgAgentDetails): Promise { try { - return this.prisma.org_agents.update({ + return await this.prisma.org_agents.update({ where: { id: storeOrgAgentDetails.id }, @@ -313,6 +322,23 @@ export class AgentServiceRepository { } } + // eslint-disable-next-line camelcase + async getOrgAgentType(orgAgentId: string): Promise { + try { + const orgAgent = await this.prisma.org_agents_type.findUnique({ + where: { + id: orgAgentId + } + }); + + return orgAgent; + + } catch (error) { + this.logger.error(`[getOrgAgentType] - error: ${JSON.stringify(error)}`); + throw error; + } + } + async getPlatfomAdminUser(platformAdminUserEmail: string): Promise { try { const platformAdminUser = await this.prisma.user.findUnique({ @@ -344,4 +370,4 @@ export class AgentServiceRepository { throw error; } } -} +} \ No newline at end of file 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 52c39c16e..05d28a583 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -32,8 +32,12 @@ 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 { CreateDidDto } from './dto/create-did.dto'; +import { validateDid } from '@credebl/common/did.validator'; +import { CreateWalletDto } from './dto/create-wallet.dto'; const seedLength = 32; + @UseFilters(CustomExceptionFilter) @Controller() @ApiTags('agents') @@ -75,6 +79,29 @@ export class AgentController { } + @Get('/orgs/agents/ledgerConfig') + @ApiOperation({ + summary: 'Get the ledger config details', + description: 'Get the ledger config details' + }) + @UseGuards(AuthGuard('jwt')) + async getLedgerDetails( + @User() reqUser: user, + @Res() res: Response + ): Promise { + + const ledgerConfigData = await this.agentService.getLedgerConfig(reqUser); + + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: ResponseMessages.agent.success.ledgerConfig, + data: ledgerConfigData + }; + + return res.status(HttpStatus.OK).json(finalResponse); + + } + /** * Spinup the agent by organization * @param agentSpinupDto @@ -96,14 +123,6 @@ export class AgentController { @Res() res: Response ): Promise { - if (seedLength !== agentSpinupDto.seed.length) { - this.logger.error(`seed must be at most 32 characters.`); - throw new BadRequestException( - ResponseMessages.agent.error.seedChar, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); - } - const regex = new RegExp('^[a-zA-Z0-9]+$'); if (!regex.test(agentSpinupDto.walletName)) { @@ -154,14 +173,6 @@ export class AgentController { createTenantDto.orgId = orgId; - if (seedLength !== createTenantDto.seed.length) { - this.logger.error(`seed must be at most 32 characters`); - throw new BadRequestException( - ResponseMessages.agent.error.seedCharCount, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); - } - const tenantDetails = await this.agentService.createTenant(createTenantDto, user); const finalResponse: IResponse = { @@ -172,4 +183,103 @@ export class AgentController { return res.status(HttpStatus.CREATED).json(finalResponse); } + + /** + * Create wallet + * @param orgId + * @returns wallet + */ + @Post('/orgs/:orgId/agents/createWallet') + @ApiOperation({ + summary: 'Create wallet', + description: 'Create wallet' + }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createWallet( + @Param('orgId') orgId: string, + @Body() createWalletDto: CreateWalletDto, + @User() user: user, + @Res() res: Response + ): Promise { + + createWalletDto.orgId = orgId; + const walletDetails = await this.agentService.createWallet(createWalletDto, user); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.agent.success.createWallet, + data: walletDetails + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + // This function will be used after multiple did method implementation in create wallet + /** + * Create did + * @param orgId + * @returns did + */ + @Post('/orgs/:orgId/agents/createDid') + @ApiOperation({ + summary: 'Create did', + description: 'Create did' + }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createDid( + @Param('orgId') orgId: string, + @Body() createDidDto: CreateDidDto, + @User() user: user, + @Res() res: Response + ): Promise { + + validateDid(createDidDto); + + if (seedLength !== createDidDto.seed.length) { + this.logger.error(`seed must be at most 32 characters.`); + throw new BadRequestException( + ResponseMessages.agent.error.seedChar, + { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } + ); + } + + const didDetails = await this.agentService.createDid(createDidDto, orgId, user); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.agent.success.createDid, + data: didDetails + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + /** + * Create Secp256k1 key pair for polygon DID + * @param orgId + * @returns Secp256k1 key pair for polygon DID + */ + @Post('/orgs/:orgId/agents/polygon/create-keys') + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.PLATFORM_ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createSecp256k1KeyPair( + @Param('orgId') orgId: string, + @Res() res: Response + ): Promise { + + const didDetails = await this.agentService.createSecp256k1KeyPair(orgId); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.agent.success.createKeys, + data: didDetails + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } } \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/agent-service.service.ts b/apps/api-gateway/src/agent-service/agent-service.service.ts index 417512bed..48cead6c9 100644 --- a/apps/api-gateway/src/agent-service/agent-service.service.ts +++ b/apps/api-gateway/src/agent-service/agent-service.service.ts @@ -4,8 +4,10 @@ import { user } from '@prisma/client'; import { BaseService } from 'libs/service/base.service'; import { AgentSpinupDto } from './dto/agent-service.dto'; import { CreateTenantDto } from './dto/create-tenant.dto'; -import { AgentSpinUpSatus } from './interface/agent-service.interface'; +import { AgentSpinUpSatus, IWalletRecord } from './interface/agent-service.interface'; import { AgentStatus } from './interface/agent-service.interface'; +import { CreateDidDto } from './dto/create-did.dto'; +import { CreateWalletDto } from './dto/create-wallet.dto'; @Injectable() export class AgentService extends BaseService { @@ -35,6 +37,19 @@ export class AgentService extends BaseService { return this.sendNatsMessage(this.agentServiceProxy, 'create-tenant', payload); } + async createDid(createDidDto: CreateDidDto, orgId:string, user: user): Promise { + const payload = { createDidDto, orgId, user }; + + // NATS call + return this.sendNatsMessage(this.agentServiceProxy, 'create-did', payload); + } + + async createWallet(createWalletDto: CreateWalletDto, user: user): Promise { + const payload = { createWalletDto, user }; + // NATS call + return this.sendNatsMessage(this.agentServiceProxy, 'create-wallet', payload); + } + async getAgentHealth(user: user, orgId:string): Promise { const payload = { user, orgId }; @@ -43,4 +58,18 @@ export class AgentService extends BaseService { } -} + async getLedgerConfig(user: user): Promise { + const payload = { user }; + + // NATS call + return this.sendNatsMessage(this.agentServiceProxy, 'get-ledger-config', payload); + } + + async createSecp256k1KeyPair(orgId:string): Promise { + const payload = {orgId}; + // NATS call + + return this.sendNatsMessage(this.agentServiceProxy, 'polygon-create-keys', payload); + } + +} \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts b/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts index a668a0025..b0cc851d2 100644 --- a/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts @@ -1,19 +1,19 @@ import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsBoolean, IsNotEmpty, IsOptional, IsString, Matches, MaxLength, MinLength, IsArray } from 'class-validator'; +import { IsBoolean, IsNotEmpty, IsOptional, IsString, Matches, MaxLength, MinLength } from 'class-validator'; +import { CreateDidDto } from './create-did.dto'; const regex = /^[a-zA-Z0-9 ]*$/; -export class AgentSpinupDto { +export class AgentSpinupDto extends CreateDidDto { @ApiProperty() + @MaxLength(25, { message: 'Maximum length for wallet must be 25 characters.' }) + @IsString({ message: 'label must be in string format.' }) @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'walletName is required' }) - @MinLength(2, { message: 'walletName must be at least 2 characters.' }) - @MaxLength(50, { message: 'walletName must be at most 50 characters.' }) - @IsString({ message: 'walletName must be in string format.' }) + @MinLength(2, { message: 'Minimum length for wallet name must be 2 characters.' }) @Matches(regex, { message: 'Wallet name must not contain special characters.' }) @Matches(/^\S*$/, { - message: 'Spaces are not allowed in wallet name' + message: 'Spaces are not allowed in label' }) walletName: string; @@ -23,31 +23,12 @@ export class AgentSpinupDto { @IsNotEmpty({ message: 'Password is required.' }) walletPassword: string; - @ApiProperty({ example: 'dfuhgfklskmjngrjekjfgjjfkoekfdad' }) - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'seed is required' }) - @MaxLength(32, { message: 'seed must be at most 32 characters.' }) - @IsString({ message: 'seed must be in string format.' }) - @Matches(/^\S*$/, { - message: 'Spaces are not allowed in seed' - }) - seed: string; - @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) @IsOptional() @ApiPropertyOptional() @IsString({ message: 'did must be in string format.' }) did?: string; - @ApiProperty({ example: ['6ba7b810-9dad-11d1-80b4-00c04fd430c8'] }) - @IsOptional() - @ApiPropertyOptional() - @IsArray({ message: 'ledgerId must be an array' }) - @IsString({ each: true, message: 'Each ledgerId must be a string' }) - @MaxLength(36, { each: true, message: 'ledgerId must be at most 36 characters.' }) - @IsNotEmpty({ message: 'please provide valid ledgerId' }) - ledgerId?: string[]; - @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) @IsOptional() @ApiPropertyOptional() diff --git a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts new file mode 100644 index 000000000..7f75116ec --- /dev/null +++ b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts @@ -0,0 +1,70 @@ +import { trim } from '@credebl/common/cast.helper'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { MaxLength, IsString, Matches, IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreateDidDto { + + @ApiProperty({ example: '000000000000000000000000000Seed1' }) + @MaxLength(32, { message: 'seed must be at most 32 characters.' }) + @Transform(({ value }) => trim(value)) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'seed must be in string format.' }) + @Matches(/^\S*$/, { + message: 'Spaces are not allowed in seed' + }) + seed?: string; + + @ApiProperty({ example: 'ed25519'}) + @IsNotEmpty({ message: 'key type is required' }) + @IsString({ message: 'key type be in string format.' }) + keyType: string; + + @ApiProperty({ example: 'indy'}) + @IsNotEmpty({ message: 'seed is required' }) + @IsString({ message: 'did must be in string format.' }) + method: string; + + @ApiProperty({example: 'bcovrin:testnet'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'network must be in string format.' }) + network?: string; + + @ApiProperty({example: 'www.github.com'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'domain must be in string format.' }) + domain?: string; + + @ApiProperty({example: 'endorser'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'role must be in string format.' }) + role?: string; + + @ApiProperty({example: ''}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'private key must be in string format.' }) + privatekey?: string; + + @ApiProperty({example: 'http://localhost:6006/docs'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'endpoint must be in string format.' }) + endpoint?: string; + + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'did must be in string format.' }) + did?: string; + + @ApiProperty({example: 'did:indy:bcovrin:testnet:UEeW111G1tYo1nEkPwMcF'}) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'endorser did must be in string format.' }) + endorserDid?: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts index 4ee5d3388..72fb459ea 100644 --- a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts @@ -1,9 +1,10 @@ import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { MaxLength, IsString, MinLength, Matches, IsNotEmpty, IsOptional, IsArray, IsUUID } from 'class-validator'; +import { MaxLength, IsString, MinLength, Matches, IsOptional } from 'class-validator'; +import { CreateDidDto } from './create-did.dto'; const labelRegex = /^[a-zA-Z0-9 ]*$/; -export class CreateTenantDto { +export class CreateTenantDto extends CreateDidDto { @ApiProperty() @MaxLength(25, { message: 'Maximum length for label must be 25 characters.' }) @IsString({ message: 'label must be in string format.' }) @@ -15,37 +16,18 @@ export class CreateTenantDto { }) label: string; - @ApiProperty({ example: 'dfuhgfklskmjngrjekjfgjjfkoekfdad' }) - @MaxLength(32, { message: 'seed must be at most 32 characters.' }) - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'seed is required' }) - @IsString({ message: 'seed must be in string format.' }) - @Matches(/^\S*$/, { - message: 'Spaces are not allowed in seed' - }) - seed: string; - - @ApiProperty({ example: ["b942473d-6fdd-4a38-b76e-a3314fca66b6"] }) - @ApiPropertyOptional() - @IsOptional() - @IsUUID(4, {each:true, message: "Please provide valid ledgerId"}) - @IsArray({ message: 'ledgerId must be an array' }) - @IsNotEmpty({ message: 'please provide valid ledgerId' }) - @IsString({ each: true, message: 'Each ledgerId must be a string' }) - @MaxLength(36, { each: true, message: 'ledgerId must be at most 36 characters.' }) - ledgerId?: string[]; - - @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) @IsOptional() @ApiPropertyOptional() @IsString({ message: 'did must be in string format.' }) - did?: string; + clientSocketId?: string; - @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) @IsOptional() @ApiPropertyOptional() @IsString({ message: 'did must be in string format.' }) - clientSocketId?: string; + did?: string; orgId: string; + } \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/dto/create-wallet.dto.ts b/apps/api-gateway/src/agent-service/dto/create-wallet.dto.ts new file mode 100644 index 000000000..eec4ac54c --- /dev/null +++ b/apps/api-gateway/src/agent-service/dto/create-wallet.dto.ts @@ -0,0 +1,28 @@ +import { trim } from '@credebl/common/cast.helper'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { MaxLength, IsString, MinLength, IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreateWalletDto { + @ApiProperty() + @MaxLength(25, { message: 'Maximum length for label must be 25 characters.' }) + @IsString({ message: 'label must be in string format.' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'label is required' }) + @MinLength(2, { message: 'Minimum length for label must be 2 characters.' }) + label: string; + + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'did must be in string format.' }) + did?: string; + + @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'did must be in string format.' }) + clientSocketId?: string; + + orgId: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts b/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts index 6ced1ed42..25511321e 100644 --- a/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts +++ b/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts @@ -5,4 +5,22 @@ export interface AgentStatus { label: string; endpoints: string[]; isInitialized: boolean; +} +interface IWalletConfig { + id: string; + key: string; + keyDerivationMethod: string; +} + +interface IConfig { + label: string; + walletConfig: IWalletConfig; +} +export interface IWalletRecord { + _tags: string; + metadata: string; + id: string; + createdAt: string; + config: IConfig; + updatedAt: string; } \ No newline at end of file diff --git a/apps/api-gateway/src/authz/authz.controller.ts b/apps/api-gateway/src/authz/authz.controller.ts index 36d2fda25..f93d61af4 100644 --- a/apps/api-gateway/src/authz/authz.controller.ts +++ b/apps/api-gateway/src/authz/authz.controller.ts @@ -27,6 +27,7 @@ import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler import { ResetPasswordDto } from './dtos/reset-password.dto'; import { ForgotPasswordDto } from './dtos/forgot-password.dto'; import { ResetTokenPasswordDto } from './dtos/reset-token-password'; +import { RefreshTokenDto } from './dtos/refresh-token.dto'; @Controller('auth') @@ -176,5 +177,24 @@ export class AuthzController { } + @Post('/refresh-token') + @ApiOperation({ + summary: 'Token from refresh token', + description: 'Get a new token from a refresh token' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + async refreshToken( + @Body() refreshTokenDto: RefreshTokenDto, + @Res() res: Response): Promise { + const tokenData = await this.authzService.refreshToken(refreshTokenDto.refreshToken); + const finalResponse: IResponseType = { + statusCode: HttpStatus.OK, + message: ResponseMessages.user.success.refreshToken, + data: tokenData + }; + + return res.status(HttpStatus.OK).json(finalResponse); + + } } \ No newline at end of file diff --git a/apps/api-gateway/src/authz/authz.service.ts b/apps/api-gateway/src/authz/authz.service.ts index 8cb6b7d01..7c58d0f7a 100644 --- a/apps/api-gateway/src/authz/authz.service.ts +++ b/apps/api-gateway/src/authz/authz.service.ts @@ -58,6 +58,10 @@ export class AuthzService extends BaseService { return this.sendNatsMessage(this.authServiceProxy, 'user-set-token-password', resetTokenPasswordDto); } + async refreshToken(refreshToken: string): Promise { + return this.sendNatsMessage(this.authServiceProxy, 'refresh-token-details', refreshToken); + } + async addUserDetails(userInfo: AddUserDetailsDto): Promise { const payload = { userInfo }; return this.sendNatsMessage(this.authServiceProxy, 'add-user', payload); diff --git a/apps/api-gateway/src/authz/dtos/refresh-token.dto.ts b/apps/api-gateway/src/authz/dtos/refresh-token.dto.ts new file mode 100644 index 000000000..e9ad0f6fa --- /dev/null +++ b/apps/api-gateway/src/authz/dtos/refresh-token.dto.ts @@ -0,0 +1,14 @@ +import { IsNotEmpty } from 'class-validator'; + +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { trim } from '@credebl/common/cast.helper'; + +export class RefreshTokenDto { + + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'refreshToken is required.' }) + refreshToken: string; + +} \ No newline at end of file diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index 5b2998dc1..8938e62b4 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -40,7 +40,8 @@ export class ConnectionService extends BaseService { goal: connectionDto.goal, handshake: connectionDto.handshake, handshakeProtocols: connectionDto.handshakeProtocols, - user + user, + recipientKey:connectionDto.recipientKey }; return this.sendNatsMessage(this.connectionServiceProxy, 'create-connection', connectionDetails); diff --git a/apps/api-gateway/src/connection/dtos/connection.dto.ts b/apps/api-gateway/src/connection/dtos/connection.dto.ts index 6ac0f90fc..63afc50ee 100644 --- a/apps/api-gateway/src/connection/dtos/connection.dto.ts +++ b/apps/api-gateway/src/connection/dtos/connection.dto.ts @@ -60,6 +60,12 @@ export class CreateConnectionDto { handshakeProtocols: string[]; orgId: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + @IsNotEmpty({ message: 'Please provide recipientKey' }) + recipientKey: string; } export class ConnectionDto { diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 390d402fc..20c1ba466 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -191,6 +191,12 @@ class CredentialsIssuanceDto { @IsOptional() credentialType:IssueCredentialType; + @ApiPropertyOptional({ default: true }) + @IsOptional() + @IsNotEmpty({ message: 'please provide valid value for reuseConnection' }) + @IsBoolean({ message: 'reuseConnection must be a boolean' }) + reuseConnection?: boolean; + orgId: string; } diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index e97e26ad7..934cc80c0 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -31,10 +31,10 @@ export class IssuanceService extends BaseService { }> { let payload; if (IssueCredentialType.INDY === issueCredentialDto.credentialType) { - payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl }; + payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl, reuseConnection : issueCredentialDto.reuseConnection }; } if (IssueCredentialType.JSONLD === issueCredentialDto.credentialType) { - payload = { credential: issueCredentialDto.credential, options: issueCredentialDto.options, comment: issueCredentialDto.comment, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl }; + payload = { credential: issueCredentialDto.credential, options: issueCredentialDto.options, comment: issueCredentialDto.comment, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, isShortenUrl: issueCredentialDto.isShortenUrl, reuseConnection : issueCredentialDto.reuseConnection }; } return this.sendNats(this.issuanceProxy, 'send-credential-create-offer-oob', payload); diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 576d05854..ac7f8d705 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -332,5 +332,12 @@ export class SendProofRequestPayload { @IsOptional() @IsNotEmpty({message:'Please provide the flag for shorten url.'}) isShortenUrl?: boolean; + + @ApiPropertyOptional({ default: true }) + @IsOptional() + @IsNotEmpty({ message: 'please provide valid value for reuseConnection' }) + @IsBoolean({ message: 'reuseConnection must be a boolean' }) + reuseConnection?: boolean; + } diff --git a/apps/connection/src/connection.repository.ts b/apps/connection/src/connection.repository.ts index d28b6faf3..2b7034878 100644 --- a/apps/connection/src/connection.repository.ts +++ b/apps/connection/src/connection.repository.ts @@ -48,7 +48,8 @@ export class ConnectionRepository { async saveAgentConnectionInvitations( connectionInvitation: string, agentId: string, - orgId: string + orgId: string, + recipientKey: string // eslint-disable-next-line camelcase ): Promise { try { @@ -57,7 +58,8 @@ export class ConnectionRepository { orgId: String(orgId), agentId, connectionInvitation, - multiUse: true + multiUse: true, + recipientKey } }); return agentDetails; diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index dc030a186..7bf0a43e1 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -51,7 +51,8 @@ export class ConnectionService { goal, goalCode, handshake, - handshakeProtocols + handshakeProtocols, + recipientKey } = payload; try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); @@ -72,7 +73,8 @@ export class ConnectionService { goal: goal || undefined, goalCode: goalCode || undefined, handshake: handshake || undefined, - handshakeProtocols: handshakeProtocols || undefined + handshakeProtocols: handshakeProtocols || undefined, + recipientKey:recipientKey || undefined }; const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -83,18 +85,19 @@ export class ConnectionService { apiKey = await this._getOrgAgentApiKey(orgId); } const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); + const connectionInvitationUrl: string = createConnectionInvitation?.message?.invitationUrl; const shortenedUrl: string = await this.storeConnectionObjectAndReturnUrl( connectionInvitationUrl, connectionPayload.multiUseInvitation ); - + const recipientsKey = createConnectionInvitation?.message?.recipientKey || recipientKey; const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, agentId, - orgId + orgId, + recipientsKey ); - const connectionDetailRecords: ConnectionResponseDetail = { id: saveConnectionDetails.id, orgId: saveConnectionDetails.orgId, @@ -105,7 +108,8 @@ export class ConnectionService { createdBy: saveConnectionDetails.createdBy, lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, lastChangedBy: saveConnectionDetails.lastChangedBy, - recordId: createConnectionInvitation.message.outOfBandRecord.id + recordId: createConnectionInvitation.message.outOfBandRecord.id, + recipientKey:saveConnectionDetails.recipientKey }; return connectionDetailRecords; } catch (error) { diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index c47ede3a5..b49d6ea9e 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -15,6 +15,7 @@ export interface IConnection { handshake: string; handshakeProtocols: string[]; orgId: string; + recipientKey?: string; } export interface IUserRequestInterface { userId: string; @@ -264,4 +265,5 @@ export interface ConnectionResponseDetail { lastChangedDateTime: Date; lastChangedBy: number; recordId: string; + recipientKey:string; } diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index 01de77bf6..06f0d1017 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -868,6 +868,18 @@ export class EcosystemRepository { // eslint-disable-next-line camelcase async deleteInvitations(invitationId: string): Promise { try { + + const findInvitation = await this.prisma.ecosystem_invitations.findUnique({ + where: { + id: invitationId, + status: EcosystemInvitationStatus.PENDING + } + }); + + if (!findInvitation) { + throw new NotFoundException('Ecosystem Invitation not found'); + } + const deletedInvitation = await this.prisma.ecosystem_invitations.delete({ where: { id: invitationId, diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 3b8307f22..050327509 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -3,6 +3,7 @@ import { Injectable, InternalServerErrorException, Logger, NotFoundException } f import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase import { + agent_invitations, credentials, file_data, file_upload, @@ -70,6 +71,23 @@ export class IssuanceRepository { } } + + async getRecipientKeyByOrgId(orgId: string): Promise { + try { + return this.prisma.agent_invitations.findMany({ + where: { + orgId + }, + orderBy: { + createDateTime: 'asc' + } + }); + } catch (error) { + this.logger.error(`Error in getRecipientKey in issuance repository: ${error.message}`); + throw error; + } + } + async getAllIssuedCredentials( user: IUserRequest, orgId: string, diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index c152c8a65..b4279d327 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -32,7 +32,7 @@ import { io } from 'socket.io-client'; import { IIssuedCredentialSearchParams, IssueCredentialType } from 'apps/api-gateway/src/issuance/interfaces'; import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto'; -import { organisation } from '@prisma/client'; +import { agent_invitations, organisation } from '@prisma/client'; @Injectable() @@ -150,7 +150,7 @@ export class IssuanceService { async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { - const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, credential, options, credentialType, isShortenUrl } = payload; + const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, credential, options, credentialType, isShortenUrl, reuseConnection } = payload; if (credentialType === IssueCredentialType.INDY) { const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( credentialDefinitionId @@ -181,8 +181,14 @@ export class IssuanceService { } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); - // eslint-disable-next-line camelcase - + let recipientKey: string | undefined; + if (true === reuseConnection) { + const data: agent_invitations[] = await this.issuanceRepository.getRecipientKeyByOrgId(orgId); + if (data && 0 < data.length) { + const [firstElement] = data; + recipientKey = firstElement?.recipientKey ?? undefined; + } + } const { agentEndPoint, organisation } = agentDetails; if (!agentDetails) { @@ -217,7 +223,8 @@ export class IssuanceService { willConfirm: payload.willConfirm || undefined, imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, label: organisation?.name, - comment: comment || '' + comment: comment || '', + recipientKey:recipientKey || undefined }; } @@ -237,7 +244,8 @@ export class IssuanceService { willConfirm: payload.willConfirm || undefined, imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, label: organisation?.name, - comment: comment || '' + comment: comment || '', + recipientKey:recipientKey || undefined }; } const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 7f799e52c..94c34f666 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -1,5 +1,6 @@ /* eslint-disable camelcase */ import { + BadRequestException, ConflictException, HttpException, Inject, diff --git a/apps/user/src/user.controller.ts b/apps/user/src/user.controller.ts index 802b7b150..76f995980 100644 --- a/apps/user/src/user.controller.ts +++ b/apps/user/src/user.controller.ts @@ -44,6 +44,11 @@ export class UserController { return loginRes; } + @MessagePattern({ cmd: 'refresh-token-details' }) + async refreshTokenDetails(refreshToken: string): Promise { + return this.userService.refreshTokenDetails(refreshToken); + } + @MessagePattern({ cmd: 'user-reset-password' }) async resetPassword(payload: IUserResetPassword): Promise { return this.userService.resetPassword(payload); diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index dbbac2214..c4a3e589b 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -374,6 +374,23 @@ export class UserService { } } + async refreshTokenDetails(refreshToken: string): Promise { + + try { + try { + const tokenResponse = await this.clientRegistrationService.getAccessToken(refreshToken); + return tokenResponse; + } catch (error) { + throw new BadRequestException(ResponseMessages.user.error.invalidRefreshToken); + } + + } catch (error) { + this.logger.error(`In refreshTokenDetails : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + + } + } + async updateFidoVerifiedUser(email: string, isFidoVerified: boolean, password: string): Promise { if (isFidoVerified) { await this.userRepository.addUserPassword(email.toLowerCase(), password); diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 2a3397514..f883e49c2 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -145,8 +145,10 @@ export interface ISendProofRequestPayload { willConfirm?: boolean; imageUrl?: string; isShortenUrl?: boolean; - type?:string; - presentationDefinition?:IProofRequestPresentationDefinition; + type?:string; + presentationDefinition?:IProofRequestPresentationDefinition; + reuseConnection?: boolean; + recipientKey?:string; } export interface IWSendProofRequestPayload { diff --git a/apps/verification/src/repositories/verification.repository.ts b/apps/verification/src/repositories/verification.repository.ts index 31b32be28..b64b24145 100644 --- a/apps/verification/src/repositories/verification.repository.ts +++ b/apps/verification/src/repositories/verification.repository.ts @@ -2,7 +2,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { PrismaService } from '@credebl/prisma-service'; import { Injectable, Logger, NotFoundException } from '@nestjs/common'; // eslint-disable-next-line camelcase -import { org_agents, organisation, platform_config, presentations } from '@prisma/client'; +import { agent_invitations, org_agents, organisation, platform_config, presentations } from '@prisma/client'; import { IProofPresentation } from '../interfaces/verification.interface'; import { IProofRequestSearchCriteria } from '../interfaces/verification.interface'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; @@ -185,4 +185,21 @@ export class VerificationRepository { throw error; } } + + // eslint-disable-next-line camelcase + async getRecipientKeyByOrgId(orgId: string): Promise { + try { + return this.prisma.agent_invitations.findMany({ + where: { + orgId + }, + orderBy: { + createDateTime: 'asc' // or 'desc' for descending order + } + }); + } catch (error) { + this.logger.error(`Error in getRecipientKey in verification repository: ${error.message}`); + throw error; + } + } } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index b00c7e2f1..5b5b0e222 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -5,7 +5,7 @@ import { map } from 'rxjs/operators'; import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IPresentationExchangeProofRequestPayload} from './interfaces/verification.interface'; import { VerificationRepository } from './repositories/verification.repository'; import { CommonConstants } from '@credebl/common/common.constant'; -import { org_agents, organisation, presentations } from '@prisma/client'; +import { agent_invitations, org_agents, organisation, presentations } from '@prisma/client'; import { OrgAgentType } from '@credebl/enum/enum'; import { ResponseMessages } from '@credebl/common/response-messages'; import * as QRCode from 'qrcode'; @@ -358,13 +358,22 @@ export class VerificationService { apiKey = await this._getOrgAgentApiKey(user.orgId); } - const { isShortenUrl, type, ...updateOutOfBandRequestProof } = outOfBandRequestProof; + const { isShortenUrl, type, reuseConnection, ...updateOutOfBandRequestProof } = outOfBandRequestProof; + let recipientKey: string | undefined; + if (true === reuseConnection) { + const data: agent_invitations[] = await this.verificationRepository.getRecipientKeyByOrgId(user.orgId); + if (data && 0 < data.length) { + const [firstElement] = data; + recipientKey = firstElement?.recipientKey ?? undefined; + } + } outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; let payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload; if (ProofRequestType.INDY === type) { updateOutOfBandRequestProof.protocolVersion = updateOutOfBandRequestProof.protocolVersion || 'v1'; + updateOutOfBandRequestProof.recipientKey = recipientKey || undefined; payload = { apiKey, url, @@ -389,7 +398,8 @@ export class VerificationService { } } }, - autoAcceptProof:outOfBandRequestProof.autoAcceptProof || 'always' + autoAcceptProof:outOfBandRequestProof.autoAcceptProof || 'always', + recipientKey:recipientKey || undefined } }; } diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 5d578b520..7c3b0ede9 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -786,10 +786,6 @@ export class ClientRegistrationService { payload.refresh_token = refreshToken; payload.client_secret = process.env.KEYCLOAK_MANAGEMENT_CLIENT_SECRET; - - this.logger.log(`access Token for platform Payload: ${JSON.stringify(payload)}`); - - if ( 'refresh_token' !== payload.grant_type || !payload.client_id || @@ -800,8 +796,6 @@ export class ClientRegistrationService { throw new Error('Invalid inputs while getting token.'); } - const strURL = await this.keycloakUrlService.GetSATURL('credebl-platform'); - this.logger.log(`getToken URL: ${strURL}`); const config = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' @@ -809,14 +803,16 @@ export class ClientRegistrationService { }; const tokenResponse = await this.commonService.httpPost( - await this.keycloakUrlService.GetSATURL('credebl-platform'), + await this.keycloakUrlService.GetSATURL(process.env.KEYCLOAK_REALM), qs.stringify(payload) , config); return tokenResponse; } catch (error) { - + this.logger.error( + `Error in getAccessToken ${JSON.stringify(error)}` + ); throw error; } } diff --git a/libs/common/src/cast.helper.ts b/libs/common/src/cast.helper.ts index 4b886c1ef..d92cb2a59 100644 --- a/libs/common/src/cast.helper.ts +++ b/libs/common/src/cast.helper.ts @@ -44,4 +44,15 @@ export function toNumber(value: string, opts: ToNumberOptions = {}): number { } return newValue; +} + +export function ledgerName(value: string): string { + let network; + network = value.replace(":", " "); + network = network.charAt(0).toUpperCase() + network.slice(1); + const words = network.split(" "); + network = `${words[0]} ${words[1].charAt(0).toUpperCase()}${words[1].slice(1)}`; + + return network; + } \ No newline at end of file diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index 14f7ddc02..b93f0a0d7 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -86,6 +86,7 @@ export enum CommonConstants { // SHARED AGENT URL_SHAGENT_CREATE_TENANT = '/multi-tenancy/create-tenant', + URL_SHAGENT_CREATE_DID = '/multi-tenancy/create-did/', URL_SHAGENT_WITH_TENANT_AGENT = '/multi-tenancy/with-tenant-agent', URL_SHAGENT_CREATE_SCHEMA = '/multi-tenancy/schema/#', URL_SHAGENT_GET_SCHEMA = '/multi-tenancy/schema/@/#', @@ -122,10 +123,13 @@ export enum CommonConstants { // server or agent URL_SERVER_STATUS = '/status', URL_AGENT_WRITE_DID = '/dids/write', - URL_AGENT_GET_DID = '/dids/#', - URL_AGENT_GET_DIDS = '/dids', + URL_AGENT_GET_DID = '/dids', URL_AGENT_GET_ENDPOINT = '/agent', + // CREATE KEYS + CREATE_POLYGON_SECP256k1_KEY = '/polygon/create-keys', + + // ENTITY NAMES ENTITY_NAME_TEMPLATE = 'templates', ENTITY_NAME_CRED_DEF = 'credential_definition', @@ -301,6 +305,12 @@ export enum CommonConstants { TRANSACTION_MULTITENANT_SIGN = '/multi-tenancy/transactions/endorse/#', TRANSACTION_MULTITENANT_SUMBIT = '/multi-tenancy/transactions/write/#', + // Static values to up platform Agent + SEED = '101111110111101100111100000Seed1', + KEYTYPE = 'ed25519', + METHOD = 'indy', + NETWORK = 'bcovrin:testnet', + ROLE = 'endorser', //CacheInfo CACHE_APIKEY_KEY = "apiKey", @@ -762,4 +772,4 @@ export const DISALLOWED_EMAIL_DOMAIN = [ 'zapto.org', 'ze.cx', 'zeroe.ml' -]; +]; \ No newline at end of file diff --git a/libs/common/src/did.validator.ts b/libs/common/src/did.validator.ts new file mode 100644 index 000000000..2537cb48a --- /dev/null +++ b/libs/common/src/did.validator.ts @@ -0,0 +1,27 @@ +import { DidMethod, KeyType } from "@credebl/enum/enum"; +import { IDidCreate } from "./interfaces/did.interface"; + +export function validateDid(createDid: IDidCreate): string[] { + const errors: string[] = []; + + if (DidMethod.WEB && !createDid.domain) { + errors.push('domain is required for Web method'); + } else if (DidMethod.INDY && !createDid.network) { + errors.push('network is required for Indy method'); + } else if (DidMethod.INDY && createDid.keyType !== KeyType.Ed25519) { + errors.push('Only ed25519 key type is supported for Indy method'); + } else if ((createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && + (createDid.keyType !== KeyType.Ed25519 && createDid.keyType !== KeyType.Bls12381g2)) { + errors.push('Only ed25519 and bls12381g2 key type is supported'); + } else if (!createDid.role) { + errors.push('role or endorserDid is required'); + } else if (DidMethod.POLYGON && !createDid.privatekey) { + errors.push('privateKey is required for polygon method'); + } else if (DidMethod.POLYGON && !createDid.endpoint) { + errors.push('endpoint is required for polygon method'); + } else if ((DidMethod.INDY || DidMethod.KEY || DidMethod.WEB) && (!createDid.seed)) { + errors.push('seed is required'); + } + + return errors; +} \ No newline at end of file diff --git a/libs/common/src/interfaces/agent-service.interface.ts b/libs/common/src/interfaces/agent-service.interface.ts index ea63de484..235e00210 100644 --- a/libs/common/src/interfaces/agent-service.interface.ts +++ b/libs/common/src/interfaces/agent-service.interface.ts @@ -10,6 +10,7 @@ export interface InvitationMessage { routingKeys: string[]; }; outOfBandRecord: OutOfBandRecord; + recipientKey?:string }; } diff --git a/libs/common/src/interfaces/connection.interface.ts b/libs/common/src/interfaces/connection.interface.ts index 0d4a34ca5..9ff7c4785 100644 --- a/libs/common/src/interfaces/connection.interface.ts +++ b/libs/common/src/interfaces/connection.interface.ts @@ -30,5 +30,6 @@ export interface IConnectionsListCount { createdBy: number; lastChangedDateTime: Date; lastChangedBy: number; + recipientKey?:string; } \ No newline at end of file diff --git a/libs/common/src/interfaces/did.interface.ts b/libs/common/src/interfaces/did.interface.ts new file mode 100644 index 000000000..8299f06d7 --- /dev/null +++ b/libs/common/src/interfaces/did.interface.ts @@ -0,0 +1,13 @@ +export interface IDidCreate { + keyType: string; + seed?: string; + domain?: string; + network?: string; + privatekey?: string; + endpoint?: string; + method: string; + did?: string; + role?: string; + endorserDid?: string; + didDocument?: object; +} \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index c75109bd6..1aac2c206 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -23,7 +23,8 @@ export const ResponseMessages = { updateUserProfile:'User profile updated successfully', resetPassword: 'Password reset successfully', degreeCertificate: 'Degree Certificate shared successfully', - resetPasswordLink: 'Reset password link has been sent to your mail' + resetPasswordLink: 'Reset password link has been sent to your mail', + refreshToken: 'Token details fetched successfully' }, error: { exists: 'User already exists', @@ -60,7 +61,8 @@ export const ResponseMessages = { resetSamePassword: 'New password should not be the current password', resetPasswordLink: 'Unable to create reset password token', invalidResetLink: 'Invalid or expired reset password link', - invalidAccessToken: 'Authentication failed' + invalidAccessToken: 'Authentication failed', + invalidRefreshToken: 'Invalid refreshToken provided' } }, organisation: { @@ -187,9 +189,13 @@ export const ResponseMessages = { agent: { success: { create: 'Agent process initiated successfully. Please wait', + createWallet: 'Wallet created successfully', + createDid: 'Did created successfully', health: 'Agent health details retrieved successfully.', + ledgerConfig: 'Ledger config details fetched successfully.', webhookUrlRegister:'Webhook Url registered successfully', - getWebhookUrl:'Webhook Url fetched successfully' + getWebhookUrl:'Webhook Url fetched successfully', + createKeys:'Key-pair created successfully' }, error: { exists: 'An agent name is already exist', @@ -214,6 +220,7 @@ export const ResponseMessages = { walletAlreadyProcessing: 'Your wallet is already processing', notAbleToSpinp: 'Agent not able to spun up', platformAdminNotAbleToSpinp: 'Platform admin agent is not spun up', + invalidLedger: 'Invalid ledger name', seedCharCount: 'seed must be at most 32 characters', nullTenantId:'TenantId must not be null', tenantIdNotFound:'TenantId not found', diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 3536f0bbc..047256879 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -8,6 +8,25 @@ export enum AgentType { ACAPY = 'ACAPY' } +export declare enum KeyType { + Ed25519 = 'ed25519', + Bls12381g1g2 = 'bls12381g1g2', + Bls12381g1 = 'bls12381g1', + Bls12381g2 = 'bls12381g2', + X25519 = 'x25519', + P256 = 'p256', + P384 = 'p384', + P521 = 'p521', + K256 = 'k256' +} + +export enum DidMethod { + INDY = 'indy', + KEY = 'key', + WEB = 'web', + POLYGON = 'polygon' +} + export enum Ledgers { Bcovrin_Testnet = 'Bcovrin Testnet', Indicio_Testnet = 'Indicio Testnet', @@ -83,4 +102,4 @@ const transitionMap: { [key in Invitation]: Invitation[] } = { [Invitation.REJECTED]: [] }; -export const transition = (currentStatus: Invitation, nextStatus: Invitation): boolean => (transitionMap[currentStatus].includes(nextStatus)); +export const transition = (currentStatus: Invitation, nextStatus: Invitation): boolean => (transitionMap[currentStatus].includes(nextStatus)); \ No newline at end of file diff --git a/libs/prisma-service/prisma/data/credebl-master-table.json b/libs/prisma-service/prisma/data/credebl-master-table.json index 028c6cfd9..94c5c465c 100644 --- a/libs/prisma-service/prisma/data/credebl-master-table.json +++ b/libs/prisma-service/prisma/data/credebl-master-table.json @@ -129,6 +129,16 @@ "registerDIDEndpoint": "https://selfserve.indiciotech.io/nym", "registerDIDPayload": "", "indyNamespace": "indicio:mainnet" + }, + { + "name": "Polygon Testnet", + "networkType": "testnet", + "poolConfig": "", + "isActive": true, + "networkString": "testnet", + "registerDIDEndpoint": "", + "registerDIDPayload": "", + "indyNamespace": "polygon:testnet" } ], "endorseData": [ @@ -186,5 +196,39 @@ "key": "multiEcosystemSupport", "value": "false" } + ], + "ledgerConfig": [ + { + "name": "indy", + "details": { + "bcovrin": { + "testnet":"did:indy:bcovrin:testnet" + }, + "indicio": { + "testnet":"did:indy:indicio:testnet", + "demonet":"did:indy:indicio:demonet", + "mainnet":"did:indy:indicio:mainnet" + } + } + }, + { + "name": "polygon", + "details": { + "mainnet": "did:polygon:mainnet", + "testnet": "did:polygon:testnet" + } + }, + { + "name": "key", + "details": { + "key": "did:key" + } + }, + { + "name": "web", + "details": { + "web": "did:web" + } + } ] } \ No newline at end of file diff --git a/libs/prisma-service/prisma/migrations/20240223140032_ledger_config/migration.sql b/libs/prisma-service/prisma/migrations/20240223140032_ledger_config/migration.sql new file mode 100644 index 000000000..ed5a71d72 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240223140032_ledger_config/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "ledgerConfig" ( + "id" UUID NOT NULL, + "name" TEXT NOT NULL, + "details" JSONB NOT NULL, + "createDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdBy" TEXT NOT NULL DEFAULT '1', + "lastChangedDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "lastChangedBy" TEXT NOT NULL DEFAULT '1', + "deletedAt" TIMESTAMP(6), + + CONSTRAINT "ledgerConfig_pkey" PRIMARY KEY ("id") +); diff --git a/libs/prisma-service/prisma/migrations/20240312064123_agent_invitation_add_recipient_key/migration.sql b/libs/prisma-service/prisma/migrations/20240312064123_agent_invitation_add_recipient_key/migration.sql new file mode 100644 index 000000000..e74b69d59 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240312064123_agent_invitation_add_recipient_key/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "agent_invitations" ADD COLUMN "recipientKey" TEXT; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 28dcfbf44..908fd8b3b 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -293,6 +293,7 @@ model agent_invitations { lastChangedBy Int @default(1) org_agents org_agents @relation(fields: [agentId], references: [id]) organisation organisation @relation(fields: [orgId], references: [id]) + recipientKey String? } model connections { @@ -486,3 +487,13 @@ model notification { lastChangedBy String @default("1") deletedAt DateTime? @db.Timestamp(6) } +model ledgerConfig { + id String @id @default(uuid()) @db.Uuid + name String + details Json + createDateTime DateTime @default(now()) @db.Timestamptz(6) + createdBy String @default("1") + lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) + lastChangedBy String @default("1") + deletedAt DateTime? @db.Timestamp(6) +} \ No newline at end of file diff --git a/libs/prisma-service/prisma/seed.ts b/libs/prisma-service/prisma/seed.ts index 7c2eea86c..fc4bf8814 100644 --- a/libs/prisma-service/prisma/seed.ts +++ b/libs/prisma-service/prisma/seed.ts @@ -13,7 +13,7 @@ const createPlatformConfig = async (): Promise => { try { const existPlatformAdmin = await prisma.platform_config.findMany(); - if (existPlatformAdmin.length === 0) { + if (0 === existPlatformAdmin.length) { const { platformConfigData } = JSON.parse(configData); const platformConfig = await prisma.platform_config.create({ data: platformConfigData @@ -38,9 +38,9 @@ const createOrgRoles = async (): Promise => { in: roleNames } } - }) + }); - if (existOrgRole.length === 0) { + if (0 === existOrgRole.length) { const orgRoles = await prisma.org_roles.createMany({ data: orgRoleData }); @@ -66,9 +66,9 @@ const createAgentTypes = async (): Promise => { in: agentType } } - }) + }); - if (existAgentType.length === 0) { + if (0 === existAgentType.length) { const agentTypes = await prisma.agents_type.createMany({ data: agentTypeData }); @@ -94,9 +94,9 @@ const createOrgAgentTypes = async (): Promise => { in: orgAgentType } } - }) + }); - if (existAgentType.length === 0) { + if (0 === existAgentType.length) { const orgAgentTypes = await prisma.org_agents_type.createMany({ data: orgAgentTypeData }); @@ -122,9 +122,9 @@ const createPlatformUser = async (): Promise => { where: { email: platformAdminData.email } - }) + }); - if (existPlatformAdminUser.length === 0) { + if (0 === existPlatformAdminUser.length) { const platformUser = await prisma.user.create({ data: platformAdminData }); @@ -152,9 +152,9 @@ const createPlatformOrganization = async (): Promise => { where: { name: platformAdminOrganizationData.name } - }) + }); - if (existPlatformAdminUser.length === 0) { + if (0 === existPlatformAdminUser.length) { const platformOrganization = await prisma.organisation.create({ data: platformAdminOrganizationData }); @@ -220,9 +220,9 @@ const createLedger = async (): Promise => { in: ledgerIndyNamespace } } - }) + }); - if (existLedgerIndyNameSpace.length === 0) { + if (0 === existLedgerIndyNameSpace.length) { const createLedger = await prisma.ledgers.createMany({ data: ledgerData @@ -250,9 +250,9 @@ const createEcosystemRoles = async (): Promise => { in: ecosystemRoleDetails } } - }) + }); - if (existEcosystemRole.length === 0) { + if (0 === existEcosystemRole.length) { const ecosystemRoles = await prisma.ecosystem_roles.createMany({ data: ecosystemRoleData }); @@ -279,10 +279,10 @@ const createEcosystemConfig = async (): Promise => { in: ecosystemConfigKey } } - }) + }); - if (existEcosystemConfig.length === 0) { + if (0 === existEcosystemConfig.length) { const configDetails = await prisma.ecosystem_config.createMany({ data: ecosystemConfigData }); @@ -298,6 +298,30 @@ const createEcosystemConfig = async (): Promise => { } }; +const createLedgerConfig = async (): Promise => { + try { + const { ledgerConfig } = JSON.parse(configData); + + const ledgerConfigList = await prisma.ledgerConfig.findMany(); + + + if (0 === ledgerConfigList.length && ledgerConfig) { + const configDetails = await prisma.ledgerConfig.createMany({ + data: ledgerConfig + }); + + logger.log(configDetails); + } else { + logger.log('Already seeding in ledger config'); + } + + + } catch (e) { + logger.error('An error occurred ecosystem config:', e); + } +}; + + async function main(): Promise { await createPlatformConfig(); @@ -310,7 +334,7 @@ async function main(): Promise { await createLedger(); await createEcosystemRoles(); await createEcosystemConfig(); - + await createLedgerConfig(); } @@ -322,4 +346,4 @@ main() logger.error(`In prisma seed initialize`, e); await prisma.$disconnect(); process.exit(1); - }); + }); \ No newline at end of file From b7386ec8a34fec4324070081c455ffce74b1291c Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 19 Mar 2024 16:41:08 +0530 Subject: [PATCH 193/231] fix: removed unnecessary import Signed-off-by: KulkarniShashank --- apps/agent-service/src/agent-service.service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 4675dd32c..9c64a0566 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -46,10 +46,7 @@ import { IDidCreate, IWallet, ITenantRecord, - IPlatformAgent, - LedgerListResponse, - IOrgLedgers, - IStoreOrgAgent + LedgerListResponse } from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; From ff0f419598d46794676bf312e6615ddbe88e8052 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 19 Mar 2024 16:57:41 +0530 Subject: [PATCH 194/231] fix: removed duplicated code Signed-off-by: KulkarniShashank --- .../src/agent-service.controller.ts | 12 ++--- .../src/agent-service.service.ts | 52 ++++++++----------- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index be4c0b9b4..26d1c066b 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -1,10 +1,8 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { AgentServiceService } from './agent-service.service'; -import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IProofPresentation, IAgentProofRequest, IPresentation, IDidCreate, IWallet, ITenantRecord } from './interface/agent-service.interface'; +import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IAgentProofRequest, IDidCreate, IWallet, ITenantRecord } from './interface/agent-service.interface'; import { user } from '@prisma/client'; -import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; -import { IProofPresentationDetails } from '@credebl/common/interfaces/verification.interface'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; @Controller() @@ -99,7 +97,7 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-get-proof-presentation-by-id' }) - async getProofPresentationById(payload: { url: string; orgId: string }): Promise { + async getProofPresentationById(payload: { url: string; orgId: string }): Promise { return this.agentServiceService.getProofPresentationById(payload.url, payload.orgId); } @@ -114,7 +112,7 @@ export class AgentServiceController { } //DONE @MessagePattern({ cmd: 'agent-verify-presentation' }) - async verifyPresentation(payload: { url: string; orgId: string }): Promise { + async verifyPresentation(payload: { url: string; orgId: string }): Promise { return this.agentServiceService.verifyPresentation(payload.url, payload.orgId); } @@ -125,7 +123,7 @@ export class AgentServiceController { } @MessagePattern({ cmd: 'agent-get-connection-details-by-connectionId' }) - async getConnectionsByconnectionId(payload: { url: string, orgId: string }): Promise { + async getConnectionsByconnectionId(payload: { url: string, orgId: string }): Promise { return this.agentServiceService.getConnectionsByconnectionId(payload.url, payload.orgId); } @@ -156,7 +154,7 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'get-agent-verified-proof-details' }) - async getVerifiedProofDetails(payload: { url: string; orgId: string }): Promise { + async getVerifiedProofDetails(payload: { url: string; orgId: string }): Promise { return this.agentServiceService.getVerifiedProofDetails(payload.url, payload.orgId); } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 9c64a0566..319de2972 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -1216,14 +1216,9 @@ export class AgentServiceService { } } - async getProofPresentationById(url: string, orgId: string): Promise { + async getProofPresentationById(url: string, orgId: string): Promise { try { - const getApiKey = await this.getOrgAgentApiKey(orgId); - const getProofPresentationById = await this.commonService - .httpGet(url, { headers: { authorization: getApiKey } }) - .then(async (response) => response) - .catch((error) => this.handleAgentSpinupStatusErrors(error)); - + const getProofPresentationById = await this.agentCall(url, orgId); return getProofPresentationById; } catch (error) { this.logger.error(`Error in proof presentation by id in agent service : ${JSON.stringify(error)}`); @@ -1261,7 +1256,7 @@ export class AgentServiceService { } } - async verifyPresentation(url: string, orgId: string): Promise { + async verifyPresentation(url: string, orgId: string): Promise { try { const getApiKey = await this.getOrgAgentApiKey(orgId); const verifyPresentation = await this.commonService @@ -1288,15 +1283,10 @@ export class AgentServiceService { } } - async getConnectionsByconnectionId(url: string, orgId: string): Promise { + async getConnectionsByconnectionId(url: string, orgId: string): Promise { try { - const getApiKey = await this.getOrgAgentApiKey(orgId); - const data = await this.commonService - .httpGet(url, { headers: { authorization: getApiKey } }) - .then(async (response) => response) - .catch((error) => this.handleAgentSpinupStatusErrors(error)); - - return data; + const getConnectionsByconnectionId = await this.agentCall(url, orgId); + return getConnectionsByconnectionId; } catch (error) { this.logger.error(`Error in getConnectionsByconnectionId in agent service : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -1373,15 +1363,10 @@ export class AgentServiceService { } } - async getVerifiedProofDetails(url: string, orgId: string): Promise { + async getVerifiedProofDetails(url: string, orgId: string): Promise { try { - const getApiKey = await this.getOrgAgentApiKey(orgId); - const getVerifiedProofData = await this.commonService - .httpGet(url, { headers: { authorization: getApiKey } }) - .then(async (response) => response) - .catch((error) => this.handleAgentSpinupStatusErrors(error)); - - return getVerifiedProofData; + const getVerifiedProofDetails = await this.agentCall(url, orgId); + return getVerifiedProofDetails; } catch (error) { this.logger.error(`Error in get verified proof details in agent service : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -1557,18 +1542,23 @@ export class AgentServiceService { async getQuestionAnswersRecord(url: string, orgId: string): Promise { try { - const getApiKey = await this.getOrgAgentApiKey(orgId); + const getQuestionAnswersRecord = await this.agentCall(url, orgId); + return getQuestionAnswersRecord; + } catch (error) { + this.logger.error(`Error in getQuestionAnswersRecord in agent service : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } - const data = await this.commonService + async agentCall(url: string, orgId: string): Promise { + const getApiKey = await this.getOrgAgentApiKey(orgId); + + const data = await this.commonService .httpGet(url, { headers: { authorization: getApiKey } }) .then(async (response) => response) .catch((error) => this.handleAgentSpinupStatusErrors(error)); - return data; - } catch (error) { - this.logger.error(`Error in getQuestionAnswersRecord in agent service : ${JSON.stringify(error)}`); - throw new RpcException(error.response ? error.response : error); - } + return data; } async natsCall(pattern: object, payload: object): Promise<{ From 9f88539e47d223d71a3cad5cd36b008bf54db190 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 19 Mar 2024 18:03:30 +0530 Subject: [PATCH 195/231] fix: removed duplicated code Signed-off-by: KulkarniShashank --- apps/connection/src/connection.service.ts | 276 +++++++++------------- 1 file changed, 112 insertions(+), 164 deletions(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 712f5fed5..41ad7b317 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -23,7 +23,6 @@ import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IQuestionPayload } from './interfaces/question-answer.interfaces'; -import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; @Injectable() export class ConnectionService { @@ -74,18 +73,18 @@ export class ConnectionService { goalCode: goalCode || undefined, handshake: handshake || undefined, handshakeProtocols: handshakeProtocols || undefined, - recipientKey:recipientKey || undefined + recipientKey: recipientKey || undefined }; const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); const url = await this.getAgentUrl(orgAgentType, agentEndPoint, agentDetails?.tenantId); const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, orgId); - const connectionInvitationUrl: string = createConnectionInvitation?.message?.invitationUrl; - const shortenedUrl: string = await this.storeConnectionObjectAndReturnUrl( + const connectionInvitationUrl = createConnectionInvitation?.response?.invitationUrl; + const shortenedUrl = await this.storeConnectionObjectAndReturnUrl( connectionInvitationUrl, connectionPayload.multiUseInvitation ); - const recipientsKey = createConnectionInvitation?.message?.recipientKey || recipientKey; + const recipientsKey = createConnectionInvitation?.response?.recipientKey || recipientKey; const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, agentId, @@ -102,8 +101,8 @@ export class ConnectionService { createdBy: saveConnectionDetails.createdBy, lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, lastChangedBy: saveConnectionDetails.lastChangedBy, - recordId: createConnectionInvitation.message.outOfBandRecord.id, - recipientKey:saveConnectionDetails.recipientKey + recordId: createConnectionInvitation.response.outOfBandRecord.id, + recipientKey: saveConnectionDetails.recipientKey }; return connectionDetailRecords; } catch (error) { @@ -136,15 +135,16 @@ export class ConnectionService { connectionPayload: object, url: string, orgId: string - ): Promise { + ): Promise<{ + response; + }> { //nats call in agent-service to create an invitation url const pattern = { cmd: 'agent-create-connection-legacy-invitation' }; const payload = { connectionPayload, url, orgId }; try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const message = await this.connectionServiceProxy.send(pattern, payload).toPromise(); - return { message }; + + return this.natsCall(pattern, payload); } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( @@ -250,35 +250,49 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } - let url: string; - if (orgAgentType === OrgAgentType.DEDICATED) { - url = `${agentEndPoint}${CommonConstants.URL_CONN_GET_CONNECTIONS}`; - } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREATEED_INVITATIONS}`.replace( - '#', - agentDetails.tenantId - ); - } else { - throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); - } - - //Create the dynamic URL for Search Criteria - const criteriaParams = []; - if (alias) { criteriaParams.push(`alias=${alias}`); } - if (myDid) { criteriaParams.push(`myDid=${myDid}`); } - if (outOfBandId) { criteriaParams.push(`outOfBandId=${outOfBandId}`); } - if (state) { criteriaParams.push(`state=${state}`); } - if (theirDid) { criteriaParams.push(`theirDid=${theirDid}`); } - if (theirLabel) { criteriaParams.push(`theirLabel=${theirLabel}`); } - - if (0 < criteriaParams.length) { - url += `?${criteriaParams.join('&')}`; - } + let url: string; + if (orgAgentType === OrgAgentType.DEDICATED) { + url = `${agentEndPoint}${CommonConstants.URL_CONN_GET_CONNECTIONS}`; + } else if (orgAgentType === OrgAgentType.SHARED) { + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREATEED_INVITATIONS}`.replace( + '#', + agentDetails.tenantId + ); + } else { + throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); + } + + //Create the dynamic URL for Search Criteria + const criteriaParams = []; + if (alias) { + criteriaParams.push(`alias=${alias}`); + } + if (myDid) { + criteriaParams.push(`myDid=${myDid}`); + } + if (outOfBandId) { + criteriaParams.push(`outOfBandId=${outOfBandId}`); + } + if (state) { + criteriaParams.push(`state=${state}`); + } + if (theirDid) { + criteriaParams.push(`theirDid=${theirDid}`); + } + if (theirLabel) { + criteriaParams.push(`theirLabel=${theirLabel}`); + } + + if (0 < criteriaParams.length) { + url += `?${criteriaParams.join('&')}`; + } const connectionResponse = await this._getAllConnections(url, orgId); return connectionResponse.response; } catch (error) { - this.logger.error(`[getConnectionsFromAgent] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}`); + this.logger.error( + `[getConnectionsFromAgent] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}` + ); throw new RpcException(error.response ? error.response : error); } @@ -293,24 +307,7 @@ export class ConnectionService { try { const pattern = { cmd: 'agent-get-all-connections' }; const payload = { url, orgId }; - return this.connectionServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ({ - response - })) - ) - .toPromise() - .catch((error) => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } catch (error) { this.logger.error( `[_getAllConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}` @@ -323,7 +320,6 @@ export class ConnectionService { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); - // const platformConfig: platform_config = await this.connectionRepository.getPlatformConfigDetails(); const { agentEndPoint } = agentDetails; if (!agentDetails) { @@ -342,7 +338,7 @@ export class ConnectionService { } const createConnectionInvitation = await this._getConnectionsByConnectionId(url, orgId); - return createConnectionInvitation; + return createConnectionInvitation?.response; } catch (error) { this.logger.error(`[getConnectionsById] - error in get connections : ${JSON.stringify(error)}`); @@ -378,50 +374,24 @@ export class ConnectionService { } } - async _getConnectionsByConnectionId(url: string, orgId: string): Promise { + async _getConnectionsByConnectionId( + url: string, + orgId: string + ): Promise<{ + response; + }> { //nats call in agent service for fetch connection details const pattern = { cmd: 'agent-get-connection-details-by-connectionId' }; const payload = { url, orgId }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error( - `[_getConnectionsByConnectionId] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } async _getQuestionAnswersRecord(url: string, orgId: string): Promise { const pattern = { cmd: 'agent-get-question-answer-record' }; const payload = { url, orgId }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error( - `[_getQuestionAnswersRecord] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } - /** * Description: Fetch agent url * @param referenceId @@ -492,14 +462,14 @@ export class ConnectionService { } } - async _getOrgAgentApiKey(orgId: string): Promise { + async _getOrgAgentApiKey(orgId: string): Promise<{ + response: string; + }> { const pattern = { cmd: 'get-org-agent-api-key' }; const payload = { orgId }; try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const message = await this.connectionServiceProxy.send(pattern, payload).toPromise(); - return message; + return this.natsCall(pattern, payload); } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( @@ -539,7 +509,7 @@ export class ConnectionService { } const createConnectionInvitation = await this._receiveInvitationUrl(url, orgId, receiveInvitationUrl); - return createConnectionInvitation; + return createConnectionInvitation.response; } catch (error) { this.logger.error(`[receiveInvitationUrl] - error in receive invitation url : ${JSON.stringify(error)}`); @@ -559,25 +529,12 @@ export class ConnectionService { url: string, orgId: string, receiveInvitationUrl: IReceiveInvitationUrl - ): Promise { + ): Promise<{ + response; + }> { const pattern = { cmd: 'agent-receive-invitation-url' }; const payload = { url, orgId, receiveInvitationUrl }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error( - `[_receiveInvitationUrl] [NATS call]- error in receive invitation url : ${JSON.stringify(error)}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } async receiveInvitation( @@ -604,7 +561,7 @@ export class ConnectionService { } const createConnectionInvitation = await this._receiveInvitation(url, orgId, receiveInvitation); - return createConnectionInvitation; + return createConnectionInvitation?.response; } catch (error) { this.logger.error(`[receiveInvitation] - error in receive invitation : ${JSON.stringify(error)}`); @@ -624,43 +581,18 @@ export class ConnectionService { url: string, orgId: string, receiveInvitation: IReceiveInvitation - ): Promise { + ): Promise<{ + response; + }> { const pattern = { cmd: 'agent-receive-invitation' }; const payload = { url, orgId, receiveInvitation }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error(`[_receiveInvitation] [NATS call]- error in receive invitation : ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } async _sendQuestion(questionPayload: IQuestionPayload, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-send-question' }; const payload = { questionPayload, url, orgId }; - - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error(`[_sendQuestion] [NATS call]- error in send question : ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + return this.natsCall(pattern, payload); } async sendQuestion(payload: IQuestionPayload): Promise { @@ -689,7 +621,7 @@ export class ConnectionService { agentDetails?.tenantId, connectionId ); - + const createQuestion = await this._sendQuestion(questionPayload, url, orgId); return createQuestion; } catch (error) { @@ -707,42 +639,58 @@ export class ConnectionService { } } - async storeConnectionObjectAndReturnUrl(connectionInvitationUrl: string, persistent: boolean): Promise { + async storeConnectionObjectAndReturnUrl( + connectionInvitationUrl: string, + persistent: boolean + ): Promise { const storeObj = connectionInvitationUrl; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; try { - const message = await this.connectionServiceProxy - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .send(pattern, payload) + const message = await this.natsCall(pattern, payload); + return message.response; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + + async natsCall( + pattern: object, + payload: object + ): Promise<{ + response: string; + }> { + try { + return this.connectionServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ({ + response + })) + ) .toPromise() .catch((error) => { - this.logger.error( - `[storeConnectionObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( - error - )}` - ); + this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message + error: error.message }, error.error ); }); - return message; } catch (error) { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.status, - error: error.message - }, - error.status - ); + this.logger.error(`[natsCall] - error in nats call : ${JSON.stringify(error)}`); + throw error; } } From 781bdc1e6afda71e33c5a4d1cb342c720a8f537c Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 19 Mar 2024 18:49:48 +0530 Subject: [PATCH 196/231] fix: removed duplicated code Signed-off-by: KulkarniShashank --- apps/connection/src/connection.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 41ad7b317..f8984b778 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -689,7 +689,7 @@ export class ConnectionService { ); }); } catch (error) { - this.logger.error(`[natsCall] - error in nats call : ${JSON.stringify(error)}`); + this.logger.error(`[ConnectionService natsCall] - error in nats call : ${JSON.stringify(error)}`); throw error; } } From fe284a22c664e6b8284f214973724c7cfa9c6e65 Mon Sep 17 00:00:00 2001 From: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:43:05 +0530 Subject: [PATCH 197/231] feat: implementation of DID document for did web method (#601) * wip: did document Signed-off-by: bhavanakarwade * remove unnecessary code Signed-off-by: bhavanakarwade * wip: store did doc in org agents table Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade --- apps/agent-service/src/agent-service.service.ts | 4 ++-- .../agent-service/src/interface/agent-service.interface.ts | 1 + .../src/repositories/agent-service.repository.ts | 1 + .../api-gateway/src/agent-service/dto/create-tenant.dto.ts | 7 +------ .../credential-definition/credential-definition.service.ts | 1 - apps/organization/repositories/organization.repository.ts | 1 + .../20240315121444_org_agents_did_document/migration.sql | 2 ++ libs/prisma-service/prisma/schema.prisma | 1 + 8 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 libs/prisma-service/prisma/migrations/20240315121444_org_agents_did_document/migration.sql diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index d25f5a714..9d9e4c350 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -736,6 +736,7 @@ export class AgentServiceService { const storeOrgAgentData: IStoreOrgAgentDetails = { did: tenantDetails.DIDCreationOption.did, + didDoc: tenantDetails.DIDCreationOption.didDocument || tenantDetails.DIDCreationOption.didDoc, //changed the didDoc into didDocument isDidPublic: true, agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, agentsTypeId: agentTypeId, @@ -906,7 +907,7 @@ export class AgentServiceService { const walletResponseDetails = await this._createTenantWallet(walletLabel, platformAdminSpinnedUp.org_agents[0].agentEndPoint, platformAdminSpinnedUp.org_agents[0].apiKey); - if (!walletResponseDetails && !walletResponseDetails.id) { + if (!walletResponseDetails && !walletResponseDetails.id) { throw new InternalServerErrorException('Error while creating the wallet'); } const didCreateOption = { @@ -1268,7 +1269,6 @@ export class AgentServiceService { // Get organization agent details const orgAgentDetails: org_agents = await this.agentServiceRepository.getOrgAgentDetails(orgId); - let agentApiKey; if (orgAgentDetails) { diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 31a649be9..b3432e890 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -166,6 +166,7 @@ export interface IStoreOrgAgentDetails { network?: string; role?: string; did?: string; + didDoc?: string; verkey?: string; isDidPublic?: boolean; agentSpinUpStatus?: number; diff --git a/apps/agent-service/src/repositories/agent-service.repository.ts b/apps/agent-service/src/repositories/agent-service.repository.ts index 096eaae29..203941378 100644 --- a/apps/agent-service/src/repositories/agent-service.repository.ts +++ b/apps/agent-service/src/repositories/agent-service.repository.ts @@ -131,6 +131,7 @@ export class AgentServiceRepository { }, data: { orgDid: storeOrgAgentDetails.did, + didDocument: storeOrgAgentDetails.didDoc, verkey: storeOrgAgentDetails.verkey, isDidPublic: storeOrgAgentDetails.isDidPublic, agentSpinUpStatus: storeOrgAgentDetails.agentSpinUpStatus, diff --git a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts index 72fb459ea..e26131431 100644 --- a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts @@ -1,19 +1,14 @@ import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { MaxLength, IsString, MinLength, Matches, IsOptional } from 'class-validator'; +import { MaxLength, IsString, MinLength, IsOptional } from 'class-validator'; import { CreateDidDto } from './create-did.dto'; -const labelRegex = /^[a-zA-Z0-9 ]*$/; export class CreateTenantDto extends CreateDidDto { @ApiProperty() @MaxLength(25, { message: 'Maximum length for label must be 25 characters.' }) @IsString({ message: 'label must be in string format.' }) @Transform(({ value }) => trim(value)) @MinLength(2, { message: 'Minimum length for label must be 2 characters.' }) - @Matches(labelRegex, { message: 'Label must not contain special characters.' }) - @Matches(/^\S*$/, { - message: 'Spaces are not allowed in label' - }) label: string; @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 94c34f666..7f799e52c 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -1,6 +1,5 @@ /* eslint-disable camelcase */ import { - BadRequestException, ConflictException, HttpException, Inject, diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index e2464aab6..067f5a49d 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -391,6 +391,7 @@ export class OrganizationRepository { select: { id: true, orgDid: true, + didDocument: true, walletName: true, agentEndPoint: true, agentSpinUpStatus: true, diff --git a/libs/prisma-service/prisma/migrations/20240315121444_org_agents_did_document/migration.sql b/libs/prisma-service/prisma/migrations/20240315121444_org_agents_did_document/migration.sql new file mode 100644 index 000000000..9c0b9a251 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240315121444_org_agents_did_document/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "org_agents" ADD COLUMN "didDocument" JSONB; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 908fd8b3b..9e55d94b3 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -186,6 +186,7 @@ model org_agents { orgId String? @unique @db.Uuid orgAgentTypeId String? @db.Uuid ledgerId String? @db.Uuid + didDocument Json? agent_invitations agent_invitations[] agents agents? @relation(fields: [agentId], references: [id]) agents_type agents_type? @relation(fields: [agentsTypeId], references: [id]) From 5fbdcfa85e2b1868c43a2b8dd867a77ad71ce856 Mon Sep 17 00:00:00 2001 From: Krishna Date: Tue, 19 Mar 2024 22:28:50 +0530 Subject: [PATCH 198/231] make array size dynamic Signed-off-by: Krishna --- apps/api-gateway/src/verification/dto/request-proof.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 3384bc48d..9e1f49174 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -336,7 +336,7 @@ export class SendProofRequestPayload { @IsEmail({}, { each: true, message: 'Please provide a valid email' }) @ArrayNotEmpty({ message: 'Email array must not be empty' }) @ArrayUnique({ message: 'Duplicate emails are not allowed' }) - @ArrayMaxSize(10, { message: 'Max 10 emails can be sent' }) + @ArrayMaxSize(Number(process.env.OOB_BATCH_SIZE), { message: `Limit reached (${process.env.OOB_BATCH_SIZE} proof request max).` }) @IsArray() @IsString({ each: true, message: 'Each emailId in the array should be a string' }) @IsOptional() From d5588c7d3e7eb04123ba0dcfe8dd6e11cee8cdca Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Wed, 20 Mar 2024 12:44:59 +0530 Subject: [PATCH 199/231] fix: solved the sonar lint issues Signed-off-by: KulkarniShashank --- apps/agent-service/src/agent-service.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 319de2972..07b0cc963 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -631,7 +631,7 @@ export class AgentServiceService { cmd: 'create-connection' }; const payload = { orgId, user, label }; - return this.natsCall(pattern, payload); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in create-connection in wallet provision : ${JSON.stringify(error)}`); } @@ -645,7 +645,7 @@ export class AgentServiceService { cmd: 'get-all-ledgers' }; const payload = {}; - return this.natsCall(pattern, payload); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in while fetching all the ledger details : ${JSON.stringify(error)}`); } @@ -658,7 +658,7 @@ export class AgentServiceService { const pattern = { cmd: 'wallet-provisioning' }; - return this.natsCall(pattern, payload); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in wallet provision : ${JSON.stringify(error)}`); throw error; From 3078b86607548b7de43a44da2d8f52e34f0b76a2 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Wed, 20 Mar 2024 12:54:55 +0530 Subject: [PATCH 200/231] fix: solved the sonar lint issues Signed-off-by: KulkarniShashank --- apps/connection/src/connection.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index f8984b778..a2acdda52 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -144,7 +144,7 @@ export class ConnectionService { try { - return this.natsCall(pattern, payload); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( @@ -307,7 +307,7 @@ export class ConnectionService { try { const pattern = { cmd: 'agent-get-all-connections' }; const payload = { url, orgId }; - return this.natsCall(pattern, payload); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error( `[_getAllConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}` @@ -469,7 +469,7 @@ export class ConnectionService { const payload = { orgId }; try { - return this.natsCall(pattern, payload); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( @@ -671,7 +671,7 @@ export class ConnectionService { }> { try { return this.connectionServiceProxy - .send(pattern, payload) + .send(pattern, payload) .pipe( map((response) => ({ response From 613c860b8c8398f82e9cf7a2c8895ef2c4cdc454 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Wed, 20 Mar 2024 17:50:41 +0530 Subject: [PATCH 201/231] Solved verification interface issue Signed-off-by: KulkarniShashank --- apps/verification/src/interfaces/verification.interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index 1a8539585..b9172233e 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -176,7 +176,7 @@ export interface IProofRequestPayload { url: string; apiKey?: string; orgId?: string - proofRequestPayload: ISendProofRequestPayload; + proofRequestPayload: ISendProofRequestPayload | ISendPresentationExchangeProofRequestPayload; } interface IWebhookPresentationProof { From b352306c6181314e6675abda86e5fe2ee53693c2 Mon Sep 17 00:00:00 2001 From: Nishad Date: Wed, 20 Mar 2024 18:43:56 +0530 Subject: [PATCH 202/231] restricted client access to update org and create client credentials Signed-off-by: Nishad --- apps/api-gateway/src/organization/organization.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 6884e5963..7e19572ed 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -352,7 +352,7 @@ export class OrganizationController { @Roles(OrgRoles.OWNER) @ApiOperation({ summary: 'Create credentials for an organization', description: 'Create client id and secret for an organization' }) @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard, UserAccessGuard) @ApiBearerAuth() async createOrgCredentials(@Param('orgId') orgId: string, @Res() res: Response, @User() reqUser: user): Promise { @@ -472,7 +472,7 @@ export class OrganizationController { @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard, UserAccessGuard) async updateOrganization(@Body() updateOrgDto: UpdateOrganizationDto, @Param('orgId') orgId: string, @Res() res: Response, @User() reqUser: user): Promise { updateOrgDto.orgId = orgId; From f5492da8a68f0e2bca3cb78142714b6fd145e539 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Wed, 20 Mar 2024 18:55:26 +0530 Subject: [PATCH 203/231] fix: bug fixes Signed-off-by: bhavanakarwade --- .../ecosystem/dtos/create-ecosystem-dto.ts | 8 +- .../src/ecosystem/ecosystem.controller.ts | 20 ++- .../src/ecosystem/ecosystem.service.ts | 9 +- .../interfaces/ecosystem.interface.ts | 42 ++++++ .../dtos/get-all-issued-credentials.dto.ts | 25 +--- .../src/issuance/interfaces/index.ts | 2 +- .../src/issuance/issuance.controller.ts | 19 ++- .../organization/dtos/send-invitation.dto.ts | 3 - .../organization/organization.controller.ts | 2 +- .../interfaces/ecosystem.interfaces.ts | 101 +++++++++---- .../interfaces/endorsements.interface.ts | 20 ++- apps/ecosystem/src/ecosystem.controller.ts | 8 +- apps/ecosystem/src/ecosystem.repository.ts | 133 +++++++++--------- apps/ecosystem/src/ecosystem.service.ts | 52 ++++--- .../interfaces/issuance.interfaces.ts | 2 +- apps/issuance/src/issuance.repository.ts | 9 +- .../credential-definition.service.ts | 1 - .../repositories/organization.repository.ts | 4 +- 18 files changed, 296 insertions(+), 164 deletions(-) create mode 100644 apps/api-gateway/src/ecosystem/interfaces/ecosystem.interface.ts diff --git a/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts b/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts index 03a49fcaa..5b5e37fbc 100644 --- a/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts @@ -9,10 +9,10 @@ export class CreateEcosystemDto { @ApiProperty() @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'ecosystem name is required.' }) - @MinLength(2, { message: 'ecosystem name must be at least 2 characters.' }) - @MaxLength(50, { message: 'ecosystem name must be at most 50 characters.' }) - @IsString({ message: 'ecosystem name must be in string format.' }) + @IsNotEmpty({ message: 'Ecosystem name is required.' }) + @MinLength(2, { message: 'Ecosystem name must be at least 2 characters.' }) + @MaxLength(50, { message: 'Ecosystem name must be at most 50 characters.' }) + @IsString({ message: 'Ecosystem name must be in string format.' }) name: string; @ApiProperty() diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 05a0ba0b6..489e5df84 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -110,7 +110,7 @@ export class EcosystemController { const finalResponse: IResponse = { statusCode: 200, message: ResponseMessages.ecosystem.success.allschema, - data: schemaList.response + data: schemaList }; return res.status(200).json(finalResponse); } @@ -124,11 +124,27 @@ export class EcosystemController { @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) @ApiBearerAuth() + @ApiQuery({ + name: 'pageNumber', + type: Number, + required: false + }) + @ApiQuery({ + name: 'pageSize', + type: Number, + required: false + }) + @ApiQuery({ + name: 'search', + type: String, + required: false + }) async getEcosystem( + @Query() paginationDto: PaginationDto, @Param('orgId') orgId: string, @Res() res: Response ): Promise { - const ecosystemList = await this.ecosystemService.getAllEcosystem(orgId); + const ecosystemList = await this.ecosystemService.getAllEcosystem(orgId, paginationDto); const finalResponse: IResponse = { statusCode: 200, message: ResponseMessages.ecosystem.success.fetch, diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index a55c21d13..924492bab 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -11,8 +11,9 @@ import { GetAllEndorsementsDto } from './dtos/get-all-endorsements.dto'; import { RequestSchemaDto, RequestCredDefDto } from './dtos/request-schema.dto'; import { CreateEcosystemDto } from './dtos/create-ecosystem-dto'; import { EditEcosystemDto } from './dtos/edit-ecosystem-dto'; -import { IEcosystemDashboard, EcosystemDetailsResult, IEcosystemInvitation, IEcosystemInvitations, IEcosystem, IEditEcosystem, IEndorsementTransaction } from 'apps/ecosystem/interfaces/ecosystem.interfaces'; +import { IEcosystemDashboard, IEcosystemInvitation, IEcosystemInvitations, IEcosystem, IEditEcosystem, IEndorsementTransaction, ISchemaResponse } from 'apps/ecosystem/interfaces/ecosystem.interfaces'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; +import { IPagination } from './interfaces/ecosystem.interface'; @Injectable() export class EcosystemService extends BaseService { @@ -45,8 +46,8 @@ export class EcosystemService extends BaseService { * * @returns Get all ecosystems */ - async getAllEcosystem(orgId: string): Promise { - const payload = { orgId }; + async getAllEcosystem(orgId: string, payload: PaginationDto): Promise { + payload['orgId'] = orgId; return this.sendNatsMessage(this.serviceProxy, 'get-all-ecosystem', payload); } @@ -144,7 +145,7 @@ export class EcosystemService extends BaseService { ecosystemId: string, orgId: string, paginationDto: PaginationDto - ): Promise<{ response: object }> { + ): Promise { const { pageNumber, pageSize, search } = paginationDto; const payload = { ecosystemId, orgId, pageNumber, pageSize, search }; return this.sendNatsMessage(this.serviceProxy, 'get-all-ecosystem-schemas', payload); diff --git a/apps/api-gateway/src/ecosystem/interfaces/ecosystem.interface.ts b/apps/api-gateway/src/ecosystem/interfaces/ecosystem.interface.ts new file mode 100644 index 000000000..0cc7133b9 --- /dev/null +++ b/apps/api-gateway/src/ecosystem/interfaces/ecosystem.interface.ts @@ -0,0 +1,42 @@ +interface EcosystemRole { + id: string; + name: string; + description: string; + createDateTime: Date; + lastChangedDateTime: Date; + deletedAt: Date; + } + +interface Ecosystem { + id: string; + name: string; + description: string; + logoUrl: string | null; + createDateTime: string; + lastChangedDateTime: string; + createdBy: string; + autoEndorsement: boolean; + ecosystemOrgs: EcosystemOrg[]; + } + + interface EcosystemOrg { + id: string; + orgId: string; + status: string; + createDateTime: string; + lastChangedDateTime: string; + ecosystemId: string; + ecosystemRoleId: string; + ecosystemRole: EcosystemRole; + } + + export interface IPagination { + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + ecosystemList: Ecosystem[]; + } + \ No newline at end of file diff --git a/apps/api-gateway/src/issuance/dtos/get-all-issued-credentials.dto.ts b/apps/api-gateway/src/issuance/dtos/get-all-issued-credentials.dto.ts index 9e07e5ecf..37be22275 100644 --- a/apps/api-gateway/src/issuance/dtos/get-all-issued-credentials.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/get-all-issued-credentials.dto.ts @@ -1,29 +1,12 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; -import { IsEnum, IsInt, IsOptional, IsString } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { IsEnum, IsOptional } from 'class-validator'; import { SortValue } from '../../enum'; import { trim } from '@credebl/common/cast.helper'; import { SortFields } from 'apps/issuance/enum/issuance.enum'; +import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; -export class IGetAllIssuedCredentialsDto { - @ApiProperty({ required: false, default: 1 }) - @IsOptional() - @Type(() => Number) - @IsInt({ message: 'Page Number should be a number' }) - pageNumber: number = 1; - - @ApiProperty({ required: false, default: 10 }) - @IsOptional() - @Type(() => Number) - @IsInt({ message: 'Page size should be a number' }) - pageSize: number; - - @ApiProperty({ required: false }) - @IsOptional() - @Type(() => String) - @IsString({ message: 'Search text should be a string' }) - @Transform(({ value }) => trim(value)) - searchByText: string; +export class IGetAllIssuedCredentialsDto extends PaginationDto { @ApiProperty({ required: false, enum: SortFields }) @Transform(({ value }) => trim(value)) diff --git a/apps/api-gateway/src/issuance/interfaces/index.ts b/apps/api-gateway/src/issuance/interfaces/index.ts index e06d34539..107c08992 100644 --- a/apps/api-gateway/src/issuance/interfaces/index.ts +++ b/apps/api-gateway/src/issuance/interfaces/index.ts @@ -75,7 +75,7 @@ export interface IIssuedCredentialSearchParams { pageSize: number; sortField: string; sortBy: string; - searchByText: string; + search: string; } export enum IssueCredentialType { diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 56c1c059c..f640f0c64 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -88,6 +88,21 @@ export class IssuanceController { summary: `Get all issued credentials for a specific organization`, description: `Get all issued credentials for a specific organization` }) + @ApiQuery({ + name: 'pageNumber', + type: Number, + required: false + }) + @ApiQuery({ + name: 'pageSize', + type: Number, + required: false + }) + @ApiQuery({ + name: 'search', + type: String, + required: false + }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @@ -98,10 +113,10 @@ export class IssuanceController { @Param('orgId') orgId: string, @Res() res: Response ): Promise { - const { pageSize, searchByText, pageNumber, sortField, sortBy } = getAllIssuedCredentials; + const { pageSize, search, pageNumber, sortField, sortBy } = getAllIssuedCredentials; const issuedCredentialsSearchCriteria: IIssuedCredentialSearchParams = { pageNumber, - searchByText, + search, pageSize, sortField, sortBy diff --git a/apps/api-gateway/src/organization/dtos/send-invitation.dto.ts b/apps/api-gateway/src/organization/dtos/send-invitation.dto.ts index 013cb70f7..456215420 100644 --- a/apps/api-gateway/src/organization/dtos/send-invitation.dto.ts +++ b/apps/api-gateway/src/organization/dtos/send-invitation.dto.ts @@ -37,8 +37,5 @@ export class BulkSendInvitationDto { @Type(() => SendInvitationDto) invitations: SendInvitationDto[]; - @ApiProperty() - @IsString({ message: 'orgId should be a string' }) - @IsNotEmpty({ message: 'orgId is required' }) orgId: string; } diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 6884e5963..8083e1767 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -415,7 +415,7 @@ export class OrganizationController { @Post('/:orgId/invitations') @ApiOperation({ summary: 'Create organization invitation', - description: 'Create send invitation' + description: 'Create organization invitation' }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @Roles(OrgRoles.OWNER, OrgRoles.SUPER_ADMIN, OrgRoles.ADMIN) diff --git a/apps/ecosystem/interfaces/ecosystem.interfaces.ts b/apps/ecosystem/interfaces/ecosystem.interfaces.ts index 11ad4b73a..2a8502d07 100644 --- a/apps/ecosystem/interfaces/ecosystem.interfaces.ts +++ b/apps/ecosystem/interfaces/ecosystem.interfaces.ts @@ -229,32 +229,6 @@ interface EcosystemRole { deletedAt: Date; } -interface EcosystemDetail { - id: string; - name: string; - description: string; - logoUrl: string; - createDateTime: Date; - lastChangedDateTime: Date; - createdBy: string; - autoEndorsement: boolean; - ecosystemOrgs: { - id: string; - orgId: string; - status: string; - createDateTime: Date; - lastChangedDateTime: Date; - ecosystemId: string; - ecosystemRoleId: string; - ecosystemRole: EcosystemRole; - }[]; -} - -export interface EcosystemDetailsResult { - totalCount: number; - ecosystemDetails: EcosystemDetail[]; -} - export interface EcosystemInvitationDetails { name: string; id: string; @@ -363,4 +337,77 @@ export interface IEcosystemInvitations { ecosystem: EcosystemInvitationDetails; createDateTime: Date; createdBy: string; -} \ No newline at end of file +} + +interface IAttributes { + isRequired: boolean; + displayName: string; + attributeName: string; + schemaDataType: string; +} + +interface ISChemaItems { + id: string; + createDateTime: string; + createdBy: string; + lastChangedDateTime: string; + lastChangedBy: string; + name: string; + version: string; + attributes: IAttributes[]; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; +} + +export interface ISchemaResponse { + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + data: ISChemaItems[]; +} + +export interface IEcosystemList { + orgId: string, + pageNumber: number; + pageSize: number; + search: string; +} + +interface Ecosystem { + id: string; + name: string; + description: string; + logoUrl: string | null; + createDateTime: string; + lastChangedDateTime: string; + createdBy: string; + autoEndorsement: boolean; + ecosystemOrgs: EcosystemOrg[]; +} + +interface EcosystemOrg { + id: string; + orgId: string; + status: string; + createDateTime: string; + lastChangedDateTime: string; + ecosystemId: string; + ecosystemRoleId: string; + ecosystemRole: EcosystemRole; +} + +export interface IPagination { + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + ecosystemList: Ecosystem[]; +} diff --git a/apps/ecosystem/interfaces/endorsements.interface.ts b/apps/ecosystem/interfaces/endorsements.interface.ts index a8024a05e..fe3ba0356 100644 --- a/apps/ecosystem/interfaces/endorsements.interface.ts +++ b/apps/ecosystem/interfaces/endorsements.interface.ts @@ -15,4 +15,22 @@ export interface GetEndorsementsPayload { pageNumber: number; pageSize: number; search: string; - } \ No newline at end of file + } + + interface ISchemaResult { + createDateTime: Date; + createdBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + } + + export interface ISchemasResponse { + schemasCount: number; + schemasResult: ISchemaResult[]; + } + \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index a2544ec30..975ebe7f2 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -7,8 +7,8 @@ import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { AcceptRejectEcosystemInvitationDto } from '../dtos/accept-reject-ecosysteminvitation.dto'; import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; import { EcosystemMembersPayload } from '../interfaces/ecosystemMembers.interface'; -import { GetEndorsementsPayload } from '../interfaces/endorsements.interface'; -import { IEcosystemDashboard, RequestCredDeffEndorsement, RequestSchemaEndorsement, IEcosystem, EcosystemDetailsResult, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction } from '../interfaces/ecosystem.interfaces'; +import { GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; +import { IEcosystemDashboard, RequestCredDeffEndorsement, RequestSchemaEndorsement, IEcosystem, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction, IEcosystemList, IPagination } from '../interfaces/ecosystem.interfaces'; @Controller() export class EcosystemController { @@ -42,7 +42,7 @@ export class EcosystemController { * @returns Get all ecosystem details */ @MessagePattern({ cmd: 'get-all-ecosystem' }) - async getAllEcosystems(@Body() payload: { orgId: string }): Promise { + async getAllEcosystems(@Body() payload: IEcosystemList): Promise { return this.ecosystemService.getAllEcosystem(payload); } @@ -121,7 +121,7 @@ export class EcosystemController { } @MessagePattern({ cmd: 'get-all-ecosystem-schemas' }) - async getAllEcosystemSchemas(@Body() payload: GetEndorsementsPayload): Promise { + async getAllEcosystemSchemas(@Body() payload: GetEndorsementsPayload): Promise { return this.ecosystemService.getAllEcosystemSchemas(payload); } diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index 06f0d1017..45085caa4 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -4,11 +4,11 @@ import { PrismaService } from '@credebl/prisma-service'; import { credential_definition, ecosystem, ecosystem_config, ecosystem_invitations, ecosystem_orgs, ecosystem_roles, endorsement_transaction, org_agents, platform_config, schema } from '@prisma/client'; import { DeploymentModeType, EcosystemInvitationStatus, EcosystemOrgStatus, EcosystemRoles, endorsementTransactionStatus, endorsementTransactionType } from '../enums/ecosystem.enum'; import { updateEcosystemOrgsDto } from '../dtos/update-ecosystemOrgs.dto'; -import { CreateEcosystem, IEcosystemInvitation, EcosystemDetailsResult, SaveSchema, SchemaTransactionResponse, saveCredDef } from '../interfaces/ecosystem.interfaces'; +import { CreateEcosystem, IEcosystemInvitation, SaveSchema, SchemaTransactionResponse, saveCredDef } from '../interfaces/ecosystem.interfaces'; import { ResponseMessages } from '@credebl/common/response-messages'; import { NotFoundException } from '@nestjs/common'; import { CommonConstants } from '@credebl/common/common.constant'; -import { GetAllSchemaList } from '../interfaces/endorsements.interface'; +import { GetAllSchemaList, ISchemasResponse } from '../interfaces/endorsements.interface'; import { SortValue } from '@credebl/enum/enum'; // eslint-disable-next-line camelcase @@ -113,63 +113,75 @@ export class EcosystemRepository { * * @returns Get all ecosystem details */ - // eslint-disable-next-line camelcase - async getAllEcosystemDetails(orgId: string): Promise { - try { - const [ecosystemDetails, ecosystemCount] = await Promise.all([ - this.prisma.ecosystem.findMany({ - where: { - ecosystemOrgs: { - some: { - orgId - } - } - }, - select: { - id: true, - name: true, - description: true, - logoUrl: true, - createDateTime: true, - lastChangedDateTime: true, - createdBy: true, - autoEndorsement: true, - ecosystemOrgs: { - where: { - orgId + async getAllEcosystemDetails( + orgId: string, + pageNumber: number, + pageSize: number, + search: string + ): Promise { + try { + const sortByName = SortValue.DESC; + + const result = await Promise.all([ + this.prisma.ecosystem.findMany({ + where: { + ecosystemOrgs: { + some: { + orgId + } }, - select: { - id: true, - orgId: true, - status: true, - createDateTime: true, - lastChangedDateTime: true, - ecosystemId: true, - ecosystemRoleId: true, - ecosystemRole: true + OR: [ + { name: { contains: search, mode: 'insensitive' } }, + { description: { contains: search, mode: 'insensitive' } } + ] + }, + select: { + id: true, + name: true, + description: true, + logoUrl: true, + createDateTime: true, + lastChangedDateTime: true, + createdBy: true, + autoEndorsement: true, + ecosystemOrgs: { + where: { + orgId + }, + select: { + id: true, + orgId: true, + status: true, + createDateTime: true, + lastChangedDateTime: true, + ecosystemId: true, + ecosystemRoleId: true, + ecosystemRole: true + } } + }, + take: Number(pageSize), + skip: (pageNumber - 1) * pageSize, + orderBy: { + name: sortByName } - } - }), - this.prisma.ecosystem.count({ - where: { - ecosystemOrgs: { - some: { - orgId + }), + this.prisma.ecosystem.count({ + where: { + ecosystemOrgs: { + some: { + orgId + } } } - } - }) - ]); + }) + ]); - return { - ecosystemDetails, - totalCount: ecosystemCount - }; - } catch (error) { - this.logger.error(`Error in get all ecosystem transaction: ${error.message}`); - throw error; - } + return result; + } catch (error) { + this.logger.error(`Error in get all ecosystem transaction: ${error.message}`); + throw error; + } } @@ -667,20 +679,7 @@ export class EcosystemRepository { } - async getAllEcosystemSchemasDetails(payload: GetAllSchemaList): Promise<{ - schemasCount: number; - schemasResult: { - createDateTime: Date; - createdBy: string; - name: string; - version: string; - attributes: string; - schemaLedgerId: string; - publisherDid: string; - issuerId: string; - orgId: string; - }[]; - }> { + async getAllEcosystemSchemasDetails(payload: GetAllSchemaList): Promise { try { const { ecosystemId, search, pageNumber, pageSize } = payload; diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index fa78b67c6..366dd849c 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -45,13 +45,14 @@ import { saveCredDef, submitTransactionPayload, IEcosystem, - EcosystemDetailsResult, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, - IEndorsementTransaction + IEndorsementTransaction, + IEcosystemList, + IPagination } from '../interfaces/ecosystem.interfaces'; -import { GetAllSchemaList, GetEndorsementsPayload } from '../interfaces/endorsements.interface'; +import { GetAllSchemaList, GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; import { CommonConstants } from '@credebl/common/common.constant'; // eslint-disable-next-line camelcase import { @@ -222,15 +223,32 @@ export class EcosystemService { * @returns all ecosystem details */ - // eslint-disable-next-line camelcase - async getAllEcosystem(payload: { orgId: string }): Promise { - const getAllEcosystemDetails = await this.ecosystemRepository.getAllEcosystemDetails(payload.orgId); + async getAllEcosystem(payload: IEcosystemList): Promise { + try { + const { orgId, pageNumber, pageSize, search } = payload; - if (!getAllEcosystemDetails) { - throw new NotFoundException(ResponseMessages.ecosystem.error.update); - } + const getOrgs = await this.ecosystemRepository.getAllEcosystemDetails( + orgId, + pageNumber, + pageSize, + search + ); - return getAllEcosystemDetails; + const ecosystemListDetails = { + totalItems: getOrgs[1], + hasNextPage: payload.pageSize * payload.pageNumber < getOrgs[1], + hasPreviousPage: 1 < payload.pageNumber, + nextPage: Number(payload.pageNumber) + 1, + previousPage: payload.pageNumber - 1, + lastPage: Math.ceil(getOrgs[1] / payload.pageSize), + ecosystemList: getOrgs[0] + }; + + return ecosystemListDetails; + } catch (error) { + this.logger.error(`In fetch ecosystem list : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } } /** @@ -1603,7 +1621,7 @@ export class EcosystemService { } } - async getAllEcosystemSchemas(ecosystemSchemas: GetAllSchemaList): Promise { + async getAllEcosystemSchemas(ecosystemSchemas: GetAllSchemaList): Promise { try { const response = await this.ecosystemRepository.getAllEcosystemSchemasDetails(ecosystemSchemas); const schemasDetails = response?.schemasResult.map((schemaAttributeItem) => { @@ -1611,15 +1629,11 @@ export class EcosystemService { return { ...schemaAttributeItem, attributes }; }); - const schemasResponse = { - totalItems: response.schemasCount, - hasNextPage: ecosystemSchemas.pageSize * ecosystemSchemas.pageNumber < response.schemasCount, - hasPreviousPage: 1 < ecosystemSchemas.pageNumber, - nextPage: ecosystemSchemas.pageNumber + 1, - previousPage: ecosystemSchemas.pageNumber - 1, - lastPage: Math.ceil(response.schemasCount / ecosystemSchemas.pageSize), - data: schemasDetails + const schemasResponse: ISchemasResponse = { + schemasCount: response.schemasCount, + schemasResult: schemasDetails }; + return schemasResponse; } catch (error) { this.logger.error(`In error fetching all ecosystem schemas: ${JSON.stringify(error)}`); diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 5cb4caa75..5a48c6b27 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -220,7 +220,7 @@ export interface IIssuedCredentialsSearchCriteria { pageSize: number; sortField: string; sortBy: string; - searchByText: string; + search: string; user?: IUserRequestInterface; } diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 050327509..1a6256ff4 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -108,11 +108,12 @@ export class IssuanceRepository { where: { orgId, OR: [ - { schemaId: { contains: issuedCredentialsSearchCriteria.searchByText, mode: 'insensitive' } }, - { connectionId: { contains: issuedCredentialsSearchCriteria.searchByText, mode: 'insensitive' } } + { schemaId: { contains: issuedCredentialsSearchCriteria.search, mode: 'insensitive' } }, + { connectionId: { contains: issuedCredentialsSearchCriteria.search, mode: 'insensitive' } } ] }, select: { + credentialExchangeId: true, createDateTime: true, createdBy: true, orgId: true, @@ -132,8 +133,8 @@ export class IssuanceRepository { where: { orgId, OR: [ - { schemaId: { contains: issuedCredentialsSearchCriteria.searchByText, mode: 'insensitive' } }, - { connectionId: { contains: issuedCredentialsSearchCriteria.searchByText, mode: 'insensitive' } } + { schemaId: { contains: issuedCredentialsSearchCriteria.search, mode: 'insensitive' } }, + { connectionId: { contains: issuedCredentialsSearchCriteria.search, mode: 'insensitive' } } ] } }); diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 94c34f666..7f799e52c 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -1,6 +1,5 @@ /* eslint-disable camelcase */ import { - BadRequestException, ConflictException, HttpException, Inject, diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index e2464aab6..f75779f85 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -8,7 +8,7 @@ import { Prisma, agent_invitations, org_agents, org_invitations, user_org_roles import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { IGetOrgById, IGetOrganization, IUpdateOrganization } from '../interfaces/organization.interface'; import { InternalServerErrorException } from '@nestjs/common'; -import { Invitation } from '@credebl/enum/enum'; +import { Invitation, SortValue } from '@credebl/enum/enum'; import { PrismaService } from '@credebl/prisma-service'; import { UserOrgRolesService } from '@credebl/user-org-roles'; import { organisation } from '@prisma/client'; @@ -553,7 +553,7 @@ export class OrganizationRepository { pageSize: number ): Promise { try { - const sortByName = 'asc'; + const sortByName = SortValue.DESC; const result = await this.prisma.$transaction([ this.prisma.organisation.findMany({ where: { From e926e5ce6a4ea0456eb939aeefd9db6bf1de50cb Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Wed, 20 Mar 2024 20:10:22 +0530 Subject: [PATCH 204/231] fix: resolved sonarlint issues Signed-off-by: bhavanakarwade --- .../src/ecosystem/ecosystem.service.ts | 2 +- .../interfaces/ecosystem.interfaces.ts | 42 ------------------- apps/ecosystem/src/ecosystem.controller.ts | 3 +- apps/ecosystem/src/ecosystem.service.ts | 4 +- .../src}/interfaces/ecosystem.interface.ts | 0 5 files changed, 5 insertions(+), 46 deletions(-) rename {apps/api-gateway/src/ecosystem => libs/common/src}/interfaces/ecosystem.interface.ts (100%) diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index 924492bab..c13027602 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -13,7 +13,7 @@ import { CreateEcosystemDto } from './dtos/create-ecosystem-dto'; import { EditEcosystemDto } from './dtos/edit-ecosystem-dto'; import { IEcosystemDashboard, IEcosystemInvitation, IEcosystemInvitations, IEcosystem, IEditEcosystem, IEndorsementTransaction, ISchemaResponse } from 'apps/ecosystem/interfaces/ecosystem.interfaces'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; -import { IPagination } from './interfaces/ecosystem.interface'; +import { IPagination } from '@credebl/common/interfaces/ecosystem.interface'; @Injectable() export class EcosystemService extends BaseService { diff --git a/apps/ecosystem/interfaces/ecosystem.interfaces.ts b/apps/ecosystem/interfaces/ecosystem.interfaces.ts index 2a8502d07..4fa335baf 100644 --- a/apps/ecosystem/interfaces/ecosystem.interfaces.ts +++ b/apps/ecosystem/interfaces/ecosystem.interfaces.ts @@ -220,15 +220,6 @@ export interface LedgerDetails { networkUrl: string; } -interface EcosystemRole { - id: string; - name: string; - description: string; - createDateTime: Date; - lastChangedDateTime: Date; - deletedAt: Date; -} - export interface EcosystemInvitationDetails { name: string; id: string; @@ -378,36 +369,3 @@ export interface IEcosystemList { pageSize: number; search: string; } - -interface Ecosystem { - id: string; - name: string; - description: string; - logoUrl: string | null; - createDateTime: string; - lastChangedDateTime: string; - createdBy: string; - autoEndorsement: boolean; - ecosystemOrgs: EcosystemOrg[]; -} - -interface EcosystemOrg { - id: string; - orgId: string; - status: string; - createDateTime: string; - lastChangedDateTime: string; - ecosystemId: string; - ecosystemRoleId: string; - ecosystemRole: EcosystemRole; -} - -export interface IPagination { - totalItems: number; - hasNextPage: boolean; - hasPreviousPage: boolean; - nextPage: number; - previousPage: number; - lastPage: number; - ecosystemList: Ecosystem[]; -} diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index 975ebe7f2..02ad7262b 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -8,7 +8,8 @@ import { AcceptRejectEcosystemInvitationDto } from '../dtos/accept-reject-ecosys import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; import { EcosystemMembersPayload } from '../interfaces/ecosystemMembers.interface'; import { GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; -import { IEcosystemDashboard, RequestCredDeffEndorsement, RequestSchemaEndorsement, IEcosystem, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction, IEcosystemList, IPagination } from '../interfaces/ecosystem.interfaces'; +import { IEcosystemDashboard, RequestCredDeffEndorsement, RequestSchemaEndorsement, IEcosystem, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction, IEcosystemList } from '../interfaces/ecosystem.interfaces'; +import { IPagination } from '@credebl/common/interfaces/ecosystem.interface'; @Controller() export class EcosystemController { diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 366dd849c..93fc208ae 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -49,8 +49,7 @@ import { IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction, - IEcosystemList, - IPagination + IEcosystemList } from '../interfaces/ecosystem.interfaces'; import { GetAllSchemaList, GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; import { CommonConstants } from '@credebl/common/common.constant'; @@ -70,6 +69,7 @@ import { import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { updateEcosystemOrgsDto } from '../dtos/update-ecosystemOrgs.dto'; +import { IPagination } from '@credebl/common/interfaces/ecosystem.interface'; @Injectable() export class EcosystemService { diff --git a/apps/api-gateway/src/ecosystem/interfaces/ecosystem.interface.ts b/libs/common/src/interfaces/ecosystem.interface.ts similarity index 100% rename from apps/api-gateway/src/ecosystem/interfaces/ecosystem.interface.ts rename to libs/common/src/interfaces/ecosystem.interface.ts From cb57d11f387d65d8e3865918a8e03c5f85d00e64 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 21 Mar 2024 13:28:49 +0530 Subject: [PATCH 205/231] Added the dotenv config package on the common-service Signed-off-by: KulkarniShashank --- libs/common/src/common.service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index 871c2b6fa..c88d28240 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -17,6 +17,8 @@ import { import { CommonConstants } from './common.constant'; import { HttpService } from '@nestjs/axios/dist'; import { ResponseService } from '@credebl/response'; +import * as dotenv from 'dotenv'; +dotenv.config(); @Injectable() export class CommonService { @@ -370,7 +372,11 @@ export class CommonService { encryptedPassword, process.env.CRYPTO_PRIVATE_KEY ); + + this.logger.debug(`encryptedPassword ::: ${encryptedPassword}`); + this.logger.debug(`process.env.CRYPTO_PRIVATE_KEY ::: ${process.env.CRYPTO_PRIVATE_KEY}`); const decryptedPassword = JSON.parse(password.toString(CryptoJS.enc.Utf8)); + this.logger.debug(`decryptedPassword ::: ${decryptedPassword}`); return decryptedPassword; } catch (error) { throw new BadRequestException('Invalid Credentials'); From dd1de6b11582f2dc1c5b786c5b99cc869adf02b6 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 21 Mar 2024 19:48:48 +0530 Subject: [PATCH 206/231] fix: solved issue for oob verification with email Signed-off-by: KulkarniShashank --- apps/verification/src/verification.service.ts | 15 ++------------- libs/common/src/common.service.ts | 3 --- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 9a4438dd3..ed5972689 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -399,7 +399,7 @@ export class VerificationService { await this.sendEmailInBatches(payload, emailId, getAgentDetails, getOrganization); return true; } else { - const presentationProof: IInvitation = await this.generateOOBProofReq(payload, getAgentDetails); + const presentationProof: IInvitation = await this.generateOOBProofReq(payload); const proofRequestInvitationUrl: string = presentationProof.invitationUrl; if (isShortenUrl) { const shortenedUrl: string = await this.storeVerificationObjectAndReturnUrl(proofRequestInvitationUrl, false); @@ -428,15 +428,9 @@ export class VerificationService { } - private async generateOOBProofReq(payload: IProofRequestPayload, getAgentDetails: org_agents): Promise { - let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { - agentApiKey = await this._getOrgAgentApiKey(getAgentDetails.orgId); - } - payload.apiKey = agentApiKey; + private async generateOOBProofReq(payload: IProofRequestPayload): Promise { const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - if (!getProofPresentation) { throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); } @@ -473,11 +467,6 @@ export class VerificationService { // This function is specifically for OOB verification using email async sendOutOfBandProofRequest(payload: IProofRequestPayload, email: string, getAgentDetails: org_agents, organizationDetails: organisation): Promise { - let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { - agentApiKey = await this._getOrgAgentApiKey(getAgentDetails.orgId); - } - payload.apiKey = agentApiKey; const getProofPresentation = await this._sendOutOfBandProofRequest(payload); if (!getProofPresentation) { diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index c88d28240..c290f1b2c 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -373,10 +373,7 @@ export class CommonService { process.env.CRYPTO_PRIVATE_KEY ); - this.logger.debug(`encryptedPassword ::: ${encryptedPassword}`); - this.logger.debug(`process.env.CRYPTO_PRIVATE_KEY ::: ${process.env.CRYPTO_PRIVATE_KEY}`); const decryptedPassword = JSON.parse(password.toString(CryptoJS.enc.Utf8)); - this.logger.debug(`decryptedPassword ::: ${decryptedPassword}`); return decryptedPassword; } catch (error) { throw new BadRequestException('Invalid Credentials'); From 649415c36a108344cb860a4377de8e5566f52e5b Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Tue, 26 Mar 2024 18:12:47 +0530 Subject: [PATCH 207/231] fix: modified interface name Signed-off-by: bhavanakarwade --- .../api-gateway/src/ecosystem/ecosystem.service.ts | 4 ++-- apps/ecosystem/src/ecosystem.controller.ts | 4 ++-- apps/ecosystem/src/ecosystem.service.ts | 14 +++++++------- libs/common/src/interfaces/ecosystem.interface.ts | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index c13027602..6d5ae7cb3 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -13,7 +13,7 @@ import { CreateEcosystemDto } from './dtos/create-ecosystem-dto'; import { EditEcosystemDto } from './dtos/edit-ecosystem-dto'; import { IEcosystemDashboard, IEcosystemInvitation, IEcosystemInvitations, IEcosystem, IEditEcosystem, IEndorsementTransaction, ISchemaResponse } from 'apps/ecosystem/interfaces/ecosystem.interfaces'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; -import { IPagination } from '@credebl/common/interfaces/ecosystem.interface'; +import { IEcosystemDetails } from '@credebl/common/interfaces/ecosystem.interface'; @Injectable() export class EcosystemService extends BaseService { @@ -46,7 +46,7 @@ export class EcosystemService extends BaseService { * * @returns Get all ecosystems */ - async getAllEcosystem(orgId: string, payload: PaginationDto): Promise { + async getAllEcosystem(orgId: string, payload: PaginationDto): Promise { payload['orgId'] = orgId; return this.sendNatsMessage(this.serviceProxy, 'get-all-ecosystem', payload); } diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index 02ad7262b..95513b5d7 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -9,7 +9,7 @@ import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; import { EcosystemMembersPayload } from '../interfaces/ecosystemMembers.interface'; import { GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; import { IEcosystemDashboard, RequestCredDeffEndorsement, RequestSchemaEndorsement, IEcosystem, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction, IEcosystemList } from '../interfaces/ecosystem.interfaces'; -import { IPagination } from '@credebl/common/interfaces/ecosystem.interface'; +import { IEcosystemDetails } from '@credebl/common/interfaces/ecosystem.interface'; @Controller() export class EcosystemController { @@ -43,7 +43,7 @@ export class EcosystemController { * @returns Get all ecosystem details */ @MessagePattern({ cmd: 'get-all-ecosystem' }) - async getAllEcosystems(@Body() payload: IEcosystemList): Promise { + async getAllEcosystems(@Body() payload: IEcosystemList): Promise { return this.ecosystemService.getAllEcosystem(payload); } diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 14e7424f8..9aa3dff26 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -69,7 +69,7 @@ import { import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { updateEcosystemOrgsDto } from '../dtos/update-ecosystemOrgs.dto'; -import { IPagination } from '@credebl/common/interfaces/ecosystem.interface'; +import { IEcosystemDetails } from '@credebl/common/interfaces/ecosystem.interface'; @Injectable() export class EcosystemService { @@ -223,11 +223,11 @@ export class EcosystemService { * @returns all ecosystem details */ - async getAllEcosystem(payload: IEcosystemList): Promise { + async getAllEcosystem(payload: IEcosystemList): Promise { try { const { orgId, pageNumber, pageSize, search } = payload; - const getOrgs = await this.ecosystemRepository.getAllEcosystemDetails( + const getEcosystemOrgs = await this.ecosystemRepository.getAllEcosystemDetails( orgId, pageNumber, pageSize, @@ -235,13 +235,13 @@ export class EcosystemService { ); const ecosystemListDetails = { - totalItems: getOrgs[1], - hasNextPage: payload.pageSize * payload.pageNumber < getOrgs[1], + totalItems: getEcosystemOrgs[1], + hasNextPage: payload.pageSize * payload.pageNumber < getEcosystemOrgs[1], hasPreviousPage: 1 < payload.pageNumber, nextPage: Number(payload.pageNumber) + 1, previousPage: payload.pageNumber - 1, - lastPage: Math.ceil(getOrgs[1] / payload.pageSize), - ecosystemList: getOrgs[0] + lastPage: Math.ceil(getEcosystemOrgs[1] / payload.pageSize), + ecosystemList: getEcosystemOrgs[0] }; return ecosystemListDetails; diff --git a/libs/common/src/interfaces/ecosystem.interface.ts b/libs/common/src/interfaces/ecosystem.interface.ts index 0cc7133b9..648153c19 100644 --- a/libs/common/src/interfaces/ecosystem.interface.ts +++ b/libs/common/src/interfaces/ecosystem.interface.ts @@ -30,7 +30,7 @@ interface Ecosystem { ecosystemRole: EcosystemRole; } - export interface IPagination { + export interface IEcosystemDetails { totalItems: number; hasNextPage: boolean; hasPreviousPage: boolean; From 71fb6183d4ab03890cb194b7d2fc9b5aab8fdafa Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Wed, 27 Mar 2024 11:25:54 +0530 Subject: [PATCH 208/231] fix: applied validations Signed-off-by: bhavanakarwade --- .../agent-service/agent-service.controller.ts | 4 +- .../src/agent-service/dto/create-did.dto.ts | 4 +- libs/common/src/did.validator.ts | 43 +++++++++---------- 3 files changed, 24 insertions(+), 27 deletions(-) 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 05d28a583..c69c4d75b 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -237,9 +237,9 @@ export class AgentController { @Res() res: Response ): Promise { - validateDid(createDidDto); + await validateDid(createDidDto); - if (seedLength !== createDidDto.seed.length) { + if (createDidDto.seed && seedLength !== createDidDto.seed.length) { this.logger.error(`seed must be at most 32 characters.`); throw new BadRequestException( ResponseMessages.agent.error.seedChar, diff --git a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts index 7f75116ec..3e8deb560 100644 --- a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts @@ -22,8 +22,8 @@ export class CreateDidDto { keyType: string; @ApiProperty({ example: 'indy'}) - @IsNotEmpty({ message: 'seed is required' }) - @IsString({ message: 'did must be in string format.' }) + @IsNotEmpty({ message: 'method is required' }) + @IsString({ message: 'method must be in string format.' }) method: string; @ApiProperty({example: 'bcovrin:testnet'}) diff --git a/libs/common/src/did.validator.ts b/libs/common/src/did.validator.ts index 2537cb48a..466643ce8 100644 --- a/libs/common/src/did.validator.ts +++ b/libs/common/src/did.validator.ts @@ -1,27 +1,24 @@ -import { DidMethod, KeyType } from "@credebl/enum/enum"; -import { IDidCreate } from "./interfaces/did.interface"; +import { DidMethod } from '@credebl/enum/enum'; +import { IDidCreate } from './interfaces/did.interface'; +import { BadRequestException } from '@nestjs/common'; -export function validateDid(createDid: IDidCreate): string[] { - const errors: string[] = []; +export function validateDid(createDid: IDidCreate): void { - if (DidMethod.WEB && !createDid.domain) { - errors.push('domain is required for Web method'); - } else if (DidMethod.INDY && !createDid.network) { - errors.push('network is required for Indy method'); - } else if (DidMethod.INDY && createDid.keyType !== KeyType.Ed25519) { - errors.push('Only ed25519 key type is supported for Indy method'); - } else if ((createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && - (createDid.keyType !== KeyType.Ed25519 && createDid.keyType !== KeyType.Bls12381g2)) { - errors.push('Only ed25519 and bls12381g2 key type is supported'); - } else if (!createDid.role) { - errors.push('role or endorserDid is required'); - } else if (DidMethod.POLYGON && !createDid.privatekey) { - errors.push('privateKey is required for polygon method'); - } else if (DidMethod.POLYGON && !createDid.endpoint) { - errors.push('endpoint is required for polygon method'); - } else if ((DidMethod.INDY || DidMethod.KEY || DidMethod.WEB) && (!createDid.seed)) { - errors.push('seed is required'); + if (DidMethod.WEB === createDid.method && !createDid.domain) { + throw new BadRequestException('domain is required for Web method'); + } else if ((createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && !createDid.network) { + throw new BadRequestException('network is required'); + } else if ((createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && 'ed25519' !== createDid.keyType) { + throw new BadRequestException('Only ed25519 key type is supported'); + } else if ((createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && ('ed25519' !== createDid.keyType && 'bls12381g2' !== createDid.keyType)) { + throw new BadRequestException('Only ed25519 and bls12381g2 key type is supported'); + } else if (DidMethod.INDY === createDid.method && (!createDid.role && !createDid.endorserDid)) { + throw new BadRequestException('role or endorserDid is required'); + } else if (DidMethod.POLYGON === createDid.method && !createDid.privatekey) { + throw new BadRequestException('privatekey is required for polygon method'); + } else if (DidMethod.POLYGON === createDid.method && !createDid.endpoint) { + throw new BadRequestException('endpoint is required for polygon method'); + } else if ((DidMethod.INDY === createDid.method || DidMethod.KEY === createDid.method || DidMethod.WEB === createDid.method) && (!createDid.seed)) { + throw new BadRequestException('seed is required'); } - - return errors; } \ No newline at end of file From 9b1b876959f507198f933142207a5b98de84ede1 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Wed, 27 Mar 2024 13:50:47 +0530 Subject: [PATCH 209/231] refactor: changed API key fetching logic Signed-off-by: tipusinghaw --- apps/agent-service/src/agent-service.controller.ts | 5 ++--- apps/api-gateway/src/schema/schema.controller.ts | 2 +- .../ledger/src/schema/interfaces/schema-payload.interface.ts | 2 +- apps/ledger/src/schema/schema.service.ts | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index c630b30c9..10587964c 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -47,9 +47,8 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-create-w3c-schema' }) - async createWC3Schema(payload: {url, apiKey, schemaRequestPayload}): Promise { - - return this.agentServiceService.createWC3Schema(payload.url, payload.apiKey, payload.schemaRequestPayload); + async createW3CSchema(payload: { url, orgId, schemaRequestPayload }): Promise { + return this.agentServiceService.createW3CSchema(payload.url, payload.orgId, payload.schemaRequestPayload); } //DONE diff --git a/apps/api-gateway/src/schema/schema.controller.ts b/apps/api-gateway/src/schema/schema.controller.ts index 6ca121d2f..15335fa03 100644 --- a/apps/api-gateway/src/schema/schema.controller.ts +++ b/apps/api-gateway/src/schema/schema.controller.ts @@ -138,7 +138,7 @@ export class SchemaController { summary: 'Create and sends a W3C-schema to the ledger.', description: 'Create and sends a W3C-schema to the ledger.' }) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) async createW3CSchema(@Res() res: Response, @Body() schemaPayload: CreateW3CSchemaDto, @Param('orgId') orgId: string, @User() user: IUserRequestInterface): Promise { diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index 5393bdddf..bd091074e 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -85,6 +85,6 @@ export interface W3CSchemaPayload { export interface W3CCreateSchema { url: string, - apiKey: string, + orgId: string, schemaRequestPayload: SchemaPayload } diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 0a2d49dd6..0f65ac7f7 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -20,6 +20,7 @@ import { OrgAgentType } from '@credebl/enum/enum'; import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { CommonConstants } from '@credebl/common/common.constant'; @Injectable() export class SchemaService extends BaseService { @@ -254,7 +255,6 @@ export class SchemaService extends BaseService { ); } const { agentEndPoint } = agentDetails; - const apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY) || await this._getOrgAgentApiKey(orgId); const getAgentDetails = await this.schemaRepository.getAgentType(orgId); const orgAgentType = await this.schemaRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); let url; @@ -266,7 +266,7 @@ export class SchemaService extends BaseService { } const W3cSchemaPayload = { url, - apiKey, + orgId, schemaRequestPayload: schemaRequestPayload.schemaPayload }; return this._createW3CSchema(W3cSchemaPayload); From 54ede5ecbebbd8f649de4d5aca50eeedf5b11e58 Mon Sep 17 00:00:00 2001 From: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:03:16 +0530 Subject: [PATCH 210/231] fix: web didDoc updation in database (#617) Signed-off-by: KulkarniShashank --- apps/agent-service/src/agent-service.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index b24ef452b..a2ab18dbe 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -763,6 +763,7 @@ export class AgentServiceService { const storeOrgAgentData: IStoreOrgAgentDetails = { did: tenantDetails.DIDCreationOption.did, isDidPublic: true, + didDoc: tenantDetails.DIDCreationOption.didDocument || tenantDetails.DIDCreationOption.didDoc, //changed the didDoc into didDocument agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, agentsTypeId: agentTypeId, orgId: payload.orgId, From 2a88d5f75ad9eb7d1c63afb29134e8521bda5f91 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Wed, 27 Mar 2024 15:17:17 +0530 Subject: [PATCH 211/231] remove space in common service Signed-off-by: KulkarniShashank --- libs/common/src/common.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index c290f1b2c..be3f4e183 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -379,5 +379,4 @@ export class CommonService { throw new BadRequestException('Invalid Credentials'); } } - } From fa546f03e6ac52ac6e51b5297ab4a91113e89a8d Mon Sep 17 00:00:00 2001 From: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:00:48 +0530 Subject: [PATCH 212/231] Develop to qa (#618) * Initial changes to send email Signed-off-by: Krishna * Initial changes to send email Signed-off-by: Krishna * feat: email functionality via oob email Signed-off-by: Krishna * remove logs Signed-off-by: Krishna * fix: logging Signed-off-by: Krishna * refactor: agent token encryption Signed-off-by: KulkarniShashank * feat: support multiple did methods (#589) * feat: support multiple did methods Signed-off-by: bhavanakarwade * refactor: removed the commented code Signed-off-by: tipusinghaw * fix: removed commented code Signed-off-by: tipusinghaw * fix: removed commented code agent-service Signed-off-by: tipusinghaw * fix: removed commented code agent-service Signed-off-by: tipusinghaw --------- Signed-off-by: bhavanakarwade Signed-off-by: tipusinghaw Co-authored-by: tipusinghaw * Add shortening url for email verification Signed-off-by: Krishna * fix: refactored tables Signed-off-by: bhavanakarwade * fix: reafctored json data Signed-off-by: bhavanakarwade * feat:add reuse connection Signed-off-by: pallavicoder * fix: refactored tables (#592) * fix: refactored tables Signed-off-by: bhavanakarwade * fix: reafctored json data Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade * fix:changed validation message Signed-off-by: pallavicoder * worked on the POST API of refresh token Signed-off-by: Nishad * fix: an incorrect message appears when delete the ecosystem invitation which is already deleted Signed-off-by: pranalidhanavade * fix: added the token validation with encryption on the wallet and DID Signed-off-by: KulkarniShashank * fix: schema id validations Signed-off-by: bhavanakarwade * feat: dedicated agent spin up Signed-off-by: bhavanakarwade * fix: solved the platform-admin agent spin-up Signed-off-by: KulkarniShashank * fix: schema validation Signed-off-by: bhavanakarwade * fix: schema validations Signed-off-by: bhavanakarwade * fix: solved the duplication code Signed-off-by: KulkarniShashank * fix: removed unnecessary import Signed-off-by: KulkarniShashank * fix: removed duplicated code Signed-off-by: KulkarniShashank * fix: removed duplicated code Signed-off-by: KulkarniShashank * fix: removed duplicated code Signed-off-by: KulkarniShashank * feat: implementation of DID document for did web method (#601) * wip: did document Signed-off-by: bhavanakarwade * remove unnecessary code Signed-off-by: bhavanakarwade * wip: store did doc in org agents table Signed-off-by: bhavanakarwade --------- Signed-off-by: bhavanakarwade * make array size dynamic Signed-off-by: Krishna * fix: solved the sonar lint issues Signed-off-by: KulkarniShashank * fix: solved the sonar lint issues Signed-off-by: KulkarniShashank * Solved verification interface issue Signed-off-by: KulkarniShashank * restricted client access to update org and create client credentials Signed-off-by: Nishad * fix: bug fixes Signed-off-by: bhavanakarwade * fix: resolved sonarlint issues Signed-off-by: bhavanakarwade * Added the dotenv config package on the common-service Signed-off-by: KulkarniShashank * fix: solved issue for oob verification with email Signed-off-by: KulkarniShashank * fix: modified interface name Signed-off-by: bhavanakarwade * fix: applied validations Signed-off-by: bhavanakarwade * fix: web didDoc updation in database (#617) Signed-off-by: KulkarniShashank * remove space in common service Signed-off-by: KulkarniShashank --------- Signed-off-by: Krishna Signed-off-by: KulkarniShashank Signed-off-by: bhavanakarwade Signed-off-by: tipusinghaw Signed-off-by: pallavicoder Signed-off-by: Nishad Signed-off-by: pranalidhanavade Co-authored-by: Krishna Co-authored-by: bhavanakarwade <137506897+bhavanakarwade@users.noreply.github.com> Co-authored-by: tipusinghaw Co-authored-by: bhavanakarwade Co-authored-by: pallavicoder Co-authored-by: Nishad Co-authored-by: pallavighule <61926403+pallavighule@users.noreply.github.com> Co-authored-by: pranalidhanavade Co-authored-by: pranalidhanavade <137780597+pranalidhanavade@users.noreply.github.com> Co-authored-by: Nishad Shirsat <103021375+nishad-ayanworks@users.noreply.github.com> Co-authored-by: Krishna <74084119+GHkrishna@users.noreply.github.com> --- .../src/agent-service.controller.ts | 86 +- .../src/agent-service.service.ts | 1175 +++++++++-------- .../src/interface/agent-service.interface.ts | 5 + .../repositories/agent-service.repository.ts | 1 + .../agent-service/agent-service.controller.ts | 4 +- .../src/agent-service/dto/create-did.dto.ts | 4 +- .../agent-service/dto/create-tenant.dto.ts | 7 +- .../ecosystem/dtos/create-ecosystem-dto.ts | 8 +- .../src/ecosystem/ecosystem.controller.ts | 20 +- .../src/ecosystem/ecosystem.service.ts | 9 +- .../dtos/get-all-issued-credentials.dto.ts | 25 +- .../src/issuance/interfaces/index.ts | 2 +- .../src/issuance/issuance.controller.ts | 19 +- .../organization/dtos/send-invitation.dto.ts | 3 - .../organization/organization.controller.ts | 6 +- .../src/verification/dto/request-proof.dto.ts | 16 +- apps/connection/src/connection.service.ts | 382 +++--- .../interfaces/ecosystem.interfaces.ts | 77 +- .../interfaces/endorsements.interface.ts | 20 +- apps/ecosystem/src/ecosystem.controller.ts | 9 +- apps/ecosystem/src/ecosystem.repository.ts | 133 +- apps/ecosystem/src/ecosystem.service.ts | 88 +- .../interfaces/issuance.interfaces.ts | 7 +- apps/issuance/src/issuance.repository.ts | 9 +- apps/issuance/src/issuance.service.ts | 49 +- .../credential-definition.service.ts | 21 +- .../credential-definition.interface.ts | 2 + apps/ledger/src/schema/schema.interface.ts | 2 + apps/ledger/src/schema/schema.service.ts | 20 +- .../repositories/organization.repository.ts | 5 +- .../src/interfaces/verification.interface.ts | 26 +- apps/verification/src/verification.service.ts | 202 ++- libs/common/src/common.constant.ts | 3 +- libs/common/src/common.service.ts | 4 +- libs/common/src/did.validator.ts | 43 +- .../src/interfaces/ecosystem.interface.ts | 42 + libs/common/src/response-messages/index.ts | 1 + .../migration.sql | 2 + libs/prisma-service/prisma/schema.prisma | 1 + 39 files changed, 1276 insertions(+), 1262 deletions(-) create mode 100644 libs/common/src/interfaces/ecosystem.interface.ts create mode 100644 libs/prisma-service/prisma/migrations/20240315121444_org_agents_did_document/migration.sql diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 57228fad8..fe8802b82 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -1,10 +1,8 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { AgentServiceService } from './agent-service.service'; -import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IProofPresentation, IAgentProofRequest, IPresentation, IDidCreate, IWallet, ITenantRecord } from './interface/agent-service.interface'; +import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IAgentProofRequest, IDidCreate, IWallet, ITenantRecord } from './interface/agent-service.interface'; import { user } from '@prisma/client'; -import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; -import { IProofPresentationDetails } from '@credebl/common/interfaces/verification.interface'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; @Controller() @@ -67,17 +65,17 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-create-connection-legacy-invitation' }) - async createLegacyConnectionInvitation(payload: { connectionPayload: IConnectionDetails, url: string, apiKey: string }): Promise { - return this.agentServiceService.createLegacyConnectionInvitation(payload.connectionPayload, payload.url, payload.apiKey); + async createLegacyConnectionInvitation(payload: { connectionPayload: IConnectionDetails, url: string, orgId: string }): Promise { + return this.agentServiceService.createLegacyConnectionInvitation(payload.connectionPayload, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-send-credential-create-offer' }) async sendCredentialCreateOffer(payload: { issueData: IIssuanceCreateOffer; url: string; - apiKey: string; + orgId: string; }): Promise { - return this.agentServiceService.sendCredentialCreateOffer(payload.issueData, payload.url, payload.apiKey); + return this.agentServiceService.sendCredentialCreateOffer(payload.issueData, payload.url, payload.orgId); } //DONE @@ -88,8 +86,8 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-get-issued-credentials-by-credentialDefinitionId' }) - async getIssueCredentialsbyCredentialRecordId(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.getIssueCredentialsbyCredentialRecordId(payload.url, payload.apiKey); + async getIssueCredentialsbyCredentialRecordId(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.getIssueCredentialsbyCredentialRecordId(payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-get-proof-presentations' }) @@ -99,8 +97,8 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-get-proof-presentation-by-id' }) - async getProofPresentationById(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.getProofPresentationById(payload.url, payload.apiKey); + async getProofPresentationById(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.getProofPresentationById(payload.url, payload.orgId); } //DONE @@ -108,25 +106,25 @@ export class AgentServiceController { async sendProofRequest(payload: { proofRequestPayload: ISendProofRequestPayload; url: string; - apiKey: string; + orgId: string; }): Promise { - return this.agentServiceService.sendProofRequest(payload.proofRequestPayload, payload.url, payload.apiKey); + return this.agentServiceService.sendProofRequest(payload.proofRequestPayload, payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-verify-presentation' }) - async verifyPresentation(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.verifyPresentation(payload.url, payload.apiKey); + async verifyPresentation(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.verifyPresentation(payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-get-all-connections' }) - async getConnections(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.getConnections(payload.url, payload.apiKey); + async getConnections(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.getConnections(payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-get-connection-details-by-connectionId' }) - async getConnectionsByconnectionId(payload: { url: string, apiKey: string }): Promise { - return this.agentServiceService.getConnectionsByconnectionId(payload.url, payload.apiKey); + async getConnectionsByconnectionId(payload: { url: string, orgId: string }): Promise { + return this.agentServiceService.getConnectionsByconnectionId(payload.url, payload.orgId); } /** @@ -149,54 +147,54 @@ export class AgentServiceController { async sendOutOfBandProofRequest(payload: { proofRequestPayload: ISendProofRequestPayload; url: string; - apiKey: string; + orgId: string; }): Promise { - return this.agentServiceService.sendOutOfBandProofRequest(payload.proofRequestPayload, payload.url, payload.apiKey); + return this.agentServiceService.sendOutOfBandProofRequest(payload.proofRequestPayload, payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'get-agent-verified-proof-details' }) - async getVerifiedProofDetails(payload: { url: string; apiKey: string }): Promise { - return this.agentServiceService.getVerifiedProofDetails(payload.url, payload.apiKey); + async getVerifiedProofDetails(payload: { url: string; orgId: string }): Promise { + return this.agentServiceService.getVerifiedProofDetails(payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-schema-endorsement-request' }) async schemaEndorsementRequest(payload: { url: string; - apiKey: string; + orgId: string; requestSchemaPayload: object; }): Promise { - return this.agentServiceService.schemaEndorsementRequest(payload.url, payload.apiKey, payload.requestSchemaPayload); + return this.agentServiceService.schemaEndorsementRequest(payload.url, payload.orgId, payload.requestSchemaPayload); } @MessagePattern({ cmd: 'agent-credDef-endorsement-request' }) async credDefEndorsementRequest(payload: { url: string; - apiKey: string; + orgId: string; requestSchemaPayload: object; }): Promise { return this.agentServiceService.credDefEndorsementRequest( payload.url, - payload.apiKey, + payload.orgId, payload.requestSchemaPayload ); } //DONE @MessagePattern({ cmd: 'agent-sign-transaction' }) - async signTransaction(payload: { url: string; apiKey: string; signEndorsementPayload: object }): Promise { - return this.agentServiceService.signTransaction(payload.url, payload.apiKey, payload.signEndorsementPayload); + async signTransaction(payload: { url: string; orgId: string; signEndorsementPayload: object }): Promise { + return this.agentServiceService.signTransaction(payload.url, payload.orgId, payload.signEndorsementPayload); } //DONE @MessagePattern({ cmd: 'agent-submit-transaction' }) - async submitTransaction(payload: { url: string; apiKey: string; submitEndorsementPayload: object }): Promise { - return this.agentServiceService.sumbitTransaction(payload.url, payload.apiKey, payload.submitEndorsementPayload); + async submitTransaction(payload: { url: string; orgId: string; submitEndorsementPayload: object }): Promise { + return this.agentServiceService.sumbitTransaction(payload.url, payload.orgId, payload.submitEndorsementPayload); } //DONE @MessagePattern({ cmd: 'agent-out-of-band-credential-offer' }) - async outOfBandCredentialOffer(payload: { outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, apiKey: string }): Promise { - return this.agentServiceService.outOfBandCredentialOffer(payload.outOfBandIssuancePayload, payload.url, payload.apiKey); + async outOfBandCredentialOffer(payload: { outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, orgId: string }): Promise { + return this.agentServiceService.outOfBandCredentialOffer(payload.outOfBandIssuancePayload, payload.url, payload.orgId); } @MessagePattern({ cmd: 'delete-wallet' }) @@ -204,42 +202,37 @@ export class AgentServiceController { return this.agentServiceService.deleteWallet(payload.url, payload.apiKey); } - @MessagePattern({ cmd: 'get-org-agent-api-key' }) - async getOrgAgentApiKey(payload: { orgId: string }): Promise { - return this.agentServiceService.getOrgAgentApiKey(payload.orgId); - } - @MessagePattern({ cmd: 'agent-receive-invitation-url' }) async receiveInvitationUrl(payload: { url, - apiKey, + orgId, receiveInvitationUrl }): Promise { - return this.agentServiceService.receiveInvitationUrl(payload.receiveInvitationUrl, payload.url, payload.apiKey); + return this.agentServiceService.receiveInvitationUrl(payload.receiveInvitationUrl, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-receive-invitation' }) async receiveInvitation(payload: { url, - apiKey, + orgId, receiveInvitation }): Promise { - return this.agentServiceService.receiveInvitation(payload.receiveInvitation, payload.url, payload.apiKey); + return this.agentServiceService.receiveInvitation(payload.receiveInvitation, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-send-question' }) async sendQuestion(payload: { url, - apiKey, + orgId, questionPayload }): Promise { - return this.agentServiceService.sendQuestion(payload.questionPayload, payload.url, payload.apiKey); + return this.agentServiceService.sendQuestion(payload.questionPayload, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-get-question-answer-record' }) - async getQuestionAnswersRecord(payload: { url: string, apiKey: string }): Promise { - return this.agentServiceService.getQuestionAnswersRecord(payload.url, payload.apiKey); + async getQuestionAnswersRecord(payload: { url: string, orgId: string }): Promise { + return this.agentServiceService.getQuestionAnswersRecord(payload.url, payload.orgId); } @MessagePattern({ cmd: 'polygon-create-keys' }) @@ -248,4 +241,3 @@ export class AgentServiceController { } } - diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index d25f5a714..90aa40b4b 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -19,7 +19,35 @@ import * as dotenv from 'dotenv'; import * as fs from 'fs'; import { map } from 'rxjs/operators'; dotenv.config(); -import { IGetCredDefAgentRedirection, IConnectionDetails, IUserRequestInterface, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse, IProofPresentation, IAgentProofRequest, IPresentation, IReceiveInvitationUrl, IReceiveInvitation, IQuestionPayload, IDidCreate, IWallet, ITenantRecord, IPlatformAgent, LedgerListResponse, IOrgLedgers, IStoreOrgAgent } from './interface/agent-service.interface'; +import { + IGetCredDefAgentRedirection, + IConnectionDetails, + IUserRequestInterface, + IAgentSpinupDto, + IStoreOrgAgentDetails, + ITenantCredDef, + ITenantDto, + ITenantSchema, + IWalletProvision, + ISendProofRequestPayload, + IIssuanceCreateOffer, + IOutOfBandCredentialOffer, + IAgentSpinUpSatus, + ICreateTenant, + IAgentStatus, + ICreateOrgAgent, + IOrgAgentsResponse, + IProofPresentation, + IAgentProofRequest, + IPresentation, + IReceiveInvitationUrl, + IReceiveInvitation, + IQuestionPayload, + IDidCreate, + IWallet, + ITenantRecord, + LedgerListResponse +} from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; import { ledgers, org_agents, organisation, platform_config } from '@prisma/client'; @@ -37,12 +65,11 @@ import { IProofPresentationDetails } from '@credebl/common/interfaces/verificati import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { ledgerName } from '@credebl/common/cast.helper'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; - +import * as CryptoJS from 'crypto-js'; @Injectable() @WebSocketGateway() export class AgentServiceService { - private readonly logger = new Logger('WalletService'); constructor( @@ -51,24 +78,21 @@ export class AgentServiceService { private readonly connectionService: ConnectionService, @Inject('NATS_CLIENT') private readonly agentServiceProxy: ClientProxy, @Inject(CACHE_MANAGER) private cacheService: Cache - ) { } + ) {} async ReplaceAt(input, search, replace, start, end): Promise { - return input.slice(0, start) - + input.slice(start, end).replace(search, replace) - + input.slice(end); + return input.slice(0, start) + input.slice(start, end).replace(search, replace) + input.slice(end); } /** * Spinup the agent by organization - * @param agentSpinupDto - * @param user + * @param agentSpinupDto + * @param user * @returns Get agent status */ async walletProvision(agentSpinupDto: IAgentSpinupDto, user: IUserRequestInterface): Promise { let agentProcess: ICreateOrgAgent; try { - // Invoke an internal function to create wallet await this.processWalletProvision(agentSpinupDto, user); const agentStatusResponse = { @@ -77,7 +101,6 @@ export class AgentServiceService { return agentStatusResponse; } catch (error) { - // Invoke an internal function to handle error to create wallet this.handleErrorOnWalletProvision(agentSpinupDto, error, agentProcess); throw new RpcException(error.response ? error.response : error); @@ -90,17 +113,16 @@ export class AgentServiceService { let agentProcess: ICreateOrgAgent; let getOrgAgent; try { - const [platformConfig, getAgentType, ledgerIdData] = await Promise.all([ this.agentServiceRepository.getPlatformConfigDetails(), this.agentServiceRepository.getAgentTypeDetails(), - this.agentServiceRepository.getLedgerDetails(agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]) + this.agentServiceRepository.getLedgerDetails( + agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet] + ) ]); - + let orgData; if (!user?.userId && agentSpinupDto?.platformAdminEmail) { - - // Get Platform admin user by platform admin email platformAdminUser = await this.agentServiceRepository.getPlatfomAdminUser(agentSpinupDto?.platformAdminEmail); @@ -113,14 +135,12 @@ export class AgentServiceService { const platformAdminOrgDetails = await this.agentServiceRepository.getPlatfomOrg(agentSpinupDto?.orgName); if (agentSpinupDto.orgId) { - // Get organization details getOrgAgent = await this.agentServiceRepository.getAgentDetails(agentSpinupDto.orgId); // Get organization data by orgId orgData = await this.agentServiceRepository.getOrgDetails(agentSpinupDto.orgId); } else { - // Get platform organization details getOrgAgent = await this.agentServiceRepository.getAgentDetails(platformAdminOrgDetails); @@ -128,27 +148,28 @@ export class AgentServiceService { orgData = await this.agentServiceRepository.getOrgDetails(platformAdminOrgDetails); } - agentSpinupDto.ledgerId = agentSpinupDto.ledgerId?.length ? agentSpinupDto.ledgerId : ledgerIdData.map(ledger => ledger?.id); + agentSpinupDto.ledgerId = agentSpinupDto.ledgerId?.length + ? agentSpinupDto.ledgerId + : ledgerIdData.map((ledger) => ledger?.id); // Get genesis URL and ledger details const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(agentSpinupDto.ledgerId); if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { - throw new BadRequestException( - ResponseMessages.agent.error.walletAlreadyProcessing, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.walletAlreadyProcessing, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { - throw new ConflictException( - ResponseMessages.agent.error.walletAlreadyCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + throw new ConflictException(ResponseMessages.agent.error.walletAlreadyCreated, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } if (!agentSpinupDto.orgId) { - if (platformAdminOrgDetails) { agentSpinupDto.orgId = platformAdminOrgDetails; } @@ -156,7 +177,9 @@ export class AgentServiceService { agentSpinupDto.agentType = agentSpinupDto.agentType || getAgentType; agentSpinupDto.tenant = agentSpinupDto.tenant || false; - agentSpinupDto.ledgerName = agentSpinupDto.ledgerName?.length ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]; + agentSpinupDto.ledgerName = agentSpinupDto.ledgerName?.length + ? agentSpinupDto.ledgerName + : [Ledgers.Indicio_Demonet]; // Invoke function for validate platform configuration this.validatePlatformConfig(platformConfig); @@ -166,8 +189,14 @@ export class AgentServiceService { const apiEndpoint = platformConfig?.apiEndpoint; // Create payload for the wallet create and store payload - const walletProvisionPayload = await this.prepareWalletProvisionPayload(agentSpinupDto, externalIp, apiEndpoint, inboundEndpoint, ledgerDetails, orgData); - + const walletProvisionPayload = await this.prepareWalletProvisionPayload( + agentSpinupDto, + externalIp, + apiEndpoint, + inboundEndpoint, + ledgerDetails, + orgData + ); // Socket connection const socket: Socket = await this.initSocketConnection(`${process.env.SOCKET_HOST}`); @@ -177,8 +206,16 @@ export class AgentServiceService { agentProcess = await this.createOrgAgent(agentSpinUpStatus, userId); // AFJ agent spin-up - this._agentSpinup(walletProvisionPayload, agentSpinupDto, platformConfig?.sgApiKey, orgData, user, socket, agentSpinupDto.ledgerId, agentProcess); - + this._agentSpinup( + walletProvisionPayload, + agentSpinupDto, + platformConfig?.sgApiKey, + orgData, + user, + socket, + agentSpinupDto.ledgerId, + agentProcess + ); } catch (error) { this.handleErrorOnWalletProvision(agentSpinupDto, error, agentProcess); throw error; @@ -188,34 +225,34 @@ export class AgentServiceService { validatePlatformConfig(platformConfig: platform_config): void { if (!platformConfig) { this.logger.error(`Platform configuration is missing or invalid`); - throw new BadRequestException( - ResponseMessages.agent.error.platformConfiguration, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.platformConfiguration, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } if (!platformConfig.apiEndpoint) { this.logger.error(`API endpoint is missing in the platform configuration`); - throw new BadRequestException( - ResponseMessages.agent.error.apiEndpoint, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.apiEndpoint, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } if (!platformConfig.externalIp) { this.logger.error(`External IP is missing in the platform configuration`); - throw new BadRequestException( - ResponseMessages.agent.error.externalIp, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.externalIp, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } if (typeof platformConfig.externalIp !== 'string') { this.logger.error(`External IP must be a string`); - throw new BadRequestException( - ResponseMessages.agent.error.externalIp, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.externalIp, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } } @@ -223,10 +260,10 @@ export class AgentServiceService { try { if (!agentProcess) { this.logger.error(`Agent process is invalid or not in a completed state`); - throw new BadRequestException( - ResponseMessages.agent.error.externalIp, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.externalIp, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } } catch (error) { this.logger.error(`Error validating agent process: ${error.message}`); @@ -237,12 +274,10 @@ export class AgentServiceService { emitAgentSpinupInitiatedEvent(agentSpinupDto: IAgentSpinupDto, socket: Socket): void { try { if (agentSpinupDto.clientSocketId) { - socket.emit('agent-spinup-process-initiated', { clientId: agentSpinupDto.clientSocketId }); // Log or perform other actions after emitting the event this.logger.log(`Agent spinup initiated event emitted for orgId ${agentSpinupDto.orgId}`); } - } catch (error) { this.logger.error(`Error emitting agent-spinup-initiated event: ${error.message}`); throw error; @@ -257,7 +292,7 @@ export class AgentServiceService { ledgerDetails: ledgers[], orgData: organisation ): Promise { - const ledgerArray = ledgerDetails.map(ledger => ({ + const ledgerArray = ledgerDetails.map((ledger) => ({ genesisTransactions: ledger.poolConfig, indyNamespace: ledger.indyNamespace })); @@ -307,13 +342,16 @@ export class AgentServiceService { this.logger.log(`Organization agent created with status: ${agentSpinUpStatus}`); return agentProcess; } catch (error) { - this.logger.error(`Error creating organization agent: ${error.message}`); throw error; } } - private async handleErrorOnWalletProvision(agentSpinupDto: IAgentSpinupDto, error: Error, agentProcess: ICreateOrgAgent): Promise { + private async handleErrorOnWalletProvision( + agentSpinupDto: IAgentSpinupDto, + error: Error, + agentProcess: ICreateOrgAgent + ): Promise { if (agentProcess) { const socket = await this.initSocketConnection(`${process.env.SOCKET_HOST}`); @@ -329,42 +367,49 @@ export class AgentServiceService { try { socket.emit('error-in-wallet-creation-process', { clientId: agentSpinupDto.clientSocketId, error }); this.logger.error(`Error in wallet creation process emitted for orgId ${agentSpinupDto.orgId}: ${error.message}`); - } catch (emitError) { this.logger.error(`Error emitting error-in-wallet-creation-process event: ${emitError.message}`); throw emitError; } } - async _agentSpinup(walletProvisionPayload: IWalletProvision, agentSpinupDto: IAgentSpinupDto, orgApiKey: string, orgData: organisation, user: IUserRequestInterface, socket: Socket, ledgerId: string[], agentProcess: ICreateOrgAgent): Promise { + async _agentSpinup( + walletProvisionPayload: IWalletProvision, + agentSpinupDto: IAgentSpinupDto, + orgApiKey: string, + orgData: organisation, + user: IUserRequestInterface, + socket: Socket, + ledgerId: string[], + agentProcess: ICreateOrgAgent + ): Promise { let ledgerIdData = []; try { if (agentSpinupDto.method !== DidMethod.KEY && agentSpinupDto.method !== DidMethod.WEB) { + const { network } = agentSpinupDto; + const ledger = await ledgerName(network); + const ledgerList = (await this._getALlLedgerDetails()) as unknown as LedgerListResponse; + const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); + if (!isLedgerExist) { + throw new BadRequestException(ResponseMessages.agent.error.invalidLedger, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); + } - const { network } = agentSpinupDto; - const ledger = await ledgerName(network); - const ledgerList = await this._getALlLedgerDetails() as unknown as LedgerListResponse; - const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); - if (!isLedgerExist) { - throw new BadRequestException( - ResponseMessages.agent.error.invalidLedger, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); + ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); } - - ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); - } /** - * Invoke wallet create and provision with agent + * Invoke wallet create and provision with agent */ const walletProvision = await this._walletProvision(walletProvisionPayload); if (!walletProvision?.response) { this.logger.error(`Agent not able to spin-up`); - throw new BadRequestException( - ResponseMessages.agent.error.notAbleToSpinup, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.notAbleToSpinup, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } const agentDetails = walletProvision.response; const agentEndPoint = `${process.env.API_GATEWAY_PROTOCOL}://${agentDetails.agentEndPoint}`; @@ -379,10 +424,12 @@ export class AgentServiceService { socket.emit('invitation-url-creation-started', { clientId: agentSpinupDto.clientSocketId }); } + const encryptedToken = await this.tokenEncryption(agentDetails?.agentToken); + const agentPayload: IStoreOrgAgentDetails = { agentEndPoint, seed: agentSpinupDto.seed, - apiKey: agentDetails.agentToken, + apiKey: encryptedToken, agentsTypeId: agentSpinupDto?.agentType, orgId: orgData.id, walletName: agentSpinupDto.walletName, @@ -391,17 +438,18 @@ export class AgentServiceService { role: agentSpinupDto.role, network: agentSpinupDto.network, keyType: agentSpinupDto.keyType, - ledgerId: ledgerIdData ? ledgerIdData.map(item => item.id) : null, + ledgerId: ledgerIdData ? ledgerIdData.map((item) => item.id) : null, did: agentSpinupDto.did, id: agentProcess?.id }; /** - * Store organization agent details + * Store organization agent details */ const storeAgentDetails = await this._storeOrgAgentDetails(agentPayload); if (storeAgentDetails) { - - const filePath = `${process.cwd()}${process.env.AFJ_AGENT_TOKEN_PATH}${orgData.id}_${orgData.name.split(' ').join('_')}.json`; + const filePath = `${process.cwd()}${process.env.AFJ_AGENT_TOKEN_PATH}${orgData.id}_${orgData.name + .split(' ') + .join('_')}.json`; if (agentDetails?.agentToken) { fs.unlink(filePath, (err) => { if (err) { @@ -425,10 +473,10 @@ export class AgentServiceService { } } else { this.logger.error(`Agent not able to spin-up`); - throw new BadRequestException( - ResponseMessages.agent.error.notAbleToSpinup, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.notAbleToSpinup, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } } catch (error) { if (agentSpinupDto.clientSocketId) { @@ -437,7 +485,7 @@ export class AgentServiceService { if (agentProcess && agentProcess?.id) { /** - * If getting error remove organization agent + * If getting error remove organization agent */ await this.agentServiceRepository.removeOrgAgent(agentProcess?.id); } @@ -447,7 +495,6 @@ export class AgentServiceService { async _storeOrgAgentDetails(payload: IStoreOrgAgentDetails): Promise { try { - /** * Get orgaization agent type and agent details */ @@ -474,24 +521,36 @@ export class AgentServiceService { } } - private async _getAgentDid(payload: IStoreOrgAgentDetails): Promise { - const { agentEndPoint, apiKey, ledgerId, seed, keyType, method, network, role, did } = payload; + const { agentEndPoint, apiKey, ledgerId, seed, keyType, method, network, role, did } = payload; const writeDid = 'write-did'; const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(ledgerId); const agentDidWriteUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; return this._retryAgentSpinup(agentDidWriteUrl, apiKey, writeDid, seed, keyType, method, network, role, did); - } private async _getDidMethod(payload: IStoreOrgAgentDetails, agentDid: object): Promise { - const { agentEndPoint, apiKey, seed, keyType, method, network, role } = payload; + const { agentEndPoint, apiKey, seed, keyType, method, network, role } = payload; const getDidDoc = 'get-did-doc'; const getDidMethodUrl = `${agentEndPoint}${CommonConstants.URL_AGENT_GET_DID}/${agentDid['did']}`; - return this._retryAgentSpinup(getDidMethodUrl, apiKey, getDidDoc, seed, keyType, method, network, role, `${agentDid['did']}`); + return this._retryAgentSpinup( + getDidMethodUrl, + apiKey, + getDidDoc, + seed, + keyType, + method, + network, + role, + `${agentDid['did']}` + ); } - private _buildStoreOrgAgentData(payload: IStoreOrgAgentDetails, getDidMethod: object, orgAgentTypeId: string): IStoreOrgAgentDetails { + private _buildStoreOrgAgentData( + payload: IStoreOrgAgentDetails, + getDidMethod: object, + orgAgentTypeId: string + ): IStoreOrgAgentDetails { return { did: getDidMethod['didDocument']?.id, verkey: getDidMethod['didDocument']?.verificationMethod[0]?.publicKeyBase58, @@ -522,34 +581,49 @@ export class AgentServiceService { } if (payload && payload?.id) { - this.agentServiceRepository.removeOrgAgent(payload?.id); } this.logger.error(`[_storeOrgAgentDetails] - Error in store agent details : ${JSON.stringify(error)}`); } - - async _retryAgentSpinup(agentUrl: string, apiKey: string, agentApiState: string, seed: string, keyType: string, method: string, network: string, role: string, did: string): Promise { + async _retryAgentSpinup( + agentUrl: string, + apiKey: string, + agentApiState: string, + seed: string, + keyType: string, + method: string, + network: string, + role: string, + did: string + ): Promise { const retryOptions = { retries: 10 }; try { + const getDcryptedToken = await this.commonService.decryptPassword(apiKey); return retry(async () => { if (agentApiState === 'write-did') { - return this.commonService.httpPost(agentUrl, { seed, keyType, method, network, role, did}, { headers: { 'authorization': apiKey } }); + return this.commonService.httpPost( + agentUrl, + { seed, keyType, method, network, role, did }, + { headers: { authorization: getDcryptedToken } } + ); } else if (agentApiState === 'get-did-doc') { - return this.commonService.httpGet(agentUrl, { headers: { 'authorization': apiKey } }); + return this.commonService.httpGet(agentUrl, { headers: { authorization: getDcryptedToken } }); } }, retryOptions); - } catch (error) { throw error; } } - - async _createLegacyConnectionInvitation(orgId: string, user: IUserRequestInterface, label: string): Promise<{ + async _createLegacyConnectionInvitation( + orgId: string, + user: IUserRequestInterface, + label: string + ): Promise<{ response; }> { try { @@ -557,22 +631,7 @@ export class AgentServiceService { cmd: 'create-connection' }; const payload = { orgId, user, label }; - return this.agentServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ( - { - response - })) - ).toPromise() - .catch(error => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.message - }, error.error); - }); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in create-connection in wallet provision : ${JSON.stringify(error)}`); } @@ -586,28 +645,12 @@ export class AgentServiceService { cmd: 'get-all-ledgers' }; const payload = {}; - return this.agentServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ( - { - response - })) - ).toPromise() - .catch(error => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.message - }, error.error); - }); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in while fetching all the ledger details : ${JSON.stringify(error)}`); } } - async _walletProvision(payload: IWalletProvision): Promise<{ response; }> { @@ -615,22 +658,7 @@ export class AgentServiceService { const pattern = { cmd: 'wallet-provisioning' }; - return this.agentServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ( - { - response - })) - ).toPromise() - .catch(error => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.message - }, error.error); - }); + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`error in wallet provision : ${JSON.stringify(error)}`); throw error; @@ -639,13 +667,12 @@ export class AgentServiceService { /** * Create tenant (Shared agent) - * @param payload - * @param user + * @param payload + * @param user * @returns Get agent status */ async createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { try { - const agentStatusResponse = { agentSpinupStatus: AgentSpinUpStatus.PROCESSED }; @@ -654,18 +681,18 @@ export class AgentServiceService { if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { this.logger.error(`Your wallet is already been created.`); - throw new ConflictException( - ResponseMessages.agent.error.walletAlreadyCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + throw new ConflictException(ResponseMessages.agent.error.walletAlreadyCreated, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { this.logger.error(`Your wallet is already processing.`); - throw new ConflictException( - ResponseMessages.agent.error.walletAlreadyProcessing, - { cause: new Error(), description: ResponseMessages.errorMessages.conflict } - ); + throw new ConflictException(ResponseMessages.agent.error.walletAlreadyProcessing, { + cause: new Error(), + description: ResponseMessages.errorMessages.conflict + }); } // Create tenant @@ -679,123 +706,124 @@ export class AgentServiceService { /** * Create tenant (Shared agent) - * @param payload - * @param user + * @param payload + * @param user * @returns Get agent status */ - async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { - let agentProcess; - let ledgerIdData = []; - try { - if (payload.method !== DidMethod.KEY && payload.method !== DidMethod.WEB) { - - const { network } = payload; - const ledger = await ledgerName(network); - const ledgerList = await this._getALlLedgerDetails() as unknown as LedgerListResponse; - const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); - if (!isLedgerExist) { - throw new BadRequestException( - ResponseMessages.agent.error.invalidLedger, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } - - ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); + async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { + let agentProcess; + let ledgerIdData = []; + try { + if (payload.method !== DidMethod.KEY && payload.method !== DidMethod.WEB) { + const { network } = payload; + const ledger = await ledgerName(network); + const ledgerList = (await this._getALlLedgerDetails()) as unknown as LedgerListResponse; + const isLedgerExist = ledgerList.response.find((existingLedgers) => existingLedgers.name === ledger); + if (!isLedgerExist) { + throw new BadRequestException(ResponseMessages.agent.error.invalidLedger, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } - const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; - - // Create and stored agent details - agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); - - // Get platform admin details - const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); - - payload.endpoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - // Create tenant wallet and DID - const tenantDetails = await this.createTenantAndNotify(payload, platformAdminSpinnedUp); - if (!tenantDetails?.walletResponseDetails?.id || !tenantDetails?.DIDCreationOption?.did) { - this.logger.error(`Error in getting wallet id and wallet did`); - throw new NotFoundException( - ResponseMessages.agent.error.notAbleToSpinUpAgent, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } + ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); + } - if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { - this.logger.error(`Platform-admin agent is not spun-up`); - throw new NotFoundException( - ResponseMessages.agent.error.platformAdminNotAbleToSpinp, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } - // Get shared agent type - const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); - // Get agent type details - const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); - - const storeOrgAgentData: IStoreOrgAgentDetails = { - did: tenantDetails.DIDCreationOption.did, - isDidPublic: true, - agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, - agentsTypeId: agentTypeId, - orgId: payload.orgId, - agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, - orgAgentTypeId, - tenantId: tenantDetails.walletResponseDetails['id'], - walletName: payload.label, - ledgerId: ledgerIdData ? ledgerIdData.map(item => item.id) : null, - id: agentProcess?.id - }; - - // Get organization data - const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); - - this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); - - await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); - - this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); - - // Create the legacy connection invitation - await this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); - - this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); - - } catch (error) { - this.handleError(error, payload.clientSocketId); - - if (agentProcess && agentProcess?.id) { - this.agentServiceRepository.removeOrgAgent(agentProcess?.id); - } - throw error; + const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; + + // Create and stored agent details + agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); + + // Get platform admin details + const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); + + payload.endpoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + // Create tenant wallet and DID + const tenantDetails = await this.createTenantAndNotify(payload, platformAdminSpinnedUp); + if (!tenantDetails?.walletResponseDetails?.id || !tenantDetails?.DIDCreationOption?.did) { + this.logger.error(`Error in getting wallet id and wallet did`); + throw new NotFoundException(ResponseMessages.agent.error.notAbleToSpinUpAgent, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); + } + + if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { + this.logger.error(`Platform-admin agent is not spun-up`); + throw new NotFoundException(ResponseMessages.agent.error.platformAdminNotAbleToSpinp, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); + } + // Get shared agent type + const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); + // Get agent type details + const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); + + const storeOrgAgentData: IStoreOrgAgentDetails = { + did: tenantDetails.DIDCreationOption.did, + isDidPublic: true, + didDoc: tenantDetails.DIDCreationOption.didDocument || tenantDetails.DIDCreationOption.didDoc, //changed the didDoc into didDocument + agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, + agentsTypeId: agentTypeId, + orgId: payload.orgId, + agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, + orgAgentTypeId, + tenantId: tenantDetails.walletResponseDetails['id'], + walletName: payload.label, + ledgerId: ledgerIdData ? ledgerIdData.map((item) => item.id) : null, + id: agentProcess?.id + }; + + // Get organization data + const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); + + this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); + + await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); + + this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); + + // Create the legacy connection invitation + await this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); + + this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); + } catch (error) { + this.handleError(error, payload.clientSocketId); + + if (agentProcess && agentProcess?.id) { + this.agentServiceRepository.removeOrgAgent(agentProcess?.id); } + throw error; } + } /** * Create wallet - * @param payload + * @param payload * @returns wallet details */ async createWallet(payload: IWallet): Promise { try { - const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( + CommonConstants.PLATFORM_ADMIN_ORG + ); - const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); - const { label } = payload; - const createTenantOptions = { - config: { label } - }; - - const tenantDetails = await this.commonService.httpPost( - `${getPlatformAgentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, - createTenantOptions, - { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } } - ); + const { label } = payload; + const createTenantOptions = { + config: { label } + }; - return tenantDetails; + const tenantDetails = await this.commonService.httpPost( + `${getPlatformAgentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, + createTenantOptions, + { headers: { authorization: getDcryptedToken } } + ); + return tenantDetails; } catch (error) { this.logger.error(`error in create wallet : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -804,64 +832,62 @@ export class AgentServiceService { /** * Create did - * @param payload + * @param payload * @returns did and didDocument */ - async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { - try { + async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { + try { const agentDetails = await this.agentServiceRepository.getOrgAgentDetails(orgId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this.getOrgAgentApiKey(orgId); - } + + const getApiKey = await this.getOrgAgentApiKey(orgId); const getOrgAgentType = await this.agentServiceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); let url; - if (getOrgAgentType.agent === OrgAgentType.DEDICATED) { - url = `${agentDetails.agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; + url = `${agentDetails.agentEndPoint}${CommonConstants.URL_AGENT_WRITE_DID}`; } else if (getOrgAgentType.agent === OrgAgentType.SHARED) { - url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`; + url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`; } - const didDetails = await this.commonService.httpPost(url, payload, - { headers: { 'authorization': apiKey } } - ); + const didDetails = await this.commonService.httpPost(url, payload, { headers: { authorization: getApiKey } }); return didDetails; - } catch (error) { - this.logger.error(`error in create did : ${JSON.stringify(error)}`); + this.logger.error(`error in create did : ${JSON.stringify(error)}`); - if (error?.response?.error?.message) { - throw new RpcException({ - statusCode: error?.response?.status, - error: error?.response?.error?.message - }); - } else { - throw new RpcException(error.response ? error.response : error); + if (error?.response?.error?.message) { + throw new RpcException({ + statusCode: error?.response?.status, + error: error?.response?.error?.message + }); + } else { + throw new RpcException(error.response ? error.response : error); + } } - } -} - /** + /** * @returns Secp256k1 key pair for polygon DID */ - async createSecp256k1KeyPair(orgId:string): Promise { - try { - const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( - CommonConstants.PLATFORM_ADMIN_ORG - ); + async createSecp256k1KeyPair(orgId: string): Promise { + try { + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( + CommonConstants.PLATFORM_ADMIN_ORG + ); - const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + const getPlatformAgentEndPoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); - const url = `${getPlatformAgentEndPoint}${CommonConstants.CREATE_POLYGON_SECP256k1_KEY}`; + const url = `${getPlatformAgentEndPoint}${CommonConstants.CREATE_POLYGON_SECP256k1_KEY}`; - const createKeyPairResponse = await this.commonService.httpPost(url, {}, { headers: { 'authorization': platformAdminSpinnedUp.org_agents[0].apiKey } }); - return createKeyPairResponse; - } catch (error) { - this.logger.error(`error in createSecp256k1KeyPair : ${JSON.stringify(error)}`); - throw new RpcException(error.response ? error.response : error); - } - } + const createKeyPairResponse = await this.commonService.httpPost( + url, + {}, + { headers: { authorization: getDcryptedToken } } + ); + return createKeyPairResponse; + } catch (error) { + this.logger.error(`error in createSecp256k1KeyPair : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } private async getPlatformAdminAndNotify(clientSocketId: string | undefined): Promise { const socket = await this.createSocketInstance(); @@ -869,14 +895,16 @@ export class AgentServiceService { socket.emit('agent-spinup-process-initiated', { clientId: clientSocketId }); } - const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent( + CommonConstants.PLATFORM_ADMIN_ORG + ); if (!platformAdminSpinnedUp) { this.logger.error(`Agent not able to spin-up`); - throw new BadRequestException( - ResponseMessages.agent.error.notAbleToSpinp, - { cause: new Error(), description: ResponseMessages.errorMessages.serverError } - ); + throw new BadRequestException(ResponseMessages.agent.error.notAbleToSpinp, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); } return platformAdminSpinnedUp; @@ -884,15 +912,15 @@ export class AgentServiceService { /** * Create tenant on the agent - * @param payload - * @param ledgerIds - * @param platformAdminSpinnedUp + * @param payload + * @param ledgerIds + * @param platformAdminSpinnedUp * @returns Get tanant status */ - + // eslint-disable-next-line @typescript-eslint/no-explicit-any private async createTenantAndNotify(payload: ITenantDto, platformAdminSpinnedUp: IOrgAgentsResponse): Promise { - const WalletSetupPayload = {...payload}; + const WalletSetupPayload = { ...payload }; const socket = await this.createSocketInstance(); if (WalletSetupPayload.clientSocketId) { socket.emit('agent-spinup-process-initiated', { clientId: WalletSetupPayload.clientSocketId }); @@ -904,64 +932,67 @@ export class AgentServiceService { delete WalletSetupPayload.orgId; delete WalletSetupPayload.ledgerId; - - const walletResponseDetails = await this._createTenantWallet(walletLabel, platformAdminSpinnedUp.org_agents[0].agentEndPoint, platformAdminSpinnedUp.org_agents[0].apiKey); - if (!walletResponseDetails && !walletResponseDetails.id) { - throw new InternalServerErrorException('Error while creating the wallet'); - } + const getDcryptedToken = await this.commonService.decryptPassword(platformAdminSpinnedUp?.org_agents[0].apiKey); + const walletResponseDetails = await this._createTenantWallet( + walletLabel, + platformAdminSpinnedUp.org_agents[0].agentEndPoint, + getDcryptedToken + ); + if (!walletResponseDetails && !walletResponseDetails.id) { + throw new InternalServerErrorException('Error while creating the wallet'); + } const didCreateOption = { didPayload: WalletSetupPayload, agentEndpoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, - apiKey: platformAdminSpinnedUp.org_agents[0].apiKey, + apiKey: getDcryptedToken, tenantId: walletResponseDetails.id }; const DIDCreationOption = await this._createDID(didCreateOption); if (!DIDCreationOption) { throw new InternalServerErrorException('Error while creating the wallet'); - } - - return {walletResponseDetails, DIDCreationOption}; + } + + return { walletResponseDetails, DIDCreationOption }; } -// + // - /** + /** * Create tenant wallet on the agent * @param createTenantWalletPayload * @returns Get tanant status */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private async _createTenantWallet(label, endpoint, agentApiKey): Promise { //remove any - - - const createTenantOptions = { - config: { label } - }; - // Invoke an API request from the agent to create multi-tenant agent - const tenantDetails = await this.commonService.httpPost( - `${endpoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, - createTenantOptions, - { headers: { authorization: agentApiKey } } - ); - return tenantDetails; -} + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private async _createTenantWallet(label, endpoint, agentApiKey): Promise { + //remove any + + const createTenantOptions = { + config: { label } + }; + // Invoke an API request from the agent to create multi-tenant agent + const tenantDetails = await this.commonService.httpPost( + `${endpoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, + createTenantOptions, + { headers: { authorization: agentApiKey } } + ); + return tenantDetails; + } - /** + /** * Create tenant wallet on the agent * @param _createDID * @returns Get tanant status */ - private async _createDID(didCreateOption): Promise { - - const {didPayload, agentEndpoint, apiKey, tenantId} = didCreateOption; - // Invoke an API request from the agent to create multi-tenant agent - const didDetails = await this.commonService.httpPost( - `${agentEndpoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${tenantId}`, - didPayload, - { headers: { authorization: apiKey } } - ); - return didDetails; -} + private async _createDID(didCreateOption): Promise { + const { didPayload, agentEndpoint, apiKey, tenantId } = didCreateOption; + // Invoke an API request from the agent to create multi-tenant agent + const didDetails = await this.commonService.httpPost( + `${agentEndpoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${tenantId}`, + didPayload, + { headers: { authorization: apiKey } } + ); + return didDetails; + } private async createSocketInstance(): Promise { return io(`${process.env.SOCKET_HOST}`, { reconnection: true, @@ -990,10 +1021,10 @@ export class AgentServiceService { async createSchema(payload: ITenantSchema): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(payload.orgId); let schemaResponse; if (OrgAgentType.DEDICATED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_CREATE_SCHEMA}`; const schemaPayload = { attributes: payload.attributes, @@ -1001,35 +1032,34 @@ export class AgentServiceService { name: payload.name, issuerId: payload.issuerId }; - schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }) - .catch(error => { - throw new InternalServerErrorException( - ResponseMessages.agent.error.agentDown, - { cause: new Error(), description: ResponseMessages.errorMessages.serverError } - ); + schemaResponse = await this.commonService + .httpPost(url, schemaPayload, { headers: { authorization: getApiKey } }) + .then(async (schema) => schema) + .catch((error) => { + throw new InternalServerErrorException(ResponseMessages.agent.error.agentDown, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); }); - } else if (OrgAgentType.SHARED === payload.agentType) { - - const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_SCHEMA}`.replace('#', `${payload.tenantId}`); + const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_SCHEMA}`.replace( + '#', + `${payload.tenantId}` + ); const schemaPayload = { attributes: payload.payload.attributes, version: payload.payload.version, name: payload.payload.name, issuerId: payload.payload.issuerId }; - schemaResponse = await this.commonService.httpPost(url, schemaPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }) - .catch(error => { - throw new InternalServerErrorException( - ResponseMessages.agent.error.agentDown, - { cause: new Error(), description: ResponseMessages.errorMessages.serverError } - ); + schemaResponse = await this.commonService + .httpPost(url, schemaPayload, { headers: { authorization: getApiKey } }) + .then(async (schema) => schema) + .catch((error) => { + throw new InternalServerErrorException(ResponseMessages.agent.error.agentDown, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); }); } return schemaResponse; @@ -1043,20 +1073,21 @@ export class AgentServiceService { try { let schemaResponse; + const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_SCHEMA_BY_ID.replace('#', `${payload.schemaId}`)}`; - schemaResponse = await this.commonService.httpGet(url, payload.schemaId) - .then(async (schema) => { - return schema; - }); - + const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_SCHEMA_BY_ID.replace( + '#', + `${payload.schemaId}` + )}`; + schemaResponse = await this.commonService.httpGet(url, payload.schemaId).then(async (schema) => schema); } else if (OrgAgentType.SHARED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}`.replace('@', `${payload.payload.schemaId}`).replace('#', `${payload.tenantId}`); + const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}` + .replace('@', `${payload.payload.schemaId}`) + .replace('#', `${payload.tenantId}`); - schemaResponse = await this.commonService.httpGet(url, { headers: { 'authorization': payload.apiKey } }) - .then(async (schema) => { - return schema; - }); + schemaResponse = await this.commonService + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (schema) => schema); } return schemaResponse; } catch (error) { @@ -1069,8 +1100,8 @@ export class AgentServiceService { try { let credDefResponse; + const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === String(payload.agentType)) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_CREATE_CRED_DEF}`; const credDefPayload = { tag: payload.tag, @@ -1078,22 +1109,22 @@ export class AgentServiceService { issuerId: payload.issuerId }; - credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); - + credDefResponse = await this.commonService + .httpPost(url, credDefPayload, { headers: { authorization: getApiKey } }) + .then(async (credDef) => credDef); } else if (OrgAgentType.SHARED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_CRED_DEF}`.replace('#', `${payload.tenantId}`); + const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_CRED_DEF}`.replace( + '#', + `${payload.tenantId}` + ); const credDefPayload = { tag: payload.payload.tag, schemaId: payload.payload.schemaId, issuerId: payload.payload.issuerId }; - credDefResponse = await this.commonService.httpPost(url, credDefPayload, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); + credDefResponse = await this.commonService + .httpPost(url, credDefPayload, { headers: { authorization: getApiKey } }) + .then(async (credDef) => credDef); } return credDefResponse; @@ -1107,19 +1138,22 @@ export class AgentServiceService { try { let credDefResponse; + const getApiKey = await this.getOrgAgentApiKey(payload.orgId); if (OrgAgentType.DEDICATED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_CRED_DEF_BY_ID.replace('#', `${payload.credentialDefinitionId}`)}`; - credDefResponse = await this.commonService.httpGet(url, payload.credentialDefinitionId) - .then(async (credDef) => { - return credDef; - }); - + const url = `${payload.agentEndPoint}${CommonConstants.URL_SCHM_GET_CRED_DEF_BY_ID.replace( + '#', + `${payload.credentialDefinitionId}` + )}`; + credDefResponse = await this.commonService + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (credDef) => credDef); } else if (OrgAgentType.SHARED === payload.agentType) { - const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CRED_DEF}`.replace('@', `${payload.payload.credentialDefinitionId}`).replace('#', `${payload.tenantId}`); - credDefResponse = await this.commonService.httpGet(url, { headers: { 'authorization': payload.apiKey } }) - .then(async (credDef) => { - return credDef; - }); + const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CRED_DEF}` + .replace('@', `${payload.payload.credentialDefinitionId}`) + .replace('#', `${payload.tenantId}`); + credDefResponse = await this.commonService + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (credDef) => credDef); } return credDefResponse; } catch (error) { @@ -1128,13 +1162,16 @@ export class AgentServiceService { } } - async createLegacyConnectionInvitation(connectionPayload: IConnectionDetails, url: string, apiKey: string): Promise { + async createLegacyConnectionInvitation( + connectionPayload: IConnectionDetails, + url: string, + orgId: string + ): Promise { try { - - + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpPost(url, connectionPayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, connectionPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return data; } catch (error) { @@ -1143,11 +1180,12 @@ export class AgentServiceService { } } - async sendCredentialCreateOffer(issueData: IIssuanceCreateOffer, url: string, apiKey: string): Promise { + async sendCredentialCreateOffer(issueData: IIssuanceCreateOffer, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpPost(url, issueData, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, issueData, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return data; } catch (error) { this.logger.error(`Error in sendCredentialCreateOffer in agent service : ${JSON.stringify(error)}`); @@ -1157,8 +1195,8 @@ export class AgentServiceService { async getProofPresentations(url: string, apiKey: string): Promise { try { const getProofPresentationsData = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpGet(url, { headers: { authorization: apiKey } }) + .then(async (response) => response); return getProofPresentationsData; } catch (error) { @@ -1170,8 +1208,8 @@ export class AgentServiceService { async getIssueCredentials(url: string, apiKey: string): Promise { try { const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpGet(url, { headers: { authorization: apiKey } }) + .then(async (response) => response); return data; } catch (error) { this.logger.error(`Error in getIssueCredentials in agent service : ${JSON.stringify(error)}`); @@ -1179,13 +1217,9 @@ export class AgentServiceService { } } - async getProofPresentationById(url: string, apiKey: string): Promise { + async getProofPresentationById(url: string, orgId: string): Promise { try { - const getProofPresentationById = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); - + const getProofPresentationById = await this.agentCall(url, orgId); return getProofPresentationById; } catch (error) { this.logger.error(`Error in proof presentation by id in agent service : ${JSON.stringify(error)}`); @@ -1193,11 +1227,12 @@ export class AgentServiceService { } } - async getIssueCredentialsbyCredentialRecordId(url: string, apiKey: string): Promise { + async getIssueCredentialsbyCredentialRecordId(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return data; } catch (error) { this.logger.error(`Error in getIssueCredentialsbyCredentialRecordId in agent service : ${JSON.stringify(error)}`); @@ -1205,11 +1240,16 @@ export class AgentServiceService { } } - async sendProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, apiKey: string): Promise { + async sendProofRequest( + proofRequestPayload: ISendProofRequestPayload, + url: string, + orgId: string + ): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const sendProofRequest = await this.commonService - .httpPost(url, proofRequestPayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, proofRequestPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return sendProofRequest; } catch (error) { this.logger.error(`Error in send proof request in agent service : ${JSON.stringify(error)}`); @@ -1217,12 +1257,13 @@ export class AgentServiceService { } } - async verifyPresentation(url: string, apiKey: string): Promise { + async verifyPresentation(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const verifyPresentation = await this.commonService - .httpPost(url, '', { headers: { 'authorization': apiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); + .httpPost(url, '', { headers: { authorization: getApiKey } }) + .then(async (response) => response) + .catch((error) => this.handleAgentSpinupStatusErrors(error)); return verifyPresentation; } catch (error) { this.logger.error(`Error in verify proof presentation in agent service : ${JSON.stringify(error)}`); @@ -1230,11 +1271,12 @@ export class AgentServiceService { } } - async getConnections(url: string, apiKey: string): Promise { + async getConnections(url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return data; } catch (error) { this.logger.error(`Error in getConnections in agent service : ${JSON.stringify(error)}`); @@ -1242,67 +1284,52 @@ export class AgentServiceService { } } - async getConnectionsByconnectionId(url: string, apiKey: string): Promise { - + async getConnectionsByconnectionId(url: string, orgId: string): Promise { try { - const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); - - return data; + const getConnectionsByconnectionId = await this.agentCall(url, orgId); + return getConnectionsByconnectionId; } catch (error) { this.logger.error(`Error in getConnectionsByconnectionId in agent service : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } - } /** * Get agent health - * @param orgId + * @param orgId * @returns agent status */ async getAgentHealthDetails(orgId: string): Promise { try { - // Get organization agent details const orgAgentDetails: org_agents = await this.agentServiceRepository.getOrgAgentDetails(orgId); - let agentApiKey; if (orgAgentDetails) { - - agentApiKey = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { - agentApiKey = await this.getOrgAgentApiKey(orgId); - } - - if (agentApiKey === undefined || null) { - agentApiKey = await this.getOrgAgentApiKey(orgId); - } + agentApiKey = await this.getOrgAgentApiKey(orgId); } if (!orgAgentDetails) { - throw new NotFoundException( - ResponseMessages.agent.error.agentNotExists, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); + throw new NotFoundException(ResponseMessages.agent.error.agentNotExists, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } if (!orgAgentDetails?.agentEndPoint) { - throw new NotFoundException( - ResponseMessages.agent.error.agentUrl, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); + throw new NotFoundException(ResponseMessages.agent.error.agentUrl, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } // Invoke an API request from the agent to assess its current status const agentHealthData = await this.commonService - .httpGet(`${orgAgentDetails.agentEndPoint}${CommonConstants.URL_AGENT_STATUS}`, { headers: { 'authorization': agentApiKey } }) - .then(async response => response); + .httpGet(`${orgAgentDetails.agentEndPoint}${CommonConstants.URL_AGENT_STATUS}`, { + headers: { authorization: agentApiKey } + }) + .then(async (response) => response); return agentHealthData; - } catch (error) { this.logger.error(`Agent health details : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -1313,18 +1340,22 @@ export class AgentServiceService { try { const getLedgerConfigData = await this.agentServiceRepository.getLedgerConfigByOrgId(); return getLedgerConfigData; - } catch (error) { this.logger.error(`Error in send out of band proof request in agent service : ${JSON.stringify(error)}`); throw error; } } - async sendOutOfBandProofRequest(proofRequestPayload: ISendProofRequestPayload, url: string, apiKey: string): Promise { + async sendOutOfBandProofRequest( + proofRequestPayload: ISendProofRequestPayload, + url: string, + orgId: string + ): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const sendProofRequest = await this.commonService - .httpPost(url, proofRequestPayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, proofRequestPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return sendProofRequest; } catch (error) { this.logger.error(`Error in send out of band proof request in agent service : ${JSON.stringify(error)}`); @@ -1332,25 +1363,22 @@ export class AgentServiceService { } } - async getVerifiedProofDetails(url: string, apiKey: string): Promise { + async getVerifiedProofDetails(url: string, orgId: string): Promise { try { - const getVerifiedProofData = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); - - return getVerifiedProofData; + const getVerifiedProofDetails = await this.agentCall(url, orgId); + return getVerifiedProofDetails; } catch (error) { this.logger.error(`Error in get verified proof details in agent service : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } } - async schemaEndorsementRequest(url: string, apiKey: string, requestSchemaPayload: object): Promise { + async schemaEndorsementRequest(url: string, orgId: string, requestSchemaPayload: object): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const schemaRequest = await this.commonService - .httpPost(url, requestSchemaPayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, requestSchemaPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return schemaRequest; } catch (error) { this.logger.error(`Error in schema endorsement request in agent service : ${JSON.stringify(error)}`); @@ -1358,23 +1386,27 @@ export class AgentServiceService { } } - async credDefEndorsementRequest(url: string, apiKey: string, requestSchemaPayload: object): Promise { + async credDefEndorsementRequest(url: string, orgId: string, requestSchemaPayload: object): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const credDefRequest = await this.commonService - .httpPost(url, requestSchemaPayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, requestSchemaPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return credDefRequest; } catch (error) { - this.logger.error(`Error in credential-definition endorsement request in agent service : ${JSON.stringify(error)}`); + this.logger.error( + `Error in credential-definition endorsement request in agent service : ${JSON.stringify(error)}` + ); throw error; } } - async signTransaction(url: string, apiKey: string, signEndorsementPayload: object): Promise { + async signTransaction(url: string, orgId: string, signEndorsementPayload: object): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const signEndorsementTransaction = await this.commonService - .httpPost(url, signEndorsementPayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, signEndorsementPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return signEndorsementTransaction; } catch (error) { @@ -1383,12 +1415,12 @@ export class AgentServiceService { } } - async sumbitTransaction(url: string, apiKey: string, submitEndorsementPayload: object): Promise { + async sumbitTransaction(url: string, orgId: string, submitEndorsementPayload: object): Promise { try { - + const getApiKey = await this.getOrgAgentApiKey(orgId); const signEndorsementTransaction = await this.commonService - .httpPost(url, submitEndorsementPayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, submitEndorsementPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return signEndorsementTransaction; } catch (error) { @@ -1397,11 +1429,16 @@ export class AgentServiceService { } } - async outOfBandCredentialOffer(outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, apiKey: string): Promise { + async outOfBandCredentialOffer( + outOfBandIssuancePayload: IOutOfBandCredentialOffer, + url: string, + orgId: string + ): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const sendOutOfbandCredentialOffer = await this.commonService - .httpPost(url, outOfBandIssuancePayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, outOfBandIssuancePayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return sendOutOfbandCredentialOffer; } catch (error) { this.logger.error(`Error in out-of-band credential in agent service : ${JSON.stringify(error)}`); @@ -1409,14 +1446,11 @@ export class AgentServiceService { } } - async deleteWallet( - url: string, - apiKey: string - ): Promise { + async deleteWallet(url: string, apiKey: string): Promise { try { const deleteWallet = await this.commonService - .httpDelete(url, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpDelete(url, { headers: { authorization: apiKey } }) + .then(async (response) => response); return deleteWallet; } catch (error) { this.logger.error(`Error in delete wallet in agent service : ${JSON.stringify(error)}`); @@ -1424,11 +1458,12 @@ export class AgentServiceService { } } - async receiveInvitationUrl(receiveInvitationUrl: IReceiveInvitationUrl, url: string, apiKey: string): Promise { + async receiveInvitationUrl(receiveInvitationUrl: IReceiveInvitationUrl, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const receiveInvitationUrlRes = await this.commonService - .httpPost(url, receiveInvitationUrl, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, receiveInvitationUrl, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return receiveInvitationUrlRes; } catch (error) { this.logger.error(`Error in receive invitation in agent service : ${JSON.stringify(error)}`); @@ -1436,11 +1471,12 @@ export class AgentServiceService { } } - async receiveInvitation(receiveInvitation: IReceiveInvitation, url: string, apiKey: string): Promise { + async receiveInvitation(receiveInvitation: IReceiveInvitation, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const receiveInvitationRes = await this.commonService - .httpPost(url, receiveInvitation, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, receiveInvitation, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return receiveInvitationRes; } catch (error) { this.logger.error(`Error in receive invitation in agent service : ${JSON.stringify(error)}`); @@ -1450,54 +1486,53 @@ export class AgentServiceService { async getOrgAgentApiKey(orgId: string): Promise { try { - let agentApiKey; - const orgAgentApiKey = await this.agentServiceRepository.getAgentApiKey(orgId); - - - const orgAgentId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); - if (orgAgentApiKey?.orgAgentTypeId === orgAgentId) { - const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); - - const [orgAgentData] = platformAdminSpinnedUp.org_agents; - const { apiKey } = orgAgentData; - if (!platformAdminSpinnedUp) { - throw new InternalServerErrorException('Agent not able to spin-up'); + const orgAgentApiKey = await this.agentServiceRepository.getAgentApiKey(orgId); + const orgAgentId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); + const cacheKey = orgAgentApiKey?.orgAgentTypeId === orgAgentId ? CommonConstants.CACHE_SHARED_APIKEY_KEY : CommonConstants.CACHE_APIKEY_KEY; + + let apiKey = await this.cacheService.get(cacheKey); + if (!apiKey) { + if (orgAgentApiKey?.orgAgentTypeId === orgAgentId) { + const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); + if (!platformAdminSpinnedUp) { + throw new InternalServerErrorException('Agent not able to spin-up'); + } + apiKey = platformAdminSpinnedUp.org_agents[0]?.apiKey; + } else { + apiKey = orgAgentApiKey?.apiKey; + } + if (!apiKey) { + throw new NotFoundException(ResponseMessages.agent.error.apiKeyNotExist); + } + await this.cacheService.set(cacheKey, apiKey, 0); } - agentApiKey = apiKey; - - } else { - agentApiKey = orgAgentApiKey?.apiKey; - } - - if (!agentApiKey) { - throw new NotFoundException(ResponseMessages.agent.error.apiKeyNotExist); - } - await this.cacheService.set(CommonConstants.CACHE_APIKEY_KEY, agentApiKey, CommonConstants.CACHE_TTL_SECONDS); - return agentApiKey; - + const decryptedToken = await this.commonService.decryptPassword(apiKey); + return decryptedToken; } catch (error) { - this.logger.error(`Agent api key details : ${JSON.stringify(error)}`); - throw error; + this.logger.error(`Agent api key details : ${JSON.stringify(error)}`); + throw error; } - } +} + async handleAgentSpinupStatusErrors(error: string): Promise { if (error && Object.keys(error).length === 0) { - throw new InternalServerErrorException( - ResponseMessages.agent.error.agentDown, - { cause: new Error(), description: ResponseMessages.errorMessages.serverError } - ); + throw new InternalServerErrorException(ResponseMessages.agent.error.agentDown, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); } else { throw error; } - } + } - async sendQuestion(questionPayload: IQuestionPayload, url: string, apiKey: string): Promise { + async sendQuestion(questionPayload: IQuestionPayload, url: string, orgId: string): Promise { try { + const getApiKey = await this.getOrgAgentApiKey(orgId); const sendQuestionRes = await this.commonService - .httpPost(url, questionPayload, { headers: { 'authorization': apiKey } }) - .then(async response => response); + .httpPost(url, questionPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); return sendQuestionRes; } catch (error) { this.logger.error(`Error in send question in agent service : ${JSON.stringify(error)}`); @@ -1505,20 +1540,60 @@ export class AgentServiceService { } } - async getQuestionAnswersRecord(url: string, apiKey: string): Promise { - + async getQuestionAnswersRecord(url: string, orgId: string): Promise { try { - const data = await this.commonService - .httpGet(url, { headers: { 'authorization': apiKey } }) - .then(async response => response) - .catch(error => this.handleAgentSpinupStatusErrors(error)); - - return data; + const getQuestionAnswersRecord = await this.agentCall(url, orgId); + return getQuestionAnswersRecord; } catch (error) { this.logger.error(`Error in getQuestionAnswersRecord in agent service : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } + } + + async agentCall(url: string, orgId: string): Promise { + const getApiKey = await this.getOrgAgentApiKey(orgId); + + const data = await this.commonService + .httpGet(url, { headers: { authorization: getApiKey } }) + .then(async (response) => response) + .catch((error) => this.handleAgentSpinupStatusErrors(error)); + return data; } -} + async natsCall(pattern: object, payload: object): Promise<{ + response: string; + }> { + try { + return this.agentServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ( + { + response + })) + ).toPromise() + .catch(error => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.statusCode, + error: error.message + }, error.error); + }); + } catch (error) { + this.logger.error(`[natsCall] - error in nats call : ${JSON.stringify(error)}`); + throw error; + } + } + + private async tokenEncryption(token: string): Promise { + try { + const encryptedToken = CryptoJS.AES.encrypt(JSON.stringify(token), process.env.CRYPTO_PRIVATE_KEY).toString(); + + return encryptedToken; + } catch (error) { + throw error; + } + } +} \ No newline at end of file diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 31a649be9..4e3b1a4ed 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -87,6 +87,7 @@ export interface ITenantSchema { agentType?: string; apiKey?: string; agentEndPoint?: string; + orgId?: string; } export interface ITenantSchemaDto { @@ -104,6 +105,7 @@ export interface IGetSchemaAgentRedirection { agentEndPoint?: string; agentType?: string; method?: string; + orgId?: string; } export interface IGetSchemaFromTenantPayload { @@ -120,6 +122,7 @@ export interface ITenantCredDef { agentType?: string; apiKey?: string; agentEndPoint?: string; + orgId?: string; } export interface IWalletProvision { @@ -166,6 +169,7 @@ export interface IStoreOrgAgentDetails { network?: string; role?: string; did?: string; + didDoc?: string; verkey?: string; isDidPublic?: boolean; agentSpinUpStatus?: number; @@ -268,6 +272,7 @@ export interface IGetCredDefAgentRedirection { agentEndPoint?: string; agentType?: string; method?: string; + orgId?: string; } export interface IGetCredDefFromTenantPayload { diff --git a/apps/agent-service/src/repositories/agent-service.repository.ts b/apps/agent-service/src/repositories/agent-service.repository.ts index 096eaae29..203941378 100644 --- a/apps/agent-service/src/repositories/agent-service.repository.ts +++ b/apps/agent-service/src/repositories/agent-service.repository.ts @@ -131,6 +131,7 @@ export class AgentServiceRepository { }, data: { orgDid: storeOrgAgentDetails.did, + didDocument: storeOrgAgentDetails.didDoc, verkey: storeOrgAgentDetails.verkey, isDidPublic: storeOrgAgentDetails.isDidPublic, agentSpinUpStatus: storeOrgAgentDetails.agentSpinUpStatus, 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 05d28a583..c69c4d75b 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -237,9 +237,9 @@ export class AgentController { @Res() res: Response ): Promise { - validateDid(createDidDto); + await validateDid(createDidDto); - if (seedLength !== createDidDto.seed.length) { + if (createDidDto.seed && seedLength !== createDidDto.seed.length) { this.logger.error(`seed must be at most 32 characters.`); throw new BadRequestException( ResponseMessages.agent.error.seedChar, diff --git a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts index 7f75116ec..3e8deb560 100644 --- a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts @@ -22,8 +22,8 @@ export class CreateDidDto { keyType: string; @ApiProperty({ example: 'indy'}) - @IsNotEmpty({ message: 'seed is required' }) - @IsString({ message: 'did must be in string format.' }) + @IsNotEmpty({ message: 'method is required' }) + @IsString({ message: 'method must be in string format.' }) method: string; @ApiProperty({example: 'bcovrin:testnet'}) diff --git a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts index 72fb459ea..e26131431 100644 --- a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts @@ -1,19 +1,14 @@ import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { MaxLength, IsString, MinLength, Matches, IsOptional } from 'class-validator'; +import { MaxLength, IsString, MinLength, IsOptional } from 'class-validator'; import { CreateDidDto } from './create-did.dto'; -const labelRegex = /^[a-zA-Z0-9 ]*$/; export class CreateTenantDto extends CreateDidDto { @ApiProperty() @MaxLength(25, { message: 'Maximum length for label must be 25 characters.' }) @IsString({ message: 'label must be in string format.' }) @Transform(({ value }) => trim(value)) @MinLength(2, { message: 'Minimum length for label must be 2 characters.' }) - @Matches(labelRegex, { message: 'Label must not contain special characters.' }) - @Matches(/^\S*$/, { - message: 'Spaces are not allowed in label' - }) label: string; @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) diff --git a/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts b/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts index 03a49fcaa..5b5e37fbc 100644 --- a/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts @@ -9,10 +9,10 @@ export class CreateEcosystemDto { @ApiProperty() @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'ecosystem name is required.' }) - @MinLength(2, { message: 'ecosystem name must be at least 2 characters.' }) - @MaxLength(50, { message: 'ecosystem name must be at most 50 characters.' }) - @IsString({ message: 'ecosystem name must be in string format.' }) + @IsNotEmpty({ message: 'Ecosystem name is required.' }) + @MinLength(2, { message: 'Ecosystem name must be at least 2 characters.' }) + @MaxLength(50, { message: 'Ecosystem name must be at most 50 characters.' }) + @IsString({ message: 'Ecosystem name must be in string format.' }) name: string; @ApiProperty() diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 05a0ba0b6..489e5df84 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -110,7 +110,7 @@ export class EcosystemController { const finalResponse: IResponse = { statusCode: 200, message: ResponseMessages.ecosystem.success.allschema, - data: schemaList.response + data: schemaList }; return res.status(200).json(finalResponse); } @@ -124,11 +124,27 @@ export class EcosystemController { @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) @ApiBearerAuth() + @ApiQuery({ + name: 'pageNumber', + type: Number, + required: false + }) + @ApiQuery({ + name: 'pageSize', + type: Number, + required: false + }) + @ApiQuery({ + name: 'search', + type: String, + required: false + }) async getEcosystem( + @Query() paginationDto: PaginationDto, @Param('orgId') orgId: string, @Res() res: Response ): Promise { - const ecosystemList = await this.ecosystemService.getAllEcosystem(orgId); + const ecosystemList = await this.ecosystemService.getAllEcosystem(orgId, paginationDto); const finalResponse: IResponse = { statusCode: 200, message: ResponseMessages.ecosystem.success.fetch, diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index a55c21d13..6d5ae7cb3 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -11,8 +11,9 @@ import { GetAllEndorsementsDto } from './dtos/get-all-endorsements.dto'; import { RequestSchemaDto, RequestCredDefDto } from './dtos/request-schema.dto'; import { CreateEcosystemDto } from './dtos/create-ecosystem-dto'; import { EditEcosystemDto } from './dtos/edit-ecosystem-dto'; -import { IEcosystemDashboard, EcosystemDetailsResult, IEcosystemInvitation, IEcosystemInvitations, IEcosystem, IEditEcosystem, IEndorsementTransaction } from 'apps/ecosystem/interfaces/ecosystem.interfaces'; +import { IEcosystemDashboard, IEcosystemInvitation, IEcosystemInvitations, IEcosystem, IEditEcosystem, IEndorsementTransaction, ISchemaResponse } from 'apps/ecosystem/interfaces/ecosystem.interfaces'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; +import { IEcosystemDetails } from '@credebl/common/interfaces/ecosystem.interface'; @Injectable() export class EcosystemService extends BaseService { @@ -45,8 +46,8 @@ export class EcosystemService extends BaseService { * * @returns Get all ecosystems */ - async getAllEcosystem(orgId: string): Promise { - const payload = { orgId }; + async getAllEcosystem(orgId: string, payload: PaginationDto): Promise { + payload['orgId'] = orgId; return this.sendNatsMessage(this.serviceProxy, 'get-all-ecosystem', payload); } @@ -144,7 +145,7 @@ export class EcosystemService extends BaseService { ecosystemId: string, orgId: string, paginationDto: PaginationDto - ): Promise<{ response: object }> { + ): Promise { const { pageNumber, pageSize, search } = paginationDto; const payload = { ecosystemId, orgId, pageNumber, pageSize, search }; return this.sendNatsMessage(this.serviceProxy, 'get-all-ecosystem-schemas', payload); diff --git a/apps/api-gateway/src/issuance/dtos/get-all-issued-credentials.dto.ts b/apps/api-gateway/src/issuance/dtos/get-all-issued-credentials.dto.ts index 9e07e5ecf..37be22275 100644 --- a/apps/api-gateway/src/issuance/dtos/get-all-issued-credentials.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/get-all-issued-credentials.dto.ts @@ -1,29 +1,12 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; -import { IsEnum, IsInt, IsOptional, IsString } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { IsEnum, IsOptional } from 'class-validator'; import { SortValue } from '../../enum'; import { trim } from '@credebl/common/cast.helper'; import { SortFields } from 'apps/issuance/enum/issuance.enum'; +import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; -export class IGetAllIssuedCredentialsDto { - @ApiProperty({ required: false, default: 1 }) - @IsOptional() - @Type(() => Number) - @IsInt({ message: 'Page Number should be a number' }) - pageNumber: number = 1; - - @ApiProperty({ required: false, default: 10 }) - @IsOptional() - @Type(() => Number) - @IsInt({ message: 'Page size should be a number' }) - pageSize: number; - - @ApiProperty({ required: false }) - @IsOptional() - @Type(() => String) - @IsString({ message: 'Search text should be a string' }) - @Transform(({ value }) => trim(value)) - searchByText: string; +export class IGetAllIssuedCredentialsDto extends PaginationDto { @ApiProperty({ required: false, enum: SortFields }) @Transform(({ value }) => trim(value)) diff --git a/apps/api-gateway/src/issuance/interfaces/index.ts b/apps/api-gateway/src/issuance/interfaces/index.ts index e06d34539..107c08992 100644 --- a/apps/api-gateway/src/issuance/interfaces/index.ts +++ b/apps/api-gateway/src/issuance/interfaces/index.ts @@ -75,7 +75,7 @@ export interface IIssuedCredentialSearchParams { pageSize: number; sortField: string; sortBy: string; - searchByText: string; + search: string; } export enum IssueCredentialType { diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 56c1c059c..f640f0c64 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -88,6 +88,21 @@ export class IssuanceController { summary: `Get all issued credentials for a specific organization`, description: `Get all issued credentials for a specific organization` }) + @ApiQuery({ + name: 'pageNumber', + type: Number, + required: false + }) + @ApiQuery({ + name: 'pageSize', + type: Number, + required: false + }) + @ApiQuery({ + name: 'search', + type: String, + required: false + }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @@ -98,10 +113,10 @@ export class IssuanceController { @Param('orgId') orgId: string, @Res() res: Response ): Promise { - const { pageSize, searchByText, pageNumber, sortField, sortBy } = getAllIssuedCredentials; + const { pageSize, search, pageNumber, sortField, sortBy } = getAllIssuedCredentials; const issuedCredentialsSearchCriteria: IIssuedCredentialSearchParams = { pageNumber, - searchByText, + search, pageSize, sortField, sortBy diff --git a/apps/api-gateway/src/organization/dtos/send-invitation.dto.ts b/apps/api-gateway/src/organization/dtos/send-invitation.dto.ts index 013cb70f7..456215420 100644 --- a/apps/api-gateway/src/organization/dtos/send-invitation.dto.ts +++ b/apps/api-gateway/src/organization/dtos/send-invitation.dto.ts @@ -37,8 +37,5 @@ export class BulkSendInvitationDto { @Type(() => SendInvitationDto) invitations: SendInvitationDto[]; - @ApiProperty() - @IsString({ message: 'orgId should be a string' }) - @IsNotEmpty({ message: 'orgId is required' }) orgId: string; } diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 6884e5963..51bb113a0 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -352,7 +352,7 @@ export class OrganizationController { @Roles(OrgRoles.OWNER) @ApiOperation({ summary: 'Create credentials for an organization', description: 'Create client id and secret for an organization' }) @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard, UserAccessGuard) @ApiBearerAuth() async createOrgCredentials(@Param('orgId') orgId: string, @Res() res: Response, @User() reqUser: user): Promise { @@ -415,7 +415,7 @@ export class OrganizationController { @Post('/:orgId/invitations') @ApiOperation({ summary: 'Create organization invitation', - description: 'Create send invitation' + description: 'Create organization invitation' }) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @Roles(OrgRoles.OWNER, OrgRoles.SUPER_ADMIN, OrgRoles.ADMIN) @@ -472,7 +472,7 @@ export class OrganizationController { @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard, UserAccessGuard) async updateOrganization(@Body() updateOrgDto: UpdateOrganizationDto, @Param('orgId') orgId: string, @Res() res: Response, @User() reqUser: user): Promise { updateOrgDto.orgId = orgId; diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index ac7f8d705..4f3004f83 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,8 +1,7 @@ +import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID, ArrayUnique, ArrayMaxSize } from 'class-validator'; +import { toLowerCase, trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, IsUUID, ValidateIf, ValidateNested } from 'class-validator'; import { Transform, Type } from 'class-transformer'; -import { toLowerCase, trim } from '@credebl/common/cast.helper'; - import { AutoAccept } from '@credebl/enum/enum'; import { IProofFormats } from '../interfaces/verification.interface'; @@ -333,6 +332,16 @@ export class SendProofRequestPayload { @IsNotEmpty({message:'Please provide the flag for shorten url.'}) isShortenUrl?: boolean; + @ApiPropertyOptional() + @IsEmail({}, { each: true, message: 'Please provide a valid email' }) + @ArrayNotEmpty({ message: 'Email array must not be empty' }) + @ArrayUnique({ message: 'Duplicate emails are not allowed' }) + @ArrayMaxSize(Number(process.env.OOB_BATCH_SIZE), { message: `Limit reached (${process.env.OOB_BATCH_SIZE} proof request max).` }) + @IsArray() + @IsString({ each: true, message: 'Each emailId in the array should be a string' }) + @IsOptional() + emailId: string[]; + @ApiPropertyOptional({ default: true }) @IsOptional() @IsNotEmpty({ message: 'please provide valid value for reuseConnection' }) @@ -340,4 +349,3 @@ export class SendProofRequestPayload { reuseConnection?: boolean; } - diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 7bf0a43e1..02a94d98c 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -23,7 +23,6 @@ import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IQuestionPayload } from './interfaces/question-answer.interfaces'; -import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; @Injectable() export class ConnectionService { @@ -74,24 +73,18 @@ export class ConnectionService { goalCode: goalCode || undefined, handshake: handshake || undefined, handshakeProtocols: handshakeProtocols || undefined, - recipientKey:recipientKey || undefined + recipientKey: recipientKey || undefined }; const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); const url = await this.getAgentUrl(orgAgentType, agentEndPoint, agentDetails?.tenantId); - - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); - - const connectionInvitationUrl: string = createConnectionInvitation?.message?.invitationUrl; - const shortenedUrl: string = await this.storeConnectionObjectAndReturnUrl( + const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, orgId); + const connectionInvitationUrl = createConnectionInvitation?.response?.invitationUrl; + const shortenedUrl = await this.storeConnectionObjectAndReturnUrl( connectionInvitationUrl, connectionPayload.multiUseInvitation ); - const recipientsKey = createConnectionInvitation?.message?.recipientKey || recipientKey; + const recipientsKey = createConnectionInvitation?.response?.recipientKey || recipientKey; const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, agentId, @@ -108,22 +101,13 @@ export class ConnectionService { createdBy: saveConnectionDetails.createdBy, lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, lastChangedBy: saveConnectionDetails.lastChangedBy, - recordId: createConnectionInvitation.message.outOfBandRecord.id, - recipientKey:saveConnectionDetails.recipientKey + recordId: createConnectionInvitation.response.outOfBandRecord.id, + recipientKey: saveConnectionDetails.recipientKey }; return connectionDetailRecords; } catch (error) { this.logger.error(`[createLegacyConnectionInvitation] - error in connection invitation: ${error}`); - if (error && error?.status && error?.status?.message && error?.status?.message?.error) { - throw new RpcException({ - message: error?.status?.message?.error?.reason - ? error?.status?.message?.error?.reason - : error?.status?.message?.error, - statusCode: error?.status?.code - }); - } else { - throw new RpcException(error.response ? error.response : error); - } + this.handleError(error); } } @@ -150,16 +134,17 @@ export class ConnectionService { async _createConnectionInvitation( connectionPayload: object, url: string, - apiKey: string - ): Promise { + orgId: string + ): Promise<{ + response; + }> { //nats call in agent-service to create an invitation url const pattern = { cmd: 'agent-create-connection-legacy-invitation' }; - const payload = { connectionPayload, url, apiKey }; + const payload = { connectionPayload, url, orgId }; try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const message = await this.connectionServiceProxy.send(pattern, payload).toPromise(); - return { message }; + + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( @@ -265,40 +250,49 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } - let url: string; - if (orgAgentType === OrgAgentType.DEDICATED) { - url = `${agentEndPoint}${CommonConstants.URL_CONN_GET_CONNECTIONS}`; - } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREATEED_INVITATIONS}`.replace( - '#', - agentDetails.tenantId - ); - } else { - throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); - } - - //Create the dynamic URL for Search Criteria - const criteriaParams = []; - if (alias) { criteriaParams.push(`alias=${alias}`); } - if (myDid) { criteriaParams.push(`myDid=${myDid}`); } - if (outOfBandId) { criteriaParams.push(`outOfBandId=${outOfBandId}`); } - if (state) { criteriaParams.push(`state=${state}`); } - if (theirDid) { criteriaParams.push(`theirDid=${theirDid}`); } - if (theirLabel) { criteriaParams.push(`theirLabel=${theirLabel}`); } - - if (0 < criteriaParams.length) { - url += `?${criteriaParams.join('&')}`; - } - - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); + let url: string; + if (orgAgentType === OrgAgentType.DEDICATED) { + url = `${agentEndPoint}${CommonConstants.URL_CONN_GET_CONNECTIONS}`; + } else if (orgAgentType === OrgAgentType.SHARED) { + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_GET_CREATEED_INVITATIONS}`.replace( + '#', + agentDetails.tenantId + ); + } else { + throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - const connectionResponse = await this._getAllConnections(url, apiKey); + //Create the dynamic URL for Search Criteria + const criteriaParams = []; + if (alias) { + criteriaParams.push(`alias=${alias}`); + } + if (myDid) { + criteriaParams.push(`myDid=${myDid}`); + } + if (outOfBandId) { + criteriaParams.push(`outOfBandId=${outOfBandId}`); + } + if (state) { + criteriaParams.push(`state=${state}`); + } + if (theirDid) { + criteriaParams.push(`theirDid=${theirDid}`); + } + if (theirLabel) { + criteriaParams.push(`theirLabel=${theirLabel}`); + } + + if (0 < criteriaParams.length) { + url += `?${criteriaParams.join('&')}`; + } + + const connectionResponse = await this._getAllConnections(url, orgId); return connectionResponse.response; } catch (error) { - this.logger.error(`[getConnectionsFromAgent] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}`); + this.logger.error( + `[getConnectionsFromAgent] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}` + ); throw new RpcException(error.response ? error.response : error); } @@ -306,31 +300,14 @@ export class ConnectionService { async _getAllConnections( url: string, - apiKey: string + orgId: string ): Promise<{ response: string; }> { try { const pattern = { cmd: 'agent-get-all-connections' }; - const payload = { url, apiKey }; - return this.connectionServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ({ - response - })) - ) - .toPromise() - .catch((error) => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.message - }, - error.error - ); - }); + const payload = { url, orgId }; + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error( `[_getAllConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}` @@ -343,7 +320,6 @@ export class ConnectionService { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); - // const platformConfig: platform_config = await this.connectionRepository.getPlatformConfigDetails(); const { agentEndPoint } = agentDetails; if (!agentDetails) { @@ -361,13 +337,8 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - // const apiKey = await this._getOrgAgentApiKey(orgId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._getConnectionsByConnectionId(url, apiKey); - return createConnectionInvitation; + const createConnectionInvitation = await this._getConnectionsByConnectionId(url, orgId); + return createConnectionInvitation?.response; } catch (error) { this.logger.error(`[getConnectionsById] - error in get connections : ${JSON.stringify(error)}`); @@ -395,71 +366,32 @@ export class ConnectionService { const label = 'get-question-answer-record'; const url = await this.getQuestionAnswerAgentUrl(label, orgAgentType, agentEndPoint, agentDetails?.tenantId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const record = await this._getQuestionAnswersRecord(url, apiKey); + const record = await this._getQuestionAnswersRecord(url, orgId); return record; } catch (error) { this.logger.error(`[sendQuestion] - error in get question answer record: ${error}`); - if (error && error?.status && error?.status?.message && error?.status?.message?.error) { - throw new RpcException({ - message: error?.status?.message?.error?.reason - ? error?.status?.message?.error?.reason - : error?.status?.message?.error, - statusCode: error?.status?.code - }); - } else { - throw new RpcException(error.response ? error.response : error); - } + this.handleError(error); } } - async _getConnectionsByConnectionId(url: string, apiKey: string): Promise { + async _getConnectionsByConnectionId( + url: string, + orgId: string + ): Promise<{ + response; + }> { //nats call in agent service for fetch connection details const pattern = { cmd: 'agent-get-connection-details-by-connectionId' }; - const payload = { url, apiKey }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error( - `[_getConnectionsByConnectionId] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + const payload = { url, orgId }; + return this.natsCall(pattern, payload); } - async _getQuestionAnswersRecord(url: string, apiKey: string): Promise { + async _getQuestionAnswersRecord(url: string, orgId: string): Promise { const pattern = { cmd: 'agent-get-question-answer-record' }; - const payload = { url, apiKey }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error( - `[_getQuestionAnswersRecord] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + const payload = { url, orgId }; + return this.natsCall(pattern, payload); } - /** * Description: Fetch agent url * @param referenceId @@ -530,14 +462,14 @@ export class ConnectionService { } } - async _getOrgAgentApiKey(orgId: string): Promise { + async _getOrgAgentApiKey(orgId: string): Promise<{ + response: string; + }> { const pattern = { cmd: 'get-org-agent-api-key' }; const payload = { orgId }; try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const message = await this.connectionServiceProxy.send(pattern, payload).toPromise(); - return message; + return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( @@ -576,12 +508,8 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._receiveInvitationUrl(url, apiKey, receiveInvitationUrl); - return createConnectionInvitation; + const createConnectionInvitation = await this._receiveInvitationUrl(url, orgId, receiveInvitationUrl); + return createConnectionInvitation.response; } catch (error) { this.logger.error(`[receiveInvitationUrl] - error in receive invitation url : ${JSON.stringify(error)}`); @@ -599,27 +527,14 @@ export class ConnectionService { async _receiveInvitationUrl( url: string, - apiKey: string, + orgId: string, receiveInvitationUrl: IReceiveInvitationUrl - ): Promise { + ): Promise<{ + response; + }> { const pattern = { cmd: 'agent-receive-invitation-url' }; - const payload = { url, apiKey, receiveInvitationUrl }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error( - `[_receiveInvitationUrl] [NATS call]- error in receive invitation url : ${JSON.stringify(error)}` - ); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + const payload = { url, orgId, receiveInvitationUrl }; + return this.natsCall(pattern, payload); } async receiveInvitation( @@ -645,12 +560,8 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._receiveInvitation(url, apiKey, receiveInvitation); - return createConnectionInvitation; + const createConnectionInvitation = await this._receiveInvitation(url, orgId, receiveInvitation); + return createConnectionInvitation?.response; } catch (error) { this.logger.error(`[receiveInvitation] - error in receive invitation : ${JSON.stringify(error)}`); @@ -668,45 +579,20 @@ export class ConnectionService { async _receiveInvitation( url: string, - apiKey: string, + orgId: string, receiveInvitation: IReceiveInvitation - ): Promise { + ): Promise<{ + response; + }> { const pattern = { cmd: 'agent-receive-invitation' }; - const payload = { url, apiKey, receiveInvitation }; - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error(`[_receiveInvitation] [NATS call]- error in receive invitation : ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + const payload = { url, orgId, receiveInvitation }; + return this.natsCall(pattern, payload); } - async _sendQuestion(questionPayload: IQuestionPayload, url: string, apiKey: string): Promise { + async _sendQuestion(questionPayload: IQuestionPayload, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-send-question' }; - const payload = { questionPayload, url, apiKey }; - - return this.connectionServiceProxy - .send(pattern, payload) - .toPromise() - .catch((error) => { - this.logger.error(`[_sendQuestion] [NATS call]- error in send question : ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message - }, - error.error - ); - }); + const payload = { questionPayload, url, orgId }; + return this.natsCall(pattern, payload); } async sendQuestion(payload: IQuestionPayload): Promise { @@ -735,11 +621,8 @@ export class ConnectionService { agentDetails?.tenantId, connectionId ); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createQuestion = await this._sendQuestion(questionPayload, url, apiKey); + + const createQuestion = await this._sendQuestion(questionPayload, url, orgId); return createQuestion; } catch (error) { this.logger.error(`[sendQuestion] - error in sending question: ${error}`); @@ -756,42 +639,71 @@ export class ConnectionService { } } - async storeConnectionObjectAndReturnUrl(connectionInvitationUrl: string, persistent: boolean): Promise { + async storeConnectionObjectAndReturnUrl( + connectionInvitationUrl: string, + persistent: boolean + ): Promise { const storeObj = connectionInvitationUrl; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; const payload = { persistent, storeObj }; try { - const message = await this.connectionServiceProxy - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .send(pattern, payload) + const message = await this.natsCall(pattern, payload); + return message.response; + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + + async natsCall( + pattern: object, + payload: object + ): Promise<{ + response: string; + }> { + try { + return this.connectionServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ({ + response + })) + ) .toPromise() .catch((error) => { - this.logger.error( - `[storeConnectionObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( - error - )}` - ); + this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, - error: error.error?.message?.error ? error.error?.message?.error : error.error, - message: error.message + error: error.message }, error.error ); }); - return message; } catch (error) { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.status, - error: error.message - }, - error.status - ); + this.logger.error(`[ConnectionService natsCall] - error in nats call : ${JSON.stringify(error)}`); + throw error; + } + } + + handleError(error): Promise { + if (error && error?.status && error?.status?.message && error?.status?.message?.error) { + throw new RpcException({ + message: error?.status?.message?.error?.reason + ? error?.status?.message?.error?.reason + : error?.status?.message?.error, + statusCode: error?.status?.code + }); + } else { + throw new RpcException(error.response ? error.response : error); } } -} +} \ No newline at end of file diff --git a/apps/ecosystem/interfaces/ecosystem.interfaces.ts b/apps/ecosystem/interfaces/ecosystem.interfaces.ts index 11ad4b73a..4fa335baf 100644 --- a/apps/ecosystem/interfaces/ecosystem.interfaces.ts +++ b/apps/ecosystem/interfaces/ecosystem.interfaces.ts @@ -220,41 +220,6 @@ export interface LedgerDetails { networkUrl: string; } -interface EcosystemRole { - id: string; - name: string; - description: string; - createDateTime: Date; - lastChangedDateTime: Date; - deletedAt: Date; -} - -interface EcosystemDetail { - id: string; - name: string; - description: string; - logoUrl: string; - createDateTime: Date; - lastChangedDateTime: Date; - createdBy: string; - autoEndorsement: boolean; - ecosystemOrgs: { - id: string; - orgId: string; - status: string; - createDateTime: Date; - lastChangedDateTime: Date; - ecosystemId: string; - ecosystemRoleId: string; - ecosystemRole: EcosystemRole; - }[]; -} - -export interface EcosystemDetailsResult { - totalCount: number; - ecosystemDetails: EcosystemDetail[]; -} - export interface EcosystemInvitationDetails { name: string; id: string; @@ -363,4 +328,44 @@ export interface IEcosystemInvitations { ecosystem: EcosystemInvitationDetails; createDateTime: Date; createdBy: string; -} \ No newline at end of file +} + +interface IAttributes { + isRequired: boolean; + displayName: string; + attributeName: string; + schemaDataType: string; +} + +interface ISChemaItems { + id: string; + createDateTime: string; + createdBy: string; + lastChangedDateTime: string; + lastChangedBy: string; + name: string; + version: string; + attributes: IAttributes[]; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + ledgerId: string; +} + +export interface ISchemaResponse { + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + data: ISChemaItems[]; +} + +export interface IEcosystemList { + orgId: string, + pageNumber: number; + pageSize: number; + search: string; +} diff --git a/apps/ecosystem/interfaces/endorsements.interface.ts b/apps/ecosystem/interfaces/endorsements.interface.ts index a8024a05e..fe3ba0356 100644 --- a/apps/ecosystem/interfaces/endorsements.interface.ts +++ b/apps/ecosystem/interfaces/endorsements.interface.ts @@ -15,4 +15,22 @@ export interface GetEndorsementsPayload { pageNumber: number; pageSize: number; search: string; - } \ No newline at end of file + } + + interface ISchemaResult { + createDateTime: Date; + createdBy: string; + name: string; + version: string; + attributes: string; + schemaLedgerId: string; + publisherDid: string; + issuerId: string; + orgId: string; + } + + export interface ISchemasResponse { + schemasCount: number; + schemasResult: ISchemaResult[]; + } + \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index a2544ec30..95513b5d7 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -7,8 +7,9 @@ import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { AcceptRejectEcosystemInvitationDto } from '../dtos/accept-reject-ecosysteminvitation.dto'; import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; import { EcosystemMembersPayload } from '../interfaces/ecosystemMembers.interface'; -import { GetEndorsementsPayload } from '../interfaces/endorsements.interface'; -import { IEcosystemDashboard, RequestCredDeffEndorsement, RequestSchemaEndorsement, IEcosystem, EcosystemDetailsResult, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction } from '../interfaces/ecosystem.interfaces'; +import { GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; +import { IEcosystemDashboard, RequestCredDeffEndorsement, RequestSchemaEndorsement, IEcosystem, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction, IEcosystemList } from '../interfaces/ecosystem.interfaces'; +import { IEcosystemDetails } from '@credebl/common/interfaces/ecosystem.interface'; @Controller() export class EcosystemController { @@ -42,7 +43,7 @@ export class EcosystemController { * @returns Get all ecosystem details */ @MessagePattern({ cmd: 'get-all-ecosystem' }) - async getAllEcosystems(@Body() payload: { orgId: string }): Promise { + async getAllEcosystems(@Body() payload: IEcosystemList): Promise { return this.ecosystemService.getAllEcosystem(payload); } @@ -121,7 +122,7 @@ export class EcosystemController { } @MessagePattern({ cmd: 'get-all-ecosystem-schemas' }) - async getAllEcosystemSchemas(@Body() payload: GetEndorsementsPayload): Promise { + async getAllEcosystemSchemas(@Body() payload: GetEndorsementsPayload): Promise { return this.ecosystemService.getAllEcosystemSchemas(payload); } diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index 06f0d1017..45085caa4 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -4,11 +4,11 @@ import { PrismaService } from '@credebl/prisma-service'; import { credential_definition, ecosystem, ecosystem_config, ecosystem_invitations, ecosystem_orgs, ecosystem_roles, endorsement_transaction, org_agents, platform_config, schema } from '@prisma/client'; import { DeploymentModeType, EcosystemInvitationStatus, EcosystemOrgStatus, EcosystemRoles, endorsementTransactionStatus, endorsementTransactionType } from '../enums/ecosystem.enum'; import { updateEcosystemOrgsDto } from '../dtos/update-ecosystemOrgs.dto'; -import { CreateEcosystem, IEcosystemInvitation, EcosystemDetailsResult, SaveSchema, SchemaTransactionResponse, saveCredDef } from '../interfaces/ecosystem.interfaces'; +import { CreateEcosystem, IEcosystemInvitation, SaveSchema, SchemaTransactionResponse, saveCredDef } from '../interfaces/ecosystem.interfaces'; import { ResponseMessages } from '@credebl/common/response-messages'; import { NotFoundException } from '@nestjs/common'; import { CommonConstants } from '@credebl/common/common.constant'; -import { GetAllSchemaList } from '../interfaces/endorsements.interface'; +import { GetAllSchemaList, ISchemasResponse } from '../interfaces/endorsements.interface'; import { SortValue } from '@credebl/enum/enum'; // eslint-disable-next-line camelcase @@ -113,63 +113,75 @@ export class EcosystemRepository { * * @returns Get all ecosystem details */ - // eslint-disable-next-line camelcase - async getAllEcosystemDetails(orgId: string): Promise { - try { - const [ecosystemDetails, ecosystemCount] = await Promise.all([ - this.prisma.ecosystem.findMany({ - where: { - ecosystemOrgs: { - some: { - orgId - } - } - }, - select: { - id: true, - name: true, - description: true, - logoUrl: true, - createDateTime: true, - lastChangedDateTime: true, - createdBy: true, - autoEndorsement: true, - ecosystemOrgs: { - where: { - orgId + async getAllEcosystemDetails( + orgId: string, + pageNumber: number, + pageSize: number, + search: string + ): Promise { + try { + const sortByName = SortValue.DESC; + + const result = await Promise.all([ + this.prisma.ecosystem.findMany({ + where: { + ecosystemOrgs: { + some: { + orgId + } }, - select: { - id: true, - orgId: true, - status: true, - createDateTime: true, - lastChangedDateTime: true, - ecosystemId: true, - ecosystemRoleId: true, - ecosystemRole: true + OR: [ + { name: { contains: search, mode: 'insensitive' } }, + { description: { contains: search, mode: 'insensitive' } } + ] + }, + select: { + id: true, + name: true, + description: true, + logoUrl: true, + createDateTime: true, + lastChangedDateTime: true, + createdBy: true, + autoEndorsement: true, + ecosystemOrgs: { + where: { + orgId + }, + select: { + id: true, + orgId: true, + status: true, + createDateTime: true, + lastChangedDateTime: true, + ecosystemId: true, + ecosystemRoleId: true, + ecosystemRole: true + } } + }, + take: Number(pageSize), + skip: (pageNumber - 1) * pageSize, + orderBy: { + name: sortByName } - } - }), - this.prisma.ecosystem.count({ - where: { - ecosystemOrgs: { - some: { - orgId + }), + this.prisma.ecosystem.count({ + where: { + ecosystemOrgs: { + some: { + orgId + } } } - } - }) - ]); + }) + ]); - return { - ecosystemDetails, - totalCount: ecosystemCount - }; - } catch (error) { - this.logger.error(`Error in get all ecosystem transaction: ${error.message}`); - throw error; - } + return result; + } catch (error) { + this.logger.error(`Error in get all ecosystem transaction: ${error.message}`); + throw error; + } } @@ -667,20 +679,7 @@ export class EcosystemRepository { } - async getAllEcosystemSchemasDetails(payload: GetAllSchemaList): Promise<{ - schemasCount: number; - schemasResult: { - createDateTime: Date; - createdBy: string; - name: string; - version: string; - attributes: string; - schemaLedgerId: string; - publisherDid: string; - issuerId: string; - orgId: string; - }[]; - }> { + async getAllEcosystemSchemasDetails(payload: GetAllSchemaList): Promise { try { const { ecosystemId, search, pageNumber, pageSize } = payload; diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index fa78b67c6..9aa3dff26 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -45,13 +45,13 @@ import { saveCredDef, submitTransactionPayload, IEcosystem, - EcosystemDetailsResult, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, - IEndorsementTransaction + IEndorsementTransaction, + IEcosystemList } from '../interfaces/ecosystem.interfaces'; -import { GetAllSchemaList, GetEndorsementsPayload } from '../interfaces/endorsements.interface'; +import { GetAllSchemaList, GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; import { CommonConstants } from '@credebl/common/common.constant'; // eslint-disable-next-line camelcase import { @@ -69,6 +69,7 @@ import { import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { updateEcosystemOrgsDto } from '../dtos/update-ecosystemOrgs.dto'; +import { IEcosystemDetails } from '@credebl/common/interfaces/ecosystem.interface'; @Injectable() export class EcosystemService { @@ -222,15 +223,32 @@ export class EcosystemService { * @returns all ecosystem details */ - // eslint-disable-next-line camelcase - async getAllEcosystem(payload: { orgId: string }): Promise { - const getAllEcosystemDetails = await this.ecosystemRepository.getAllEcosystemDetails(payload.orgId); + async getAllEcosystem(payload: IEcosystemList): Promise { + try { + const { orgId, pageNumber, pageSize, search } = payload; - if (!getAllEcosystemDetails) { - throw new NotFoundException(ResponseMessages.ecosystem.error.update); - } + const getEcosystemOrgs = await this.ecosystemRepository.getAllEcosystemDetails( + orgId, + pageNumber, + pageSize, + search + ); + + const ecosystemListDetails = { + totalItems: getEcosystemOrgs[1], + hasNextPage: payload.pageSize * payload.pageNumber < getEcosystemOrgs[1], + hasPreviousPage: 1 < payload.pageNumber, + nextPage: Number(payload.pageNumber) + 1, + previousPage: payload.pageNumber - 1, + lastPage: Math.ceil(getEcosystemOrgs[1] / payload.pageSize), + ecosystemList: getEcosystemOrgs[0] + }; - return getAllEcosystemDetails; + return ecosystemListDetails; + } catch (error) { + this.logger.error(`In fetch ecosystem list : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } } /** @@ -754,7 +772,6 @@ export class EcosystemService { endorsementTransactionType.SCHEMA, ecosystemMemberDetails.tenantId ); - const apiKey = await this._getOrgAgentApiKey(orgId); const attributeArray = requestSchemaPayload.attributes.map((item) => item.attributeName); const schemaTransactionPayload = { @@ -769,7 +786,7 @@ export class EcosystemService { const schemaTransactionRequest: SchemaMessage = await this._requestSchemaEndorsement( schemaTransactionPayload, url, - apiKey + orgId ); const schemaTransactionResponse = { @@ -916,7 +933,6 @@ export class EcosystemService { endorsementTransactionType.CREDENTIAL_DEFINITION, ecosystemMemberDetails.tenantId ); - const apiKey = await this._getOrgAgentApiKey(orgId); const credDefTransactionPayload = { endorserDid: ecosystemLeadAgentDetails.orgDid, endorse: requestCredDefPayload.endorse, @@ -928,7 +944,7 @@ export class EcosystemService { const credDefTransactionRequest: CredDefMessage = await this._requestCredDeffEndorsement( credDefTransactionPayload, url, - apiKey + orgId ); if ('failed' === credDefTransactionRequest.message.credentialDefinitionState.state) { @@ -992,9 +1008,9 @@ export class EcosystemService { } } - async _requestSchemaEndorsement(requestSchemaPayload: object, url: string, apiKey: string): Promise { + async _requestSchemaEndorsement(requestSchemaPayload: object, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-schema-endorsement-request' }; - const payload = { requestSchemaPayload, url, apiKey }; + const payload = { requestSchemaPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1012,9 +1028,9 @@ export class EcosystemService { } } - async _requestCredDeffEndorsement(requestSchemaPayload: object, url: string, apiKey: string): Promise { + async _requestCredDeffEndorsement(requestSchemaPayload: object, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-credDef-endorsement-request' }; - const payload = { requestSchemaPayload, url, apiKey }; + const payload = { requestSchemaPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1065,17 +1081,14 @@ export class EcosystemService { endorsementTransactionType.SIGN, ecosystemLeadAgentDetails?.tenantId ); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(ecosystemLeadDetails.orgId); - } + const jsonString = endorsementTransactionPayload.requestPayload.toString(); const payload = { transaction: jsonString, endorserDid: endorsementTransactionPayload.endorserDid }; - const schemaTransactionRequest: SignedTransactionMessage = await this._signTransaction(payload, url, apiKey); + const schemaTransactionRequest: SignedTransactionMessage = await this._signTransaction(payload, url, ecosystemLeadDetails.orgId); if (!schemaTransactionRequest) { throw new InternalServerErrorException(ResponseMessages.ecosystem.error.signRequestError); @@ -1180,9 +1193,9 @@ export class EcosystemService { * @param url * @returns sign message */ - async _signTransaction(signEndorsementPayload: object, url: string, apiKey: string): Promise { + async _signTransaction(signEndorsementPayload: object, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-sign-transaction' }; - const payload = { signEndorsementPayload, url, apiKey }; + const payload = { signEndorsementPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1369,12 +1382,7 @@ export class EcosystemService { } } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - - const submitTransactionRequest = await this._submitTransaction(payload, url, apiKey); + const submitTransactionRequest = await this._submitTransaction(payload, url, orgId); if ('failed' === submitTransactionRequest['message'].state) { throw new InternalServerErrorException(ResponseMessages.ecosystem.error.sumbitTransaction); @@ -1448,9 +1456,9 @@ export class EcosystemService { * @param url * @returns sign message */ - async _submitTransaction(submitEndorsementPayload: object, url: string, apiKey: string): Promise { + async _submitTransaction(submitEndorsementPayload: object, url: string, orgId: string): Promise { const pattern = { cmd: 'agent-submit-transaction' }; - const payload = { submitEndorsementPayload, url, apiKey }; + const payload = { submitEndorsementPayload, url, orgId }; try { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1603,7 +1611,7 @@ export class EcosystemService { } } - async getAllEcosystemSchemas(ecosystemSchemas: GetAllSchemaList): Promise { + async getAllEcosystemSchemas(ecosystemSchemas: GetAllSchemaList): Promise { try { const response = await this.ecosystemRepository.getAllEcosystemSchemasDetails(ecosystemSchemas); const schemasDetails = response?.schemasResult.map((schemaAttributeItem) => { @@ -1611,15 +1619,11 @@ export class EcosystemService { return { ...schemaAttributeItem, attributes }; }); - const schemasResponse = { - totalItems: response.schemasCount, - hasNextPage: ecosystemSchemas.pageSize * ecosystemSchemas.pageNumber < response.schemasCount, - hasPreviousPage: 1 < ecosystemSchemas.pageNumber, - nextPage: ecosystemSchemas.pageNumber + 1, - previousPage: ecosystemSchemas.pageNumber - 1, - lastPage: Math.ceil(response.schemasCount / ecosystemSchemas.pageSize), - data: schemasDetails + const schemasResponse: ISchemasResponse = { + schemasCount: response.schemasCount, + schemasResult: schemasDetails }; + return schemasResponse; } catch (error) { this.logger.error(`In error fetching all ecosystem schemas: ${JSON.stringify(error)}`); diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index 5cb4caa75..5747d7a69 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -85,7 +85,8 @@ export interface IPattern { export interface ISendOfferNatsPayload { issueData: IIssueData, url: string, - apiKey: string; + apiKey?: string; + orgId?: string; } export interface IIssueCredentialsDefinitions { @@ -220,7 +221,7 @@ export interface IIssuedCredentialsSearchCriteria { pageSize: number; sortField: string; sortBy: string; - searchByText: string; + search: string; user?: IUserRequestInterface; } @@ -254,6 +255,6 @@ export interface SendEmailCredentialOffer { organisation: organisation; errors; url: string; - apiKey: string; + orgId: string; organizationDetails: organisation; } \ No newline at end of file diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 050327509..1a6256ff4 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -108,11 +108,12 @@ export class IssuanceRepository { where: { orgId, OR: [ - { schemaId: { contains: issuedCredentialsSearchCriteria.searchByText, mode: 'insensitive' } }, - { connectionId: { contains: issuedCredentialsSearchCriteria.searchByText, mode: 'insensitive' } } + { schemaId: { contains: issuedCredentialsSearchCriteria.search, mode: 'insensitive' } }, + { connectionId: { contains: issuedCredentialsSearchCriteria.search, mode: 'insensitive' } } ] }, select: { + credentialExchangeId: true, createDateTime: true, createdBy: true, orgId: true, @@ -132,8 +133,8 @@ export class IssuanceRepository { where: { orgId, OR: [ - { schemaId: { contains: issuedCredentialsSearchCriteria.searchByText, mode: 'insensitive' } }, - { connectionId: { contains: issuedCredentialsSearchCriteria.searchByText, mode: 'insensitive' } } + { schemaId: { contains: issuedCredentialsSearchCriteria.search, mode: 'insensitive' } }, + { connectionId: { contains: issuedCredentialsSearchCriteria.search, mode: 'insensitive' } } ] } }); diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index b4279d327..92ae0a39f 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -101,11 +101,6 @@ export class IssuanceService { const issuanceMethodLabel = 'create-offer'; const url = await this.getAgentUrl(issuanceMethodLabel, orgAgentType, agentEndPoint, agentDetails?.tenantId); - let apiKey; - apiKey = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } const issueData: IIssueData = { protocolVersion: 'v1', connectionId, @@ -121,7 +116,7 @@ export class IssuanceService { comment }; - const credentialCreateOfferDetails: ICreateOfferResponse = await this._sendCredentialCreateOffer(issueData, url, apiKey); + const credentialCreateOfferDetails: ICreateOfferResponse = await this._sendCredentialCreateOffer(issueData, url, orgId); if (credentialCreateOfferDetails && 0 < Object.keys(credentialCreateOfferDetails).length) { delete credentialCreateOfferDetails._tags; @@ -200,11 +195,7 @@ export class IssuanceService { const issuanceMethodLabel = 'create-offer-oob'; const url = await this.getAgentUrl(issuanceMethodLabel, orgAgentType, agentEndPoint, agentDetails?.tenantId); - let apiKey; - apiKey = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } + let issueData; if (credentialType === IssueCredentialType.INDY) { @@ -248,7 +239,7 @@ export class IssuanceService { recipientKey:recipientKey || undefined }; } - const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); + const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, orgId); if (isShortenUrl) { const invitationUrl: string = credentialCreateOfferDetails.response?.invitationUrl; const url: string = await this.storeIssuanceObjectReturnUrl(invitationUrl); @@ -334,10 +325,10 @@ export class IssuanceService { } } - async _sendCredentialCreateOffer(issueData: IIssueData, url: string, apiKey: string): Promise { + async _sendCredentialCreateOffer(issueData: IIssueData, url: string, orgId: string): Promise { try { const pattern = { cmd: 'agent-send-credential-create-offer' }; - const payload: ISendOfferNatsPayload = { issueData, url, apiKey }; + const payload: ISendOfferNatsPayload = { issueData, url, orgId }; return await this.natsCallAgent(pattern, payload); } catch (error) { this.logger.error(`[_sendCredentialCreateOffer] [NATS call]- error in create credentials : ${JSON.stringify(error)}`); @@ -406,13 +397,8 @@ export class IssuanceService { const issuanceMethodLabel = 'get-issue-credential-by-credential-id'; const url = await this.getAgentUrl(issuanceMethodLabel, orgAgentType, agentEndPoint, agentDetails?.tenantId, credentialRecordId); - // const apiKey = platformConfig?.sgApiKey; - // const apiKey = await this._getOrgAgentApiKey(orgId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const createConnectionInvitation = await this._getIssueCredentialsbyCredentialRecordId(url, apiKey); + + const createConnectionInvitation = await this._getIssueCredentialsbyCredentialRecordId(url, orgId); return createConnectionInvitation?.response; } catch (error) { this.logger.error(`[getIssueCredentialsbyCredentialRecordId] - error in get credentials : ${JSON.stringify(error)}`); @@ -438,12 +424,12 @@ export class IssuanceService { } } - async _getIssueCredentialsbyCredentialRecordId(url: string, apiKey: string): Promise<{ + async _getIssueCredentialsbyCredentialRecordId(url: string, orgId: string): Promise<{ response: string; }> { try { const pattern = { cmd: 'agent-get-issued-credentials-by-credentialDefinitionId' }; - const payload = { url, apiKey }; + const payload = { url, orgId }; return await this.natsCall(pattern, payload); } catch (error) { @@ -532,11 +518,6 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl throw new NotFoundException(ResponseMessages.issuance.error.organizationNotFound); } - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const errors = []; let credentialOfferResponse; const arraycredentialOfferResponse = []; @@ -553,7 +534,7 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl organisation: organisation; errors: string[]; url: string; - apiKey: string; + orgId: string; organizationDetails: organisation; } = { credentialType, @@ -565,7 +546,7 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl organisation, errors, url, - apiKey, + orgId, organizationDetails, iterator: undefined, emailId: emailId || '', @@ -631,7 +612,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO organisation, errors, url, - apiKey, + orgId, organizationDetails } = sendEmailCredentialOffer; @@ -678,7 +659,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO }; } - const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, apiKey); + const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, orgId); if (!credentialCreateOfferDetails) { errors.push(new NotFoundException(ResponseMessages.issuance.error.credentialOfferNotFound)); @@ -738,12 +719,12 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO } } - async _outOfBandCredentialOffer(outOfBandIssuancePayload: object, url: string, apiKey: string): Promise<{ + async _outOfBandCredentialOffer(outOfBandIssuancePayload: object, url: string, orgId: string): Promise<{ response; }> { try { const pattern = { cmd: 'agent-out-of-band-credential-offer' }; - const payload = { outOfBandIssuancePayload, url, apiKey }; + const payload = { outOfBandIssuancePayload, url, orgId }; return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`[_outOfBandCredentialOffer] [NATS call]- error in out of band : ${JSON.stringify(error)}`); diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 94c34f666..1d19e5041 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -1,6 +1,5 @@ /* eslint-disable camelcase */ import { - BadRequestException, ConflictException, HttpException, Inject, @@ -18,7 +17,6 @@ import { map } from 'rxjs/operators'; import { OrgAgentType } from '@credebl/enum/enum'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { CommonConstants } from '@credebl/common/common.constant'; @Injectable() export class CredentialDefinitionService extends BaseService { constructor( @@ -37,11 +35,7 @@ export class CredentialDefinitionService extends BaseService { // eslint-disable-next-line yoda const did = credDef.orgDid?.split(':').length >= 4 ? credDef.orgDid : orgDid; const getAgentDetails = await this.credentialDefinitionRepository.getAgentType(credDef.orgId); - // const apiKey = await this._getOrgAgentApiKey(credDef.orgId); - let apiKey:string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(credDef.orgId); - } + const userId = user.id; credDef.tag = credDef.tag.trim(); const dbResult: credential_definition = await this.credentialDefinitionRepository.getByAttribute( @@ -61,7 +55,7 @@ export class CredentialDefinitionService extends BaseService { schemaId: credDef.schemaLedgerId, issuerId: did, agentEndPoint, - apiKey, + orgId: credDef.orgId, agentType: OrgAgentType.DEDICATED }; @@ -79,7 +73,7 @@ export class CredentialDefinitionService extends BaseService { issuerId: did }, agentEndPoint, - apiKey, + orgId: credDef.orgId, agentType: OrgAgentType.SHARED }; credDefResponseFromAgentService = await this._createCredentialDefinition(CredDefPayload); @@ -182,17 +176,12 @@ export class CredentialDefinitionService extends BaseService { const { agentEndPoint } = await this.credentialDefinitionRepository.getAgentDetailsByOrgId(String(orgId)); const getAgentDetails = await this.credentialDefinitionRepository.getAgentType(String(orgId)); const orgAgentType = await this.credentialDefinitionRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); - // const apiKey = await this._getOrgAgentApiKey(String(orgId)); - let apiKey:string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(String(orgId)); - } + let credDefResponse; if (OrgAgentType.DEDICATED === orgAgentType) { const getSchemaPayload = { credentialDefinitionId, - apiKey, + orgId, agentEndPoint, agentType: OrgAgentType.DEDICATED }; diff --git a/apps/ledger/src/credential-definition/interfaces/credential-definition.interface.ts b/apps/ledger/src/credential-definition/interfaces/credential-definition.interface.ts index b290bce9a..3fcd058e1 100644 --- a/apps/ledger/src/credential-definition/interfaces/credential-definition.interface.ts +++ b/apps/ledger/src/credential-definition/interfaces/credential-definition.interface.ts @@ -8,6 +8,7 @@ export interface CreateCredDefAgentRedirection { agentType?: string; apiKey?: string; agentEndPoint?: string; + orgId?: string; } export interface ITenantCredDef { @@ -24,6 +25,7 @@ export interface GetCredDefAgentRedirection { agentEndPoint?: string; agentType?: string; method?: string; + orgId?: string; } export interface GetCredDefFromTenantPayload { diff --git a/apps/ledger/src/schema/schema.interface.ts b/apps/ledger/src/schema/schema.interface.ts index ad4f58ed3..3ce52edbb 100644 --- a/apps/ledger/src/schema/schema.interface.ts +++ b/apps/ledger/src/schema/schema.interface.ts @@ -23,6 +23,7 @@ export interface CreateSchemaAgentRedirection { agentType?: string; apiKey?: string; agentEndPoint?: string; + orgId?: string; } export interface ITenantSchemaDto { @@ -40,6 +41,7 @@ export interface GetSchemaAgentRedirection { agentEndPoint?: string; agentType?: string; method?: string; + orgId?: string; } export interface GetSchemaFromTenantPayload { diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 7d253557b..3d674470c 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -20,7 +20,6 @@ import { OrgAgentType } from '@credebl/enum/enum'; import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { CommonConstants } from '@credebl/common/common.constant'; @Injectable() export class SchemaService extends BaseService { @@ -38,10 +37,6 @@ export class SchemaService extends BaseService { orgId: string ): Promise { - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } const userId = user.id; try { @@ -144,7 +139,7 @@ export class SchemaService extends BaseService { name: schema.schemaName, issuerId, agentEndPoint, - apiKey, + orgId, agentType: OrgAgentType.DEDICATED }; schemaResponseFromAgentService = await this._createSchema(schemaPayload); @@ -162,7 +157,7 @@ export class SchemaService extends BaseService { issuerId: did }, agentEndPoint, - apiKey, + orgId, agentType: OrgAgentType.SHARED }; schemaResponseFromAgentService = await this._createSchema(schemaPayload); @@ -278,20 +273,13 @@ export class SchemaService extends BaseService { const { agentEndPoint } = await this.schemaRepository.getAgentDetailsByOrgId(orgId); const getAgentDetails = await this.schemaRepository.getAgentType(orgId); const orgAgentType = await this.schemaRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); - // const apiKey = ''; - - let apiKey; - apiKey = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } let schemaResponse; if (OrgAgentType.DEDICATED === orgAgentType) { const getSchemaPayload = { schemaId, - apiKey, + orgId, agentEndPoint, agentType: OrgAgentType.DEDICATED }; @@ -304,7 +292,7 @@ export class SchemaService extends BaseService { payload: { schemaId }, agentType: OrgAgentType.SHARED, agentEndPoint, - apiKey + orgId }; schemaResponse = await this._getSchemaById(getSchemaPayload); } diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index e2464aab6..240969af3 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -8,7 +8,7 @@ import { Prisma, agent_invitations, org_agents, org_invitations, user_org_roles import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { IGetOrgById, IGetOrganization, IUpdateOrganization } from '../interfaces/organization.interface'; import { InternalServerErrorException } from '@nestjs/common'; -import { Invitation } from '@credebl/enum/enum'; +import { Invitation, SortValue } from '@credebl/enum/enum'; import { PrismaService } from '@credebl/prisma-service'; import { UserOrgRolesService } from '@credebl/user-org-roles'; import { organisation } from '@prisma/client'; @@ -391,6 +391,7 @@ export class OrganizationRepository { select: { id: true, orgDid: true, + didDocument: true, walletName: true, agentEndPoint: true, agentSpinUpStatus: true, @@ -553,7 +554,7 @@ export class OrganizationRepository { pageSize: number ): Promise { try { - const sortByName = 'asc'; + const sortByName = SortValue.DESC; const result = await this.prisma.$transaction([ this.prisma.organisation.findMany({ where: { diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index f883e49c2..b9172233e 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -1,5 +1,5 @@ -import { AutoAccept } from "@credebl/enum/enum"; -import { IUserRequest } from "@credebl/user-request/user-request.interface"; +import { AutoAccept } from '@credebl/enum/enum'; +import { IUserRequest } from '@credebl/user-request/user-request.interface'; interface IProofRequestAttribute { attributeName?: string; @@ -31,17 +31,20 @@ export interface IGetAllProofPresentations { export interface IGetProofPresentationById { url: string; - apiKey: string; + apiKey?: string; + orgId?: string; } export interface IVerifyPresentation { url: string; - apiKey: string; + apiKey?: string; + orgId?: string; } export interface IVerifiedProofData { url: string; - apiKey: string; + apiKey?: string; + orgId?: string } export interface IProofPresentationData { @@ -129,8 +132,9 @@ export interface ISendPresentationExchangeProofRequestPayload { } export interface IPresentationExchangeProofRequestPayload { url: string; - apiKey: string; + apiKey?: string; proofRequestPayload: ISendPresentationExchangeProofRequestPayload; + orgId?: string; } export interface ISendProofRequestPayload { @@ -144,6 +148,7 @@ export interface ISendProofRequestPayload { parentThreadId?: string; willConfirm?: boolean; imageUrl?: string; + emailId?: string[] isShortenUrl?: boolean; type?:string; presentationDefinition?:IProofRequestPresentationDefinition; @@ -162,14 +167,16 @@ export interface IWSendProofRequestPayload { parentThreadId?: string; willConfirm?: boolean; imageUrl?: string; + emailId?: string[]; type?:string; presentationDefinition?:IProofRequestPresentationDefinition; } export interface IProofRequestPayload { url: string; - apiKey: string; - proofRequestPayload: ISendProofRequestPayload; + apiKey?: string; + orgId?: string + proofRequestPayload: ISendProofRequestPayload | ISendPresentationExchangeProofRequestPayload; } interface IWebhookPresentationProof { @@ -213,3 +220,6 @@ export interface IProofRequestSearchCriteria { searchByText: string; } +export interface IInvitation{ + invitationUrl?: string; +} \ No newline at end of file diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 5b5b0e222..0476139c2 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -2,7 +2,7 @@ import { BadRequestException, HttpException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs/operators'; -import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IPresentationExchangeProofRequestPayload} from './interfaces/verification.interface'; +import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IInvitation} from './interfaces/verification.interface'; import { VerificationRepository } from './repositories/verification.repository'; import { CommonConstants } from '@credebl/common/common.constant'; import { agent_invitations, org_agents, organisation, presentations } from '@prisma/client'; @@ -121,12 +121,9 @@ export class VerificationService { const verificationMethodLabel = 'get-proof-presentation-by-id'; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId, '', proofId); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const payload = { apiKey, url }; + + const payload = { orgId, url }; const getProofPresentationById = await this._getProofPresentationById(payload); return getProofPresentationById?.response; @@ -222,11 +219,8 @@ export class VerificationService { const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); const verificationMethodLabel = 'request-proof'; const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(requestProof.orgId); - } - const payload = { apiKey, url, proofRequestPayload }; + + const payload = { orgId: requestProof.orgId, url, proofRequestPayload }; const getProofPresentationById = await this._sendProofRequest(payload); return getProofPresentationById?.response; @@ -268,15 +262,12 @@ export class VerificationService { try { const getAgentData = await this.verificationRepository.getAgentEndPoint(orgId); const orgAgentTypeData = await this.verificationRepository.getOrgAgentType(getAgentData?.orgAgentTypeId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - + const verificationMethod = 'accept-presentation'; - + const url = await this.getAgentUrl(verificationMethod, orgAgentTypeData, getAgentData?.agentEndPoint, getAgentData?.tenantId, '', proofId); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const payload = { apiKey, url }; + + const payload = { orgId, url }; const getProofPresentationById = await this._verifyPresentation(payload); return getProofPresentationById?.response; } catch (error) { @@ -350,15 +341,14 @@ export class VerificationService { } outOfBandRequestProof['label'] = label; + const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); const verificationMethodLabel = 'create-request-out-of-band'; const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(user.orgId); - } + - const { isShortenUrl, type, reuseConnection, ...updateOutOfBandRequestProof } = outOfBandRequestProof; + // Destructuring 'outOfBandRequestProof' to remove emailId, as it is not used while agent operation + const { isShortenUrl, emailId, type, reuseConnection, ...updateOutOfBandRequestProof } = outOfBandRequestProof; let recipientKey: string | undefined; if (true === reuseConnection) { const data: agent_invitations[] = await this.verificationRepository.getRecipientKeyByOrgId(user.orgId); @@ -369,13 +359,14 @@ export class VerificationService { } outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; - let payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload; + + let payload: IProofRequestPayload; if (ProofRequestType.INDY === type) { updateOutOfBandRequestProof.protocolVersion = updateOutOfBandRequestProof.protocolVersion || 'v1'; updateOutOfBandRequestProof.recipientKey = recipientKey || undefined; payload = { - apiKey, + orgId: user.orgId, url, proofRequestPayload: updateOutOfBandRequestProof }; @@ -384,7 +375,7 @@ export class VerificationService { if (ProofRequestType.PRESENTATIONEXCHANGE === type) { payload = { - apiKey, + orgId: user.orgId, url, proofRequestPayload: { protocolVersion:outOfBandRequestProof.protocolVersion || 'v2', @@ -398,37 +389,30 @@ export class VerificationService { } } }, - autoAcceptProof:outOfBandRequestProof.autoAcceptProof || 'always', + autoAcceptProof:outOfBandRequestProof.autoAcceptProof, recipientKey:recipientKey || undefined } - }; + }; } - - const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - //apply presentation shorting URL - if (isShortenUrl) { - const proofRequestInvitationUrl: string = getProofPresentation?.response?.invitationUrl; - const shortenedUrl: string = await this.storeVerificationObjectAndReturnUrl(proofRequestInvitationUrl, false); - this.logger.log('shortenedUrl', shortenedUrl); - if (shortenedUrl) { - getProofPresentation.response.invitationUrl = shortenedUrl; + if (emailId) { + await this.sendEmailInBatches(payload, emailId, getAgentDetails, getOrganization); + return true; + } else { + const presentationProof: IInvitation = await this.generateOOBProofReq(payload); + const proofRequestInvitationUrl: string = presentationProof.invitationUrl; + if (isShortenUrl) { + const shortenedUrl: string = await this.storeVerificationObjectAndReturnUrl(proofRequestInvitationUrl, false); + this.logger.log('shortenedUrl', shortenedUrl); + if (shortenedUrl) { + presentationProof.invitationUrl = shortenedUrl; + } } - } - if (!getProofPresentation) { - throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); - } - return getProofPresentation.response; - - // Unused code : to be segregated - // if (outOfBandRequestProof.emailId) { - // const batchSize = 100; // Define the batch size according to your needs - // const { emailId } = outOfBandRequestProof; // Assuming it's an array - // await this.sendEmailInBatches(payload, emailId, getAgentDetails, organizationDetails, batchSize); - // return true; - // } else { - // return this.generateOOBProofReq(payload, getAgentDetails); - // } + if (!presentationProof) { + throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); + } + return presentationProof; + } } catch (error) { this.logger.error(`[sendOutOfBandPresentationRequest] - error in out of band proof request : ${error.message}`); this.verificationErrorHandling(error); @@ -444,15 +428,9 @@ export class VerificationService { } - private async generateOOBProofReq(payload: IProofRequestPayload, getAgentDetails: org_agents): Promise { - let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { - agentApiKey = await this._getOrgAgentApiKey(getAgentDetails.orgId); - } - payload.apiKey = agentApiKey; + private async generateOOBProofReq(payload: IProofRequestPayload): Promise { const getProofPresentation = await this._sendOutOfBandProofRequest(payload); - if (!getProofPresentation) { throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); } @@ -460,56 +438,45 @@ export class VerificationService { } - async sendEmailInBatches(payload: IProofRequestPayload, emailIds: string[] | string, getAgentDetails: org_agents, organizationDetails: organisation, batchSize: number): Promise { + // Currently batch size is not used, as length of emails sent is restricted to '10' + async sendEmailInBatches(payload: IProofRequestPayload, emailIds: string[], getAgentDetails: org_agents, organizationDetails: organisation): Promise { + try { const accumulatedErrors = []; - if (Array.isArray(emailIds)) { - - for (let i = 0; i < emailIds.length; i += batchSize) { - const batch = emailIds.slice(i, i + batchSize); - const emailPromises = batch.map(async email => { + for (const email of emailIds) { try { - await this.sendOutOfBandProofRequest(payload, email, getAgentDetails, organizationDetails); - } catch (error) { - accumulatedErrors.push(error); - } - }); - - await Promise.all(emailPromises); - } - } else { - await this.sendOutOfBandProofRequest(payload, emailIds, getAgentDetails, organizationDetails); - } + await this.sendOutOfBandProofRequest(payload, email, getAgentDetails, organizationDetails); + await this.delay(500); + } catch (error) { + this.logger.error(`Error sending email to ${email}::::::`, error); + accumulatedErrors.push(error); + } + } if (0 < accumulatedErrors.length) { this.logger.error(accumulatedErrors); throw new Error(ResponseMessages.verification.error.emailSend); } + + } catch (error) { + this.logger.error('[sendEmailInBatches] - error in sending email in batches'); + throw new Error(ResponseMessages.verification.error.batchEmailSend); + } } + // This function is specifically for OOB verification using email async sendOutOfBandProofRequest(payload: IProofRequestPayload, email: string, getAgentDetails: org_agents, organizationDetails: organisation): Promise { - let agentApiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!agentApiKey || null === agentApiKey || undefined === agentApiKey) { - agentApiKey = await this._getOrgAgentApiKey(getAgentDetails.orgId); - } - payload.apiKey = agentApiKey; const getProofPresentation = await this._sendOutOfBandProofRequest(payload); if (!getProofPresentation) { throw new Error(ResponseMessages.verification.error.proofPresentationNotFound); } - const invitationId = getProofPresentation?.response?.invitation['@id']; - - if (!invitationId) { - throw new Error(ResponseMessages.verification.error.invitationNotFound); - } - - const shortenedUrl = getAgentDetails?.tenantId - ? `${getAgentDetails?.agentEndPoint}/multi-tenancy/url/${getAgentDetails?.tenantId}/${invitationId}` - : `${getAgentDetails?.agentEndPoint}/url/${invitationId}`; - + const invitationUrl = getProofPresentation?.response?.invitationUrl; + // Currently have shortenedUrl to store only for 30 days + const persist: boolean = false; + const shortenedUrl = await this.storeVerificationObjectAndReturnUrl(invitationUrl, persist); const qrCodeOptions: QRCode.QRCodeToDataURLOptions = { type: 'image/png' }; const outOfBandVerificationQrCode = await QRCode.toDataURL(shortenedUrl, qrCodeOptions); @@ -546,11 +513,11 @@ export class VerificationService { * @param payload * @returns Get requested proof presentation details */ - async _sendOutOfBandProofRequest(payload: IProofRequestPayload | IPresentationExchangeProofRequestPayload): Promise<{ + async _sendOutOfBandProofRequest(payload: IProofRequestPayload): Promise<{ response; }> { try { - + const pattern = { cmd: 'agent-send-out-of-band-proof-request' }; @@ -723,12 +690,8 @@ export class VerificationService { let schemaId; const orgAgentType = await this.verificationRepository.getOrgAgentType(getAgentDetails?.orgAgentTypeId); const url = await this.getAgentUrl(verificationMethodLabel, orgAgentType, getAgentDetails?.agentEndPoint, getAgentDetails?.tenantId, '', proofId); - let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); - } - const payload = { apiKey, url }; + + const payload = { orgId, url }; const getProofPresentationById = await this._getVerifiedProofDetails(payload); @@ -926,23 +889,26 @@ export class VerificationService { async natsCall(pattern: object, payload: object): Promise<{ response: string; }> { - return this.verificationServiceProxy - .send(pattern, payload) - .pipe( - map((response) => ( - { - response - })) - ) - .toPromise() - .catch(error => { - this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException({ - status: error.statusCode, - error: error.error, - message: error.message - }, error.error); - }); + return this.verificationServiceProxy + .send(pattern, payload) + .pipe( + map((response) => ( + { + response + })) + ) + .toPromise() + .catch(error => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException({ + status: error.statusCode, + error: error.error, + message: error.message + }, error.error); + }); + } + + async delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); } - -} \ No newline at end of file +} \ No newline at end of file diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index b93f0a0d7..83a0e78ee 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -313,7 +313,8 @@ export enum CommonConstants { ROLE = 'endorser', //CacheInfo -CACHE_APIKEY_KEY = "apiKey", +CACHE_SHARED_APIKEY_KEY = "dedicatedApiKey", +CACHE_APIKEY_KEY = "sharedApiKey", CACHE_TTL_SECONDS = 604800 } diff --git a/libs/common/src/common.service.ts b/libs/common/src/common.service.ts index 871c2b6fa..be3f4e183 100644 --- a/libs/common/src/common.service.ts +++ b/libs/common/src/common.service.ts @@ -17,6 +17,8 @@ import { import { CommonConstants } from './common.constant'; import { HttpService } from '@nestjs/axios/dist'; import { ResponseService } from '@credebl/response'; +import * as dotenv from 'dotenv'; +dotenv.config(); @Injectable() export class CommonService { @@ -370,11 +372,11 @@ export class CommonService { encryptedPassword, process.env.CRYPTO_PRIVATE_KEY ); + const decryptedPassword = JSON.parse(password.toString(CryptoJS.enc.Utf8)); return decryptedPassword; } catch (error) { throw new BadRequestException('Invalid Credentials'); } } - } diff --git a/libs/common/src/did.validator.ts b/libs/common/src/did.validator.ts index 2537cb48a..466643ce8 100644 --- a/libs/common/src/did.validator.ts +++ b/libs/common/src/did.validator.ts @@ -1,27 +1,24 @@ -import { DidMethod, KeyType } from "@credebl/enum/enum"; -import { IDidCreate } from "./interfaces/did.interface"; +import { DidMethod } from '@credebl/enum/enum'; +import { IDidCreate } from './interfaces/did.interface'; +import { BadRequestException } from '@nestjs/common'; -export function validateDid(createDid: IDidCreate): string[] { - const errors: string[] = []; +export function validateDid(createDid: IDidCreate): void { - if (DidMethod.WEB && !createDid.domain) { - errors.push('domain is required for Web method'); - } else if (DidMethod.INDY && !createDid.network) { - errors.push('network is required for Indy method'); - } else if (DidMethod.INDY && createDid.keyType !== KeyType.Ed25519) { - errors.push('Only ed25519 key type is supported for Indy method'); - } else if ((createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && - (createDid.keyType !== KeyType.Ed25519 && createDid.keyType !== KeyType.Bls12381g2)) { - errors.push('Only ed25519 and bls12381g2 key type is supported'); - } else if (!createDid.role) { - errors.push('role or endorserDid is required'); - } else if (DidMethod.POLYGON && !createDid.privatekey) { - errors.push('privateKey is required for polygon method'); - } else if (DidMethod.POLYGON && !createDid.endpoint) { - errors.push('endpoint is required for polygon method'); - } else if ((DidMethod.INDY || DidMethod.KEY || DidMethod.WEB) && (!createDid.seed)) { - errors.push('seed is required'); + if (DidMethod.WEB === createDid.method && !createDid.domain) { + throw new BadRequestException('domain is required for Web method'); + } else if ((createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && !createDid.network) { + throw new BadRequestException('network is required'); + } else if ((createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && 'ed25519' !== createDid.keyType) { + throw new BadRequestException('Only ed25519 key type is supported'); + } else if ((createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && ('ed25519' !== createDid.keyType && 'bls12381g2' !== createDid.keyType)) { + throw new BadRequestException('Only ed25519 and bls12381g2 key type is supported'); + } else if (DidMethod.INDY === createDid.method && (!createDid.role && !createDid.endorserDid)) { + throw new BadRequestException('role or endorserDid is required'); + } else if (DidMethod.POLYGON === createDid.method && !createDid.privatekey) { + throw new BadRequestException('privatekey is required for polygon method'); + } else if (DidMethod.POLYGON === createDid.method && !createDid.endpoint) { + throw new BadRequestException('endpoint is required for polygon method'); + } else if ((DidMethod.INDY === createDid.method || DidMethod.KEY === createDid.method || DidMethod.WEB === createDid.method) && (!createDid.seed)) { + throw new BadRequestException('seed is required'); } - - return errors; } \ No newline at end of file diff --git a/libs/common/src/interfaces/ecosystem.interface.ts b/libs/common/src/interfaces/ecosystem.interface.ts new file mode 100644 index 000000000..648153c19 --- /dev/null +++ b/libs/common/src/interfaces/ecosystem.interface.ts @@ -0,0 +1,42 @@ +interface EcosystemRole { + id: string; + name: string; + description: string; + createDateTime: Date; + lastChangedDateTime: Date; + deletedAt: Date; + } + +interface Ecosystem { + id: string; + name: string; + description: string; + logoUrl: string | null; + createDateTime: string; + lastChangedDateTime: string; + createdBy: string; + autoEndorsement: boolean; + ecosystemOrgs: EcosystemOrg[]; + } + + interface EcosystemOrg { + id: string; + orgId: string; + status: string; + createDateTime: string; + lastChangedDateTime: string; + ecosystemId: string; + ecosystemRoleId: string; + ecosystemRole: EcosystemRole; + } + + export interface IEcosystemDetails { + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + ecosystemList: Ecosystem[]; + } + \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 1aac2c206..5363cdd80 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -302,6 +302,7 @@ export const ResponseMessages = { proofNotFound: 'Proof presentation not found', invitationNotFound: 'Invitation not found', platformConfigNotFound: 'Platform config not found', + batchEmailSend: 'Unable to send email in batches', emailSend: 'Unable to send email to the user' } }, diff --git a/libs/prisma-service/prisma/migrations/20240315121444_org_agents_did_document/migration.sql b/libs/prisma-service/prisma/migrations/20240315121444_org_agents_did_document/migration.sql new file mode 100644 index 000000000..9c0b9a251 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240315121444_org_agents_did_document/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "org_agents" ADD COLUMN "didDocument" JSONB; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 908fd8b3b..9e55d94b3 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -186,6 +186,7 @@ model org_agents { orgId String? @unique @db.Uuid orgAgentTypeId String? @db.Uuid ledgerId String? @db.Uuid + didDocument Json? agent_invitations agent_invitations[] agents agents? @relation(fields: [agentId], references: [id]) agents_type agents_type? @relation(fields: [agentsTypeId], references: [id]) From 25646c3393d6061e484d10ff4c3e5bce7c6c4a57 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 27 Mar 2024 17:16:32 +0530 Subject: [PATCH 213/231] feat: adeya app changes for white labelling Signed-off-by: pranalidhanavade --- .../templates/out-of-band-issuance.template.ts | 16 ++++++++-------- .../out-of-band-verification.template.ts | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/issuance/templates/out-of-band-issuance.template.ts b/apps/issuance/templates/out-of-band-issuance.template.ts index e97c04918..30e276440 100644 --- a/apps/issuance/templates/out-of-band-issuance.template.ts +++ b/apps/issuance/templates/out-of-band-issuance.template.ts @@ -25,14 +25,14 @@ export class OutOfBandIssuance { To acknowledge and access your credential, kindly proceed with following steps:
    -
  • Download the ADEYA SSI App from - Android Play Store or -iOS App Store. (Skip, if already downloaded) +
  • Download the ${process.env.MOBILE_APP_NAME} from + Android Play Store or +iOS App Store. (Skip, if already downloaded)
  • -
  • Complete the onboarding process in ADEYA.
  • -
  • Open the “Accept Credential” link below in this email (This will open the link in the ADEYA App)
  • -
  • Accept the Credential in ADEYA.
  • -
  • Check "Credentials" tab in ADEYA to view the issued credential.
  • +
  • Complete the onboarding process in ${process.env.MOBILE_APP}.
  • +
  • Open the “Accept Credential” link below in this email (This will open the link in the ${process.env.MOBILE_APP} App)
  • +
  • Accept the Credential in ${process.env.MOBILE_APP}.
  • +
  • Check "Credentials" tab in ${process.env.MOBILE_APP} to view the issued credential.

- Note: If the above steps do not work for you, please open the attached QR Code image in this email on another device, and scan the QR code using the ADEYA SSI App on your mobile device. + Note: If the above steps do not work for you, please open the attached QR Code image in this email on another device, and scan the QR code using the ${process.env.MOBILE_APP_NAME} on your mobile device. The QR Code is single-use.

diff --git a/apps/verification/templates/out-of-band-verification.template.ts b/apps/verification/templates/out-of-band-verification.template.ts index a7a385775..fb9773646 100644 --- a/apps/verification/templates/out-of-band-verification.template.ts +++ b/apps/verification/templates/out-of-band-verification.template.ts @@ -24,13 +24,13 @@ export class OutOfBandVerification {

${orgName} has requested verification of your digital credential. To share requested credential kindly follow below steps:

    -
  • Download the ADEYA SSI App from - Android Play Store or -iOS App Store. (Skip, if already downloaded) +
  • Download the ${process.env.MOBILE_APP_NAME} from + Android Play Store or +iOS App Store. (Skip, if already downloaded)
  • -
  • Complete the onboarding process in ADEYA.
  • -
  • Open the “Share Credential” link below in this email (This will open the link in the ADEYA App)
  • -
  • Tap the "Send Proof" button in ADEYA to share you credential data.
  • +
  • Complete the onboarding process in ${process.env.MOBILE_APP}.
  • +
  • Open the “Share Credential” link below in this email (This will open the link in the ${process.env.MOBILE_APP} App)
  • +
  • Tap the "Send Proof" button in ${process.env.MOBILE_APP} to share you credential data.

- Note: If the above steps do not work for you, please open the attached QR Code image in this email on another device, and scan the QR code using the ADEYA SSI App on your mobile device. + Note: If the above steps do not work for you, please open the attached QR Code image in this email on another device, and scan the QR code using the ${process.env.MOBILE_APP_NAME} on your mobile device. The QR Code is single-use.

From d6aa57963c8b7b8cde90cf573a06721c3e96ffe2 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 27 Mar 2024 17:22:20 +0530 Subject: [PATCH 214/231] feat: play store and app store links from .env file Signed-off-by: pranalidhanavade --- apps/issuance/templates/out-of-band-issuance.template.ts | 2 +- .../verification/templates/out-of-band-verification.template.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/issuance/templates/out-of-band-issuance.template.ts b/apps/issuance/templates/out-of-band-issuance.template.ts index 30e276440..a2f68176d 100644 --- a/apps/issuance/templates/out-of-band-issuance.template.ts +++ b/apps/issuance/templates/out-of-band-issuance.template.ts @@ -26,7 +26,7 @@ export class OutOfBandIssuance { To acknowledge and access your credential, kindly proceed with following steps:
  • Download the ${process.env.MOBILE_APP_NAME} from - Android Play Store or + Android Play Store or iOS App Store. (Skip, if already downloaded)
  • Complete the onboarding process in ${process.env.MOBILE_APP}.
  • diff --git a/apps/verification/templates/out-of-band-verification.template.ts b/apps/verification/templates/out-of-band-verification.template.ts index fb9773646..ebb5e0693 100644 --- a/apps/verification/templates/out-of-band-verification.template.ts +++ b/apps/verification/templates/out-of-band-verification.template.ts @@ -25,7 +25,7 @@ export class OutOfBandVerification { ${orgName} has requested verification of your digital credential. To share requested credential kindly follow below steps:
    • Download the ${process.env.MOBILE_APP_NAME} from - Android Play Store or + Android Play Store or iOS App Store. (Skip, if already downloaded)
    • Complete the onboarding process in ${process.env.MOBILE_APP}.
    • From 3212ad8655f3f1610e2c4c54be246f55043a8c13 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Thu, 28 Mar 2024 13:41:49 +0530 Subject: [PATCH 215/231] fix: logger issue Signed-off-by: tipusinghaw --- apps/ledger/src/schema/schema.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 0f65ac7f7..c3af49025 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -320,7 +320,7 @@ export class SchemaService extends BaseService { })) ).toPromise() .catch(error => { - this.logger.error(`Error in creating WC3 schema : ${JSON.stringify(error)}`); + this.logger.error(`Error in creating W3C schema : ${JSON.stringify(error)}`); throw new RpcException(error.error ? error.error.message : error.message); }); return W3CSchemaResponse; From 02e9d0fb172c3e1e28e3be581b6219ba1498a5b1 Mon Sep 17 00:00:00 2001 From: tipusinghaw <126460794+tipusinghaw@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:04:21 +0530 Subject: [PATCH 216/231] feat: Added W3C-schema API (#600) * feat: added w3c schema Signed-off-by: tipusinghaw * feat: added interface Signed-off-by: tipusinghaw * fix: schema dto Signed-off-by: tipusinghaw * fix: sonar cloud issue Signed-off-by: tipusinghaw * fix: changed the interface of create schema Signed-off-by: tipusinghaw * fix: multiple error messages Signed-off-by: tipusinghaw * refactor: changed API key fetching logic Signed-off-by: tipusinghaw * fix: logger issue Signed-off-by: tipusinghaw --------- Signed-off-by: tipusinghaw --- .../src/agent-service.controller.ts | 6 ++ .../src/agent-service.service.ts | 11 ++++ .../api-gateway/src/dtos/create-schema.dto.ts | 19 ++++++ .../src/interfaces/ISchemaSearch.interface.ts | 6 ++ .../src/schema/schema.controller.ts | 22 ++++++- apps/api-gateway/src/schema/schema.service.ts | 7 ++- .../interfaces/schema-payload.interface.ts | 16 +++++ apps/ledger/src/schema/schema.controller.ts | 8 ++- apps/ledger/src/schema/schema.service.ts | 63 ++++++++++++++++++- libs/common/src/common.constant.ts | 4 ++ 10 files changed, 157 insertions(+), 5 deletions(-) diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 26d1c066b..10587964c 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -45,6 +45,12 @@ export class AgentServiceController { return this.agentServiceService.createSchema(payload); } + //DONE + @MessagePattern({ cmd: 'agent-create-w3c-schema' }) + async createW3CSchema(payload: { url, orgId, schemaRequestPayload }): Promise { + return this.agentServiceService.createW3CSchema(payload.url, payload.orgId, payload.schemaRequestPayload); + } + //DONE @MessagePattern({ cmd: 'agent-get-schema' }) async getSchemaById(payload: IGetSchemaAgentRedirection): Promise { diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index a2ab18dbe..eb5cf2e2d 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -1561,6 +1561,17 @@ export class AgentServiceService { return data; } + async createW3CSchema(url: string, orgId: string, schemaRequestPayload): Promise { + try { + const getApiKey = await this.getOrgAgentApiKey(orgId); + const schemaRequest = await this.commonService + .httpPost(url, schemaRequestPayload, { headers: { 'authorization': getApiKey } }) + .then(async response => response); + return schemaRequest; + } catch (error) { + this.logger.error(`Error in schema endorsement request in agent service : ${JSON.stringify(error)}`); + } + } async natsCall(pattern: object, payload: object): Promise<{ response: string; }> { diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index 5ba17afe9..65b109a5c 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -70,3 +70,22 @@ export class CreateSchemaDto { @IsString({ message: 'orgDid must be a string' }) orgDid: string; } + +export class CreateW3CSchemaDto { + @ApiProperty() + @IsNotEmpty({ message: 'schemaObject is required' }) + schema: object; + + @ApiProperty() + @IsString({ message: 'schemaName must be a string' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'schemaName is required' }) + schemaName: string; + + @ApiProperty() + @IsString({ message: 'did must be a string' }) + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'did is required' }) + did: string; + +} diff --git a/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts b/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts index 2f83d26cb..6266a4e37 100644 --- a/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts +++ b/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts @@ -10,3 +10,9 @@ export interface ISchemaSearchPayload { user?: IUserRequestInterface } + +export interface W3CSchemaPayload { + schema: object; + schemaName: string; + did: string; + } diff --git a/apps/api-gateway/src/schema/schema.controller.ts b/apps/api-gateway/src/schema/schema.controller.ts index 596be7c4d..15335fa03 100644 --- a/apps/api-gateway/src/schema/schema.controller.ts +++ b/apps/api-gateway/src/schema/schema.controller.ts @@ -17,7 +17,7 @@ import { OrgRoles } from 'libs/org-roles/enums'; import { Roles } from '../authz/decorators/roles.decorator'; import { IUserRequestInterface } from './interfaces'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; -import { CreateSchemaDto } from '../dtos/create-schema.dto'; +import { CreateSchemaDto, CreateW3CSchemaDto } from '../dtos/create-schema.dto'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { CredDefSortFields, SortFields } from 'apps/ledger/src/schema/enum/schema.enum'; @@ -133,6 +133,26 @@ export class SchemaController { return res.status(HttpStatus.OK).json(finalResponse); } + @Post('/:orgId/polygon-w3c/schemas') + @ApiOperation({ + summary: 'Create and sends a W3C-schema to the ledger.', + description: 'Create and sends a W3C-schema to the ledger.' + }) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createW3CSchema(@Res() res: Response, @Body() schemaPayload: CreateW3CSchemaDto, @Param('orgId') orgId: string, @User() user: IUserRequestInterface): Promise { + + const schemaDetails = await this.appService.createW3CSchema(schemaPayload, orgId); + + const finalResponse: IResponseType = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.schema.success.create, + data: schemaDetails + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + @Post('/:orgId/schemas') @ApiOperation({ summary: 'Create and sends a schema to the ledger.', diff --git a/apps/api-gateway/src/schema/schema.service.ts b/apps/api-gateway/src/schema/schema.service.ts index a1d0863a3..f0655d307 100644 --- a/apps/api-gateway/src/schema/schema.service.ts +++ b/apps/api-gateway/src/schema/schema.service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from '../../../../libs/service/base.service'; import { CreateSchemaDto } from '../dtos/create-schema.dto'; -import { ISchemaSearchPayload } from '../interfaces/ISchemaSearch.interface'; +import { ISchemaSearchPayload, W3CSchemaPayload } from '../interfaces/ISchemaSearch.interface'; import { IUserRequestInterface } from './interfaces'; import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { GetCredentialDefinitionBySchemaIdDto } from './dtos/get-all-schema.dto'; @@ -19,6 +19,11 @@ export class SchemaService extends BaseService { return this.sendNatsMessage(this.schemaServiceProxy, 'create-schema', payload); } + createW3CSchema(schemaPayload: W3CSchemaPayload, orgId: string): Promise { + const payload = { schemaPayload, orgId }; + return this.sendNatsMessage(this.schemaServiceProxy, 'create-w3c-schema', payload); + } + getSchemaById(schemaId: string, orgId: string): Promise<{ response: object; }> { diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index fb5a3f37e..bd091074e 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -72,3 +72,19 @@ export interface ISchemaExist { version: string; } +interface SchemaPayload { + schema: object, + schemaName: string, + did: string + } + +export interface W3CSchemaPayload { + schemaPayload: SchemaPayload, + orgId: string + } + +export interface W3CCreateSchema { + url: string, + orgId: string, + schemaRequestPayload: SchemaPayload +} diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index 68003ac49..dafc53a89 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -5,7 +5,8 @@ import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, - ISchemaSearchPayload + ISchemaSearchPayload, + W3CSchemaPayload } from './interfaces/schema-payload.interface'; import { schema } from '@prisma/client'; import { @@ -24,6 +25,11 @@ export class SchemaController { return this.schemaService.createSchema(schema, user, orgId); } + @MessagePattern({ cmd: 'create-w3c-schema' }) + async createW3CSchema(payload: W3CSchemaPayload): Promise { + return this.schemaService.createW3CSchema(payload); + } + @MessagePattern({ cmd: 'get-schema-by-id' }) async getSchemaById(payload: ISchema): Promise { const { schemaId, orgId } = payload; diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 3d674470c..c3af49025 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -11,7 +11,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { SchemaRepository } from './repositories/schema.repository'; import { schema } from '@prisma/client'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria } from './interfaces/schema-payload.interface'; +import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria, W3CCreateSchema, W3CSchemaPayload } from './interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; @@ -20,6 +20,7 @@ import { OrgAgentType } from '@credebl/enum/enum'; import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { CommonConstants } from '@credebl/common/common.constant'; @Injectable() export class SchemaService extends BaseService { @@ -241,6 +242,43 @@ export class SchemaService extends BaseService { } } + async createW3CSchema( + schemaRequestPayload: W3CSchemaPayload + ): Promise { + try { + const { orgId } = schemaRequestPayload; + const agentDetails = await this.schemaRepository.getAgentDetailsByOrgId(orgId); + if (!agentDetails) { + throw new NotFoundException( + ResponseMessages.schema.error.agentDetailsNotFound, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); + } + const { agentEndPoint } = agentDetails; + const getAgentDetails = await this.schemaRepository.getAgentType(orgId); + const orgAgentType = await this.schemaRepository.getOrgAgentType(getAgentDetails.org_agents[0].orgAgentTypeId); + let url; + if (OrgAgentType.DEDICATED === orgAgentType) { + url = `${agentEndPoint}${CommonConstants.DEDICATED_CREATE_POLYGON_W3C_SCHEMA}`; + } else if (OrgAgentType.SHARED === orgAgentType) { + const { tenantId } = await this.schemaRepository.getAgentDetailsByOrgId(orgId); + url = `${agentEndPoint}${CommonConstants.SHARED_CREATE_POLYGON_W3C_SCHEMA}${tenantId}`; + } + const W3cSchemaPayload = { + url, + orgId, + schemaRequestPayload: schemaRequestPayload.schemaPayload + }; + return this._createW3CSchema(W3cSchemaPayload); + } catch (error) { + this.logger.error( + `[createSchema] - outer Error: ${JSON.stringify(error)}` + ); + throw new RpcException(error.error ? error.error.message : error.message); + } + } + + async _createSchema(payload: CreateSchemaAgentRedirection): Promise<{ response: string; }> { @@ -267,6 +305,27 @@ export class SchemaService extends BaseService { return schemaResponse; } + async _createW3CSchema(payload: W3CCreateSchema): Promise<{ + response: string; + }> { + const natsPattern = { + cmd: 'agent-create-w3c-schema' + }; + const W3CSchemaResponse = await this.schemaServiceProxy + .send(natsPattern, payload) + .pipe( + map((response) => ( + { + response + })) + ).toPromise() + .catch(error => { + this.logger.error(`Error in creating W3C schema : ${JSON.stringify(error)}`); + throw new RpcException(error.error ? error.error.message : error.message); + }); + return W3CSchemaResponse; + } + async getSchemaById(schemaId: string, orgId: string): Promise { try { @@ -455,7 +514,7 @@ export class SchemaService extends BaseService { async _getOrgAgentApiKey(orgId: string): Promise { const pattern = { cmd: 'get-org-agent-api-key' }; const payload = { orgId }; - + try { // eslint-disable-next-line @typescript-eslint/no-explicit-any const message = await this.schemaServiceProxy.send(pattern, payload).toPromise(); diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index 83a0e78ee..a15b61224 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -84,6 +84,10 @@ export enum CommonConstants { URL_SCHM_GET_CRED_DEF_BY_ID = '/credential-definitions/#', URL_SCHM_GET_CRED_DEF_BY_ATTRB = '/credential-definitions/created', + // POLYGON BASED W3C SCHEMAS + DEDICATED_CREATE_POLYGON_W3C_SCHEMA = '/polygon/create-schema', + SHARED_CREATE_POLYGON_W3C_SCHEMA = '/multi-tenancy/polygon-wc3/schema/', + // SHARED AGENT URL_SHAGENT_CREATE_TENANT = '/multi-tenancy/create-tenant', URL_SHAGENT_CREATE_DID = '/multi-tenancy/create-did/', From 4132ad07e313dc08d0648e7ffa69b49a49425ad9 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Thu, 28 Mar 2024 16:33:56 +0530 Subject: [PATCH 217/231] fix: private key length validations Signed-off-by: bhavanakarwade --- apps/api-gateway/src/agent-service/dto/create-did.dto.ts | 1 + libs/common/src/did.validator.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts index 3e8deb560..808feece7 100644 --- a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts @@ -48,6 +48,7 @@ export class CreateDidDto { @IsOptional() @ApiPropertyOptional() @IsString({ message: 'private key must be in string format.' }) + @Transform(({ value }) => trim(value)) privatekey?: string; @ApiProperty({example: 'http://localhost:6006/docs'}) diff --git a/libs/common/src/did.validator.ts b/libs/common/src/did.validator.ts index 466643ce8..8ffa7de2f 100644 --- a/libs/common/src/did.validator.ts +++ b/libs/common/src/did.validator.ts @@ -16,6 +16,8 @@ export function validateDid(createDid: IDidCreate): void { throw new BadRequestException('role or endorserDid is required'); } else if (DidMethod.POLYGON === createDid.method && !createDid.privatekey) { throw new BadRequestException('privatekey is required for polygon method'); + } else if (DidMethod.POLYGON === createDid.method && createDid.privatekey && 64 !== createDid.privatekey.length) { + throw new BadRequestException('Private key must be exactly 64 characters long'); } else if (DidMethod.POLYGON === createDid.method && !createDid.endpoint) { throw new BadRequestException('endpoint is required for polygon method'); } else if ((DidMethod.INDY === createDid.method || DidMethod.KEY === createDid.method || DidMethod.WEB === createDid.method) && (!createDid.seed)) { From 88c14761551784d58458eb502a799d08ec09abd3 Mon Sep 17 00:00:00 2001 From: bhavanakarwade Date: Thu, 28 Mar 2024 18:02:39 +0530 Subject: [PATCH 218/231] fix: refactored validate did function Signed-off-by: bhavanakarwade --- .../src/agent-service/dto/create-did.dto.ts | 7 +++ libs/common/src/did.validator.ts | 55 ++++++++++++------- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts index 808feece7..bc36bac98 100644 --- a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts @@ -18,29 +18,34 @@ export class CreateDidDto { @ApiProperty({ example: 'ed25519'}) @IsNotEmpty({ message: 'key type is required' }) + @Transform(({ value }) => trim(value)) @IsString({ message: 'key type be in string format.' }) keyType: string; @ApiProperty({ example: 'indy'}) @IsNotEmpty({ message: 'method is required' }) + @Transform(({ value }) => trim(value)) @IsString({ message: 'method must be in string format.' }) method: string; @ApiProperty({example: 'bcovrin:testnet'}) @IsOptional() @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'network must be in string format.' }) network?: string; @ApiProperty({example: 'www.github.com'}) @IsOptional() @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'domain must be in string format.' }) domain?: string; @ApiProperty({example: 'endorser'}) @IsOptional() @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'role must be in string format.' }) role?: string; @@ -60,12 +65,14 @@ export class CreateDidDto { @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) @IsOptional() @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'did must be in string format.' }) did?: string; @ApiProperty({example: 'did:indy:bcovrin:testnet:UEeW111G1tYo1nEkPwMcF'}) @IsOptional() @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'endorser did must be in string format.' }) endorserDid?: string; } \ No newline at end of file diff --git a/libs/common/src/did.validator.ts b/libs/common/src/did.validator.ts index 8ffa7de2f..cd36579cf 100644 --- a/libs/common/src/did.validator.ts +++ b/libs/common/src/did.validator.ts @@ -3,24 +3,41 @@ import { IDidCreate } from './interfaces/did.interface'; import { BadRequestException } from '@nestjs/common'; export function validateDid(createDid: IDidCreate): void { + const errors = []; - if (DidMethod.WEB === createDid.method && !createDid.domain) { - throw new BadRequestException('domain is required for Web method'); - } else if ((createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && !createDid.network) { - throw new BadRequestException('network is required'); - } else if ((createDid.method === DidMethod.INDY || createDid.method === DidMethod.POLYGON) && 'ed25519' !== createDid.keyType) { - throw new BadRequestException('Only ed25519 key type is supported'); - } else if ((createDid.method === DidMethod.WEB || createDid.method === DidMethod.KEY) && ('ed25519' !== createDid.keyType && 'bls12381g2' !== createDid.keyType)) { - throw new BadRequestException('Only ed25519 and bls12381g2 key type is supported'); - } else if (DidMethod.INDY === createDid.method && (!createDid.role && !createDid.endorserDid)) { - throw new BadRequestException('role or endorserDid is required'); - } else if (DidMethod.POLYGON === createDid.method && !createDid.privatekey) { - throw new BadRequestException('privatekey is required for polygon method'); - } else if (DidMethod.POLYGON === createDid.method && createDid.privatekey && 64 !== createDid.privatekey.length) { - throw new BadRequestException('Private key must be exactly 64 characters long'); - } else if (DidMethod.POLYGON === createDid.method && !createDid.endpoint) { - throw new BadRequestException('endpoint is required for polygon method'); - } else if ((DidMethod.INDY === createDid.method || DidMethod.KEY === createDid.method || DidMethod.WEB === createDid.method) && (!createDid.seed)) { - throw new BadRequestException('seed is required'); + 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.POLYGON === createDid.method && !createDid.endpoint: + errors.push('endpoint is required for polygon method'); + break; + case (DidMethod.INDY === createDid.method || DidMethod.KEY === createDid.method || DidMethod.WEB === createDid.method) && (!createDid.seed): + errors.push('seed is required'); + break; + default: + break; } -} \ No newline at end of file + + if (0 < errors.length) { + throw new BadRequestException(errors); + } +} From 1eda14227935b73117fb8628bc6cd10e99216ad0 Mon Sep 17 00:00:00 2001 From: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Date: Fri, 29 Mar 2024 20:41:35 +0530 Subject: [PATCH 219/231] feat: out-of-band connection invitation (#623) Signed-off-by: KulkarniShashank --- .../src/agent-service.controller.ts | 7 +- .../src/agent-service.service.ts | 18 +- .../src/interface/agent-service.interface.ts | 667 +++++++++--------- .../src/connection/connection.controller.ts | 31 +- .../src/connection/connection.service.ts | 9 +- .../src/connection/dtos/connection.dto.ts | 53 ++ .../src/connection/enums/connections.enum.ts | 5 + .../src/verification/dto/request-proof.dto.ts | 6 + apps/connection/src/connection.controller.ts | 6 + apps/connection/src/connection.service.ts | 153 +++- .../src/interfaces/connection.interfaces.ts | 21 + libs/common/src/common.constant.ts | 2 + 12 files changed, 635 insertions(+), 343 deletions(-) diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 10587964c..7b7b65d21 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -1,7 +1,7 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { AgentServiceService } from './agent-service.service'; -import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IAgentProofRequest, IDidCreate, IWallet, ITenantRecord } from './interface/agent-service.interface'; +import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IAgentProofRequest, IDidCreate, IWallet, ITenantRecord, ICreateConnectionInvitation } from './interface/agent-service.interface'; import { user } from '@prisma/client'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; @@ -246,5 +246,10 @@ export class AgentServiceController { return this.agentServiceService.createSecp256k1KeyPair(payload.orgId); } + + @MessagePattern({ cmd: 'agent-create-connection-invitation' }) + async createConnectionInvitation(payload: { url: string; orgId: string; connectionPayload: ICreateConnectionInvitation }): Promise { + return this.agentServiceService.createConnectionInvitation(payload.url, payload.orgId, payload.connectionPayload); + } } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index eb5cf2e2d..5a595fbf1 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -46,7 +46,8 @@ import { IDidCreate, IWallet, ITenantRecord, - LedgerListResponse + LedgerListResponse, + ICreateConnectionInvitation } from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; @@ -1572,6 +1573,21 @@ export class AgentServiceService { this.logger.error(`Error in schema endorsement request in agent service : ${JSON.stringify(error)}`); } } + + async createConnectionInvitation(url: string, orgId: string, connectionPayload: ICreateConnectionInvitation): Promise { + try { + const getApiKey = await this.getOrgAgentApiKey(orgId); + + const createConnectionInvitation = await this.commonService + .httpPost(url, connectionPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); + return createConnectionInvitation; + } catch (error) { + this.logger.error(`Error in create connection invitation in agent service : ${JSON.stringify(error)}`); + throw error; + } + } + async natsCall(pattern: object, payload: object): Promise<{ response: string; }> { diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 4e3b1a4ed..38f5f9388 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -1,161 +1,161 @@ import { UserRoleOrgPermsDto } from 'apps/api-gateway/src/dtos/user-role-org-perms.dto'; export interface IAgentSpinupDto { - walletName: string; - walletPassword: string; - seed: string; - orgId?: string; - orgName?: string; - ledgerId?: string[]; - keyType: string; - domain?: string; - privatekey?: string; - endpoint?: string; - role?: string; - network?: string - endorserDid?: string - method: string; - did?: string; - agentType?: string; - transactionApproval?: boolean; - clientSocketId?: string - tenant?: boolean; - ledgerName?: string[]; - platformAdminEmail?: string; - apiKey?: string; + walletName: string; + walletPassword: string; + seed: string; + orgId?: string; + orgName?: string; + ledgerId?: string[]; + keyType: string; + domain?: string; + privatekey?: string; + endpoint?: string; + role?: string; + network?: string; + endorserDid?: string; + method: string; + did?: string; + agentType?: string; + transactionApproval?: boolean; + clientSocketId?: string; + tenant?: boolean; + ledgerName?: string[]; + platformAdminEmail?: string; + apiKey?: string; } export interface IOutOfBandCredentialOffer { - emailId: string; - attributes: IAttributes[]; - credentialDefinitionId: string; - comment: string; - protocolVersion?: string; - orgId: string; - goalCode?: string, - parentThreadId?: string, - willConfirm?: boolean, - label?: string + emailId: string; + attributes: IAttributes[]; + credentialDefinitionId: string; + comment: string; + protocolVersion?: string; + orgId: string; + goalCode?: string; + parentThreadId?: string; + willConfirm?: boolean; + label?: string; } export interface ITenantDto { - label: string; - seed?: string; - keyType: string; - ledgerId: string[]; - domain?: string; - privatekey?: string; - endpoint?: string; - role?: string; - network?: string - endorserDid?: string - method: string; - orgId: string; - did?: string; - tenantId?: string; - didDocument?: string; - clientSocketId?: string; + label: string; + seed?: string; + keyType: string; + ledgerId: string[]; + domain?: string; + privatekey?: string; + endpoint?: string; + role?: string; + network?: string; + endorserDid?: string; + method: string; + orgId: string; + did?: string; + tenantId?: string; + didDocument?: string; + clientSocketId?: string; } export interface IWallet { - label: string; - orgId: string; - did?: string; - clientSocketId?: string; + label: string; + orgId: string; + did?: string; + clientSocketId?: string; } export interface IDidCreate { - keyType: KeyType; - seed: string; - domain?: string; - network?: string; - privatekey?: string; - endpoint?: string; - method: string; - did?: string; - role?: string; - endorserDid?: string; + keyType: KeyType; + seed: string; + domain?: string; + network?: string; + privatekey?: string; + endpoint?: string; + method: string; + did?: string; + role?: string; + endorserDid?: string; } export interface ITenantSchema { - tenantId?: string; - attributes: string[]; - version: string; - name: string; - issuerId?: string; - payload?: ITenantSchemaDto; - method?: string; - agentType?: string; - apiKey?: string; - agentEndPoint?: string; - orgId?: string; + tenantId?: string; + attributes: string[]; + version: string; + name: string; + issuerId?: string; + payload?: ITenantSchemaDto; + method?: string; + agentType?: string; + apiKey?: string; + agentEndPoint?: string; + orgId?: string; } export interface ITenantSchemaDto { - attributes: string[]; - version: string; - name: string; - issuerId: string; + attributes: string[]; + version: string; + name: string; + issuerId: string; } export interface IGetSchemaAgentRedirection { - schemaId?: string; - tenantId?: string; - payload?: IGetSchemaFromTenantPayload; - apiKey?: string; - agentEndPoint?: string; - agentType?: string; - method?: string; - orgId?: string; + schemaId?: string; + tenantId?: string; + payload?: IGetSchemaFromTenantPayload; + apiKey?: string; + agentEndPoint?: string; + agentType?: string; + method?: string; + orgId?: string; } export interface IGetSchemaFromTenantPayload { - schemaId: string; + schemaId: string; } export interface ITenantCredDef { - tenantId?: string; - tag?: string; - schemaId?: string; - issuerId?: string; - payload?: ITenantCredDef; - method?: string; - agentType?: string; - apiKey?: string; - agentEndPoint?: string; - orgId?: string; + tenantId?: string; + tag?: string; + schemaId?: string; + issuerId?: string; + payload?: ITenantCredDef; + method?: string; + agentType?: string; + apiKey?: string; + agentEndPoint?: string; + orgId?: string; } export interface IWalletProvision { - orgId: string; - externalIp: string; - walletName: string; - walletPassword: string; - seed: string; - webhookEndpoint: string; - walletStorageHost: string; - walletStoragePort: string; - walletStorageUser: string; - walletStoragePassword: string; - containerName: string; - agentType: string; - orgName: string; - indyLedger: string; - afjVersion: string; - protocol: string; - tenant: boolean; - inboundEndpoint: string; - apiKey?: string; + orgId: string; + externalIp: string; + walletName: string; + walletPassword: string; + seed: string; + webhookEndpoint: string; + walletStorageHost: string; + walletStoragePort: string; + walletStorageUser: string; + walletStoragePassword: string; + containerName: string; + agentType: string; + orgName: string; + indyLedger: string; + afjVersion: string; + protocol: string; + tenant: boolean; + inboundEndpoint: string; + apiKey?: string; } export interface IPlatformConfigDto { - externalIP: string; - genesisURL: string; - adminKey: string; - lastInternalIP: string; - platformTestNetApiKey: string; - sgEmailFrom: string; - apiEndpoint: string; - tailsFileServer: string; + externalIP: string; + genesisURL: string; + adminKey: string; + lastInternalIP: string; + platformTestNetApiKey: string; + sgEmailFrom: string; + apiEndpoint: string; + tailsFileServer: string; } export interface IStoreOrgAgentDetails { @@ -184,335 +184,348 @@ export interface IStoreOrgAgentDetails { } export interface IStoreOrgAgent { - id?: string; - clientSocketId?: string; - agentEndPoint?: string; - apiKey?: string; - seed?: string; - did?: string; - verkey?: string; - isDidPublic?: boolean; - agentSpinUpStatus?: number; - walletName?: string; - agentsTypeId?: string; - orgId?: string; - agentId?: string; - orgAgentTypeId?: string; - tenantId?: string; - ledgerId?: unknown; - agentType?: string; + id?: string; + clientSocketId?: string; + agentEndPoint?: string; + apiKey?: string; + seed?: string; + did?: string; + verkey?: string; + isDidPublic?: boolean; + agentSpinUpStatus?: number; + walletName?: string; + agentsTypeId?: string; + orgId?: string; + agentId?: string; + orgAgentTypeId?: string; + tenantId?: string; + ledgerId?: unknown; + agentType?: string; } - export interface IConnectionDetails { - multiUseInvitation?: boolean; - autoAcceptConnection: boolean; + multiUseInvitation?: boolean; + autoAcceptConnection: boolean; } export interface IUserRequestInterface { - userId?: string; - id?: string; - email: string; - orgId: string; - agentEndPoint?: string; - apiKey?: string; - tenantId?: string; - tenantName?: string; - tenantOrgId?: string; - userRoleOrgPermissions?: UserRoleOrgPermsDto[]; - orgName?: string; - selectedOrg: IOrgInterface; + userId?: string; + id?: string; + email: string; + orgId: string; + agentEndPoint?: string; + apiKey?: string; + tenantId?: string; + tenantName?: string; + tenantOrgId?: string; + userRoleOrgPermissions?: UserRoleOrgPermsDto[]; + orgName?: string; + selectedOrg: IOrgInterface; } export interface IOrgInterface { - id: string; - userId: string; - orgRoleId: string; - orgId: string; - orgRole: object; - organisation: IOrganizationAgentInterface; + id: string; + userId: string; + orgRoleId: string; + orgId: string; + orgRole: object; + organisation: IOrganizationAgentInterface; } export interface IOrganizationAgentInterface { - name: string; - description: string; - org_agents: IOrgAgentInterface[] - + name: string; + description: string; + org_agents: IOrgAgentInterface[]; } export interface IPlatformAgent { - seed: string; - keyType: string; - method: string; - network: string; - role: string; - } - + seed: string; + keyType: string; + method: string; + network: string; + role: string; +} + export interface IOrgAgentInterface { - orgDid: string; - verkey: string; - agentEndPoint: string; - agentOptions: string; - walletName: string; - agentsTypeId: string; - orgId: string; + orgDid: string; + verkey: string; + agentEndPoint: string; + agentOptions: string; + walletName: string; + agentsTypeId: string; + orgId: string; } export interface ITenantCredDefDto { - tag: string; - schemaId: string; - issuerId: string; + tag: string; + schemaId: string; + issuerId: string; } export interface IGetCredDefAgentRedirection { - credentialDefinitionId?: string; - tenantId?: string; - payload?: IGetCredDefFromTenantPayload; - apiKey?: string; - agentEndPoint?: string; - agentType?: string; - method?: string; - orgId?: string; + credentialDefinitionId?: string; + tenantId?: string; + payload?: IGetCredDefFromTenantPayload; + apiKey?: string; + agentEndPoint?: string; + agentType?: string; + method?: string; + orgId?: string; } export interface IGetCredDefFromTenantPayload { - credentialDefinitionId: string; + credentialDefinitionId: string; } export interface IIssuanceCreateOffer { - connectionId: string; - credentialFormats: ICredentialFormats; - autoAcceptCredential: string; - comment: string; + connectionId: string; + credentialFormats: ICredentialFormats; + autoAcceptCredential: string; + comment: string; } export interface ICredentialFormats { - indy: IIndy; - credentialDefinitionId: string; + indy: IIndy; + credentialDefinitionId: string; } export interface IIndy { - attributes: IAttributes[]; + attributes: IAttributes[]; } export interface IAttributes { - name: string; - value: string; + name: string; + value: string; } export interface ISendProofRequestPayload { - comment: string; - connectionId?: string; - proofFormats: IProofFormats; - autoAcceptProof: string; - goalCode?: string; - parentThreadId?: string; - willConfirm?: boolean; - protocolVersion?: string; + comment: string; + connectionId?: string; + proofFormats: IProofFormats; + autoAcceptProof: string; + goalCode?: string; + parentThreadId?: string; + willConfirm?: boolean; + protocolVersion?: string; } export interface IAgentStatus { - label: string; - endpoints: string[]; - isInitialized: boolean; + label: string; + endpoints: string[]; + isInitialized: boolean; } interface IProofFormats { - indy: IndyProof + indy: IndyProof; } interface IndyProof { - name: string; - version: string; - requested_attributes: IRequestedAttributes; - requested_predicates: IRequestedPredicates; + name: string; + version: string; + requested_attributes: IRequestedAttributes; + requested_predicates: IRequestedPredicates; } interface IRequestedAttributes { - [key: string]: IRequestedAttributesName; + [key: string]: IRequestedAttributesName; } interface IRequestedAttributesName { - name: string; - restrictions: IRequestedRestriction[] + name: string; + restrictions: IRequestedRestriction[]; } interface IRequestedPredicates { - [key: string]: IRequestedPredicatesName; + [key: string]: IRequestedPredicatesName; } interface IRequestedPredicatesName { - name: string; - restrictions: IRequestedRestriction[] + name: string; + restrictions: IRequestedRestriction[]; } interface IRequestedRestriction { - cred_def_id?: string; - schema_id?: string; - schema_issuer_did?: string; - schema_name?: string; - issuer_did?: string; + cred_def_id?: string; + schema_id?: string; + schema_issuer_did?: string; + schema_name?: string; + issuer_did?: string; } export interface IAgentSpinUpSatus { - agentSpinupStatus: number; + agentSpinupStatus: number; } interface IWalletConfig { - id: string; - key: string; - keyDerivationMethod: string; + id: string; + key: string; + keyDerivationMethod: string; } interface IConfig { - label: string; - walletConfig: IWalletConfig; + label: string; + walletConfig: IWalletConfig; } export interface ITenantRecord { - _tags: string; - metadata: string; - id: string; - createdAt: string; - config: IConfig; - updatedAt: string; + _tags: string; + metadata: string; + id: string; + createdAt: string; + config: IConfig; + updatedAt: string; } export interface ICreateTenant { - tenantRecord: ITenantRecord; - did: string; - verkey: string; + tenantRecord: ITenantRecord; + did: string; + verkey: string; } - export interface IOrgAgent { - agentSpinUpStatus: number; + agentSpinUpStatus: number; } export interface IOrgLedgers { - id: string; + id: string; } export interface ICreateOrgAgent { - id: string; + id: string; } interface IOrgAgentEndPoint { - agentSpinUpStatus: number; - agentEndPoint: string; - apiKey; + agentSpinUpStatus: number; + agentEndPoint: string; + apiKey; } export interface IOrgAgentsResponse { - org_agents: IOrgAgentEndPoint[]; + org_agents: IOrgAgentEndPoint[]; } export interface IStoreAgent { - id: string; + id: string; } export interface IAcceptCredentials { - credentialRecordId: string; + credentialRecordId: string; } export interface IAgentProofRequest { - metadata: object; - id: string; - createdAt: string; - protocolVersion: string; - state: string; - connectionId: string; - threadId: string; - autoAcceptProof: string; - updatedAt: string; + metadata: object; + id: string; + createdAt: string; + protocolVersion: string; + state: string; + connectionId: string; + threadId: string; + autoAcceptProof: string; + updatedAt: string; } export interface IPresentation { - _tags: ITags; - metadata: object; - id: string; + _tags: ITags; + metadata: object; + id: string; } export interface IStoreAgent { - id: string; + id: string; } export interface IReceiveInvite { - alias?: string; - label?: string; - imageUrl?: string; - autoAcceptConnection?: boolean; - autoAcceptInvitation?: boolean; - reuseConnection?: boolean; - acceptInvitationTimeoutMs?: number; + alias?: string; + label?: string; + imageUrl?: string; + autoAcceptConnection?: boolean; + autoAcceptInvitation?: boolean; + reuseConnection?: boolean; + acceptInvitationTimeoutMs?: number; } export interface IReceiveInvitationUrl extends IReceiveInvite { - invitationUrl: string; + invitationUrl: string; } interface IService { - id: string; - serviceEndpoint: string; - type: string; - recipientKeys: string[]; - routingKeys: string[]; - accept: string[]; + id: string; + serviceEndpoint: string; + type: string; + recipientKeys: string[]; + routingKeys: string[]; + accept: string[]; } interface IInvitation { - '@id': string; - '@type': string; - label: string; - goalCode: string; - goal: string; - accept: string[]; - handshake_protocols: string[]; - services: (IService | string)[]; - imageUrl?: string; + '@id': string; + '@type': string; + label: string; + goalCode: string; + goal: string; + accept: string[]; + handshake_protocols: string[]; + services: (IService | string)[]; + imageUrl?: string; } export interface IReceiveInvitation extends IReceiveInvite { - invitation: IInvitation; + invitation: IInvitation; } export interface IProofPresentation { - createdAt: string; - protocolVersion: string; - state: string; - connectionId: string; - threadId: string; - autoAcceptProof: string; - updatedAt: string; - isVerified: boolean; + createdAt: string; + protocolVersion: string; + state: string; + connectionId: string; + threadId: string; + autoAcceptProof: string; + updatedAt: string; + isVerified: boolean; } interface ITags { - connectionId: string; - state: string; - threadId: string; + connectionId: string; + state: string; + threadId: string; } export interface IValidResponses { - text: string; - } - export interface IQuestionPayload { - detail: string; - validResponses: IValidResponses[]; - question: string; - orgId?: string; - connectionId: string; - tenantId: string; - } + text: string; +} +export interface IQuestionPayload { + detail: string; + validResponses: IValidResponses[]; + question: string; + orgId?: string; + connectionId: string; + tenantId: string; +} interface Ledger { - id: string; - createDateTime: string; - lastChangedDateTime: string; - name: string; - networkType: string; - poolConfig: string; - isActive: boolean; - networkString: string; - registerDIDEndpoint: string; - registerDIDPayload: string; - indyNamespace: string; - networkUrl: string | null; - } - - export interface LedgerListResponse { - response: Ledger[]; - } \ No newline at end of file + id: string; + createDateTime: string; + lastChangedDateTime: string; + name: string; + networkType: string; + poolConfig: string; + isActive: boolean; + networkString: string; + registerDIDEndpoint: string; + registerDIDPayload: string; + indyNamespace: string; + networkUrl: string | null; +} + +export interface LedgerListResponse { + response: Ledger[]; +} + +export interface ICreateConnectionInvitation { + label?: string; + alias?: string; + imageUrl?: string; + goalCode?: string; + goal?: string; + handshake?: boolean; + handshakeProtocols?: object[]; + messages?: object[]; + multiUseInvitation?: boolean; + autoAcceptConnection?: boolean; + routing?: object; + appendedAttachments?: object[]; + orgId?: string; +} diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 9fc45bfb7..d3dad9185 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -7,7 +7,7 @@ import { User } from '../authz/decorators/user.decorator'; import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; import { ConnectionService } from './connection.service'; -import { ConnectionDto, CreateConnectionDto, ReceiveInvitationDto, ReceiveInvitationUrlDto } from './dtos/connection.dto'; +import { ConnectionDto, CreateConnectionDto, CreateOutOfBandConnectionInvitation, ReceiveInvitationDto, ReceiveInvitationUrlDto } from './dtos/connection.dto'; import { IUserRequestInterface } from './interfaces'; import { Response } from 'express'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; @@ -192,6 +192,35 @@ export class ConnectionController { } + /** + * Create out-of-band connection invitation + * @param connectionDto + * @param res + * @returns Created out-of-band connection invitation url + */ + @Post('/orgs/:orgId/connection-invitation') + @ApiOperation({ summary: 'Create outbound out-of-band connection invitation', description: 'Create outbound out-of-band connection invitation' }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async createConnectionInvitation( + @Param('orgId') orgId: string, + @Body() createOutOfBandConnectionInvitation: CreateOutOfBandConnectionInvitation, + @User() reqUser: IUserRequestInterface, + @Res() res: Response + ): Promise { + + createOutOfBandConnectionInvitation.orgId = orgId; + const connectionData = await this.connectionService.createConnectionInvitation(createOutOfBandConnectionInvitation, reqUser); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.connection.success.create, + data: connectionData + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + + } + @Post('/orgs/:orgId/question-answer/question/:connectionId') @ApiOperation({ summary: '', description: 'send question' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index 8938e62b4..3d5e6ac70 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -2,7 +2,7 @@ import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { Inject, Injectable} from '@nestjs/common'; import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; -import { ConnectionDto, CreateConnectionDto, ReceiveInvitationDto, ReceiveInvitationUrlDto } from './dtos/connection.dto'; +import { ConnectionDto, CreateConnectionDto, CreateOutOfBandConnectionInvitation, ReceiveInvitationDto, ReceiveInvitationUrlDto } from './dtos/connection.dto'; import { IReceiveInvitationRes, IUserRequestInterface } from './interfaces'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { AgentConnectionSearchCriteria, IConnectionDetailsById, IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; @@ -149,4 +149,11 @@ export class ConnectionService extends BaseService { } } + createConnectionInvitation( + createOutOfBandConnectionInvitation: CreateOutOfBandConnectionInvitation, + user: IUserRequestInterface + ): Promise { + const payload = { user, createOutOfBandConnectionInvitation }; + return this.sendNatsMessage(this.connectionServiceProxy, 'create-connection-invitation', payload); + } } diff --git a/apps/api-gateway/src/connection/dtos/connection.dto.ts b/apps/api-gateway/src/connection/dtos/connection.dto.ts index 63afc50ee..afe499c6a 100644 --- a/apps/api-gateway/src/connection/dtos/connection.dto.ts +++ b/apps/api-gateway/src/connection/dtos/connection.dto.ts @@ -2,6 +2,59 @@ import { ArrayNotEmpty, IsArray, IsBoolean, IsNotEmpty, IsNumber, IsOptional, Is import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; +import { HandshakeProtocol } from '../enums/connections.enum'; + +export class CreateOutOfBandConnectionInvitation { + @ApiPropertyOptional() + @IsOptional() + label?: string; + + @ApiPropertyOptional() + @IsOptional() + alias?: string; + + @ApiPropertyOptional() + @IsOptional() + imageUrl?: string; + + @ApiPropertyOptional() + @IsOptional() + goalCode?: string; + + @ApiPropertyOptional() + @IsOptional() + goal?: string; + + @ApiPropertyOptional() + @IsOptional() + handshake?: boolean; + + @ApiPropertyOptional() + @IsOptional() + handshakeProtocols?: HandshakeProtocol[]; + + @ApiPropertyOptional() + @IsOptional() + messages?: object[]; + + @ApiPropertyOptional() + @IsOptional() + multiUseInvitation?: boolean; + + @ApiPropertyOptional() + @IsOptional() + autoAcceptConnection?: boolean; + + @ApiPropertyOptional() + @IsOptional() + routing?: object; + + @ApiPropertyOptional() + @IsOptional() + appendedAttachments?: object[]; + + orgId; +} export class CreateConnectionDto { @ApiPropertyOptional() diff --git a/apps/api-gateway/src/connection/enums/connections.enum.ts b/apps/api-gateway/src/connection/enums/connections.enum.ts index 3ea871b2a..1c4a24600 100644 --- a/apps/api-gateway/src/connection/enums/connections.enum.ts +++ b/apps/api-gateway/src/connection/enums/connections.enum.ts @@ -9,4 +9,9 @@ export enum Connections { responseReceived = 'response-received', complete = 'complete', abandoned = 'abandoned' +} + +export declare enum HandshakeProtocol { + Connections = "https://didcomm.org/connections/1.0", + DidExchange = "https://didcomm.org/didexchange/1.0" } \ No newline at end of file diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 9e1f49174..48479e119 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -232,6 +232,12 @@ export class ProofRequestPresentationDefinition { export class SendProofRequestPayload { + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid goal code' }) + @IsString({ message: 'goal code should be string' }) + goalCode?: string; + @ApiPropertyOptional() @IsString({ message: 'protocolVersion must be in string' }) @IsNotEmpty({ message: 'please provide valid protocol version' }) diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index ca67a4149..9b8c5e645 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -5,6 +5,7 @@ import { GetAllConnections, IConnection, ICreateConnection, + ICreateOutOfbandConnectionInvitation, IFetchConnectionById, IFetchConnections, IReceiveInvitationByOrg, @@ -94,4 +95,9 @@ export class ConnectionController { async getQuestionAnswersRecord(orgId: string): Promise { return this.connectionService.getQuestionAnswersRecord(orgId); } + + @MessagePattern({ cmd: 'create-connection-invitation' }) + async createConnectionInvitation(payload: ICreateOutOfbandConnectionInvitation): Promise { + return this.connectionService.createConnectionInvitation(payload); + } } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index a2acdda52..737be584a 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -12,7 +12,9 @@ import { ICreateConnection, IReceiveInvitation, IReceiveInvitationResponse, - IReceiveInvitationUrl + IReceiveInvitationUrl, + ICreateOutOfbandConnectionInvitation, + ICreateConnectionInvitation } from './interfaces/connection.interfaces'; import { ConnectionRepository } from './connection.repository'; import { ResponseMessages } from '@credebl/common/response-messages'; @@ -143,7 +145,6 @@ export class ConnectionService { const payload = { connectionPayload, url, orgId }; try { - return await this.natsCall(pattern, payload); } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); @@ -397,15 +398,30 @@ export class ConnectionService { * @param referenceId * @returns agent URL */ - async getAgentUrl(orgAgentType: string, agentEndPoint: string, tenantId?: string): Promise { + async getAgentUrl( + orgAgentType: string, + agentEndPoint: string, + tenantId?: string, + connectionInvitationFlag?: string + ): Promise { try { let url; - if (orgAgentType === OrgAgentType.DEDICATED) { - url = `${agentEndPoint}${CommonConstants.URL_CONN_LEGACY_INVITE}`; - } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_INVITATION}`.replace('#', tenantId); + if ('connection-invitation' === connectionInvitationFlag) { + if (orgAgentType === OrgAgentType.DEDICATED) { + url = `${agentEndPoint}${CommonConstants.URL_CONN_INVITE}`; + } else if (orgAgentType === OrgAgentType.SHARED) { + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_CONNECTION_INVITATION}`.replace('#', tenantId); + } else { + throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); + } } else { - throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); + if (orgAgentType === OrgAgentType.DEDICATED) { + url = `${agentEndPoint}${CommonConstants.URL_CONN_LEGACY_INVITE}`; + } else if (orgAgentType === OrgAgentType.SHARED) { + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_INVITATION}`.replace('#', tenantId); + } else { + throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); + } } return url; } catch (error) { @@ -639,10 +655,7 @@ export class ConnectionService { } } - async storeConnectionObjectAndReturnUrl( - connectionInvitationUrl: string, - persistent: boolean - ): Promise { + async storeConnectionObjectAndReturnUrl(connectionInvitationUrl: string, persistent: boolean): Promise { const storeObj = connectionInvitationUrl; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; @@ -663,6 +676,122 @@ export class ConnectionService { } } + /** + * Create connection invitation URL + * @param orgId + * @param user + * @returns Connection invitation URL + */ + async createConnectionInvitation(payload: ICreateOutOfbandConnectionInvitation): Promise { + try { + const { + alias, + appendedAttachments, + autoAcceptConnection, + goal, + goalCode, + handshake, + handshakeProtocols, + imageUrl, + messages, + multiUseInvitation, + orgId, + routing + } = payload?.createOutOfBandConnectionInvitation; + + const agentDetails = await this.connectionRepository.getAgentEndPoint(payload?.createOutOfBandConnectionInvitation?.orgId); + + const { agentEndPoint, id, organisation } = agentDetails; + const agentId = id; + if (!agentDetails) { + throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); + } + + const connectionPayload = { + multiUseInvitation: multiUseInvitation ?? true, + autoAcceptConnection: autoAcceptConnection ?? true, + alias: alias || undefined, + imageUrl: organisation.logoUrl || imageUrl || undefined, + label: organisation.name, + goal: goal || undefined, + goalCode: goalCode || undefined, + handshake: handshake || undefined, + handshakeProtocols: handshakeProtocols || undefined, + appendedAttachments: appendedAttachments || undefined, + routing: routing || undefined, + messages: messages || undefined + }; + + const createConnectionInvitationFlag = 'connection-invitation'; + const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + const url = await this.getAgentUrl( + orgAgentType, + agentEndPoint, + agentDetails?.tenantId, + createConnectionInvitationFlag + ); + const createConnectionInvitation = await this._createOutOfBandConnectionInvitation(connectionPayload, url, orgId); + const connectionInvitationUrl = createConnectionInvitation?.response?.invitationUrl; + const shortenedUrl = await this.storeConnectionObjectAndReturnUrl( + connectionInvitationUrl, + connectionPayload.multiUseInvitation + ); + const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( + shortenedUrl, + agentId, + orgId, + null + ); + const connectionDetailRecords: ConnectionResponseDetail = { + id: saveConnectionDetails.id, + orgId: saveConnectionDetails.orgId, + agentId: saveConnectionDetails.agentId, + connectionInvitation: saveConnectionDetails.connectionInvitation, + multiUse: saveConnectionDetails.multiUse, + createDateTime: saveConnectionDetails.createDateTime, + createdBy: saveConnectionDetails.createdBy, + lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, + lastChangedBy: saveConnectionDetails.lastChangedBy, + recordId: createConnectionInvitation.response.outOfBandRecord.id, + recipientKey: saveConnectionDetails.recipientKey + }; + return connectionDetailRecords; + } catch (error) { + this.logger.error(`[createConnectionInvitation] - error in connection oob invitation: ${error}`); + this.handleError(error); + } + } + + /** + * Store shortening URL + * @param orgId + * @returns connection invitation URL + */ + async _createOutOfBandConnectionInvitation( + connectionPayload: ICreateConnectionInvitation, + url: string, + orgId: string + ): Promise<{ + response; + }> { + //nats call in agent-service to create an invitation url + const pattern = { cmd: 'agent-create-connection-invitation' }; + const payload = { connectionPayload, url, orgId }; + + try { + return await this.natsCall(pattern, payload); + } catch (error) { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + } + } + async natsCall( pattern: object, payload: object diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index b49d6ea9e..af3f24a76 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -267,3 +267,24 @@ export interface ConnectionResponseDetail { recordId: string; recipientKey:string; } + +export interface ICreateConnectionInvitation { + label?: string; + alias?: string; + imageUrl?: string; + goalCode?: string; + goal?: string; + handshake?: boolean; + handshakeProtocols?: object[]; + messages?: object[]; + multiUseInvitation?: boolean; + autoAcceptConnection?: boolean; + routing?: object; + appendedAttachments?: object[]; + orgId?: string; +} + +export interface ICreateOutOfbandConnectionInvitation { + user: IUserRequestInterface, + createOutOfBandConnectionInvitation: ICreateConnectionInvitation, +} \ No newline at end of file diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index a15b61224..ebb8f4171 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -23,6 +23,7 @@ export enum CommonConstants { URL_CONN_REMOVE_CONNECTION_BY_ID = '/connections/#/remove', URL_CONN_METADATA = '/connections/#/metadata', URL_CONN_LEGACY_INVITE = '/oob/create-legacy-invitation', + URL_CONN_INVITE = '/oob/create-invitation', URL_RECEIVE_INVITATION_URL = '/oob/receive-invitation-url', URL_RECEIVE_INVITATION = '/oob/receive-invitation', URL_CONN_INVITATION = '/url', @@ -97,6 +98,7 @@ export enum CommonConstants { URL_SHAGENT_CREATE_CRED_DEF = '/multi-tenancy/credential-definition/#', URL_SHAGENT_GET_CRED_DEF = '/multi-tenancy/credential-definition/@/#', URL_SHAGENT_CREATE_INVITATION = '/multi-tenancy/create-legacy-invitation/#', + URL_SHAGENT_CREATE_CONNECTION_INVITATION = '/multi-tenancy/create-invitation/#', URL_SHAGENT_GET_CREATEED_INVITATIONS = '/multi-tenancy/connections/#', URL_SHAGENT_GET_CREATEED_INVITATION_BY_CONNECTIONID = '/multi-tenancy/connections/#/@', URL_SHAGENT_CREATE_OFFER = '/multi-tenancy/credentials/create-offer/#', From b6e651d82500e449b6dd1692252ff3be9faf03e1 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 4 Apr 2024 16:52:31 +0530 Subject: [PATCH 220/231] Solved sonarlint issues Signed-off-by: KulkarniShashank --- apps/connection/src/connection.service.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 4d48c09d6..1fbd983fe 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -655,10 +655,7 @@ export class ConnectionService { } } - async storeConnectionObjectAndReturnUrl( - connectionInvitationUrl: string, - persistent: boolean - ): Promise { + async storeConnectionObjectAndReturnUrl(connectionInvitationUrl: string, persistent: boolean): Promise { const storeObj = connectionInvitationUrl; //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; @@ -687,6 +684,7 @@ export class ConnectionService { */ async createConnectionInvitation(payload: ICreateOutOfbandConnectionInvitation): Promise { try { + const { createOutOfBandConnectionInvitation } = payload; const { alias, appendedAttachments, @@ -700,16 +698,18 @@ export class ConnectionService { multiUseInvitation, orgId, routing - } = payload?.createOutOfBandConnectionInvitation; + } = createOutOfBandConnectionInvitation; - const agentDetails = await this.connectionRepository.getAgentEndPoint(payload?.createOutOfBandConnectionInvitation?.orgId); + const agentDetails = await this.connectionRepository.getAgentEndPoint( + payload?.createOutOfBandConnectionInvitation?.orgId + ); const { agentEndPoint, id, organisation } = agentDetails; const agentId = id; if (!agentDetails) { throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); } - + const connectionPayload = { multiUseInvitation: multiUseInvitation ?? true, autoAcceptConnection: autoAcceptConnection ?? true, @@ -724,7 +724,7 @@ export class ConnectionService { routing: routing || undefined, messages: messages || undefined }; - + const createConnectionInvitationFlag = 'connection-invitation'; const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); const url = await this.getAgentUrl( @@ -745,7 +745,7 @@ export class ConnectionService { orgId, null ); - const connectionDetailRecords: ConnectionResponseDetail = { + const connectionStorePayload: ConnectionResponseDetail = { id: saveConnectionDetails.id, orgId: saveConnectionDetails.orgId, agentId: saveConnectionDetails.agentId, @@ -758,7 +758,7 @@ export class ConnectionService { recordId: createConnectionInvitation.response.outOfBandRecord.id, recipientKey: saveConnectionDetails.recipientKey }; - return connectionDetailRecords; + return connectionStorePayload; } catch (error) { this.logger.error(`[createConnectionInvitation] - error in connection oob invitation: ${error}`); this.handleError(error); @@ -838,4 +838,4 @@ export class ConnectionService { throw new RpcException(error.response ? error.response : error); } } -} \ No newline at end of file +} From d0d6c1ddcf6dc6f36d41c7c1e3741f155b1deaeb Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Thu, 4 Apr 2024 17:58:31 +0530 Subject: [PATCH 221/231] feat: W3C schema builder Signed-off-by: tipusinghaw --- .../src/agent-service.service.ts | 2 +- .../api-gateway/src/dtos/create-schema.dto.ts | 26 ++- .../src/interfaces/ISchemaSearch.interface.ts | 7 +- .../interfaces/schema-payload.interface.ts | 20 +- apps/ledger/src/schema/schema.controller.ts | 3 +- apps/ledger/src/schema/schema.service.ts | 215 ++++++++++++++++-- 6 files changed, 247 insertions(+), 26 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 5a595fbf1..24773534b 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -1570,7 +1570,7 @@ export class AgentServiceService { .then(async response => response); return schemaRequest; } catch (error) { - this.logger.error(`Error in schema endorsement request in agent service : ${JSON.stringify(error)}`); + this.logger.error(`Error in createW3CSchema request in agent service : ${JSON.stringify(error)}`); } } diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index 65b109a5c..09180c22c 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -73,19 +73,35 @@ export class CreateSchemaDto { export class CreateW3CSchemaDto { @ApiProperty() - @IsNotEmpty({ message: 'schemaObject is required' }) - schema: object; + @IsNotEmpty({ message: 'schemaAttribute is required' }) + schemaAttributes: SchemaAttributes []; @ApiProperty() @IsString({ message: 'schemaName must be a string' }) - @Transform(({ value }) => trim(value)) + @Transform(({ value }) => value.trim()) @IsNotEmpty({ message: 'schemaName is required' }) schemaName: string; @ApiProperty() @IsString({ message: 'did must be a string' }) - @Transform(({ value }) => trim(value)) + @Transform(({ value }) => value.trim()) @IsNotEmpty({ message: 'did is required' }) did: string; - + + @ApiProperty() + @IsString({ message: 'description must be a string' }) + @IsOptional() + description?: string; } + +export class SchemaAttributes { + @ApiProperty() + @IsNotEmpty({ message: 'type is required' }) + @IsString({ message: 'type must be a string' }) + type: string; + + @ApiProperty() + @IsNotEmpty({ message: 'title is required' }) + @IsString({ message: 'title must be a string' }) + title: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts b/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts index 6266a4e37..51c10b237 100644 --- a/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts +++ b/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts @@ -12,7 +12,12 @@ export interface ISchemaSearchPayload { export interface W3CSchemaPayload { - schema: object; + schemaAttributes: W3CSchemaAttributes []; schemaName: string; did: string; } + + interface W3CSchemaAttributes { + type: string, + title: string + } diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index bd091074e..f56ffbfda 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -72,11 +72,17 @@ export interface ISchemaExist { version: string; } -interface SchemaPayload { - schema: object, +export interface SchemaPayload { + schemaAttributes: W3CSchemaAttributes [], schemaName: string, - did: string + did: string, + description: string } + + export interface W3CSchemaAttributes { + type: string, + title: string, + } export interface W3CSchemaPayload { schemaPayload: SchemaPayload, @@ -86,5 +92,11 @@ export interface W3CSchemaPayload { export interface W3CCreateSchema { url: string, orgId: string, - schemaRequestPayload: SchemaPayload + schemaRequestPayload: object } + +export interface IdAttribute extends W3CSchemaAttributes { + format: string; + order?: string +} + diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index dafc53a89..e428607a0 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -27,7 +27,8 @@ export class SchemaController { @MessagePattern({ cmd: 'create-w3c-schema' }) async createW3CSchema(payload: W3CSchemaPayload): Promise { - return this.schemaService.createW3CSchema(payload); + const {orgId, schemaPayload} = payload; + return this.schemaService.createW3CSchema(orgId, schemaPayload); } @MessagePattern({ cmd: 'get-schema-by-id' }) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index c3af49025..69e5471ad 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -11,7 +11,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { SchemaRepository } from './repositories/schema.repository'; import { schema } from '@prisma/client'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria, W3CCreateSchema, W3CSchemaPayload } from './interfaces/schema-payload.interface'; +import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria, SchemaPayload, W3CCreateSchema } from './interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; @@ -242,17 +242,15 @@ export class SchemaService extends BaseService { } } - async createW3CSchema( - schemaRequestPayload: W3CSchemaPayload - ): Promise { + async createW3CSchema(orgId:string, schemaPayload: SchemaPayload): Promise { try { - const { orgId } = schemaRequestPayload; + const { description, did, schemaAttributes, schemaName} = schemaPayload; const agentDetails = await this.schemaRepository.getAgentDetailsByOrgId(orgId); if (!agentDetails) { - throw new NotFoundException( - ResponseMessages.schema.error.agentDetailsNotFound, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); + throw new NotFoundException(ResponseMessages.schema.error.agentDetailsNotFound, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } const { agentEndPoint } = agentDetails; const getAgentDetails = await this.schemaRepository.getAgentType(orgId); @@ -264,21 +262,210 @@ export class SchemaService extends BaseService { const { tenantId } = await this.schemaRepository.getAgentDetailsByOrgId(orgId); url = `${agentEndPoint}${CommonConstants.SHARED_CREATE_POLYGON_W3C_SCHEMA}${tenantId}`; } + + const schemaObject = await this.w3cSchemaBuilder(schemaAttributes, schemaName, description); + const agentSchemaPayload = { + schema:schemaObject, + did, + schemaName + }; + const W3cSchemaPayload = { url, orgId, - schemaRequestPayload: schemaRequestPayload.schemaPayload + schemaRequestPayload: agentSchemaPayload }; return this._createW3CSchema(W3cSchemaPayload); } catch (error) { - this.logger.error( - `[createSchema] - outer Error: ${JSON.stringify(error)}` - ); + this.logger.error(`[createSchema] - outer Error: ${JSON.stringify(error)}`); throw new RpcException(error.error ? error.error.message : error.message); } } - + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + private async w3cSchemaBuilder(schemaAttributes, schemaName: string, description: string) { + const schemaAttributeJson = schemaAttributes.map((attribute, index) => ({ + [attribute.title]: { + type: attribute.type.toLowerCase(), + order: index, + title: attribute.title + } + })); + + // Add the format property to the id key + schemaAttributeJson.unshift({ + id: { + type: 'string', + format: 'uri' + } + }); + + const nestedObject = {}; + schemaAttributeJson.forEach((obj) => { + // eslint-disable-next-line prefer-destructuring + const key = Object.keys(obj)[0]; + nestedObject[key] = obj[key]; + }); + + const schemaNameObject = {}; + schemaNameObject[schemaName] = { + "const": schemaName + }; + const date = new Date().toISOString(); + + const W3CSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: `${date}-${schemaName}`, + type: 'object', + required: ['@context', 'issuer', 'issuanceDate', 'type', 'credentialSubject'], + properties: { + '@context': { + $ref: '#/definitions/context' + }, + type: { + type: 'array', + items: { + anyOf: [ + { + $ref: '#/definitions/VerifiableCredential' + }, + { + const: '#/definitions/$AAdharCard' + } + ] + } + }, + credentialSubject: { + $ref: '#/definitions/credentialSubject' + }, + id: { + type: 'string', + format: 'uri' + }, + issuer: { + $ref: '#/definitions/uriOrId' + }, + issuanceDate: { + type: 'string', + format: 'date-time' + }, + expirationDate: { + type: 'string', + format: 'date-time' + }, + credentialStatus: { + $ref: '#/definitions/credentialStatus' + }, + credentialSchema: { + $ref: '#/definitions/credentialSchema' + } + }, + definitions: { + context: { + type: 'array', + items: [ + { + const: 'https://www.w3.org/2018/credentials/v1' + } + ], + additionalItems: { + oneOf: [ + { + type: 'string', + format: 'uri' + }, + { + type: 'object' + }, + { + type: 'array', + items: { + $ref: '#/definitions/context' + } + } + ] + }, + minItems: 1, + uniqueItems: true + }, + credentialSubject: { + type: 'object', + required: ['id'], + additionalProperties: false, + properties: nestedObject + }, + VerifiableCredential: { + const: 'VerifiableCredential' + }, + credentialSchema: { + oneOf: [ + { + $ref: '#/definitions/idAndType' + }, + { + type: 'array', + items: { + $ref: '#/definitions/idAndType' + }, + minItems: 1, + uniqueItems: true + } + ] + }, + credentialStatus: { + oneOf: [ + { + $ref: '#/definitions/idAndType' + }, + { + type: 'array', + items: { + $ref: '#/definitions/idAndType' + }, + minItems: 1, + uniqueItems: true + } + ] + }, + idAndType: { + type: 'object', + required: ['id', 'type'], + properties: { + id: { + type: 'string', + format: 'uri' + }, + type: { + type: 'string' + } + } + }, + uriOrId: { + oneOf: [ + { + type: 'string', + format: 'uri' + }, + { + type: 'object', + required: ['id'], + properties: { + id: { + type: 'string', + format: 'uri' + } + } + } + ] + }, + ...schemaNameObject + }, + title: schemaName, + description: `${description}` + }; + return W3CSchema; + } + async _createSchema(payload: CreateSchemaAgentRedirection): Promise<{ response: string; }> { From de7c2209cee24771462376eceb92eb532d426209 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Thu, 4 Apr 2024 19:29:16 +0530 Subject: [PATCH 222/231] fix: import common constants in schema service Signed-off-by: KulkarniShashank --- apps/ledger/src/schema/schema.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 85052cd8c..c3af49025 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -20,6 +20,7 @@ import { OrgAgentType } from '@credebl/enum/enum'; import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { CommonConstants } from '@credebl/common/common.constant'; @Injectable() export class SchemaService extends BaseService { From 7474a2cc0e330378f188b86596d1a8312ccb7a9a Mon Sep 17 00:00:00 2001 From: Shashank Kulkarni <44693969+KulkarniShashank@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:31:51 +0530 Subject: [PATCH 223/231] fix: import common constants in schema service (#628) Signed-off-by: KulkarniShashank --- apps/ledger/src/schema/schema.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 85052cd8c..c3af49025 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -20,6 +20,7 @@ import { OrgAgentType } from '@credebl/enum/enum'; import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { CommonConstants } from '@credebl/common/common.constant'; @Injectable() export class SchemaService extends BaseService { From 8668a05c97fb3de9e87feee20ebc043eaca904f0 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Fri, 5 Apr 2024 12:36:42 +0530 Subject: [PATCH 224/231] fix: required message Signed-off-by: tipusinghaw --- apps/api-gateway/src/dtos/create-schema.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index 09180c22c..08175c362 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -73,7 +73,7 @@ export class CreateSchemaDto { export class CreateW3CSchemaDto { @ApiProperty() - @IsNotEmpty({ message: 'schemaAttribute is required' }) + @IsNotEmpty({ message: 'Schema attributes are required' }) schemaAttributes: SchemaAttributes []; @ApiProperty() From f075cf44242f0b676a2f1b852844296ba4efffa9 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Fri, 5 Apr 2024 12:41:25 +0530 Subject: [PATCH 225/231] refactor: Added error handling for schema builder Signed-off-by: tipusinghaw --- apps/ledger/src/schema/schema.service.ts | 7 +++++++ libs/common/src/response-messages/index.ts | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 69e5471ad..51d068c80 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -264,6 +264,13 @@ export class SchemaService extends BaseService { } const schemaObject = await this.w3cSchemaBuilder(schemaAttributes, schemaName, description); + + if (!schemaObject) { + throw new BadRequestException(ResponseMessages.schema.error.schemaBuilder, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); + } const agentSchemaPayload = { schema:schemaObject, did, diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 5363cdd80..89d149584 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -161,7 +161,8 @@ export const ResponseMessages = { notStoredCredential: 'User credential not stored', agentDetailsNotFound: 'Agent details not found', failedFetchSchema: 'Failed to fetch schema data', - atLeastOneRequired: 'At least one of the attributes should have isReuired as `true`' + atLeastOneRequired: 'At least one of the attributes should have isReuired as `true`', + schemaBuilder: 'Error while creating schema JSON`' } }, credentialDefinition: { From 873e02a76b6098446dbe365808f92a72e49025df Mon Sep 17 00:00:00 2001 From: tipusinghaw <126460794+tipusinghaw@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:46:58 +0530 Subject: [PATCH 226/231] feat: implemented W3C schema builder (#627) * feat: added w3c schema Signed-off-by: tipusinghaw * feat: added interface Signed-off-by: tipusinghaw * fix: schema dto Signed-off-by: tipusinghaw * fix: sonar cloud issue Signed-off-by: tipusinghaw * fix: changed the interface of create schema Signed-off-by: tipusinghaw * fix: multiple error messages Signed-off-by: tipusinghaw * refactor: changed API key fetching logic Signed-off-by: tipusinghaw * fix: logger issue Signed-off-by: tipusinghaw * feat: W3C schema builder Signed-off-by: tipusinghaw * fix: required message Signed-off-by: tipusinghaw * refactor: Added error handling for schema builder Signed-off-by: tipusinghaw --------- Signed-off-by: tipusinghaw --- .../src/agent-service.service.ts | 2 +- .../api-gateway/src/dtos/create-schema.dto.ts | 26 +- .../src/interfaces/ISchemaSearch.interface.ts | 7 +- .../interfaces/schema-payload.interface.ts | 20 +- apps/ledger/src/schema/schema.controller.ts | 3 +- apps/ledger/src/schema/schema.service.ts | 222 ++++++++++++++++-- libs/common/src/response-messages/index.ts | 3 +- 7 files changed, 256 insertions(+), 27 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 5a595fbf1..24773534b 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -1570,7 +1570,7 @@ export class AgentServiceService { .then(async response => response); return schemaRequest; } catch (error) { - this.logger.error(`Error in schema endorsement request in agent service : ${JSON.stringify(error)}`); + this.logger.error(`Error in createW3CSchema request in agent service : ${JSON.stringify(error)}`); } } diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index 65b109a5c..08175c362 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -73,19 +73,35 @@ export class CreateSchemaDto { export class CreateW3CSchemaDto { @ApiProperty() - @IsNotEmpty({ message: 'schemaObject is required' }) - schema: object; + @IsNotEmpty({ message: 'Schema attributes are required' }) + schemaAttributes: SchemaAttributes []; @ApiProperty() @IsString({ message: 'schemaName must be a string' }) - @Transform(({ value }) => trim(value)) + @Transform(({ value }) => value.trim()) @IsNotEmpty({ message: 'schemaName is required' }) schemaName: string; @ApiProperty() @IsString({ message: 'did must be a string' }) - @Transform(({ value }) => trim(value)) + @Transform(({ value }) => value.trim()) @IsNotEmpty({ message: 'did is required' }) did: string; - + + @ApiProperty() + @IsString({ message: 'description must be a string' }) + @IsOptional() + description?: string; } + +export class SchemaAttributes { + @ApiProperty() + @IsNotEmpty({ message: 'type is required' }) + @IsString({ message: 'type must be a string' }) + type: string; + + @ApiProperty() + @IsNotEmpty({ message: 'title is required' }) + @IsString({ message: 'title must be a string' }) + title: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts b/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts index 6266a4e37..51c10b237 100644 --- a/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts +++ b/apps/api-gateway/src/interfaces/ISchemaSearch.interface.ts @@ -12,7 +12,12 @@ export interface ISchemaSearchPayload { export interface W3CSchemaPayload { - schema: object; + schemaAttributes: W3CSchemaAttributes []; schemaName: string; did: string; } + + interface W3CSchemaAttributes { + type: string, + title: string + } diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index bd091074e..f56ffbfda 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -72,11 +72,17 @@ export interface ISchemaExist { version: string; } -interface SchemaPayload { - schema: object, +export interface SchemaPayload { + schemaAttributes: W3CSchemaAttributes [], schemaName: string, - did: string + did: string, + description: string } + + export interface W3CSchemaAttributes { + type: string, + title: string, + } export interface W3CSchemaPayload { schemaPayload: SchemaPayload, @@ -86,5 +92,11 @@ export interface W3CSchemaPayload { export interface W3CCreateSchema { url: string, orgId: string, - schemaRequestPayload: SchemaPayload + schemaRequestPayload: object } + +export interface IdAttribute extends W3CSchemaAttributes { + format: string; + order?: string +} + diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index dafc53a89..e428607a0 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -27,7 +27,8 @@ export class SchemaController { @MessagePattern({ cmd: 'create-w3c-schema' }) async createW3CSchema(payload: W3CSchemaPayload): Promise { - return this.schemaService.createW3CSchema(payload); + const {orgId, schemaPayload} = payload; + return this.schemaService.createW3CSchema(orgId, schemaPayload); } @MessagePattern({ cmd: 'get-schema-by-id' }) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index c3af49025..51d068c80 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -11,7 +11,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { SchemaRepository } from './repositories/schema.repository'; import { schema } from '@prisma/client'; -import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria, W3CCreateSchema, W3CSchemaPayload } from './interfaces/schema-payload.interface'; +import { ISchema, ISchemaCredDeffSearchInterface, ISchemaExist, ISchemaPayload, ISchemaSearchCriteria, SchemaPayload, W3CCreateSchema } from './interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; @@ -242,17 +242,15 @@ export class SchemaService extends BaseService { } } - async createW3CSchema( - schemaRequestPayload: W3CSchemaPayload - ): Promise { + async createW3CSchema(orgId:string, schemaPayload: SchemaPayload): Promise { try { - const { orgId } = schemaRequestPayload; + const { description, did, schemaAttributes, schemaName} = schemaPayload; const agentDetails = await this.schemaRepository.getAgentDetailsByOrgId(orgId); if (!agentDetails) { - throw new NotFoundException( - ResponseMessages.schema.error.agentDetailsNotFound, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); + throw new NotFoundException(ResponseMessages.schema.error.agentDetailsNotFound, { + cause: new Error(), + description: ResponseMessages.errorMessages.notFound + }); } const { agentEndPoint } = agentDetails; const getAgentDetails = await this.schemaRepository.getAgentType(orgId); @@ -264,21 +262,217 @@ export class SchemaService extends BaseService { const { tenantId } = await this.schemaRepository.getAgentDetailsByOrgId(orgId); url = `${agentEndPoint}${CommonConstants.SHARED_CREATE_POLYGON_W3C_SCHEMA}${tenantId}`; } + + const schemaObject = await this.w3cSchemaBuilder(schemaAttributes, schemaName, description); + + if (!schemaObject) { + throw new BadRequestException(ResponseMessages.schema.error.schemaBuilder, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); + } + const agentSchemaPayload = { + schema:schemaObject, + did, + schemaName + }; + const W3cSchemaPayload = { url, orgId, - schemaRequestPayload: schemaRequestPayload.schemaPayload + schemaRequestPayload: agentSchemaPayload }; return this._createW3CSchema(W3cSchemaPayload); } catch (error) { - this.logger.error( - `[createSchema] - outer Error: ${JSON.stringify(error)}` - ); + this.logger.error(`[createSchema] - outer Error: ${JSON.stringify(error)}`); throw new RpcException(error.error ? error.error.message : error.message); } } - + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + private async w3cSchemaBuilder(schemaAttributes, schemaName: string, description: string) { + const schemaAttributeJson = schemaAttributes.map((attribute, index) => ({ + [attribute.title]: { + type: attribute.type.toLowerCase(), + order: index, + title: attribute.title + } + })); + + // Add the format property to the id key + schemaAttributeJson.unshift({ + id: { + type: 'string', + format: 'uri' + } + }); + + const nestedObject = {}; + schemaAttributeJson.forEach((obj) => { + // eslint-disable-next-line prefer-destructuring + const key = Object.keys(obj)[0]; + nestedObject[key] = obj[key]; + }); + + const schemaNameObject = {}; + schemaNameObject[schemaName] = { + "const": schemaName + }; + const date = new Date().toISOString(); + + const W3CSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: `${date}-${schemaName}`, + type: 'object', + required: ['@context', 'issuer', 'issuanceDate', 'type', 'credentialSubject'], + properties: { + '@context': { + $ref: '#/definitions/context' + }, + type: { + type: 'array', + items: { + anyOf: [ + { + $ref: '#/definitions/VerifiableCredential' + }, + { + const: '#/definitions/$AAdharCard' + } + ] + } + }, + credentialSubject: { + $ref: '#/definitions/credentialSubject' + }, + id: { + type: 'string', + format: 'uri' + }, + issuer: { + $ref: '#/definitions/uriOrId' + }, + issuanceDate: { + type: 'string', + format: 'date-time' + }, + expirationDate: { + type: 'string', + format: 'date-time' + }, + credentialStatus: { + $ref: '#/definitions/credentialStatus' + }, + credentialSchema: { + $ref: '#/definitions/credentialSchema' + } + }, + definitions: { + context: { + type: 'array', + items: [ + { + const: 'https://www.w3.org/2018/credentials/v1' + } + ], + additionalItems: { + oneOf: [ + { + type: 'string', + format: 'uri' + }, + { + type: 'object' + }, + { + type: 'array', + items: { + $ref: '#/definitions/context' + } + } + ] + }, + minItems: 1, + uniqueItems: true + }, + credentialSubject: { + type: 'object', + required: ['id'], + additionalProperties: false, + properties: nestedObject + }, + VerifiableCredential: { + const: 'VerifiableCredential' + }, + credentialSchema: { + oneOf: [ + { + $ref: '#/definitions/idAndType' + }, + { + type: 'array', + items: { + $ref: '#/definitions/idAndType' + }, + minItems: 1, + uniqueItems: true + } + ] + }, + credentialStatus: { + oneOf: [ + { + $ref: '#/definitions/idAndType' + }, + { + type: 'array', + items: { + $ref: '#/definitions/idAndType' + }, + minItems: 1, + uniqueItems: true + } + ] + }, + idAndType: { + type: 'object', + required: ['id', 'type'], + properties: { + id: { + type: 'string', + format: 'uri' + }, + type: { + type: 'string' + } + } + }, + uriOrId: { + oneOf: [ + { + type: 'string', + format: 'uri' + }, + { + type: 'object', + required: ['id'], + properties: { + id: { + type: 'string', + format: 'uri' + } + } + } + ] + }, + ...schemaNameObject + }, + title: schemaName, + description: `${description}` + }; + return W3CSchema; + } + async _createSchema(payload: CreateSchemaAgentRedirection): Promise<{ response: string; }> { diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 5363cdd80..89d149584 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -161,7 +161,8 @@ export const ResponseMessages = { notStoredCredential: 'User credential not stored', agentDetailsNotFound: 'Agent details not found', failedFetchSchema: 'Failed to fetch schema data', - atLeastOneRequired: 'At least one of the attributes should have isReuired as `true`' + atLeastOneRequired: 'At least one of the attributes should have isReuired as `true`', + schemaBuilder: 'Error while creating schema JSON`' } }, credentialDefinition: { From b8f09d7e2ea278afea882f8ecfce0edb94b317f5 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Fri, 5 Apr 2024 13:20:33 +0530 Subject: [PATCH 227/231] fix: schema name in schema builder Signed-off-by: tipusinghaw --- apps/ledger/src/schema/schema.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 51d068c80..a5c71aa9c 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -337,7 +337,7 @@ export class SchemaService extends BaseService { $ref: '#/definitions/VerifiableCredential' }, { - const: '#/definitions/$AAdharCard' + const: `#/definitions/$${schemaName}` } ] } From 99f60a5d358d70691acb0835675c33e08affeaca Mon Sep 17 00:00:00 2001 From: tipusinghaw <126460794+tipusinghaw@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:31:08 +0530 Subject: [PATCH 228/231] fix: change schema name in schema builder (#644) * feat: added w3c schema Signed-off-by: tipusinghaw * feat: added interface Signed-off-by: tipusinghaw * fix: schema dto Signed-off-by: tipusinghaw * fix: sonar cloud issue Signed-off-by: tipusinghaw * fix: changed the interface of create schema Signed-off-by: tipusinghaw * fix: multiple error messages Signed-off-by: tipusinghaw * refactor: changed API key fetching logic Signed-off-by: tipusinghaw * fix: logger issue Signed-off-by: tipusinghaw * feat: W3C schema builder Signed-off-by: tipusinghaw * fix: required message Signed-off-by: tipusinghaw * refactor: Added error handling for schema builder Signed-off-by: tipusinghaw * fix: schema name in schema builder Signed-off-by: tipusinghaw --------- Signed-off-by: tipusinghaw --- apps/ledger/src/schema/schema.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index 51d068c80..a5c71aa9c 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -337,7 +337,7 @@ export class SchemaService extends BaseService { $ref: '#/definitions/VerifiableCredential' }, { - const: '#/definitions/$AAdharCard' + const: `#/definitions/$${schemaName}` } ] } From 247f503c746e603940edfc05df85779f2a2175a7 Mon Sep 17 00:00:00 2001 From: Krishna Date: Wed, 10 Apr 2024 15:18:14 +0530 Subject: [PATCH 229/231] fix: email template for oob verification Signed-off-by: Krishna --- apps/verification/src/verification.service.ts | 2 +- .../templates/out-of-band-verification.template.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index ed5972689..dabf7fbd0 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -489,7 +489,7 @@ export class VerificationService { this.emailData.emailFrom = platformConfigData.emailFrom; this.emailData.emailTo = email; this.emailData.emailSubject = `${process.env.PLATFORM_NAME} Platform: Verification of Your Credentials`; - this.emailData.emailHtml = await this.outOfBandVerification.outOfBandVerification(email, organizationDetails.name, outOfBandVerificationQrCode); + this.emailData.emailHtml = await this.outOfBandVerification.outOfBandVerification(email, organizationDetails.name, shortenedUrl); this.emailData.emailAttachments = [ { filename: 'qrcode.png', diff --git a/apps/verification/templates/out-of-band-verification.template.ts b/apps/verification/templates/out-of-band-verification.template.ts index ebb5e0693..cbe4d4af2 100644 --- a/apps/verification/templates/out-of-band-verification.template.ts +++ b/apps/verification/templates/out-of-band-verification.template.ts @@ -1,6 +1,6 @@ export class OutOfBandVerification { - public outOfBandVerification(email: string, orgName: string, verificationQrCode: string): string { + public outOfBandVerification(email: string, orgName: string, shortenedUrl: string): string { try { return ` @@ -33,7 +33,7 @@ export class OutOfBandVerification {
    • Tap the "Send Proof" button in ${process.env.MOBILE_APP} to share you credential data.
    • - Share Credential From e8d9d1fb0c1c267aa1600c9acfb6a8b951106bb4 Mon Sep 17 00:00:00 2001 From: Krishna Date: Thu, 11 Apr 2024 13:56:36 +0530 Subject: [PATCH 230/231] fix: incorrect error message Signed-off-by: Krishna --- apps/api-gateway/src/authz/guards/org-roles.guard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-gateway/src/authz/guards/org-roles.guard.ts b/apps/api-gateway/src/authz/guards/org-roles.guard.ts index c109525fb..f3c1178d1 100644 --- a/apps/api-gateway/src/authz/guards/org-roles.guard.ts +++ b/apps/api-gateway/src/authz/guards/org-roles.guard.ts @@ -84,7 +84,7 @@ export class OrgRolesGuard implements CanActivate { return false; } else { - throw new BadRequestException('organization is required'); + throw new BadRequestException('Please provide valid orgId'); } // Sending user friendly message if a user attempts to access an API that is inaccessible to their role From 5199c864c2b992c6bb7d0e386e95cf5bb6912b32 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Thu, 11 Apr 2024 14:13:12 +0530 Subject: [PATCH 231/231] fix: added example in w3c schema DTO Signed-off-by: tipusinghaw --- apps/api-gateway/src/dtos/create-schema.dto.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index 08175c362..380b797a3 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -72,7 +72,15 @@ export class CreateSchemaDto { } export class CreateW3CSchemaDto { - @ApiProperty() + @ApiProperty({ + type: [], + 'example': [ + { + title: 'name', + type: 'string' + } + ] + }) @IsNotEmpty({ message: 'Schema attributes are required' }) schemaAttributes: SchemaAttributes []; @@ -90,8 +98,8 @@ export class CreateW3CSchemaDto { @ApiProperty() @IsString({ message: 'description must be a string' }) - @IsOptional() - description?: string; + @IsNotEmpty({ message: 'description is required' }) + description: string; } export class SchemaAttributes {