diff --git a/apps/api-gateway/src/connection/dtos/connection.dto.ts b/apps/api-gateway/src/connection/dtos/connection.dto.ts index cc8b0303f..73ef33524 100644 --- a/apps/api-gateway/src/connection/dtos/connection.dto.ts +++ b/apps/api-gateway/src/connection/dtos/connection.dto.ts @@ -3,6 +3,7 @@ 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'; +import { IsNotSQLInjection } from '@credebl/common/cast.helper'; export class CreateOutOfBandConnectionInvitation { @ApiPropertyOptional() @@ -58,6 +59,12 @@ export class CreateOutOfBandConnectionInvitation { @IsOptional() @IsNotEmpty({ message: 'Please provide recipientKey' }) recipientKey: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + @IsNotEmpty({ message: 'Please provide invitation did' }) + invitationDid?: string; orgId; } @@ -67,12 +74,14 @@ export class CreateConnectionDto { @IsOptional() @IsString({ message: 'alias must be a string' }) @IsNotEmpty({ message: 'please provide valid alias' }) + @IsNotSQLInjection({ message: 'alias is required.' }) alias: string; @ApiPropertyOptional() @IsOptional() @IsString({ message: 'label must be a string' }) @IsNotEmpty({ message: 'please provide valid label' }) + @IsNotSQLInjection({ message: 'label is required.' }) label: string; @ApiPropertyOptional() @@ -125,6 +134,12 @@ export class CreateConnectionDto { @IsOptional() @IsNotEmpty({ message: 'Please provide recipientKey' }) recipientKey: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + @IsNotEmpty({ message: 'Please provide invitation did' }) + invitationDid?: string; } export class ConnectionDto { diff --git a/apps/api-gateway/src/credential-definition/dto/get-all-platform-cred-defs.dto.ts b/apps/api-gateway/src/credential-definition/dto/get-all-platform-cred-defs.dto.ts new file mode 100644 index 000000000..1224f50f0 --- /dev/null +++ b/apps/api-gateway/src/credential-definition/dto/get-all-platform-cred-defs.dto.ts @@ -0,0 +1,30 @@ +import { trim } from '@credebl/common/cast.helper'; +import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; +import { CredDefSortFields, SortValue } from '@credebl/enum/enum'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsEnum, IsOptional, IsUUID } from 'class-validator'; + +export class GetAllPlatformCredDefsDto extends PaginationDto { + + @ApiProperty({ example: '1a7eac11-ff05-40d7-8351-4d7467687cad'}) + @Transform(({ value }) => trim(value)) + @ApiPropertyOptional() + @IsOptional() + @IsUUID('4', { message: 'Invalid format of ledgerId' }) + ledgerId: string; + + @ApiProperty({ + required: false + }) + @Transform(({ value }) => trim(value)) + @IsOptional() + @IsEnum(CredDefSortFields) + sortField: string = CredDefSortFields.CREATED_DATE_TIME; + + @ApiProperty({ required: false }) + @Transform(({ value }) => trim(value)) + @IsOptional() + sortBy: string = SortValue.DESC; + +} diff --git a/apps/api-gateway/src/dtos/create-schema.dto.ts b/apps/api-gateway/src/dtos/create-schema.dto.ts index 380b797a3..cc7aca6ba 100644 --- a/apps/api-gateway/src/dtos/create-schema.dto.ts +++ b/apps/api-gateway/src/dtos/create-schema.dto.ts @@ -2,7 +2,7 @@ import { ArrayMinSize, IsArray, IsBoolean, IsNotEmpty, IsOptional, IsString, Val import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; -import { trim } from '@credebl/common/cast.helper'; +import { IsNotSQLInjection, trim } from '@credebl/common/cast.helper'; class AttributeValue { @@ -41,6 +41,7 @@ export class CreateSchemaDto { @IsString({ message: 'schemaName must be a string' }) @Transform(({ value }) => trim(value)) @IsNotEmpty({ message: 'schemaName is required' }) + @IsNotSQLInjection({ message: 'SchemaName is required.' }) schemaName: string; @ApiProperty({ 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 5b5e37fbc..f2be132ff 100644 --- a/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts @@ -2,7 +2,7 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from '@nestjs/swagge import { IsBoolean, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; import { Transform, Type } from 'class-transformer'; -import { trim } from '@credebl/common/cast.helper'; +import { IsNotSQLInjection, trim } from '@credebl/common/cast.helper'; @ApiExtraModels() export class CreateEcosystemDto { @@ -13,6 +13,7 @@ export class CreateEcosystemDto { @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.' }) + @IsNotSQLInjection({ message: 'Ecosystem name is required.' }) name: string; @ApiProperty() diff --git a/apps/api-gateway/src/ecosystem/dtos/edit-ecosystem-dto.ts b/apps/api-gateway/src/ecosystem/dtos/edit-ecosystem-dto.ts index 2c09343b6..a7011ee31 100644 --- a/apps/api-gateway/src/ecosystem/dtos/edit-ecosystem-dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/edit-ecosystem-dto.ts @@ -2,7 +2,7 @@ import { ApiExtraModels, ApiPropertyOptional } from '@nestjs/swagger'; import { IsBoolean, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; import { Transform } from 'class-transformer'; -import { trim } from '@credebl/common/cast.helper'; +import { IsNotSQLInjection, trim } from '@credebl/common/cast.helper'; @ApiExtraModels() export class EditEcosystemDto { @@ -13,6 +13,7 @@ export class EditEcosystemDto { @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.' }) + @IsNotSQLInjection({ message: 'Ecosystem name is required.' }) name?: string; @ApiPropertyOptional() diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 7e8302f07..d3f090aff 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/array-type */ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { ArrayMaxSize, ArrayMinSize, IsArray, IsBoolean, IsDefined, IsEmail, IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, MaxLength, ValidateIf, ValidateNested } from 'class-validator'; +import { ArrayMaxSize, ArrayMinSize, IsArray, IsBoolean, IsDefined, IsEmail, IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, MaxLength, ValidateNested } from 'class-validator'; import { IsCredentialJsonLdContext, SingleOrArray } from '../utils/helper'; import { IssueCredentialType, JsonLdCredentialDetailCredentialStatusOptions, JsonLdCredentialDetailOptionsOptions, JsonObject } from '../interfaces'; import { Transform, Type } from 'class-transformer'; @@ -125,11 +125,11 @@ export class Attribute { } export class CredentialsIssuanceDto { - @ValidateIf((obj) => obj.credentialType === IssueCredentialType.INDY) @ApiProperty({ example: 'string' }) @IsNotEmpty({ message: 'Credential definition Id is required' }) @IsString({ message: 'Credential definition id should be string' }) @Transform(({ value }) => value.trim()) + @IsOptional() credentialDefinitionId?: string; @ApiProperty({ example: 'string' }) diff --git a/apps/api-gateway/src/issuance/dtos/multi-connection.dto.ts b/apps/api-gateway/src/issuance/dtos/multi-connection.dto.ts index e58f6c7a4..23a4f56f0 100644 --- a/apps/api-gateway/src/issuance/dtos/multi-connection.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/multi-connection.dto.ts @@ -13,12 +13,12 @@ class ConnectionAttributes { connectionId: string; @ApiProperty({ - example: [ - { - value: 'string', - name: 'string' - } - ] + example: [ + { + value: 'string', + name: 'string' + } + ] }) @IsArray() @ValidateNested({ each: true }) @@ -28,6 +28,7 @@ class ConnectionAttributes { @IsOptional() attributes?: Attribute[]; + @ApiProperty() @IsNotEmpty({ message: 'Please provide valid credential' }) @IsObject({ message: 'credential should be an object' }) @Type(() => Credential) @@ -36,11 +37,11 @@ class ConnectionAttributes { credential?: Credential; @ApiProperty() - @IsOptional() @IsNotEmpty({ message: 'Please provide valid options' }) @IsObject({ message: 'options should be an object' }) - @ValidateNested({ each: true }) @Type(() => JsonLdCredentialDetailOptions) + @IsOptional() + @ValidateNested({ each: true }) options?:JsonLdCredentialDetailOptions; } diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index a5916034a..a579b788b 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -534,17 +534,26 @@ export class IssuanceController { issueCredentialDto.credentialType = credentialType; const credOffer = issueCredentialDto?.credentialData || []; - if (IssueCredentialType.INDY !== credentialType && IssueCredentialType.JSONLD !== credentialType) { + + if (IssueCredentialType.INDY !== credentialType && IssueCredentialType.JSONLD !== credentialType) { throw new NotFoundException(ResponseMessages.issuance.error.invalidCredentialType); -} - if (issueCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.credential || 0 === Object.keys(offer?.credential).length))) { + } + + if (credentialType === IssueCredentialType.INDY && !issueCredentialDto.credentialDefinitionId) { + throw new BadRequestException(ResponseMessages.credentialDefinition.error.isRequired); + } + + if (issueCredentialDto.credentialType !== IssueCredentialType.INDY && !credOffer.every(offer => (!offer?.attributes || 0 === Object.keys(offer?.attributes).length))) { + throw new BadRequestException(ResponseMessages.issuance.error.attributesAreRequired); + } + + if (issueCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.credential || 0 === Object.keys(offer?.credential).length))) { throw new BadRequestException(ResponseMessages.issuance.error.credentialNotPresent); } - if (issueCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.options || 0 === Object.keys(offer?.options).length))) { + if (issueCredentialDto.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.sendCredentialCreateOffer(issueCredentialDto, user); const finalResponse: IResponse = { diff --git a/apps/api-gateway/src/main.ts b/apps/api-gateway/src/main.ts index 966744a40..aafe38b14 100644 --- a/apps/api-gateway/src/main.ts +++ b/apps/api-gateway/src/main.ts @@ -15,9 +15,11 @@ import { NodeEnvironment } from '@credebl/enum/enum'; dotenv.config(); async function bootstrap(): Promise { - const app = await NestFactory.create(AppModule, { - logger: NodeEnvironment.PRODUCTION !== process.env.PLATFORM_PROFILE_MODE ? ['log', 'debug', 'error', 'verbose', 'warn'] : ['error', 'warn'] + logger: + NodeEnvironment.PRODUCTION !== process.env.PLATFORM_PROFILE_MODE + ? ['log', 'debug', 'error', 'verbose', 'warn'] + : ['error', 'warn'] }); app.connectMicroservice({ @@ -30,6 +32,18 @@ async function bootstrap(): Promise { app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb' })); + app.use(function (req, res, next) { + let err = null; + try { + decodeURIComponent(req.path); + } catch (e) { + err = e; + } + if (err) { + return res.status(500).json({ message: 'Invalid URL' }); + } + next(); + }); const options = new DocumentBuilder() .setTitle(`${process.env.PLATFORM_NAME}`) @@ -68,11 +82,12 @@ 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}`); } bootstrap(); - 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 61e2c0f73..8bc30fbe9 100644 --- a/apps/api-gateway/src/organization/dtos/create-organization-dto.ts +++ b/apps/api-gateway/src/organization/dtos/create-organization-dto.ts @@ -2,7 +2,7 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from '@nestjs/swagge import { IsNotEmpty, IsOptional, IsString, IsUrl, MaxLength, MinLength } from 'class-validator'; import { Transform } from 'class-transformer'; -import { trim } from '@credebl/common/cast.helper'; +import { IsNotSQLInjection, trim } from '@credebl/common/cast.helper'; @ApiExtraModels() export class CreateOrganizationDto { @@ -13,6 +13,7 @@ export class CreateOrganizationDto { @MinLength(2, { message: 'Organization name must be at least 2 characters.' }) @MaxLength(50, { message: 'Organization name must be at most 50 characters.' }) @IsString({ message: 'Organization name must be in string format.' }) + @IsNotSQLInjection({ message: 'Organization name is required.' }) name: string; @ApiPropertyOptional() diff --git a/apps/api-gateway/src/organization/dtos/update-organization-dto.ts b/apps/api-gateway/src/organization/dtos/update-organization-dto.ts index 0ac4d194e..ed2029c4f 100644 --- a/apps/api-gateway/src/organization/dtos/update-organization-dto.ts +++ b/apps/api-gateway/src/organization/dtos/update-organization-dto.ts @@ -1,8 +1,8 @@ import { ApiExtraModels, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsNotEmpty, IsOptional, IsString, IsBoolean, MaxLength, MinLength } from 'class-validator'; +import { IsNotEmpty, IsOptional, IsString, IsBoolean, MaxLength, MinLength, Validate } from 'class-validator'; import { Transform } from 'class-transformer'; -import { trim } from '@credebl/common/cast.helper'; +import { ImageBase64Validator, trim } from '@credebl/common/cast.helper'; @ApiExtraModels() export class UpdateOrganizationDto { @@ -31,7 +31,7 @@ export class UpdateOrganizationDto { @ApiPropertyOptional() @IsOptional() @Transform(({ value }) => trim(value)) - @IsString({ message: 'logo must be in string format.' }) + @Validate(ImageBase64Validator) logo?: string = ''; @ApiPropertyOptional() diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 83aed1195..772c7b028 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -1,6 +1,6 @@ import { ApiBearerAuth, ApiExcludeEndpoint, ApiForbiddenResponse, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; import { CommonService } from '@credebl/common'; -import { Controller, Get, Put, Param, UseGuards, UseFilters, Post, Body, Res, HttpStatus, Query, Delete, ParseUUIDPipe, BadRequestException } from '@nestjs/common'; +import { Controller, Get, Put, Param, UseGuards, UseFilters, Post, Body, Res, HttpStatus, Query, Delete, ParseUUIDPipe, BadRequestException, ValidationPipe, UsePipes } from '@nestjs/common'; import { OrganizationService } from './organization.service'; import { CreateOrganizationDto } from './dtos/create-organization-dto'; import IResponse from '@credebl/common/interfaces/response.interface'; @@ -499,8 +499,12 @@ export class OrganizationController { @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @ApiParam({ + name: 'orgId' + }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard, UserAccessGuard) - async updateOrganization(@Body() updateOrgDto: UpdateOrganizationDto, @Param('orgId') orgId: string, @Res() res: Response, @User() reqUser: user): Promise { + @UsePipes(new ValidationPipe()) + async updateOrganization(@Body() updateOrgDto: UpdateOrganizationDto, @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for orgId`); }})) orgId: string, @Res() res: Response, @User() reqUser: user): Promise { updateOrgDto.orgId = orgId; await this.organizationService.updateOrganization(updateOrgDto, reqUser.id, orgId); diff --git a/apps/api-gateway/src/platform/platform.controller.ts b/apps/api-gateway/src/platform/platform.controller.ts index 6eb091a6e..1359e1b45 100644 --- a/apps/api-gateway/src/platform/platform.controller.ts +++ b/apps/api-gateway/src/platform/platform.controller.ts @@ -12,7 +12,8 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { AuthGuard } from '@nestjs/passport'; import * as QRCode from 'qrcode'; -import { SortFields } from '@credebl/enum/enum'; +import { CredDefSortFields, SortFields } from '@credebl/enum/enum'; +import { GetAllPlatformCredDefsDto } from '../credential-definition/dto/get-all-platform-cred-defs.dto'; @Controller('') @UseFilters(CustomExceptionFilter) @@ -58,6 +59,36 @@ export class PlatformController { return res.status(HttpStatus.OK).json(finalResponse); } + + @Get('/platform/cred-defs') + @ApiTags('credential-definitions') + @ApiOperation({ + summary: 'Get all credential-definitions from platform.', + description: 'Get all credential-definitions list from platform.' + }) + @ApiQuery({ + name: 'sortField', + enum: CredDefSortFields, + required: false + }) + @ApiBearerAuth() + @UseGuards(AuthGuard('jwt')) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + async getAllCredDefs( + @Query() getAllPlatformCredDefs: GetAllPlatformCredDefsDto, + @Res() res: Response, + @User() user: IUserRequestInterface + ): Promise { + const schemasResponse = await this.platformService.getAllPlatformCredDefs(getAllPlatformCredDefs, user); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: ResponseMessages.credentialDefinition.success.fetch, + data: schemasResponse + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + @Get('/platform/ledgers') @ApiTags('ledgers') @ApiOperation({ diff --git a/apps/api-gateway/src/platform/platform.service.ts b/apps/api-gateway/src/platform/platform.service.ts index b0063e3eb..6bfa64dfd 100644 --- a/apps/api-gateway/src/platform/platform.service.ts +++ b/apps/api-gateway/src/platform/platform.service.ts @@ -4,6 +4,8 @@ import { BaseService } from '../../../../libs/service/base.service'; import { ILedgers, ISchemaSearchPayload } from '../interfaces/ISchemaSearch.interface'; import { IUserRequestInterface } from '../interfaces/IUserRequestInterface'; import { INetworkUrl, ISchemaDetails } from '@credebl/common/interfaces/schema.interface'; +import { GetAllPlatformCredDefsDto } from '../credential-definition/dto/get-all-platform-cred-defs.dto'; +import { IPlatformCredDefsData } from '@credebl/common/interfaces/cred-def.interface'; @Injectable() export class PlatformService extends BaseService { @@ -19,6 +21,11 @@ export class PlatformService extends BaseService { } + async getAllPlatformCredDefs(getAllPlatformCredDefs: GetAllPlatformCredDefsDto, user: IUserRequestInterface): Promise { + const credDefs = { ...getAllPlatformCredDefs, user }; + return this.sendNatsMessage(this.platformServiceProxy, 'get-all-platform-cred-defs', credDefs); + } + async getAllLedgers(): Promise { const payload = {}; return this.sendNatsMessage(this.platformServiceProxy, 'get-all-ledgers', payload); diff --git a/apps/api-gateway/src/schema/dtos/get-all-schema.dto.ts b/apps/api-gateway/src/schema/dtos/get-all-schema.dto.ts index 4fa5e3280..3af21ac57 100644 --- a/apps/api-gateway/src/schema/dtos/get-all-schema.dto.ts +++ b/apps/api-gateway/src/schema/dtos/get-all-schema.dto.ts @@ -4,8 +4,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { IsEnum, IsOptional, IsUUID, Min } from 'class-validator'; import { toNumber, trim } from '@credebl/common/cast.helper'; -import { CredDefSortFields, SortFields } from 'apps/ledger/src/schema/enum/schema.enum'; -import { SortValue } from '@credebl/enum/enum'; +import { CredDefSortFields, SortFields, SortValue } from '@credebl/enum/enum'; export class GetAllSchemaDto { @ApiProperty({ required: false }) diff --git a/apps/api-gateway/src/schema/schema.controller.ts b/apps/api-gateway/src/schema/schema.controller.ts index 9a1a172c3..f20450a13 100644 --- a/apps/api-gateway/src/schema/schema.controller.ts +++ b/apps/api-gateway/src/schema/schema.controller.ts @@ -19,7 +19,7 @@ import { IUserRequestInterface } from './interfaces'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; 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'; +import { CredDefSortFields, SortFields } from '@credebl/enum/enum'; @UseFilters(CustomExceptionFilter) @Controller('orgs') diff --git a/apps/api-gateway/src/verification/dto/get-all-proof-requests.dto.ts b/apps/api-gateway/src/verification/dto/get-all-proof-requests.dto.ts index a4adc4ad7..d414112fb 100644 --- a/apps/api-gateway/src/verification/dto/get-all-proof-requests.dto.ts +++ b/apps/api-gateway/src/verification/dto/get-all-proof-requests.dto.ts @@ -1,28 +1,12 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; -import { IsEnum, IsOptional, Max, Min } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { IsEnum, IsOptional } from 'class-validator'; import { SortValue } from '../../enum'; -import { toNumber, trim } from '@credebl/common/cast.helper'; +import { trim } from '@credebl/common/cast.helper'; import { SortFields } from '../enum/verification.enum'; +import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; -export class GetAllProofRequestsDto { - @ApiProperty({ required: false, example: '1' }) - @Transform(({ value }) => toNumber(value)) - @IsOptional() - pageNumber: number = 1; - - @ApiProperty({ required: false }) - @IsOptional() - @Transform(({ value }) => trim(value)) - @Type(() => String) - searchByText: string = ''; - - @ApiProperty({ required: false, example: '10' }) - @IsOptional() - @Transform(({ value }) => toNumber(value)) - @Min(1, { message: 'Page size must be greater than 0' }) - @Max(100, { message: 'Page size must be less than 100' }) - pageSize: number = 10; +export class GetAllProofRequestsDto extends PaginationDto { @ApiProperty({ enum: [SortValue.DESC, SortValue.ASC], 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 3bcb2e304..cf0642766 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,5 +1,5 @@ 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 { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { AutoAccept } from '@credebl/enum/enum'; @@ -195,7 +195,7 @@ export class RequestProofDto extends ProofPayload { @ApiProperty() @IsString() @Transform(({ value }) => trim(value)) - @Transform(({ value }) => toLowerCase(value)) + @IsUUID() @IsNotEmpty({ message: 'connectionId is required.' }) connectionId: string; diff --git a/apps/api-gateway/src/verification/interfaces/verification.interface.ts b/apps/api-gateway/src/verification/interfaces/verification.interface.ts index a722da72a..cac18fa67 100644 --- a/apps/api-gateway/src/verification/interfaces/verification.interface.ts +++ b/apps/api-gateway/src/verification/interfaces/verification.interface.ts @@ -13,7 +13,7 @@ export interface IProofRequestSearchCriteria { pageSize: number; sortField: string; sortBy: string; - searchByText: string; + search: string; user?: IUserRequestInterface } diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index ba7d892da..74a89e0fe 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -137,10 +137,10 @@ export class VerificationController { @User() user: IUserRequest, @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for orgId`); }})) orgId: string ): Promise { - const { pageSize, searchByText, pageNumber, sortField, sortBy } = getAllProofRequests; + const { pageSize, search, pageNumber, sortField, sortBy } = getAllProofRequests; const proofRequestsSearchCriteria: IProofRequestSearchCriteria = { pageNumber, - searchByText, + search, pageSize, sortField, sortBy diff --git a/apps/connection/src/connection.repository.ts b/apps/connection/src/connection.repository.ts index 430304f97..e5d461d34 100644 --- a/apps/connection/src/connection.repository.ts +++ b/apps/connection/src/connection.repository.ts @@ -49,7 +49,7 @@ export class ConnectionRepository { connectionInvitation: string, agentId: string, orgId: string, - recipientKey: string + invitationDid : string // eslint-disable-next-line camelcase ): Promise { try { @@ -59,7 +59,7 @@ export class ConnectionRepository { agentId, connectionInvitation, multiUse: true, - recipientKey + invitationDid } }); return agentDetails; diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 1c1eaa7f2..73acbf018 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -620,7 +620,8 @@ export class ConnectionService { multiUseInvitation, orgId, routing, - recipientKey + recipientKey, + invitationDid } = payload?.createOutOfBandConnectionInvitation; const agentDetails = await this.connectionRepository.getAgentEndPoint(payload?.createOutOfBandConnectionInvitation?.orgId); @@ -645,7 +646,8 @@ export class ConnectionService { appendedAttachments: appendedAttachments || undefined, routing: routing || undefined, messages: messages || undefined, - recipientKey: recipientKey || undefined + recipientKey: recipientKey || undefined, + invitationDid: invitationDid || undefined }; const createConnectionInvitationFlag = 'connection-invitation'; @@ -662,12 +664,13 @@ export class ConnectionService { connectionInvitationUrl, connectionPayload.multiUseInvitation ); - const recipientsKey = createConnectionInvitation?.response?.recipientKey || recipientKey; + + const invitationsDid = createConnectionInvitation?.response?.invitationDid || invitationDid; const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, agentId, orgId, - recipientsKey + invitationsDid ); const connectionDetailRecords: ConnectionResponseDetail = { id: saveConnectionDetails.id, @@ -680,7 +683,7 @@ export class ConnectionService { lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, lastChangedBy: saveConnectionDetails.lastChangedBy, recordId: createConnectionInvitation.response.outOfBandRecord.id, - recipientKey: saveConnectionDetails.recipientKey + invitationDid: saveConnectionDetails.invitationDid }; return connectionDetailRecords; } catch (error) { diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index 703e5081b..49f06697a 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -16,6 +16,7 @@ export interface IConnection { handshakeProtocols: string[]; orgId: string; recipientKey?: string; + invitationDid?: string } export interface IUserRequestInterface { userId: string; @@ -265,7 +266,7 @@ export interface ConnectionResponseDetail { lastChangedDateTime: Date; lastChangedBy: number; recordId: string; - recipientKey:string; + invitationDid?: string } export interface ICreateConnectionInvitation { @@ -283,6 +284,7 @@ export interface ICreateConnectionInvitation { appendedAttachments?: object[]; orgId?: string; recipientKey?: string; + invitationDid?: string; } export interface ICreateOutOfbandConnectionInvitation { diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 04a19af33..e52a2610a 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -1452,17 +1452,22 @@ export class EcosystemService { async submitTransaction(transactionPayload: TransactionPayload): Promise { try { const { endorsementId, ecosystemId, ecosystemLeadAgentEndPoint, orgId } = transactionPayload; + const checkEndorsementRequestIsSubmitted = await this.ecosystemRepository.getEndorsementTransactionById( + endorsementId, + endorsementTransactionStatus.SUBMITED + ); + + if (checkEndorsementRequestIsSubmitted) { + throw new ConflictException(ResponseMessages.ecosystem.error.transactionSubmitted); + } + const endorsementTransactionPayload = await this.ecosystemRepository.getEndorsementTransactionById( endorsementId, endorsementTransactionStatus.SIGNED ); if (!endorsementTransactionPayload) { - throw new InternalServerErrorException(ResponseMessages.ecosystem.error.invalidTransaction); - } - - if ('submitted' === endorsementTransactionPayload.status) { - throw new ConflictException(ResponseMessages.ecosystem.error.transactionSubmitted); + throw new BadRequestException(ResponseMessages.ecosystem.error.transactionNotSigned); } const ecosystemMemberDetails = await this.getEcosystemMemberDetails(endorsementTransactionPayload); diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 1a6256ff4..579c5002e 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -72,7 +72,7 @@ export class IssuanceRepository { } - async getRecipientKeyByOrgId(orgId: string): Promise { + async getInvitationDidByOrgId(orgId: string): Promise { try { return this.prisma.agent_invitations.findMany({ where: { @@ -83,7 +83,7 @@ export class IssuanceRepository { } }); } catch (error) { - this.logger.error(`Error in getRecipientKey in issuance repository: ${error.message}`); + this.logger.error(`Error in getInvitationDid in issuance repository: ${error.message}`); throw error; } } diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index dbf92c525..15bbc9d19 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -58,7 +58,6 @@ export class IssuanceService { const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( credentialDefinitionId ); - if (schemaResponse?.attributes) { const schemaResponseError = []; const attributesArray: IAttributes[] = JSON.parse(schemaResponse.attributes); @@ -82,31 +81,26 @@ export class IssuanceService { } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); - if (!agentDetails) { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } - const { agentEndPoint } = agentDetails; const orgAgentType = await this.issuanceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); - if (!orgAgentType) { throw new NotFoundException(ResponseMessages.issuance.error.orgAgentTypeNotFound); } const issuanceMethodLabel = 'create-offer'; - const url = await this.getAgentUrl(issuanceMethodLabel, orgAgentType, agentEndPoint, agentDetails?.tenantId); - const issuancePromises: Promise[] = []; + const issuancePromises = credentialData.map(async (credentials) => { + const { connectionId, attributes, credential, options } = credentials; + let issueData; - let issueData; - if (payload.credentialType === IssueCredentialType.INDY) { - for (const credentials of credentialData) { - const { connectionId, attributes } = credentials; + if (payload.credentialType === IssueCredentialType.INDY) { issueData = { - protocolVersion: 'v1', + protocolVersion: payload.protocolVersion || 'v1', connectionId, credentialFormats: { indy: { @@ -118,13 +112,7 @@ export class IssuanceService { autoAcceptCredential: payload.autoAcceptCredential || 'always', comment }; - } - } - - if (payload.credentialType === IssueCredentialType.JSONLD) { - for (const credentials of credentialData) { - const { connectionId, credential, options } = credentials; - + } else if (payload.credentialType === IssueCredentialType.JSONLD) { issueData = { protocolVersion: payload.protocolVersion || 'v2', connectionId, @@ -138,16 +126,17 @@ export class IssuanceService { comment: comment || '' }; } - } - await this.delay(500); - const credentialCreateOfferDetails = this._sendCredentialCreateOffer(issueData, url, orgId); - issuancePromises.push(credentialCreateOfferDetails); + await this.delay(500); + return this._sendCredentialCreateOffer(issueData, url, orgId); + }); + const results = await Promise.allSettled(issuancePromises); return results; } catch (error) { this.logger.error(`[sendCredentialCreateOffer] - error in create credentials : ${JSON.stringify(error)}`); const errorStack = error?.status?.message?.error?.reason || error?.status?.message?.error; + if (errorStack) { throw new RpcException({ error: errorStack?.message ? errorStack?.message : errorStack, @@ -194,12 +183,12 @@ export class IssuanceService { } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); - let recipientKey: string | undefined; + let invitationDid: string | undefined; if (true === reuseConnection) { - const data: agent_invitations[] = await this.issuanceRepository.getRecipientKeyByOrgId(orgId); + const data: agent_invitations[] = await this.issuanceRepository.getInvitationDidByOrgId(orgId); if (data && 0 < data.length) { const [firstElement] = data; - recipientKey = firstElement?.recipientKey ?? undefined; + invitationDid = firstElement?.invitationDid ?? undefined; } } const { agentEndPoint, organisation } = agentDetails; @@ -233,7 +222,7 @@ export class IssuanceService { imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, label: organisation?.name, comment: comment || '', - recipientKey:recipientKey || undefined + invitationDid:invitationDid || undefined }; } @@ -254,7 +243,7 @@ export class IssuanceService { imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, label: organisation?.name, comment: comment || '', - recipientKey:recipientKey || undefined + invitationDid:invitationDid || undefined }; } const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, orgId); diff --git a/apps/ledger/src/credential-definition/credential-definition.controller.ts b/apps/ledger/src/credential-definition/credential-definition.controller.ts index 8bf48516b..36bd6d1c3 100644 --- a/apps/ledger/src/credential-definition/credential-definition.controller.ts +++ b/apps/ledger/src/credential-definition/credential-definition.controller.ts @@ -4,11 +4,11 @@ import { Controller, Logger } from '@nestjs/common'; import { CredentialDefinitionService } from './credential-definition.service'; import { MessagePattern } from '@nestjs/microservices'; -import { GetAllCredDefsPayload, GetCredDefBySchemaId } from './interfaces/create-credential-definition.interface'; +import { GetAllCredDefsPayload, GetCredDefBySchemaId, IPlatformCredDefs } from './interfaces/create-credential-definition.interface'; import { CreateCredDefPayload, GetCredDefPayload } from './interfaces/create-credential-definition.interface'; import { credential_definition } from '@prisma/client'; import { CredDefSchema } from './interfaces/credential-definition.interface'; -import { ICredDefDetails } from '@credebl/common/interfaces/cred-def.interface'; +import { ICredDefDetails, IPlatformCredDefsData } from '@credebl/common/interfaces/cred-def.interface'; @Controller('credential-definitions') export class CredentialDefinitionController { private logger = new Logger(); @@ -25,6 +25,11 @@ export class CredentialDefinitionController { return this.credDefService.getCredentialDefinitionById(payload); } + @MessagePattern({ cmd: 'get-all-platform-cred-defs' }) + async getAllSchema(credDefPayload: IPlatformCredDefs): Promise { + return this.credDefService.getAllPlatformCredDefs(credDefPayload); + } + @MessagePattern({ cmd: 'get-all-credential-definitions' }) async getAllCredDefs(payload: GetAllCredDefsPayload): Promise { return this.credDefService.getAllCredDefs(payload); diff --git a/apps/ledger/src/credential-definition/credential-definition.service.ts b/apps/ledger/src/credential-definition/credential-definition.service.ts index 5d03ca2e7..d9b9ed687 100644 --- a/apps/ledger/src/credential-definition/credential-definition.service.ts +++ b/apps/ledger/src/credential-definition/credential-definition.service.ts @@ -9,7 +9,7 @@ import { import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { CredentialDefinitionRepository } from './repositories/credential-definition.repository'; -import { CreateCredDefPayload, CredDefPayload, GetAllCredDefsPayload, GetCredDefBySchemaId, GetCredDefPayload } from './interfaces/create-credential-definition.interface'; +import { CreateCredDefPayload, CredDefPayload, GetAllCredDefsPayload, GetCredDefBySchemaId, GetCredDefPayload, IPlatformCredDefs } from './interfaces/create-credential-definition.interface'; import { credential_definition } from '@prisma/client'; import { ResponseMessages } from '@credebl/common/response-messages'; import { CreateCredDefAgentRedirection, CredDefSchema, GetCredDefAgentRedirection } from './interfaces/credential-definition.interface'; @@ -17,7 +17,7 @@ import { map } from 'rxjs/operators'; import { OrgAgentType, SortValue } from '@credebl/enum/enum'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; -import { ICredDefDetails } from '@credebl/common/interfaces/cred-def.interface'; +import { ICredDefDetails, IPlatformCredDefsData } from '@credebl/common/interfaces/cred-def.interface'; @Injectable() export class CredentialDefinitionService extends BaseService { constructor( @@ -171,6 +171,34 @@ export class CredentialDefinitionService extends BaseService { } } + async getAllPlatformCredDefs(credDefsPayload: IPlatformCredDefs): Promise { + try { + const { pageSize, pageNumber } = credDefsPayload; + const response = await this.credentialDefinitionRepository.getAllPlatformCredDefsDetails(credDefsPayload); + + const credDefResponse: IPlatformCredDefsData = { + totalItems: response.credDefCount, + hasNextPage: pageSize * pageNumber < response.credDefCount, + hasPreviousPage: 1 < pageNumber, + nextPage: pageNumber + 1, + previousPage: pageNumber - 1, + lastPage: Math.ceil(response.credDefCount / pageSize), + data: response.credDefResult + }; + + if (0 !== response.credDefCount) { + return credDefResponse; + } else { + throw new NotFoundException(ResponseMessages.credentialDefinition.error.NotFound); + } + + + } catch (error) { + this.logger.error(`Error in retrieving all credential definitions: ${error}`); + throw new RpcException(error.response ? error.response : error); + } + } + async getCredentialDefinitionById(payload: GetCredDefPayload): Promise { try { const { credentialDefinitionId, orgId } = payload; diff --git a/apps/ledger/src/credential-definition/interfaces/create-credential-definition.interface.ts b/apps/ledger/src/credential-definition/interfaces/create-credential-definition.interface.ts index 7067508a5..0e8a31cbd 100644 --- a/apps/ledger/src/credential-definition/interfaces/create-credential-definition.interface.ts +++ b/apps/ledger/src/credential-definition/interfaces/create-credential-definition.interface.ts @@ -22,6 +22,16 @@ export interface CreateCredDefPayload { orgId?: string; } +export interface IPlatformCredDefs { + ledgerId?: string; + pageNumber?: number; + pageSize?: number; + sortField?: string; + sortBy?: string; + search?: string; + user: IUserRequestInterface +} + export interface CredDefPayload { userId?: string, schemaId?: string; @@ -46,7 +56,6 @@ export class GetAllCredDefsDto { revocable?: boolean; sortByValue?: string; } - export interface GetAllCredDefsPayload { credDefSearchCriteria: GetAllCredDefsDto, user: IUserRequestInterface, diff --git a/apps/ledger/src/credential-definition/repositories/credential-definition.repository.ts b/apps/ledger/src/credential-definition/repositories/credential-definition.repository.ts index d0b67b1f5..8345d6fce 100644 --- a/apps/ledger/src/credential-definition/repositories/credential-definition.repository.ts +++ b/apps/ledger/src/credential-definition/repositories/credential-definition.repository.ts @@ -1,11 +1,11 @@ /* eslint-disable camelcase */ -import { CredDefPayload, GetAllCredDefsDto } from '../interfaces/create-credential-definition.interface'; +import { CredDefPayload, GetAllCredDefsDto, IPlatformCredDefs } from '../interfaces/create-credential-definition.interface'; import { PrismaService } from '@credebl/prisma-service'; import { credential_definition, org_agents, org_agents_type, organisation, schema } from '@prisma/client'; import { Injectable, Logger } from '@nestjs/common'; import { ResponseMessages } from '@credebl/common/response-messages'; import { BulkCredDefSchema, CredDefSchema } from '../interfaces/credential-definition.interface'; -import { ICredDefData } from '@credebl/common/interfaces/cred-def.interface'; +import { ICredDefData, IPlatformCredDefDetails } from '@credebl/common/interfaces/cred-def.interface'; import { SortValue } from '@credebl/enum/enum'; @Injectable() @@ -57,6 +57,51 @@ export class CredentialDefinitionRepository { } } + async getAllPlatformCredDefsDetails(credDefsPayload: IPlatformCredDefs): Promise { + try { + const { ledgerId, search, sortBy, sortField, pageNumber, pageSize } = credDefsPayload || {}; + const credDefResult = await this.prisma.credential_definition.findMany({ + where: { + schema: { + ledgerId + }, + OR: [ + { tag: { contains: search, mode: 'insensitive' } }, + { credentialDefinitionId: { contains: search, mode: 'insensitive' } }, + { schemaLedgerId: { contains: search, mode: 'insensitive' } } + ] + }, + select: { + createDateTime: true, + tag: true, + schemaId: true, + orgId: true, + schemaLedgerId: true, + createdBy: true, + credentialDefinitionId: true, + revocable: true + }, + orderBy: { + [sortField]: SortValue.DESC === sortBy ? SortValue.DESC : SortValue.ASC + }, + take: Number(pageSize), + skip: (pageNumber - 1) * pageSize + }); + + const credDefCount = await this.prisma.credential_definition.count({ + where: { + schema: { + ledgerId + } + } + }); + return { credDefCount, credDefResult }; + } catch (error) { + this.logger.error(`Error in getting credential definitions: ${error}`); + throw error; + } + } + async getByAttribute(schema: string, tag: string): Promise { try { const response = await this.prisma.credential_definition.findFirst({ where: { schemaLedgerId: schema, tag: { contains: tag, mode: 'insensitive' } } }); diff --git a/apps/ledger/src/schema/enum/schema.enum.ts b/apps/ledger/src/schema/enum/schema.enum.ts index cafabff1d..ada972ce4 100644 --- a/apps/ledger/src/schema/enum/schema.enum.ts +++ b/apps/ledger/src/schema/enum/schema.enum.ts @@ -9,11 +9,3 @@ export enum SortFields { PUBLISHER_DID = 'publisherDid', ISSUER_ID = 'issuerId' } - -export enum CredDefSortFields { - ID = 'id', - CREATED_DATE_TIME = 'createDateTime', - TAG = 'tag', - LEDGER_ID = 'schemaLedgerId', - CRED_DEF_ID= 'credentialDefinitionId' -} \ No newline at end of file diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index ecfc8904d..fc13bdf66 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -164,6 +164,7 @@ export interface ISendProofRequestPayload { presentationDefinition?:IProofRequestPresentationDefinition; reuseConnection?: boolean; recipientKey?:string; + invitationDid?: string } export interface IWSendProofRequestPayload { @@ -227,7 +228,7 @@ export interface IProofRequestSearchCriteria { pageSize: number; sortField: string; sortBy: string; - searchByText: string; + search: string; } export interface IInvitation{ diff --git a/apps/verification/src/repositories/verification.repository.ts b/apps/verification/src/repositories/verification.repository.ts index b64b24145..80e79377f 100644 --- a/apps/verification/src/repositories/verification.repository.ts +++ b/apps/verification/src/repositories/verification.repository.ts @@ -65,9 +65,9 @@ export class VerificationRepository { where: { orgId, OR: [ - { connectionId: { contains: proofRequestsSearchCriteria.searchByText, mode: 'insensitive' } }, - { state: { contains: proofRequestsSearchCriteria.searchByText, mode: 'insensitive' } }, - { presentationId: { contains: proofRequestsSearchCriteria.searchByText, mode: 'insensitive' } } + { connectionId: { contains: proofRequestsSearchCriteria.search, mode: 'insensitive' } }, + { state: { contains: proofRequestsSearchCriteria.search, mode: 'insensitive' } }, + { presentationId: { contains: proofRequestsSearchCriteria.search, mode: 'insensitive' } } ] }, select: { @@ -90,9 +90,9 @@ export class VerificationRepository { where: { orgId, OR: [ - { connectionId: { contains: proofRequestsSearchCriteria.searchByText, mode: 'insensitive' } }, - { state: { contains: proofRequestsSearchCriteria.searchByText, mode: 'insensitive' } }, - { presentationId: { contains: proofRequestsSearchCriteria.searchByText, mode: 'insensitive' } } + { connectionId: { contains: proofRequestsSearchCriteria.search, mode: 'insensitive' } }, + { state: { contains: proofRequestsSearchCriteria.search, mode: 'insensitive' } }, + { presentationId: { contains: proofRequestsSearchCriteria.search, mode: 'insensitive' } } ] } }); @@ -187,7 +187,7 @@ export class VerificationRepository { } // eslint-disable-next-line camelcase - async getRecipientKeyByOrgId(orgId: string): Promise { + async getInvitationDidByOrgId(orgId: string): Promise { try { return this.prisma.agent_invitations.findMany({ where: { @@ -198,7 +198,7 @@ export class VerificationRepository { } }); } catch (error) { - this.logger.error(`Error in getRecipientKey in verification repository: ${error.message}`); + this.logger.error(`Error in getInvitationDid in verification repository: ${error.message}`); throw error; } } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 22daea915..bd863f5a5 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -348,12 +348,12 @@ export class VerificationService { // Destructuring 'outOfBandRequestProof' to remove emailId, as it is not used while agent operation const { isShortenUrl, emailId, type, reuseConnection, ...updateOutOfBandRequestProof } = outOfBandRequestProof; - let recipientKey: string | undefined; + let invitationDid: string | undefined; if (true === reuseConnection) { - const data: agent_invitations[] = await this.verificationRepository.getRecipientKeyByOrgId(user.orgId); + const data: agent_invitations[] = await this.verificationRepository.getInvitationDidByOrgId(user.orgId); if (data && 0 < data.length) { const [firstElement] = data; - recipientKey = firstElement?.recipientKey ?? undefined; + invitationDid = firstElement?.invitationDid ?? undefined; } } outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || AutoAccept.Always; @@ -363,7 +363,7 @@ export class VerificationService { if (ProofRequestType.INDY === type) { updateOutOfBandRequestProof.protocolVersion = updateOutOfBandRequestProof.protocolVersion || 'v1'; - updateOutOfBandRequestProof.recipientKey = recipientKey || undefined; + updateOutOfBandRequestProof.invitationDid = invitationDid || undefined; payload = { orgId: user.orgId, url, @@ -377,6 +377,7 @@ export class VerificationService { orgId: user.orgId, url, proofRequestPayload: { + goalCode: outOfBandRequestProof.goalCode, protocolVersion:outOfBandRequestProof.protocolVersion || 'v2', comment:outOfBandRequestProof.comment, label, @@ -390,7 +391,7 @@ export class VerificationService { } }, autoAcceptProof:outOfBandRequestProof.autoAcceptProof, - recipientKey:recipientKey || undefined + invitationDid:invitationDid || undefined } }; } @@ -682,6 +683,7 @@ export class VerificationService { } } + // TODO: This function is only for anoncreds indy async getVerifiedProofdetails(proofId: string, orgId: string): Promise { try { const getAgentDetails = await this.verificationRepository.getAgentEndPoint(orgId); diff --git a/libs/common/src/cast.helper.ts b/libs/common/src/cast.helper.ts index d92cb2a59..be7995c74 100644 --- a/libs/common/src/cast.helper.ts +++ b/libs/common/src/cast.helper.ts @@ -1,3 +1,6 @@ +import { BadRequestException } from '@nestjs/common'; +import { ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, isBase64, isMimeType, isUUID, registerDecorator } from 'class-validator'; + interface ToNumberOptions { default?: number; min?: number; @@ -55,4 +58,88 @@ export function ledgerName(value: string): string { return network; -} \ No newline at end of file +} + +export function isSafeString(value: string): boolean { + // Define a regular expression to allow alphanumeric characters, spaces, and some special characters + const safeRegex = /^[a-zA-Z0-9\s!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]*$/; + + // Check if the value matches the safe regex + return safeRegex.test(value); + } + + export const IsNotSQLInjection = (validationOptions?: ValidationOptions): PropertyDecorator => (object: object, propertyName: string) => { + registerDecorator({ + name: 'isNotSQLInjection', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value) { + // Check if the value contains any common SQL injection keywords + const sqlKeywords = ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'DROP', 'UNION', 'WHERE', 'AND', 'OR']; + for (const keyword of sqlKeywords) { + if (value.includes(keyword)) { + return false; // Value contains a SQL injection keyword + } + } + return true; // Value does not contain any SQL injection keywords + }, + defaultMessage(args: ValidationArguments) { + return `${args.property} contains SQL injection keywords.`; + } + } + }); + }; + +@ValidatorConstraint({ name: 'customText', async: false }) +export class ImageBase64Validator implements ValidatorConstraintInterface { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-vars + validate(value: string, args: ValidationArguments) { + // Implement your custom validation logic here + // Validation to allow option param logo + if ('' == value) { + return true; + } + if (!value || 'string' !== typeof value) { + throw new BadRequestException('Invalid base64 string'); + } + const parts = value.split(','); + if (2 !== parts.length) { + throw new BadRequestException('Invalid data URI'); + } + // eslint-disable-next-line prefer-destructuring + const mimeType = parts[0].split(';')[0].split(':')[1]; + // eslint-disable-next-line prefer-destructuring + const base64Data = parts[1]; + + // Validate MIME type + if (!isMimeType(mimeType)) { + throw new BadRequestException('Please provide valid MIME type'); + } + // Validate base64 data + if (!isBase64(base64Data) || '' == base64Data || null == base64Data) { + throw new BadRequestException('Invalid base64 string'); + } + return true; + } + + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-vars + defaultMessage(_args: ValidationArguments) { + return 'Default message received from [ImageBase64Validator]'; + } +} + +// export const IsNotUUID = (validationOptions?: ValidationOptions): PropertyDecorator => (object: object, propertyName: string) => { +// registerDecorator({ +// name: 'isNotUUID', +// target: object.constructor, +// propertyName, +// options: validationOptions, +// validator: { +// validate(value) { +// return !isUUID(value); +// } +// } +// }); +// }; \ No newline at end of file diff --git a/libs/common/src/dtos/pagination.dto.ts b/libs/common/src/dtos/pagination.dto.ts index f321c0ed3..c141c961b 100644 --- a/libs/common/src/dtos/pagination.dto.ts +++ b/libs/common/src/dtos/pagination.dto.ts @@ -1,12 +1,12 @@ -import { Transform, Type } from 'class-transformer'; -import { toNumber } from '@credebl/common/cast.helper'; +import { Type } from 'class-transformer'; import { ApiProperty } from '@nestjs/swagger'; -import { IsOptional, Max, Min } from 'class-validator'; +import { IsNumber, IsOptional, Max, Min } from 'class-validator'; export class PaginationDto { + @Type(() => Number) @ApiProperty({ required: false, default: 1 }) @IsOptional() - @Transform(({ value }) => toNumber(value)) + @IsNumber() @Min(1, { message: 'Page number must be greater than 0' }) pageNumber: number = 1; @@ -15,9 +15,10 @@ export class PaginationDto { @Type(() => String) search: string = ''; + @Type(() => Number) @ApiProperty({ required: false, default: 10 }) @IsOptional() - @Transform(({ value }) => toNumber(value)) + @IsNumber() @Min(1, { message: 'Page size must be greater than 0' }) @Max(100, { message: 'Page size must be less than 100' }) pageSize: number = 10; diff --git a/libs/common/src/interfaces/agent-service.interface.ts b/libs/common/src/interfaces/agent-service.interface.ts index 235e00210..26613351d 100644 --- a/libs/common/src/interfaces/agent-service.interface.ts +++ b/libs/common/src/interfaces/agent-service.interface.ts @@ -11,6 +11,7 @@ export interface InvitationMessage { }; outOfBandRecord: OutOfBandRecord; recipientKey?:string + invitationDid?: string }; } diff --git a/libs/common/src/interfaces/connection.interface.ts b/libs/common/src/interfaces/connection.interface.ts index 9ff7c4785..996b48c4c 100644 --- a/libs/common/src/interfaces/connection.interface.ts +++ b/libs/common/src/interfaces/connection.interface.ts @@ -31,5 +31,6 @@ export interface IConnectionsListCount { lastChangedDateTime: Date; lastChangedBy: number; recipientKey?:string; + invitationDid?: string } \ No newline at end of file diff --git a/libs/common/src/interfaces/cred-def.interface.ts b/libs/common/src/interfaces/cred-def.interface.ts index ec6b204fd..dc9a45f0f 100644 --- a/libs/common/src/interfaces/cred-def.interface.ts +++ b/libs/common/src/interfaces/cred-def.interface.ts @@ -1,10 +1,4 @@ -export interface ICredDefDetails { - totalItems: number; - hasNextPage: boolean; - hasPreviousPage: boolean; - nextPage: number; - previousPage: number; - lastPage: number; +export interface ICredDefDetails extends IPaginationDetails{ data: ICredDefData[]; } @@ -18,3 +12,22 @@ export interface ICredDefData { orgId: string; revocable: boolean; } + +export interface IPlatformCredDefDetails { + credDefCount: number; + credDefResult: ICredDefData[]; + } + + export interface IPaginationDetails { + totalItems: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + nextPage: number; + previousPage: number; + lastPage: number; + } + + export interface IPlatformCredDefsData extends IPaginationDetails{ + data: ICredDefData[]; + } + \ 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 cad52d332..5a5d393c1 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -181,6 +181,7 @@ export const ResponseMessages = { NotSaved: 'Error in saving credential definition.', Conflict: 'Credential definition already exists', schemaIdNotFound: 'SchemaLedgerId not found', + isRequired: 'Credential definition Id is required', OrgDidNotFound: 'OrgDid not found', credDefIdNotFound: 'Credential Definition Id not found' } @@ -290,6 +291,7 @@ export const ResponseMessages = { orgAgentTypeNotFound: 'Organization agent type not found', credentialNotPresent: 'credential is required', optionsNotPresent:'options are required', + attributesAreRequired: 'attributes are required', invalidCredentialType:'invalid credential type' } }, @@ -376,6 +378,7 @@ export const ResponseMessages = { invalidEcosystemId: 'Invalid ecosystem Id', invalidTransaction: 'Transaction does not exist', transactionSubmitted: 'Transaction already submitted', + transactionNotSigned: 'Transaction request is not signed', invalidAgentUrl: 'Invalid agent url', EndorsementTransactionNotFoundException: 'Endorsement transaction with status requested not found', OrgOrEcosystemNotFoundExceptionForEndorsementTransaction: 'The endorsement transaction status cant be updated', diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 7e4c028ce..d5af443fd 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -13,6 +13,13 @@ export enum SortFields { ISSUER_ID = 'issuerId' } +export enum CredDefSortFields { + CREATED_DATE_TIME = 'createDateTime', + TAG = 'tag', + LEDGER_ID = 'schemaLedgerId', + CRED_DEF_ID= 'credentialDefinitionId' +} + export enum AgentType { AFJ = 'AFJ', ACAPY = 'ACAPY' diff --git a/libs/prisma-service/prisma/migrations/20240430114046_add_invitation_did/migration.sql b/libs/prisma-service/prisma/migrations/20240430114046_add_invitation_did/migration.sql new file mode 100644 index 000000000..6afe6c21e --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240430114046_add_invitation_did/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "agent_invitations" ADD COLUMN "invitationDid" TEXT; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index bd3cbf946..36a639341 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -198,17 +198,17 @@ model org_agents { } model org_dids { - id String @id @default(uuid()) @db.Uuid - createDateTime DateTime @default(now()) @db.Timestamptz(6) - createdBy String @db.Uuid - lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) - lastChangedBy String @db.Uuid - orgId String @db.Uuid - isPrimaryDid Boolean - did String @db.VarChar(500) + id String @id @default(uuid()) @db.Uuid + createDateTime DateTime @default(now()) @db.Timestamptz(6) + createdBy String @db.Uuid + lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) + lastChangedBy String @db.Uuid + orgId String @db.Uuid + isPrimaryDid Boolean + did String @db.VarChar(500) didDocument Json - orgAgentId String @db.Uuid - org_agents org_agents @relation(fields: [orgAgentId], references: [id]) + orgAgentId String @db.Uuid + org_agents org_agents @relation(fields: [orgAgentId], references: [id]) } model org_agents_type { @@ -309,6 +309,7 @@ model agent_invitations { org_agents org_agents @relation(fields: [agentId], references: [id]) organisation organisation @relation(fields: [orgId], references: [id]) recipientKey String? + invitationDid String? } model connections {