add @context value check probe

This commit is contained in:
Markus Gylling 2022-08-31 20:30:10 +02:00
parent 5e6a277185
commit bbcfac14fc
5 changed files with 122 additions and 8 deletions

View File

@ -30,6 +30,8 @@ import org.oneedtech.inspect.util.resource.ResourceType;
import org.oneedtech.inspect.util.resource.UriResource; import org.oneedtech.inspect.util.resource.UriResource;
import org.oneedtech.inspect.util.resource.context.ResourceContext; import org.oneedtech.inspect.util.resource.context.ResourceContext;
import org.oneedtech.inspect.util.spec.Specification; import org.oneedtech.inspect.util.spec.Specification;
import org.oneedtech.inspect.vc.Credential.Type;
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
import org.oneedtech.inspect.vc.probe.CredentialParseProbe; import org.oneedtech.inspect.vc.probe.CredentialParseProbe;
import org.oneedtech.inspect.vc.probe.ExpirationVerifierProbe; import org.oneedtech.inspect.vc.probe.ExpirationVerifierProbe;
import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe; import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe;
@ -90,14 +92,15 @@ public class OB30Inspector extends VCInspector {
//we expect the above to place a generated object in the context //we expect the above to place a generated object in the context
Credential crd = ctx.getGeneratedObject(Credential.ID); Credential crd = ctx.getGeneratedObject(Credential.ID);
//TODO check context IRIs? the schema doesnt do this
//TODO new check: that subject @id or IdentityObject is available (at least one is the req) //TODO new check: that subject @id or IdentityObject is available (at least one is the req)
//type property //context and type properties
probeCount++; Credential.Type type = Type.OpenBadgeCredential;
accumulator.add(new TypePropertyProbe(OpenBadgeCredential).run(crd.getJson(), ctx)); for(Probe<JsonNode> probe : List.of(new ContextPropertyProbe(type), new TypePropertyProbe(type))) {
if(broken(accumulator)) return abort(ctx, accumulator, probeCount); probeCount++;
accumulator.add(probe.run(crd.getJson(), ctx));
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
}
//canonical schema and inline schemata //canonical schema and inline schemata
SchemaKey schema = crd.getSchemaKey().orElseThrow(); SchemaKey schema = crd.getSchemaKey().orElseThrow();

View File

@ -0,0 +1,63 @@
package org.oneedtech.inspect.vc.probe;
import static org.oneedtech.inspect.util.code.Defensives.checkNotNull;
import java.util.List;
import java.util.Map;
import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.Credential;
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.ImmutableMap;
/**
* A Probe that verifies a credential's context property.
*
* @author mgylling
*/
public class ContextPropertyProbe extends Probe<JsonNode> {
private final Credential.Type type;
public ContextPropertyProbe(Credential.Type type) {
super(ID);
this.type = checkNotNull(type);
}
@Override
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
ArrayNode contextNode = (ArrayNode) root.get("@context");
if (contextNode == null) {
return fatal("No @context property", ctx);
}
List<String> expected = values.get(type);
if(expected == null) {
return fatal(type.name() + " not recognized", ctx);
}
List<String> given = JsonNodeUtil.asStringList(contextNode);
int pos = 0;
for(String uri : expected) {
if((given.size() < pos+1) || !given.get(pos).equals(uri)) {
return error("missing required @context uri " + uri + " at position " + (pos+1), ctx);
}
pos++;
}
return success(ctx);
}
private final static Map<Credential.Type, List<String>> values = new ImmutableMap.Builder<Credential.Type, List<String>>()
.put(Credential.Type.OpenBadgeCredential,
List.of("https://www.w3.org/2018/credentials/v1",
"https://imsglobal.github.io/openbadges-specification/context.json")) //TODO will change (https://purl.imsglobal.org/spec/ob/v3p0/context/ob_v3p0.jsonld)
.build();
public static final String ID = ContextPropertyProbe.class.getSimpleName();
}

View File

@ -10,6 +10,7 @@ import org.oneedtech.inspect.core.Inspector.Behavior;
import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe; import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
import org.oneedtech.inspect.core.report.Report; import org.oneedtech.inspect.core.report.Report;
import org.oneedtech.inspect.test.PrintHelper; import org.oneedtech.inspect.test.PrintHelper;
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
import org.oneedtech.inspect.vc.probe.ExpirationVerifierProbe; import org.oneedtech.inspect.vc.probe.ExpirationVerifierProbe;
import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe; import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe;
import org.oneedtech.inspect.vc.probe.IssuanceVerifierProbe; import org.oneedtech.inspect.vc.probe.IssuanceVerifierProbe;
@ -109,6 +110,17 @@ public class OB30Tests {
}); });
} }
@Test
void testSimpleJsonContextError() {
//removed one of the reqd context uris
assertDoesNotThrow(()->{
Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_ERR_CONTEXT.asFileResource());
if(verbose) PrintHelper.print(report, true);
assertInvalid(report);
assertHasProbeID(report, ContextPropertyProbe.ID, true);
});
}
@Test @Test
void testSimpleJsonSchemaError() throws Exception { void testSimpleJsonSchemaError() throws Exception {
//issuer removed //issuer removed

View File

@ -17,6 +17,7 @@ public class Samples {
public final static Sample SIMPLE_JSON_EXPIRED = new Sample("ob30/simple-err-expired.json", false); public final static Sample SIMPLE_JSON_EXPIRED = new Sample("ob30/simple-err-expired.json", false);
public final static Sample SIMPLE_JSON_ISSUED = new Sample("ob30/simple-err-issued.json", false); public final static Sample SIMPLE_JSON_ISSUED = new Sample("ob30/simple-err-issued.json", false);
public final static Sample SIMPLE_JSON_ISSUER = new Sample("ob30/simple-err-issuer.json", false); public final static Sample SIMPLE_JSON_ISSUER = new Sample("ob30/simple-err-issuer.json", false);
public final static Sample SIMPLE_JSON_ERR_CONTEXT = new Sample("ob30/simple-err-context.json", false);
} }
public static final class PNG { public static final class PNG {
public final static Sample SIMPLE_JWT_PNG = new Sample("ob30/simple-jwt.png", true); public final static Sample SIMPLE_JWT_PNG = new Sample("ob30/simple-jwt.png", true);

View File

@ -0,0 +1,35 @@
{
"@context": [
"https://imsglobal.github.io/openbadges-specification/context.json",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "http://example.edu/credentials/3732",
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"issuer": {
"id": "https://example.edu/issuers/565049",
"type": [
"Profile"
],
"name": "Example University"
},
"issuanceDate": "2010-01-01T00:00:00Z",
"name": "Example University Degree",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"type": [
"AchievementSubject"
]
},
"proof": [
{
"type": "Ed25519Signature2020",
"created": "2022-06-28T16:28:36Z",
"verificationMethod": "did:key:z6MkkUD3J14nkYzn46QeuaVSnp7dF85QJKwKvJvfsjx79aXj",
"proofPurpose": "assertionMethod",
"proofValue": "z3MUt2ZuU8Byqivxh6GphEM65AFYyNaGYibm97xLTafM7uGufZQLKvJR8itZwxKskvtFM3CUty46v26DZidMNoQnM"
}
]
}