diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java index 96e4236..c90a864 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java @@ -5,7 +5,7 @@ import static org.oneedtech.inspect.core.Inspector.Behavior.RESET_CACHES_ON_RUN; import static org.oneedtech.inspect.core.report.ReportUtil.onProbeException; import static org.oneedtech.inspect.util.code.Defensives.*; import static org.oneedtech.inspect.util.json.ObjectMapperCache.Config.DEFAULT; -import static org.oneedtech.inspect.vc.Credential.CREDENTIAL_KEY; +import static org.oneedtech.inspect.vc.AbstractBaseCredential.CREDENTIAL_KEY; import static org.oneedtech.inspect.vc.Credential.ProofType.EXTERNAL; import static org.oneedtech.inspect.vc.payload.PayloadParser.fromJwt; import static org.oneedtech.inspect.vc.util.JsonNodeUtil.asNodeList; @@ -35,6 +35,8 @@ import org.oneedtech.inspect.util.resource.UriResource; import org.oneedtech.inspect.util.resource.context.ResourceContext; import org.oneedtech.inspect.util.spec.Specification; import org.oneedtech.inspect.vc.Credential.Type; +import org.oneedtech.inspect.vc.payload.PngParser; +import org.oneedtech.inspect.vc.payload.SvgParser; import org.oneedtech.inspect.vc.probe.ContextPropertyProbe; import org.oneedtech.inspect.vc.probe.CredentialParseProbe; import org.oneedtech.inspect.vc.probe.CredentialSubjectProbe; @@ -57,111 +59,118 @@ import com.google.common.collect.ImmutableList; */ public class OB30Inspector extends VCInspector implements SubInspector { protected final List> userProbes; - - protected OB30Inspector(OB30Inspector.Builder builder) { - super(builder); - this.userProbes = ImmutableList.copyOf(builder.probes); + + protected OB30Inspector(OB30Inspector.Builder builder) { + super(builder); + this.userProbes = ImmutableList.copyOf(builder.probes); } - + //https://docs.google.com/document/d/1_imUl2K-5tMib0AUxwA9CWb0Ap1b3qif0sXydih68J0/edit# //https://imsglobal.github.io/openbadges-specification/ob_v3p0.html#verificaton-and-validation - + /* * This inspector supports both standalone openbadge verification, as well as verification of - * AchievementCredentials embedded in e.g. CLR. - * + * AchievementCredentials embedded in e.g. CLR. + * * When verifying a standalone AchievementCredential, call the run(Resource) method. When verifying - * an embedded AchievementCredential, call the run(Resource, Map) method. + * an embedded AchievementCredential, call the run(Resource, Map) method. */ - + @Override - public Report run(Resource resource) { + public Report run(Resource resource) { super.check(resource); //TODO because URIs, this should be a fetch and cache - + if(getBehavior(RESET_CACHES_ON_RUN) == TRUE) { JsonSchemaCache.reset(); CachingDocumentLoader.reset(); } - + ObjectMapper mapper = ObjectMapperCache.get(DEFAULT); JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper); - + RunContext ctx = new RunContext.Builder() .put(this) .put(resource) .put(Key.JACKSON_OBJECTMAPPER, mapper) .put(Key.JSONPATH_EVALUATOR, jsonPath) - .build(); - + .put(Key.GENERATED_OBJECT_BUILDER, new Credential.Builder()) + .put(Key.PNG_CREDENTIAL_KEY, PngParser.Keys.OB30) + .put(Key.SVG_CREDENTIAL_QNAME, SvgParser.QNames.OB30) + .build(); + List accumulator = new ArrayList<>(); int probeCount = 0; - - try { + + try { //detect type (png, svg, json, jwt) and extract json data probeCount++; - accumulator.add(new CredentialParseProbe().run(resource, ctx)); + accumulator.add(new CredentialParseProbe().run(resource, ctx)); if(broken(accumulator, true)) return abort(ctx, accumulator, probeCount); - - //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 ob = ctx.getGeneratedObject(Credential.ID); - + //call the subinspector method of this Report subReport = this.run(resource, Map.of(Credential.CREDENTIAL_KEY, ob)); probeCount += subReport.getSummary().getTotalRun(); accumulator.add(subReport); - + //finally, run any user-added probes for(Probe probe : userProbes) { probeCount++; accumulator.add(probe.run(ob, ctx)); } - + } catch (Exception e) { accumulator.add(onProbeException(Probe.ID.NO_UNCAUGHT_EXCEPTIONS, resource, e)); } - - return new Report(ctx, new ReportItems(accumulator), probeCount); + + return new Report(ctx, new ReportItems(accumulator), probeCount); } - + @Override public Report run(Resource resource, Map parentObjects) { - + Credential ob = checkNotNull((Credential)parentObjects.get(CREDENTIAL_KEY)); - + ObjectMapper mapper = ObjectMapperCache.get(DEFAULT); JsonPathEvaluator jsonPath = new JsonPathEvaluator(mapper); + Credential.Builder credentialBuilder = new Credential.Builder(); RunContext ctx = new RunContext.Builder() .put(this) .put(resource) .put(Key.JACKSON_OBJECTMAPPER, mapper) .put(Key.JSONPATH_EVALUATOR, jsonPath) + .put(Key.GENERATED_OBJECT_BUILDER, credentialBuilder) + .put(Key.PNG_CREDENTIAL_KEY, PngParser.Keys.OB30) + .put(Key.SVG_CREDENTIAL_QNAME, SvgParser.QNames.OB30) .build(); - + List accumulator = new ArrayList<>(); int probeCount = 0; - + try { - + //context and type properties Credential.Type type = Type.OpenBadgeCredential; - for(Probe probe : List.of(new ContextPropertyProbe(type), new TypePropertyProbe(type))) { + for(Probe probe : List.of(new ContextPropertyProbe(type), new TypePropertyProbe(type))) { probeCount++; accumulator.add(probe.run(ob.getJson(), ctx)); if(broken(accumulator)) return abort(ctx, accumulator, probeCount); } - + //canonical schema and inline schemata SchemaKey schema = ob.getSchemaKey().orElseThrow(); - for(Probe probe : List.of(new JsonSchemaProbe(schema), new InlineJsonSchemaProbe(schema))) { + for(Probe probe : List.of(new JsonSchemaProbe(schema), new InlineJsonSchemaProbe(schema))) { probeCount++; accumulator.add(probe.run(ob.getJson(), ctx)); if(broken(accumulator)) return abort(ctx, accumulator, probeCount); } - - //credentialSubject + + //credentialSubject probeCount++; accumulator.add(new CredentialSubjectProbe().run(ob.getJson(), ctx)); - + //signatures, proofs probeCount++; if(ob.getProofType() == EXTERNAL){ @@ -169,57 +178,57 @@ public class OB30Inspector extends VCInspector implements SubInspector { accumulator.add(new ExternalProofProbe().run(ob, ctx)); } else { //The credential not contained in a jwt, must have an internal proof. - accumulator.add(new EmbeddedProofProbe().run(ob, ctx)); + accumulator.add(new EmbeddedProofProbe().run(ob, ctx)); } if(broken(accumulator)) return abort(ctx, accumulator, probeCount); - + //check refresh service if we are not already refreshed probeCount++; if(resource.getContext().get(REFRESHED) != TRUE) { - Optional newID = checkRefreshService(ob, ctx); - if(newID.isPresent()) { + Optional newID = checkRefreshService(ob, ctx); + if(newID.isPresent()) { return this.run( new UriResource(new URI(newID.get())) .setContext(new ResourceContext(REFRESHED, TRUE))); } } - + //revocation, expiration and issuance - for(Probe probe : List.of(new RevocationListProbe(), - new ExpirationProbe(), new IssuanceProbe())) { + for(Probe probe : List.of(new RevocationListProbe(), + new ExpirationProbe(), new IssuanceProbe())) { probeCount++; accumulator.add(probe.run(ob, ctx)); if(broken(accumulator)) return abort(ctx, accumulator, probeCount); } - - //embedded endorsements - EndorsementInspector endorsementInspector = new EndorsementInspector.Builder().build(); - + + //embedded endorsements + EndorsementInspector endorsementInspector = new EndorsementInspector.Builder().build(); + List endorsements = asNodeList(ob.getJson(), "$..endorsement", jsonPath); for(JsonNode node : endorsements) { probeCount++; - Credential endorsement = new Credential(resource, node); + Credential endorsement = credentialBuilder.resource(resource).jsonData(node).build(); accumulator.add(endorsementInspector.run(resource, Map.of(CREDENTIAL_KEY, endorsement))); - } - - //embedded jwt endorsements + } + + //embedded jwt endorsements endorsements = asNodeList(ob.getJson(), "$..endorsementJwt", jsonPath); for(JsonNode node : endorsements) { probeCount++; String jwt = node.asText(); JsonNode vcNode = fromJwt(jwt, ctx); - Credential endorsement = new Credential(resource, vcNode, jwt); + Credential endorsement = credentialBuilder.resource(resource).jsonData(node).jwt(jwt).build(); accumulator.add(endorsementInspector.run(resource, Map.of(CREDENTIAL_KEY, endorsement))); } - + } catch (Exception e) { accumulator.add(onProbeException(Probe.ID.NO_UNCAUGHT_EXCEPTIONS, resource, e)); } - - return new Report(ctx, new ReportItems(accumulator), probeCount); - + + return new Report(ctx, new ReportItems(accumulator), probeCount); + } - + public static class Builder extends VCInspector.Builder { @SuppressWarnings("unchecked") @Override @@ -228,5 +237,5 @@ public class OB30Inspector extends VCInspector implements SubInspector { set(ResourceType.OPENBADGE); return new OB30Inspector(this); } - } + } } \ No newline at end of file