Skip to content

Commit

Permalink
adjust schema validation interface to enable short circuit
Browse files Browse the repository at this point in the history
  • Loading branch information
OR13 committed Jul 2, 2024
1 parent d190912 commit 607885d
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 6 deletions.
4 changes: 3 additions & 1 deletion src/cr1/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,12 @@ export type ConformanceWarningMessage = {
reference: string
}

export type SchemaValidation = 'succeeded' | 'failed' | 'ignored'

export type ValidationResult = {
valid: boolean
content: VerifiableCredential
schema: Record<string, { valid: boolean, errors?: JsonSchemaError[] }>
schema: Record<string, { validation?: SchemaValidation, errors?: JsonSchemaError[], }>
status: Record<string, { valid: boolean, purpose: string, errors?: StatusListError[] }>
warnings: ConformanceWarningMessage[]
}
Expand Down
6 changes: 5 additions & 1 deletion src/cr1/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export const validator = ({ resolver }: RequestValidator) => {
id: schema.id,
type: 'application/schema+json',
})
if (credentialSchema === true) {
validation.schema[schema.id] = { validation: 'ignored' } as any
continue;
}
const schemaContent = decoder.decode(credentialSchema.content)
const parsedSchemaContent = JSON.parse(schemaContent)
let valid: any;
Expand All @@ -58,7 +62,7 @@ export const validator = ({ resolver }: RequestValidator) => {
} catch (e) {
valid = false
}
validation.schema[schema.id] = { valid }
validation.schema[schema.id] = { validation: valid ? 'succeeded' : 'failed' }
if (!valid) {
validation.valid = false
validation.schema[schema.id].errors = compiledSchemaValidator.errors as JsonSchemaError[]
Expand Down
2 changes: 1 addition & 1 deletion test/json-schema-tests/better-schema-errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ credentialSubject:
expect(validation1.valid).toBe(false);
expect(validation1.schema).toEqual({
"https://vendor.example/api/schemas/product-passport": {
"valid": false,
"validation": "failed",
"errors": [
{
"instancePath": "/credentialSubject",
Expand Down
100 changes: 100 additions & 0 deletions test/json-schema-tests/optional-schema-validation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as jose from "jose";
import moment from "moment";

import * as transmute from "../../src";

const alg = `ES256`;
const issuer = `did:example:123`;
const baseURL = `https://vendor.example/api`;

let publicKey: any;
let issued: any;

beforeAll(async () => {
const privateKey = await transmute.key.generate({
alg,
type: "application/jwk+json",
});
publicKey = await transmute.key.publicFromPrivate({
type: "application/jwk+json",
content: privateKey,
});
issued = await transmute
.issuer({
alg,
type: "application/vc+ld+json+jwt",
signer: {
sign: async (bytes: Uint8Array) => {
const jws = await new jose.CompactSign(bytes)
.setProtectedHeader({ kid: `${issuer}#key-42`, alg })
.sign(
await transmute.key.importKeyLike({
type: "application/jwk+json",
content: privateKey,
})
);
return transmute.text.encoder.encode(jws);
},
},
})
.issue({
claimset: transmute.text.encoder.encode(`
"@context":
- https://www.w3.org/ns/credentials/v2
- ${baseURL}/context/v2
id: ${baseURL}/credentials/3732
type:
- VerifiableCredential
- ExampleDegreeCredential
issuer:
id: ${issuer}
name: "Example University"
validFrom: ${moment().toISOString()}
credentialSchema:
id: ${baseURL}/schemas/product-passport
type: JsonSchema
credentialSubject:
id: did:example:ebfeb1f712ebc6f1c276e12ec21
unexpectedProperty: unexpectedValue
degree:
type: ExampleBachelorDegree
subtype: Bachelor of Science and Arts
`),
});
})

it("can disable schema validation", async () => {
const validator = await transmute.validator({
resolver: {
resolve: async (opts: any) => {
// console.log(opts)
const { id, type, content } = opts
// Resolve external resources according to verifier policy
// In this case, we return inline exampes...
if (id === `${baseURL}/schemas/product-passport`) {
return true; // resolving the special case "true" ignores validation
}
if (content != undefined && type === `application/vc+ld+json+jwt`) {
const { kid } = jose.decodeProtectedHeader(
transmute.text.decoder.decode(content)
);
// lookup public key on a resolver
if (kid === `did:example:123#key-42`) {
return {
type: "application/jwk+json",
content: publicKey,
};
}
}
throw new Error("Resolver option not supported.");
},
},
});
const validation1 = await validator.validate({
type: "application/vc+ld+json+jwt",
content: issued,
});
expect(validation1.valid).toBe(true);
// console.log(JSON.stringify(validation1, null, 2))
expect(validation1.schema['https://vendor.example/api/schemas/product-passport'].validation).toBe('ignored')
});
2 changes: 1 addition & 1 deletion test/jwt-product-passports/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ credentialSubject:
content: issued,
})
expect(validated.valid).toBe(true)
expect(validated.schema[`${baseURL}/schemas/product-passport`].valid).toBe(true)
expect(validated.schema[`${baseURL}/schemas/product-passport`].validation).toBe('succeeded')
expect(validated.status[`${baseURL}/credentials/status/3#${revocationIndex}`].valid).toBe(false)
expect(validated.status[`${baseURL}/credentials/status/4#${suspensionIndex}`].valid).toBe(false)

Expand Down
4 changes: 2 additions & 2 deletions test/w3c-cr-1/3-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ credentialSubject:
}),
})
expect(validation.valid).toBe(true);
expect(validation.schema).toEqual({ 'https://issuer.example/schemas/42': { valid: true } });
expect(validation.schema).toEqual({ 'https://issuer.example/schemas/42': { validation: 'succeeded' } });
})

it('failure', async () => {
Expand Down Expand Up @@ -157,7 +157,7 @@ credentialSubject:
expect(validation.valid).toBe(false);
expect(validation.schema).toEqual({
"https://issuer.example/schemas/52": {
"valid": false,
"validation": 'failed',
"errors": [
{
"instancePath": "/credentialSubject/id",
Expand Down

0 comments on commit 607885d

Please sign in to comment.