diff --git a/packages/core/src/modules/credentials/CredentialUtils.ts b/packages/core/src/modules/credentials/CredentialUtils.ts index 43b4b5653b..2716920d71 100644 --- a/packages/core/src/modules/credentials/CredentialUtils.ts +++ b/packages/core/src/modules/credentials/CredentialUtils.ts @@ -1,5 +1,5 @@ import type { LinkedAttachment } from '../../utils/LinkedAttachment' -import type { CredValues } from 'indy-sdk' +import type { CredValues, Schema } from 'indy-sdk' import { hash as sha256 } from '@stablelib/sha256' import BigNumber from 'bn.js' @@ -175,4 +175,19 @@ export class CredentialUtils { // Check if number is integer and in range of int32 return Number.isInteger(number) && number >= minI32 && number <= maxI32 } + + public static checkAttributesMatch(schema: Schema, credentialPreview: CredentialPreview) { + const schemaAttributes = schema.attrNames + const credAttributes = credentialPreview.attributes.map((a) => a.name) + + const difference = credAttributes + .filter((x) => !schemaAttributes.includes(x)) + .concat(schemaAttributes.filter((x) => !credAttributes.includes(x))) + + if (difference.length > 0) { + throw new AriesFrameworkError( + `The credential preview attributes do not match the schema attributes (difference is: ${difference}, needs: ${schemaAttributes})` + ) + } + } } diff --git a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts index 25c17eb36e..cdab70bbfa 100644 --- a/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts +++ b/packages/core/src/modules/credentials/__tests__/CredentialService.test.ts @@ -37,7 +37,7 @@ import { CredentialMetadataKeys } from '../repository/credentialMetadataTypes' import { CredentialService } from '../services' import { CredentialProblemReportMessage } from './../messages/CredentialProblemReportMessage' -import { credDef, credOffer, credReq } from './fixtures' +import { credDef, credOffer, credReq, schema } from './fixtures' // Mock classes jest.mock('../repository/CredentialRepository') @@ -175,6 +175,7 @@ describe('CredentialService', () => { ) mockFunction(ledgerService.getCredentialDefinition).mockReturnValue(Promise.resolve(credDef)) + mockFunction(ledgerService.getSchema).mockReturnValue(Promise.resolve(schema)) }) describe('createCredentialOffer', () => { @@ -259,6 +260,44 @@ describe('CredentialService', () => { ], }) }) + + test('throw error if credential preview attributes do not match with schema attributes', async () => { + const credentialPreview = CredentialPreview.fromRecord({ + test: 'credential', + error: 'yes', + }) + + expect( + credentialService.createOffer( + { + ...credentialTemplate, + preview: credentialPreview, + }, + connection + ) + ).rejects.toThrowError( + `The credential preview attributes do not match the schema attributes (difference is: test,error,name,age, needs: name,age)` + ) + + const credentialPreviewWithExtra = CredentialPreview.fromRecord({ + test: 'credential', + error: 'yes', + name: 'John', + age: '99', + }) + + await expect( + credentialService.createOffer( + { + ...credentialTemplate, + preview: credentialPreviewWithExtra, + }, + connection + ) + ).rejects.toThrowError( + `The credential preview attributes do not match the schema attributes (difference is: test,error, needs: name,age)` + ) + }) }) describe('processCredentialOffer', () => { diff --git a/packages/core/src/modules/credentials/__tests__/fixtures.ts b/packages/core/src/modules/credentials/__tests__/fixtures.ts index 87757bc90b..b8e5e7451c 100644 --- a/packages/core/src/modules/credentials/__tests__/fixtures.ts +++ b/packages/core/src/modules/credentials/__tests__/fixtures.ts @@ -1,3 +1,5 @@ +import type { Schema } from 'indy-sdk' + export const credDef = { ver: '1.0', id: 'TL1EaPFCZ8Si5aUrqScBDt:3:CL:16:TAG', @@ -49,3 +51,12 @@ export const credReq = { }, nonce: '784158051402761459123237', } + +export const schema: Schema = { + name: 'schema', + attrNames: ['name', 'age'], + id: 'TL1EaPFCZ8Si5aUrqScBDt:2:test-schema-1599055118161:1.0', + seqNo: 989798923653, + ver: '1.0', + version: '1.0', +} diff --git a/packages/core/src/modules/credentials/services/CredentialService.ts b/packages/core/src/modules/credentials/services/CredentialService.ts index 1a39489138..c942b79913 100644 --- a/packages/core/src/modules/credentials/services/CredentialService.ts +++ b/packages/core/src/modules/credentials/services/CredentialService.ts @@ -265,6 +265,10 @@ export class CredentialService { credentialRecord.autoAcceptCredential = credentialTemplate.autoAcceptCredential ?? credentialRecord.autoAcceptCredential + // Check if credential preview attributes match the schema attributes + const schema = await this.ledgerService.getSchema(credOffer.schema_id) + CredentialUtils.checkAttributesMatch(schema, preview) + await this.updateState(credentialRecord, CredentialState.OfferSent) return { message: credentialOfferMessage, credentialRecord } @@ -302,6 +306,10 @@ export class CredentialService { ? CredentialUtils.createAndLinkAttachmentsToPreview(linkedAttachments, preview) : preview + // Check if credential preview attributes match the schema attributes + const schema = await this.ledgerService.getSchema(credOffer.schema_id) + CredentialUtils.checkAttributesMatch(schema, credentialPreview) + // Construct offer message const credentialOfferMessage = new OfferCredentialMessage({ comment, diff --git a/packages/core/tests/credentials-auto-accept.test.ts b/packages/core/tests/credentials-auto-accept.test.ts index 3ebc8a8fa4..bf6b088f84 100644 --- a/packages/core/tests/credentials-auto-accept.test.ts +++ b/packages/core/tests/credentials-auto-accept.test.ts @@ -11,12 +11,15 @@ import testLogger from './logger' const credentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', }) const newCredentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', - lastname: 'Appleseed', + 'x-ray': 'another x-ray value', + profile_picture: 'another profile picture', }) describe('auto accept credentials', () => { @@ -261,6 +264,16 @@ describe('auto accept credentials', () => { name: 'age', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, ], }, 'offers~attach': expect.any(Array), @@ -363,8 +376,14 @@ describe('auto accept credentials', () => { value: '99', }, { - name: 'lastname', - value: 'Appleseed', + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'another x-ray value', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'another profile picture', }, ], }, @@ -422,6 +441,16 @@ describe('auto accept credentials', () => { name: 'age', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, ], }, 'offers~attach': expect.any(Array), @@ -453,16 +482,24 @@ describe('auto accept credentials', () => { '@type': 'https://didcomm.org/issue-credential/1.0/credential-preview', attributes: [ { + 'mime-type': 'text/plain', name: 'name', value: 'John', }, { + 'mime-type': 'text/plain', name: 'age', value: '99', }, { - name: 'lastname', - value: 'Appleseed', + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'another x-ray value', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'another profile picture', }, ], }, diff --git a/packages/core/tests/credentials.test.ts b/packages/core/tests/credentials.test.ts index b371f86079..011c8ccb85 100644 --- a/packages/core/tests/credentials.test.ts +++ b/packages/core/tests/credentials.test.ts @@ -12,6 +12,20 @@ import testLogger from './logger' const credentialPreview = CredentialPreview.fromRecord({ name: 'John', age: '99', + 'x-ray': 'some x-ray', + profile_picture: 'profile picture', +}) + +const credentialPreviewWithoutProfilePicture = CredentialPreview.fromRecord({ + name: 'John', + age: '99', + 'x-ray': 'some x-ray', +}) + +const credentialPreviewWithoutXray = CredentialPreview.fromRecord({ + name: 'John', + age: '99', + profile_picture: 'profile picture', }) describe('credentials', () => { @@ -80,6 +94,16 @@ describe('credentials', () => { 'mime-type': 'text/plain', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, ], }, 'offers~attach': expect.any(Array), @@ -180,6 +204,16 @@ describe('credentials', () => { 'mime-type': 'text/plain', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, ], }, 'offers~attach': expect.any(Array), @@ -251,7 +285,7 @@ describe('credentials', () => { test('Alice starts with credential proposal, with attachments, to Faber', async () => { testLogger.test('Alice sends credential proposal to Faber') let aliceCredentialRecord = await aliceAgent.credentials.proposeCredential(aliceConnection.id, { - credentialProposal: credentialPreview, + credentialProposal: credentialPreviewWithoutProfilePicture, credentialDefinitionId: credDefId, linkedAttachments: [ new LinkedAttachment({ @@ -300,6 +334,11 @@ describe('credentials', () => { 'mime-type': 'text/plain', value: '99', }, + { + name: 'x-ray', + 'mime-type': 'text/plain', + value: 'some x-ray', + }, { name: 'profile_picture', 'mime-type': 'image/png', @@ -374,7 +413,7 @@ describe('credentials', () => { test('Faber starts with credential, with attachments, offer to Alice', async () => { testLogger.test('Faber sends credential offer to Alice') faberCredentialRecord = await faberAgent.credentials.offerCredential(faberConnection.id, { - preview: credentialPreview, + preview: credentialPreviewWithoutXray, credentialDefinitionId: credDefId, comment: 'some comment about credential', linkedAttachments: [ @@ -414,6 +453,11 @@ describe('credentials', () => { 'mime-type': 'text/plain', value: '99', }, + { + name: 'profile_picture', + 'mime-type': 'text/plain', + value: 'profile picture', + }, { name: 'x-ray', value: 'hl:zQmdsy1SSKztP7CGRiP2SuMV41Xxy9g69QswhUiSeo3d4pH',