Skip to content

Commit

Permalink
Merge pull request #181 from identity-com/feature/IDCOM-1592__allow_r…
Browse files Browse the repository at this point in the history
…eferences_in_credential_schemas

IDCOM-1592 Updated to check for properties in references schemas
  • Loading branch information
explicit-projects authored Dec 20, 2021
2 parents caf067a + a323f0e commit c5310e3
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 4 deletions.
84 changes: 84 additions & 0 deletions __test__/creds/VerifiableCredential.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ const signAttestationSubject = (subject, xprv, xpub) => {
};
};

class TestSchemaLoader extends CVCSchemaLoader {
// eslint-disable-next-line class-methods-use-this
valid(identifier) {
return /^(claim|credential|type)-(test):.*$/.test(identifier);
}

async loadSchema(identifier) {
try {
// eslint-disable-next-line global-require,import/no-dynamic-require
return require(`../schema/fixtures/${identifier}.schema.json`);
// eslint-disable-next-line no-empty
} catch (e) {
}

return super.loadSchema(identifier);
}
}

describe('Unit tests for Verifiable Credentials', () => {
beforeAll(() => {
schemaLoader.addLoader(new CVCSchemaLoader());
Expand Down Expand Up @@ -1882,3 +1900,69 @@ describe('Signned Verifiable Credentials', () => {
expect(signerVerifier.isSignatureValid(dataOnlyCredential)).toBeTruthy();
});
});
describe('Referenced Schemas for Verifiable Credentials', () => {
beforeAll(() => {
schemaLoader.addLoader(new TestSchemaLoader());
schemaLoader.addLoader(new CVCSchemaLoader());
});

test('Loads a schema the contains a reference', async () => {
const type = await Claim.create('claim-cvc:Document.type-v1', 'passport', '1');
const number = await Claim.create('claim-cvc:Document.number-v1', 'FP12345', '1');
const nameValue = {
givenNames: 'e8qhs4Iak1',
familyNames: 'e8qak1',
otherNames: 'qhs4I',
};
const name = await Claim.create('claim-cvc:Document.name-v1', nameValue, '1');
const gender = await Claim.create('claim-cvc:Document.gender-v1', 'M', '1');
const issueCountry = await Claim.create('claim-cvc:Document.issueCountry-v1', 'Brazil', '1');
const placeOfBirth = await Claim.create('claim-cvc:Document.placeOfBirth-v1', 'Belo Horizonte', '1');
const dateOfBirthValue = identityDateOfBirth;
const dateOfBirth = await Claim.create('claim-cvc:Document.dateOfBirth-v1', dateOfBirthValue, '1');
const dateOfExpiryValue = {
day: 12,
month: 2,
year: 2025,
};
const dateOfExpiry = await Claim.create('claim-cvc:Document.dateOfExpiry-v1', dateOfExpiryValue, '1');
const nationality = await Claim.create('claim-cvc:Document.nationality-v1', 'Brazilian', '1');

const evidencesValue = {
idDocumentFront: {
algorithm: 'sha256',
data: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
},
idDocumentBack: {
algorithm: 'sha256',
data: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
},
selfie: {
algorithm: 'sha256',
data: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
},
};
const evidences = await Claim.create('claim-cvc:Document.evidences-v1', evidencesValue, '1');

const credential = await VC.create(
'credential-test:IdDocument-v1', '', null, [type, number, name, gender,
issueCountry, placeOfBirth, dateOfBirth, dateOfExpiry, nationality, evidences], '1',
);

expect(credential).toBeDefined();
const filtered = credential.filter(['claim-cvc:Document.dateOfBirth-v1']);
expect(filtered).toBeDefined();
});

test('Validates a schema the contains a reference', async () => {
const type = await Claim.create('claim-cvc:Document.type-v1', 'passport', '1');
const number = await Claim.create('claim-cvc:Document.number-v1', 'FP12345', '1');

const createCredential = VC.create(
'credential-test:IdDocument-v1', '', null, [type, number], '1',
);

expect(createCredential).rejects.toThrow('Missing required claim(s): claim-cvc:Document.name-v1, '
+ 'claim-cvc:Document.issueCountry-v1, claim-cvc:Document.dateOfBirth-v1, claim-cvc:Document.evidences-v1');
});
});
12 changes: 12 additions & 0 deletions __test__/schema/fixtures/credential-test:IdDocument-v1.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://identity.com/schemas/credential-test:IdDocument-v1",
"title": "credential-test:IdDocument-v1",
"type": "object",
"allOf": [
{
"$ref": "http://identity.com/schemas/credential-cvc:IdDocument-v2"
}
],
"additionalProperties": false
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@identity.com/credential-commons",
"version": "2.0.1",
"version": "2.0.2",
"author": "Identity.com Community",
"license": "MIT",
"description": "Verifiable Credential and Attestation Library",
Expand Down
44 changes: 41 additions & 3 deletions src/schemas/jsonSchema/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ class SchemaLoader {
definition.depends.push(propertySchema.title);
}

if (schema.properties.claim.required && schema.properties.claim.required.includes(property)) {
const schemaProperties = await this.flattenCredentialSchemaProperties(schema);

if (schemaProperties.claim.required && schemaProperties.claim.required.includes(property)) {
definition.required.push(propertySchema.title);
}
}
Expand All @@ -216,6 +218,40 @@ class SchemaLoader {
}
}

/**
* Flattens the properties of a schema if there are any referenced schemas
* @param schema
* @returns {Promise<*>}
*/
async flattenCredentialSchemaProperties(schema) {
let properties = schema.properties ? schema.properties : {};

if (schema.allOf) {
const promises = schema.allOf.map(async (allOf) => {
if (allOf.$ref) {
const refSchema = await this.loadSchemaFromUri(allOf.$ref);
const refProperties = await this.flattenCredentialSchemaProperties(refSchema);

properties = {
...properties,
...refProperties,
};
}

if (allOf.properties) {
properties = {
...properties,
...allOf.properties,
};
}
});

await Promise.all(promises);
}

return properties;
}

/**
* Adds a credential definition to be backwards compatible with the old schema structure.
*/
Expand All @@ -230,12 +266,14 @@ class SchemaLoader {
definition.transient = true;
}

if (schema.properties.claim.required) {
const properties = await this.flattenCredentialSchemaProperties(schema);

if (properties.claim.required) {
definition.required = [];
}

const references = [];
_.forEach(schema.properties.claim.properties, (vo) => {
_.forEach(properties.claim.properties, (vo) => {
_.forEach(vo.properties, (vi, ki) => {
references.push({ ref: vo.properties[ki].$ref, property: ki });
});
Expand Down

0 comments on commit c5310e3

Please sign in to comment.