Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support to select multiple cred-def and connections for verification(BE) #646

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 45 additions & 16 deletions apps/api-gateway/src/verification/dto/request-proof.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,25 @@ export class ProofRequestAttribute {
@ApiPropertyOptional()
@IsString()
@IsOptional()
@IsNotEmpty({ message: 'schemaId is required.' })
schemaId?: string;

@ApiPropertyOptional()
@ValidateIf((obj) => obj.value !== undefined)
@IsString()
@IsOptional()
@IsNotEmpty({ message: 'condition is required.' })
condition?: string;

@ValidateIf((obj) => obj.condition !== undefined)
@ApiPropertyOptional()
@IsOptional()
@IsNotEmpty({ message: 'value is required.' })
@IsNumberString({}, { message: 'Value must be a number' })
value?: string;

@ApiPropertyOptional()
@IsString()
@IsOptional()
@IsNotEmpty({ message: 'credDefId is required.' })
credDefId?: string;
}

Expand Down Expand Up @@ -68,26 +70,16 @@ class ProofPayload {
protocolVersion: string;
}

export class RequestProofDto extends ProofPayload {
class PresentationPayload extends ProofPayload {

@ApiProperty()
@IsString()
@Transform(({ value }) => trim(value))
@Transform(({ value }) => toLowerCase(value))
@IsNotEmpty({ message: 'connectionId is required.' })
connectionId: string;

@ApiProperty({
'example': [
{
attributeName: 'attributeName',
condition: '>=',
value: 'predicates',
credDefId: 'string',
schemaId: 'string'
}
],
type: () => [ProofRequestAttribute]
})
@ApiProperty()
@IsArray({ message: 'attributes must be in array' })
@ValidateNested()
@IsObject({ each: true })
Expand All @@ -100,7 +92,7 @@ export class RequestProofDto extends ProofPayload {
@IsString({ message: 'comment must be in string' })
comment: string;

orgId: string;
orgId: string;

@ApiPropertyOptional()
@IsString({ message: 'auto accept proof must be in string' })
Expand All @@ -110,6 +102,43 @@ export class RequestProofDto extends ProofPayload {
message: `Invalid auto accept proof. It should be one of: ${Object.values(AutoAccept).join(', ')}`
})
autoAcceptProof: AutoAccept;


}
export class RequestProofDto {

@ApiProperty({
'example': [
{
goalCode: 'string',
parentThreadId: 'string',
willConfirm: true,
protocolVersion: 'v1',
connectionId: 'string',
attributes: [
{
attributeName: 'attributeName',
condition: '>=',
value: 'predicates',
credDefId: 'string',
schemaId: 'string'
}
],
comment: 'string',
autoAcceptProof: 'always'
}

]
})
@IsArray({ message: 'attributes must be in array' })
@ArrayMaxSize(Number(process.env.OOB_BATCH_SIZE), { message: `Limit reached (${process.env.OOB_BATCH_SIZE} connections max).`})
@ValidateNested()
@IsObject({ each: true })
@IsNotEmpty({ message: 'please provide valid attributes' })
@Type(() => PresentationPayload)
presentationData: PresentationPayload[];

orgId: string;
}

export class OutOfBandRequestProof extends ProofPayload {
Expand Down
26 changes: 17 additions & 9 deletions apps/api-gateway/src/verification/verification.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,24 @@ export class VerificationController {
@Body() requestProof: RequestProofDto
): Promise<Response> {

const attributeArray = [];
for (const attrData of requestProof.attributes) {
if (0 === attributeArray.length) {
attributeArray.push(Object.values(attrData)[0]);
} else if (!attributeArray.includes(Object.values(attrData)[0])) {
attributeArray.push(Object.values(attrData)[0]);
} else {
throw new BadRequestException('Please provide unique attribute names');
}
let attributeArray = [];
const attributeError = [];

requestProof.presentationData.forEach((presentation, position) => {
presentation.attributes.forEach((attrData, index) => {

if (0 === attributeArray.length) {
attributeArray.push(Object.values(attrData)[0]);
} else if (!attributeArray.includes(Object.values(attrData)[0])) {
attributeArray.push(Object.values(attrData)[0]);
} else {
attributeError.push(`Duplicate attribute(s) found in presentationData at position [${position}] in attributes at position [${index}]`);
}
});
attributeArray = [];
});
if (0 < attributeError.length) {
throw new BadRequestException(attributeError);
}

requestProof.orgId = orgId;
Expand Down
6 changes: 6 additions & 0 deletions apps/verification/src/interfaces/verification.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ interface IProofRequestAttribute {
credentialName: string;
}

export interface IPresentationPayload {
connectionId?: string;
attributes: IProofRequestAttribute[];

}
export interface IRequestProof {
orgId: string;
connectionId?: string;
attributes: IProofRequestAttribute[];
presentationData: IPresentationPayload[];
comment: string;
autoAcceptProof: AutoAccept;
protocolVersion?: string;
Expand Down
2 changes: 1 addition & 1 deletion apps/verification/src/verification.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class VerificationController {
* @returns Requested proof presentation details
*/
@MessagePattern({ cmd: 'send-proof-request' })
async sendProofRequest(payload: { requestProof: IRequestProof, user: IUserRequest }): Promise<string> {
async sendProofRequest(payload: { requestProof: IRequestProof, user: IUserRequest }): Promise<string[]> {
return this.verificationService.sendProofRequest(payload.requestProof);
}

Expand Down
109 changes: 57 additions & 52 deletions apps/verification/src/verification.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable camelcase */
import { BadRequestException, HttpException, Inject, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common';
import { 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, IInvitation} from './interfaces/verification.interface';
import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IInvitation, IPresentationPayload} 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';
Expand Down Expand Up @@ -169,10 +169,16 @@ export class VerificationService {
* @param orgId
* @returns Requested proof presentation details
*/
async sendProofRequest(requestProof: IRequestProof): Promise<string> {
async sendProofRequest(requestProof: IRequestProof): Promise<string[]> {
try {
const comment = requestProof.comment ? requestProof.comment : '';

const { presentationData } = requestProof;

const presentationResponse = [];

for (const presentation of presentationData) {

let proofRequestPayload: ISendProofRequestPayload = {
protocolVersion: '',
comment: '',
Expand All @@ -191,24 +197,22 @@ export class VerificationService {
parentThreadId: '',
willConfirm: false
};

const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(requestProof);
const { connectionId } = presentation;
const { requestedAttributes, requestedPredicates } = await this._proofRequestPayload(presentation);

proofRequestPayload = {
protocolVersion: requestProof.protocolVersion ? requestProof.protocolVersion : 'v1',
comment,
connectionId: requestProof.connectionId,
connectionId,
proofFormats: {
indy: {
name: 'Proof Request',
version: '1.0',
// eslint-disable-next-line camelcase
requested_attributes: requestedAttributes,
// eslint-disable-next-line camelcase
requested_predicates: requestedPredicates
}
},
autoAcceptProof: requestProof.autoAcceptProof ? requestProof.autoAcceptProof : 'never',
autoAcceptProof: requestProof.autoAcceptProof ? requestProof.autoAcceptProof : 'always',
goalCode: requestProof.goalCode || undefined,
parentThreadId: requestProof.parentThreadId || undefined,
willConfirm: requestProof.willConfirm || undefined
Expand All @@ -223,8 +227,11 @@ export class VerificationService {
const payload = { orgId: requestProof.orgId, url, proofRequestPayload };

const getProofPresentationById = await this._sendProofRequest(payload);
return getProofPresentationById?.response;
} catch (error) {
presentationResponse.push(getProofPresentationById?.response);
}

return presentationResponse;
} catch (error) {
this.logger.error(`[verifyPresentation] - error in verify presentation : ${JSON.stringify(error)}`);
this.verificationErrorHandling(error);

Expand Down Expand Up @@ -530,58 +537,56 @@ export class VerificationService {
}
}

async _proofRequestPayload(proofRequestpayload: IRequestProof): Promise<{
async _proofRequestPayload(proofRequestpayload: IPresentationPayload): Promise<{
requestedAttributes;
requestedPredicates;
}> {
try {
let requestedAttributes = {};
const requestedPredicates = {};
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 [
attributeReferent,
{
[attributeKey]: attributeElement,

requestedAttributes = Object.fromEntries(proofRequestpayload?.attributes?.map((attribute, attrIndex) => {

const attributeElement = attribute.attributeName || attribute.attributeNames;
const attributeReferent = `additionalProp${attrIndex + 1}`;
const attributeKey = attribute.attributeName ? 'name' : 'names';

if (!attribute.condition && !attribute.value) {

return [
attributeReferent,
{
[attributeKey]: attributeElement,
restrictions: [
{
cred_def_id: attribute?.credDefId ? attribute?.credDefId : undefined,
schema_id: attribute?.schemaId
}
]
}
];

} else {
requestedPredicates[attributeReferent] = {
p_type: attribute.condition,
name: attributeElement,
p_value: parseInt(attribute.value),
restrictions: [
{
cred_def_id: proofRequestpayload.attributes[index].credDefId ? proofRequestpayload.attributes[index].credDefId : undefined,
schema_id: proofRequestpayload.attributes[index].schemaId
cred_def_id: attribute?.credDefId ? attribute?.credDefId : undefined,
schema_id: attribute?.schemaId
}
]
}
];
} else {
requestedPredicates[attributeReferent] = {
p_type: attribute.condition,
name: attributeElement,
p_value: parseInt(attribute.value),
restrictions: [
{
cred_def_id: proofRequestpayload.attributes[index].credDefId ? proofRequestpayload.attributes[index].credDefId : undefined,
schema_id: proofRequestpayload.attributes[index].schemaId
}
]
};
}

return [attributeReferent];
}));
};
}

return [attributeReferent];
}));

return {
requestedAttributes,
requestedPredicates
};
} else {
throw new BadRequestException(ResponseMessages.verification.error.proofNotSend);
}
return {
requestedAttributes,
requestedPredicates
};
} catch (error) {
this.logger.error(`[proofRequestPayload] - error in proof request payload : ${JSON.stringify(error)}`);
throw new RpcException(error.response ? error.response : error);
Expand Down