Set new context keys and use the new credentials builder

This commit is contained in:
Xavi Aracil 2022-11-22 18:04:29 +01:00
parent 6c9485cf02
commit e56739370f

View File

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