Credential renamed to VerifiableCredential
This commit is contained in:
parent
0e077c7256
commit
3ab9c52300
@ -5,8 +5,8 @@ import static org.oneedtech.inspect.core.probe.RunContext.Key.*;
|
|||||||
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.checkNotNull;
|
import static org.oneedtech.inspect.util.code.Defensives.checkNotNull;
|
||||||
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.VerifiableCredential.ProofType.EXTERNAL;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -25,7 +25,7 @@ import org.oneedtech.inspect.util.json.ObjectMapperCache;
|
|||||||
import org.oneedtech.inspect.util.resource.Resource;
|
import org.oneedtech.inspect.util.resource.Resource;
|
||||||
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.vc.Credential.Type;
|
import org.oneedtech.inspect.vc.VerifiableCredential.Type;
|
||||||
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
|
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
|
||||||
import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe;
|
import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe;
|
||||||
import org.oneedtech.inspect.vc.probe.ExpirationProbe;
|
import org.oneedtech.inspect.vc.probe.ExpirationProbe;
|
||||||
@ -39,32 +39,32 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An inspector for EndorsementCredential objects.
|
* An inspector for EndorsementCredential objects.
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class EndorsementInspector extends VCInspector implements SubInspector {
|
public class EndorsementInspector extends VCInspector implements SubInspector {
|
||||||
|
|
||||||
protected <B extends VCInspector.Builder<?>> EndorsementInspector(B builder) {
|
protected <B extends VCInspector.Builder<?>> EndorsementInspector(B builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Report run(Resource resource, Map<String, GeneratedObject> parentObjects) {
|
public Report run(Resource resource, Map<String, GeneratedObject> parentObjects) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The resource param is the top-level credential that embeds the endorsement, we
|
* The resource param is the top-level credential that embeds the endorsement, we
|
||||||
* expect parentObjects to provide a pointer to the JsonNode we should check.
|
* expect parentObjects to provide a pointer to the JsonNode we should check.
|
||||||
*
|
*
|
||||||
* The parent inspector is responsible to decode away possible jwt-ness, so that
|
* The parent inspector is responsible to decode away possible jwt-ness, so that
|
||||||
* what we get here is a verbatim json node.
|
* what we get here is a verbatim json node.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Credential endorsement = (Credential) checkNotNull(parentObjects.get(CREDENTIAL_KEY));
|
VerifiableCredential endorsement = (VerifiableCredential) checkNotNull(parentObjects.get(CREDENTIAL_KEY));
|
||||||
|
|
||||||
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(JACKSON_OBJECTMAPPER, mapper)
|
.put(JACKSON_OBJECTMAPPER, mapper)
|
||||||
@ -74,18 +74,18 @@ public class EndorsementInspector extends VCInspector implements SubInspector {
|
|||||||
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.EndorsementCredential;
|
VerifiableCredential.Type type = Type.EndorsementCredential;
|
||||||
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(endorsement.getJson(), ctx));
|
accumulator.add(probe.run(endorsement.getJson(), ctx));
|
||||||
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
|
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
//inline schema (parent inspector has already validated against canonical)
|
//inline schema (parent inspector has already validated against canonical)
|
||||||
accumulator.add(new InlineJsonSchemaProbe().run(endorsement.getJson(), ctx));
|
accumulator.add(new InlineJsonSchemaProbe().run(endorsement.getJson(), ctx));
|
||||||
|
|
||||||
//signatures, proofs
|
//signatures, proofs
|
||||||
probeCount++;
|
probeCount++;
|
||||||
if(endorsement.getProofType() == EXTERNAL){
|
if(endorsement.getProofType() == EXTERNAL){
|
||||||
@ -93,16 +93,16 @@ public class EndorsementInspector extends VCInspector implements SubInspector {
|
|||||||
accumulator.add(new ExternalProofProbe().run(endorsement, ctx));
|
accumulator.add(new ExternalProofProbe().run(endorsement, 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(endorsement, ctx));
|
accumulator.add(new EmbeddedProofProbe().run(endorsement, 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 just like in external CLR)
|
//check refresh service if we are not already refreshed (check just like in external CLR)
|
||||||
probeCount++;
|
probeCount++;
|
||||||
if(resource.getContext().get(REFRESHED) != TRUE) {
|
if(resource.getContext().get(REFRESHED) != TRUE) {
|
||||||
Optional<String> newID = checkRefreshService(endorsement, ctx);
|
Optional<String> newID = checkRefreshService(endorsement, ctx);
|
||||||
if(newID.isPresent()) {
|
if(newID.isPresent()) {
|
||||||
//TODO resource.type
|
//TODO resource.type
|
||||||
return this.run(
|
return this.run(
|
||||||
new UriResource(new URI(newID.get()))
|
new UriResource(new URI(newID.get()))
|
||||||
@ -111,8 +111,8 @@ public class EndorsementInspector extends VCInspector implements SubInspector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//revocation, expiration and issuance
|
//revocation, expiration and issuance
|
||||||
for(Probe<Credential> probe : List.of(new RevocationListProbe(),
|
for(Probe<VerifiableCredential> probe : List.of(new RevocationListProbe(),
|
||||||
new ExpirationProbe(), new IssuanceProbe())) {
|
new ExpirationProbe(), new IssuanceProbe())) {
|
||||||
probeCount++;
|
probeCount++;
|
||||||
accumulator.add(probe.run(endorsement, ctx));
|
accumulator.add(probe.run(endorsement, ctx));
|
||||||
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
|
if(broken(accumulator)) return abort(ctx, accumulator, probeCount);
|
||||||
@ -129,7 +129,7 @@ public class EndorsementInspector extends VCInspector implements SubInspector {
|
|||||||
public <R extends Resource> Report run(R resource) {
|
public <R extends Resource> Report run(R resource) {
|
||||||
throw new IllegalStateException("must use #run(resource, map)");
|
throw new IllegalStateException("must use #run(resource, map)");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder extends VCInspector.Builder<EndorsementInspector.Builder> {
|
public static class Builder extends VCInspector.Builder<EndorsementInspector.Builder> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
@ -137,5 +137,5 @@ public class EndorsementInspector extends VCInspector implements SubInspector {
|
|||||||
return new EndorsementInspector(this);
|
return new EndorsementInspector(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ 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.AbstractBaseCredential.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.VerifiableCredential.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;
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ 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.VerifiableCredential.Type;
|
||||||
import org.oneedtech.inspect.vc.payload.PngParser;
|
import org.oneedtech.inspect.vc.payload.PngParser;
|
||||||
import org.oneedtech.inspect.vc.payload.SvgParser;
|
import org.oneedtech.inspect.vc.payload.SvgParser;
|
||||||
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
|
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe;
|
||||||
@ -58,7 +58,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class OB30Inspector extends VCInspector implements SubInspector {
|
public class OB30Inspector extends VCInspector implements SubInspector {
|
||||||
protected final List<Probe<Credential>> userProbes;
|
protected final List<Probe<VerifiableCredential>> userProbes;
|
||||||
|
|
||||||
protected OB30Inspector(OB30Inspector.Builder builder) {
|
protected OB30Inspector(OB30Inspector.Builder builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
@ -93,7 +93,7 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
|||||||
.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, new Credential.Builder())
|
.put(Key.GENERATED_OBJECT_BUILDER, new VerifiableCredential.Builder())
|
||||||
.put(Key.PNG_CREDENTIAL_KEY, PngParser.Keys.OB30)
|
.put(Key.PNG_CREDENTIAL_KEY, PngParser.Keys.OB30)
|
||||||
.put(Key.SVG_CREDENTIAL_QNAME, SvgParser.QNames.OB30)
|
.put(Key.SVG_CREDENTIAL_QNAME, SvgParser.QNames.OB30)
|
||||||
.build();
|
.build();
|
||||||
@ -108,15 +108,15 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
|||||||
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);
|
VerifiableCredential ob = ctx.getGeneratedObject(VerifiableCredential.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(VerifiableCredential.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<VerifiableCredential> probe : userProbes) {
|
||||||
probeCount++;
|
probeCount++;
|
||||||
accumulator.add(probe.run(ob, ctx));
|
accumulator.add(probe.run(ob, ctx));
|
||||||
}
|
}
|
||||||
@ -131,11 +131,11 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
|||||||
@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));
|
VerifiableCredential ob = checkNotNull((VerifiableCredential)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();
|
VerifiableCredential.Builder credentialBuilder = new VerifiableCredential.Builder();
|
||||||
RunContext ctx = new RunContext.Builder()
|
RunContext ctx = new RunContext.Builder()
|
||||||
.put(this)
|
.put(this)
|
||||||
.put(resource)
|
.put(resource)
|
||||||
@ -152,7 +152,7 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
//context and type properties
|
//context and type properties
|
||||||
Credential.Type type = Type.OpenBadgeCredential;
|
VerifiableCredential.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));
|
||||||
@ -194,7 +194,7 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//revocation, expiration and issuance
|
//revocation, expiration and issuance
|
||||||
for(Probe<Credential> probe : List.of(new RevocationListProbe(),
|
for(Probe<VerifiableCredential> 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));
|
||||||
@ -207,7 +207,7 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
|||||||
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 = credentialBuilder.resource(resource).jsonData(node).build();
|
VerifiableCredential 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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ public class OB30Inspector extends VCInspector implements SubInspector {
|
|||||||
probeCount++;
|
probeCount++;
|
||||||
String jwt = node.asText();
|
String jwt = node.asText();
|
||||||
JsonNode vcNode = fromJwt(jwt, ctx);
|
JsonNode vcNode = fromJwt(jwt, ctx);
|
||||||
Credential endorsement = credentialBuilder.resource(resource).jsonData(node).jwt(jwt).build();
|
VerifiableCredential 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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,19 +18,19 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public abstract class VCInspector extends Inspector {
|
public abstract class VCInspector extends Inspector {
|
||||||
|
|
||||||
protected <B extends VCInspector.Builder<?>> VCInspector(B builder) {
|
protected <B extends VCInspector.Builder<?>> VCInspector(B builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Report abort(RunContext ctx, List<ReportItems> accumulator, int probeCount) {
|
protected Report abort(RunContext ctx, List<ReportItems> accumulator, int probeCount) {
|
||||||
return new Report(ctx, new ReportItems(accumulator), probeCount);
|
return new Report(ctx, new ReportItems(accumulator), probeCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean broken(List<ReportItems> accumulator) {
|
protected boolean broken(List<ReportItems> accumulator) {
|
||||||
return broken(accumulator, false);
|
return broken(accumulator, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean broken(List<ReportItems> accumulator, boolean force) {
|
protected boolean broken(List<ReportItems> accumulator, boolean force) {
|
||||||
if(!force && getBehavior(Inspector.Behavior.VALIDATOR_FAIL_FAST) == Boolean.FALSE) {
|
if(!force && getBehavior(Inspector.Behavior.VALIDATOR_FAIL_FAST) == Boolean.FALSE) {
|
||||||
return false;
|
return false;
|
||||||
@ -40,15 +40,15 @@ public abstract class VCInspector extends Inspector {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the AchievementCredential or EndorsementCredential has a “refreshService” property and the type of the
|
* If the AchievementCredential or EndorsementCredential has a “refreshService” property and the type of the
|
||||||
* RefreshService object is “1EdTechCredentialRefresh”, you should fetch the refreshed credential from the URL
|
* RefreshService object is “1EdTechCredentialRefresh”, you should fetch the refreshed credential from the URL
|
||||||
* provided, then start the verification process over using the response as input. If the request fails,
|
* provided, then start the verification process over using the response as input. If the request fails,
|
||||||
* the credential is invalid.
|
* the credential is invalid.
|
||||||
*/
|
*/
|
||||||
protected Optional<String> checkRefreshService(Credential crd, RunContext ctx) {
|
protected Optional<String> checkRefreshService(VerifiableCredential crd, RunContext ctx) {
|
||||||
JsonNode refreshServiceNode = crd.getJson().get("refreshService");
|
JsonNode refreshServiceNode = crd.getJson().get("refreshService");
|
||||||
if(refreshServiceNode != null) {
|
if(refreshServiceNode != null) {
|
||||||
JsonNode serviceTypeNode = refreshServiceNode.get("type");
|
JsonNode serviceTypeNode = refreshServiceNode.get("type");
|
||||||
if(serviceTypeNode != null && serviceTypeNode.asText().equals("1EdTechCredentialRefresh")) {
|
if(serviceTypeNode != null && serviceTypeNode.asText().equals("1EdTechCredentialRefresh")) {
|
||||||
@ -56,22 +56,22 @@ public abstract class VCInspector extends Inspector {
|
|||||||
if(serviceURINode != null) {
|
if(serviceURINode != null) {
|
||||||
return Optional.of(serviceURINode.asText());
|
return Optional.of(serviceURINode.asText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static final String REFRESHED = "is.refreshed.credential";
|
protected static final String REFRESHED = "is.refreshed.credential";
|
||||||
|
|
||||||
public abstract static class Builder<B extends VCInspector.Builder<B>> extends Inspector.Builder<B> {
|
public abstract static class Builder<B extends VCInspector.Builder<B>> extends Inspector.Builder<B> {
|
||||||
final List<Probe<Credential>> probes;
|
final List<Probe<VerifiableCredential>> probes;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
super();
|
super();
|
||||||
this.probes = new ArrayList<>();
|
this.probes = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCInspector.Builder<B> add(Probe<Credential> probe) {
|
public VCInspector.Builder<B> add(Probe<VerifiableCredential> probe) {
|
||||||
probes.add(probe);
|
probes.add(probe);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package org.oneedtech.inspect.vc;
|
package org.oneedtech.inspect.vc;
|
||||||
|
|
||||||
import static org.oneedtech.inspect.vc.Credential.Type.AchievementCredential;
|
import static org.oneedtech.inspect.vc.VerifiableCredential.Type.AchievementCredential;
|
||||||
import static org.oneedtech.inspect.vc.Credential.Type.ClrCredential;
|
import static org.oneedtech.inspect.vc.VerifiableCredential.Type.ClrCredential;
|
||||||
import static org.oneedtech.inspect.vc.Credential.Type.EndorsementCredential;
|
import static org.oneedtech.inspect.vc.VerifiableCredential.Type.EndorsementCredential;
|
||||||
import static org.oneedtech.inspect.vc.Credential.Type.VerifiablePresentation;
|
import static org.oneedtech.inspect.vc.VerifiableCredential.Type.VerifiablePresentation;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -23,14 +23,14 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
* and the extracted JSON data plus any other stuff Probes need.
|
* and the extracted JSON data plus any other stuff Probes need.
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class Credential extends AbstractBaseCredential {
|
public class VerifiableCredential extends AbstractBaseCredential {
|
||||||
final Credential.Type credentialType;
|
final VerifiableCredential.Type credentialType;
|
||||||
|
|
||||||
protected Credential(Resource resource, JsonNode data, String jwt, Map<String, SchemaKey> schemas) {
|
protected VerifiableCredential(Resource resource, JsonNode data, String jwt, Map<String, SchemaKey> schemas) {
|
||||||
super(ID, resource, data, jwt, schemas);
|
super(ID, resource, data, jwt, schemas);
|
||||||
|
|
||||||
ArrayNode typeNode = (ArrayNode)jsonData.get("type");
|
ArrayNode typeNode = (ArrayNode)jsonData.get("type");
|
||||||
this.credentialType = Credential.Type.valueOf(typeNode);
|
this.credentialType = VerifiableCredential.Type.valueOf(typeNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCredentialType() {
|
public String getCredentialType() {
|
||||||
@ -41,7 +41,7 @@ public class Credential extends AbstractBaseCredential {
|
|||||||
return jwt == null ? ProofType.EMBEDDED : ProofType.EXTERNAL;
|
return jwt == null ? ProofType.EMBEDDED : ProofType.EXTERNAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<Credential.Type, SchemaKey> schemas = new ImmutableMap.Builder<Credential.Type, SchemaKey>()
|
private static final Map<VerifiableCredential.Type, SchemaKey> schemas = new ImmutableMap.Builder<VerifiableCredential.Type, SchemaKey>()
|
||||||
.put(AchievementCredential, Catalog.OB_30_ACHIEVEMENTCREDENTIAL_JSON)
|
.put(AchievementCredential, Catalog.OB_30_ACHIEVEMENTCREDENTIAL_JSON)
|
||||||
.put(ClrCredential, Catalog.CLR_20_CLRCREDENTIAL_JSON)
|
.put(ClrCredential, Catalog.CLR_20_CLRCREDENTIAL_JSON)
|
||||||
.put(VerifiablePresentation, Catalog.CLR_20_CLRCREDENTIAL_JSON)
|
.put(VerifiablePresentation, Catalog.CLR_20_CLRCREDENTIAL_JSON)
|
||||||
@ -58,7 +58,7 @@ public class Credential extends AbstractBaseCredential {
|
|||||||
VerifiableCredential, //this is an underspecifier in our context
|
VerifiableCredential, //this is an underspecifier in our context
|
||||||
Unknown;
|
Unknown;
|
||||||
|
|
||||||
public static Credential.Type valueOf (ArrayNode typeArray) {
|
public static VerifiableCredential.Type valueOf (ArrayNode typeArray) {
|
||||||
if(typeArray != null) {
|
if(typeArray != null) {
|
||||||
Iterator<JsonNode> iter = typeArray.iterator();
|
Iterator<JsonNode> iter = typeArray.iterator();
|
||||||
while(iter.hasNext()) {
|
while(iter.hasNext()) {
|
||||||
@ -91,18 +91,18 @@ public class Credential extends AbstractBaseCredential {
|
|||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder extends AbstractBaseCredential.Builder<Credential> {
|
public static class Builder extends AbstractBaseCredential.Builder<VerifiableCredential> {
|
||||||
@Override
|
@Override
|
||||||
public Credential build() {
|
public VerifiableCredential build() {
|
||||||
// transform key of schemas map to string because the type of the key in the base map is generic
|
// transform key of schemas map to string because the type of the key in the base map is generic
|
||||||
// and our specific key is an Enum
|
// and our specific key is an Enum
|
||||||
return new Credential(getResource(), getJsonData(), getJwt(),
|
return new VerifiableCredential(getResource(), getJsonData(), getJwt(),
|
||||||
schemas.entrySet().stream().collect(Collectors.toMap(
|
schemas.entrySet().stream().collect(Collectors.toMap(
|
||||||
entry -> entry.getKey().toString(),
|
entry -> entry.getKey().toString(),
|
||||||
entry -> entry.getValue())));
|
entry -> entry.getValue())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String ID = Credential.class.getCanonicalName();
|
public static final String ID = VerifiableCredential.class.getCanonicalName();
|
||||||
|
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ import org.oneedtech.inspect.core.probe.RunContext;
|
|||||||
import org.oneedtech.inspect.util.resource.Resource;
|
import org.oneedtech.inspect.util.resource.Resource;
|
||||||
import org.oneedtech.inspect.util.resource.ResourceType;
|
import org.oneedtech.inspect.util.resource.ResourceType;
|
||||||
import org.oneedtech.inspect.vc.AbstractBaseCredential;
|
import org.oneedtech.inspect.vc.AbstractBaseCredential;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package org.oneedtech.inspect.vc.probe;
|
package org.oneedtech.inspect.vc.probe;
|
||||||
|
|
||||||
import static org.oneedtech.inspect.vc.Credential.Type.*;
|
import static org.oneedtech.inspect.vc.VerifiableCredential.Type.*;
|
||||||
|
|
||||||
import static org.oneedtech.inspect.util.code.Defensives.checkNotNull;
|
import static org.oneedtech.inspect.util.code.Defensives.checkNotNull;
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ import java.util.Set;
|
|||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
|
|
||||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||||
|
|
||||||
@ -21,13 +21,13 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A Probe that verifies a credential's context property.
|
* A Probe that verifies a credential's context property.
|
||||||
*
|
*
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class ContextPropertyProbe extends Probe<JsonNode> {
|
public class ContextPropertyProbe extends Probe<JsonNode> {
|
||||||
private final Credential.Type type;
|
private final VerifiableCredential.Type type;
|
||||||
|
|
||||||
public ContextPropertyProbe(Credential.Type type) {
|
public ContextPropertyProbe(VerifiableCredential.Type type) {
|
||||||
super(ID);
|
super(ID);
|
||||||
this.type = checkNotNull(type);
|
this.type = checkNotNull(type);
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ public class ContextPropertyProbe extends Probe<JsonNode> {
|
|||||||
.filter(s->s.contains(type))
|
.filter(s->s.contains(type))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(()-> new IllegalArgumentException(type.name() + " not recognized")));
|
.orElseThrow(()-> new IllegalArgumentException(type.name() + " not recognized")));
|
||||||
|
|
||||||
List<String> given = JsonNodeUtil.asStringList(contextNode);
|
List<String> given = JsonNodeUtil.asStringList(contextNode);
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
for (String uri : expected) {
|
for (String uri : expected) {
|
||||||
@ -58,7 +58,7 @@ public class ContextPropertyProbe extends Probe<JsonNode> {
|
|||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static Map<Set<Credential.Type>, List<String>> values = new ImmutableMap.Builder<Set<Credential.Type>, List<String>>()
|
private final static Map<Set<VerifiableCredential.Type>, List<String>> values = new ImmutableMap.Builder<Set<VerifiableCredential.Type>, List<String>>()
|
||||||
.put(Set.of(OpenBadgeCredential, AchievementCredential, EndorsementCredential),
|
.put(Set.of(OpenBadgeCredential, AchievementCredential, EndorsementCredential),
|
||||||
List.of("https://www.w3.org/2018/credentials/v1",
|
List.of("https://www.w3.org/2018/credentials/v1",
|
||||||
//"https://purl.imsglobal.org/spec/ob/v3p0/context.json")) //dev legacy
|
//"https://purl.imsglobal.org/spec/ob/v3p0/context.json")) //dev legacy
|
||||||
@ -69,7 +69,7 @@ public class ContextPropertyProbe extends Probe<JsonNode> {
|
|||||||
// "https://purl.imsglobal.org/spec/ob/v3p0/context.json")) //dev legacy
|
// "https://purl.imsglobal.org/spec/ob/v3p0/context.json")) //dev legacy
|
||||||
"https://purl.imsglobal.org/spec/clr/v2p0/context.json",
|
"https://purl.imsglobal.org/spec/clr/v2p0/context.json",
|
||||||
"https://purl.imsglobal.org/spec/ob/v3p0/context.json"))
|
"https://purl.imsglobal.org/spec/ob/v3p0/context.json"))
|
||||||
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static final String ID = ContextPropertyProbe.class.getSimpleName();
|
public static final String ID = ContextPropertyProbe.class.getSimpleName();
|
||||||
|
@ -9,7 +9,7 @@ import org.oneedtech.inspect.util.resource.Resource;
|
|||||||
import org.oneedtech.inspect.util.resource.ResourceType;
|
import org.oneedtech.inspect.util.resource.ResourceType;
|
||||||
import org.oneedtech.inspect.util.resource.detect.TypeDetector;
|
import org.oneedtech.inspect.util.resource.detect.TypeDetector;
|
||||||
import org.oneedtech.inspect.vc.AbstractBaseCredential;
|
import org.oneedtech.inspect.vc.AbstractBaseCredential;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
import org.oneedtech.inspect.vc.payload.PayloadParserFactory;
|
import org.oneedtech.inspect.vc.payload.PayloadParserFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,7 +39,7 @@ public class CredentialParseProbe extends Probe<Resource> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Credential.RECOGNIZED_PAYLOAD_TYPES.contains(type.get())) {
|
if(!VerifiableCredential.RECOGNIZED_PAYLOAD_TYPES.contains(type.get())) {
|
||||||
return fatal("Payload type not supported: " + type.get().getName(), context);
|
return fatal("Payload type not supported: " + type.get().getName(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import java.util.Optional;
|
|||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
|
|
||||||
import com.apicatalog.jsonld.StringUtils;
|
import com.apicatalog.jsonld.StringUtils;
|
||||||
import com.apicatalog.jsonld.document.Document;
|
import com.apicatalog.jsonld.document.Document;
|
||||||
@ -15,7 +15,6 @@ import com.apicatalog.jsonld.loader.DocumentLoaderOptions;
|
|||||||
import com.apicatalog.multibase.Multibase;
|
import com.apicatalog.multibase.Multibase;
|
||||||
import com.apicatalog.multicodec.Multicodec;
|
import com.apicatalog.multicodec.Multicodec;
|
||||||
import com.apicatalog.multicodec.Multicodec.Codec;
|
import com.apicatalog.multicodec.Multicodec.Codec;
|
||||||
import com.danubetech.verifiablecredentials.VerifiableCredential;
|
|
||||||
|
|
||||||
import foundation.identity.jsonld.ConfigurableDocumentLoader;
|
import foundation.identity.jsonld.ConfigurableDocumentLoader;
|
||||||
import info.weboftrust.ldsignatures.LdProof;
|
import info.weboftrust.ldsignatures.LdProof;
|
||||||
@ -28,7 +27,7 @@ import jakarta.json.JsonStructure;
|
|||||||
*
|
*
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class EmbeddedProofProbe extends Probe<Credential> {
|
public class EmbeddedProofProbe extends Probe<VerifiableCredential> {
|
||||||
|
|
||||||
public EmbeddedProofProbe() {
|
public EmbeddedProofProbe() {
|
||||||
super(ID);
|
super(ID);
|
||||||
@ -39,11 +38,11 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
* (https://github.com/danubetech/verifiable-credentials-java)
|
* (https://github.com/danubetech/verifiable-credentials-java)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception {
|
||||||
|
|
||||||
// TODO: What there are multiple proofs?
|
// TODO: What there are multiple proofs?
|
||||||
|
|
||||||
VerifiableCredential vc = VerifiableCredential.fromJson(new StringReader(crd.getJson().toString()));
|
com.danubetech.verifiablecredentials.VerifiableCredential vc = com.danubetech.verifiablecredentials.VerifiableCredential.fromJson(new StringReader(crd.getJson().toString()));
|
||||||
ConfigurableDocumentLoader documentLoader = new ConfigurableDocumentLoader();
|
ConfigurableDocumentLoader documentLoader = new ConfigurableDocumentLoader();
|
||||||
documentLoader.setEnableHttp(true);
|
documentLoader.setEnableHttp(true);
|
||||||
documentLoader.setEnableHttps(true);
|
documentLoader.setEnableHttps(true);
|
||||||
@ -97,7 +96,7 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
if (keyStructure.isEmpty()) {
|
if (keyStructure.isEmpty()) {
|
||||||
return error("Key document not found at " + method, ctx);
|
return error("Key document not found at " + method, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First look for a Ed25519VerificationKey2020 document
|
// First look for a Ed25519VerificationKey2020 document
|
||||||
controller = keyStructure.get().asJsonObject().getString("controller");
|
controller = keyStructure.get().asJsonObject().getString("controller");
|
||||||
if (StringUtils.isBlank(controller)) {
|
if (StringUtils.isBlank(controller)) {
|
||||||
@ -113,7 +112,7 @@ public class EmbeddedProofProbe extends Probe<Credential> {
|
|||||||
} else {
|
} else {
|
||||||
publicKeyMultibase = keyStructure.get().asJsonObject().getString("publicKeyMultibase");
|
publicKeyMultibase = keyStructure.get().asJsonObject().getString("publicKeyMultibase");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return error("Invalid verification key URL: " + e.getMessage(), ctx);
|
return error("Invalid verification key URL: " + e.getMessage(), ctx);
|
||||||
}
|
}
|
||||||
|
@ -5,39 +5,39 @@ import java.time.ZonedDateTime;
|
|||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Probe that verifies a credential's expiration status
|
* A Probe that verifies a credential's expiration status
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class ExpirationProbe extends Probe<Credential> {
|
public class ExpirationProbe extends Probe<VerifiableCredential> {
|
||||||
|
|
||||||
public ExpirationProbe() {
|
public ExpirationProbe() {
|
||||||
super(ID);
|
super(ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception {
|
||||||
/*
|
/*
|
||||||
* If the AchievementCredential or EndorsementCredential has an “expirationDate” property
|
* If the AchievementCredential or EndorsementCredential has an “expirationDate” property
|
||||||
* and the expiration date is prior to the current date, the credential has expired.
|
* and the expiration date is prior to the current date, the credential has expired.
|
||||||
*/
|
*/
|
||||||
JsonNode node = crd.getJson().get("expirationDate");
|
JsonNode node = crd.getJson().get("expirationDate");
|
||||||
if(node != null) {
|
if(node != null) {
|
||||||
try {
|
try {
|
||||||
ZonedDateTime expirationDate = ZonedDateTime.parse(node.textValue());
|
ZonedDateTime expirationDate = ZonedDateTime.parse(node.textValue());
|
||||||
if (ZonedDateTime.now().isAfter(expirationDate)) {
|
if (ZonedDateTime.now().isAfter(expirationDate)) {
|
||||||
return fatal("The credential has expired (expiration date was " + node.asText() + ").", ctx);
|
return fatal("The credential has expired (expiration date was " + node.asText() + ").", ctx);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return exception("Error while checking expirationDate: " + e.getMessage(), ctx.getResource());
|
return exception("Error while checking expirationDate: " + e.getMessage(), ctx.getResource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String ID = ExpirationProbe.class.getSimpleName();
|
public static final String ID = ExpirationProbe.class.getSimpleName();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import org.apache.http.util.EntityUtils;
|
|||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
|
|
||||||
import com.auth0.jwt.JWT;
|
import com.auth0.jwt.JWT;
|
||||||
import com.auth0.jwt.JWTVerifier;
|
import com.auth0.jwt.JWTVerifier;
|
||||||
@ -36,32 +36,32 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Probe that verifies credential external proof (jwt)
|
* A Probe that verifies credential external proof (jwt)
|
||||||
* @author mlyon
|
* @author mlyon
|
||||||
*/
|
*/
|
||||||
public class ExternalProofProbe extends Probe<Credential> {
|
public class ExternalProofProbe extends Probe<VerifiableCredential> {
|
||||||
|
|
||||||
public ExternalProofProbe() {
|
public ExternalProofProbe() {
|
||||||
super(ID);
|
super(ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception {
|
||||||
try {
|
try {
|
||||||
verifySignature(crd, ctx);
|
verifySignature(crd, ctx);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return fatal("Error verifying jwt signature: " + e.getMessage(), ctx);
|
return fatal("Error verifying jwt signature: " + e.getMessage(), ctx);
|
||||||
}
|
}
|
||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifySignature(Credential crd, RunContext ctx) throws Exception {
|
private void verifySignature(VerifiableCredential crd, RunContext ctx) throws Exception {
|
||||||
checkTrue(crd.getJwt().isPresent(), "no jwt supplied");
|
checkTrue(crd.getJwt().isPresent(), "no jwt supplied");
|
||||||
checkTrue(crd.getJwt().get().length() > 0, "no jwt supplied");
|
checkTrue(crd.getJwt().get().length() > 0, "no jwt supplied");
|
||||||
|
|
||||||
DecodedJWT decodedJwt = null;
|
DecodedJWT decodedJwt = null;
|
||||||
String jwt = crd.getJwt().get();
|
String jwt = crd.getJwt().get();
|
||||||
|
|
||||||
List<String> parts = Splitter.on('.').splitToList(jwt);
|
List<String> parts = Splitter.on('.').splitToList(jwt);
|
||||||
if(parts.size() != 3) throw new IllegalArgumentException("invalid jwt");
|
if(parts.size() != 3) throw new IllegalArgumentException("invalid jwt");
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ public class ExternalProofProbe extends Probe<Credential> {
|
|||||||
|
|
||||||
if(jwk == null && kid == null) { throw new Exception("Key must present in either jwk or kid value."); }
|
if(jwk == null && kid == null) { throw new Exception("Key must present in either jwk or kid value."); }
|
||||||
if(kid != null){
|
if(kid != null){
|
||||||
//Load jwk JsonNode from url and do the rest the same below.
|
//Load jwk JsonNode from url and do the rest the same below.
|
||||||
//TODO Consider additional testing.
|
//TODO Consider additional testing.
|
||||||
String kidUrl = kid.textValue();
|
String kidUrl = kid.textValue();
|
||||||
String jwkResponse = fetchJwk(kidUrl);
|
String jwkResponse = fetchJwk(kidUrl);
|
||||||
@ -145,7 +145,7 @@ public class ExternalProofProbe extends Probe<Credential> {
|
|||||||
|
|
||||||
return responseString;
|
return responseString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String ID = ExternalProofProbe.class.getSimpleName();
|
public static final String ID = ExternalProofProbe.class.getSimpleName();
|
||||||
|
|
||||||
}
|
}
|
@ -11,64 +11,64 @@ import org.oneedtech.inspect.core.probe.RunContext;
|
|||||||
import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
|
import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.schema.SchemaKey;
|
import org.oneedtech.inspect.schema.SchemaKey;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect inline schemas in a credential and run them.
|
* Detect inline schemas in a credential and run them.
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class InlineJsonSchemaProbe extends Probe<JsonNode> {
|
public class InlineJsonSchemaProbe extends Probe<JsonNode> {
|
||||||
private static final Set<String> types = Set.of("1EdTechJsonSchemaValidator2019");
|
private static final Set<String> types = Set.of("1EdTechJsonSchemaValidator2019");
|
||||||
private SchemaKey skip;
|
private SchemaKey skip;
|
||||||
|
|
||||||
public InlineJsonSchemaProbe() {
|
public InlineJsonSchemaProbe() {
|
||||||
super(ID);
|
super(ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InlineJsonSchemaProbe(SchemaKey skip) {
|
public InlineJsonSchemaProbe(SchemaKey skip) {
|
||||||
super(ID);
|
super(ID);
|
||||||
this.skip = skip;
|
this.skip = skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
|
public ReportItems run(JsonNode root, RunContext ctx) throws Exception {
|
||||||
List<ReportItems> accumulator = new ArrayList<>();
|
List<ReportItems> accumulator = new ArrayList<>();
|
||||||
Set<String> ioErrors = new HashSet<>();
|
Set<String> ioErrors = new HashSet<>();
|
||||||
|
|
||||||
//note - we don't get deep nested ones in e.g. EndorsementCredential
|
//note - we don't get deep nested ones in e.g. EndorsementCredential
|
||||||
JsonNode credentialSchemaNode = root.get("credentialSchema");
|
JsonNode credentialSchemaNode = root.get("credentialSchema");
|
||||||
if(credentialSchemaNode == null) return success(ctx);
|
if(credentialSchemaNode == null) return success(ctx);
|
||||||
|
|
||||||
ArrayNode schemas = (ArrayNode) credentialSchemaNode; //TODO guard this cast
|
ArrayNode schemas = (ArrayNode) credentialSchemaNode; //TODO guard this cast
|
||||||
|
|
||||||
for(JsonNode schemaNode : schemas) {
|
for(JsonNode schemaNode : schemas) {
|
||||||
JsonNode typeNode = schemaNode.get("type");
|
JsonNode typeNode = schemaNode.get("type");
|
||||||
if(typeNode == null || !types.contains(typeNode.asText())) continue;
|
if(typeNode == null || !types.contains(typeNode.asText())) continue;
|
||||||
JsonNode idNode = schemaNode.get("id");
|
JsonNode idNode = schemaNode.get("id");
|
||||||
if(idNode == null) continue;
|
if(idNode == null) continue;
|
||||||
String id = idNode.asText().strip();
|
String id = idNode.asText().strip();
|
||||||
if(ioErrors.contains(id)) continue;
|
if(ioErrors.contains(id)) continue;
|
||||||
if(equals(skip, id)) continue;
|
if(equals(skip, id)) continue;
|
||||||
try {
|
try {
|
||||||
accumulator.add(new JsonSchemaProbe(id).run(root, ctx));
|
accumulator.add(new JsonSchemaProbe(id).run(root, ctx));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if(!ioErrors.contains(id)) {
|
if(!ioErrors.contains(id)) {
|
||||||
ioErrors.add(id);
|
ioErrors.add(id);
|
||||||
accumulator.add(error("Could not read schema resource " + id, ctx));
|
accumulator.add(error("Could not read schema resource " + id, ctx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ReportItems(accumulator);
|
return new ReportItems(accumulator);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean equals(SchemaKey key, String id) {
|
private boolean equals(SchemaKey key, String id) {
|
||||||
if(key == null) return false;
|
if(key == null) return false;
|
||||||
return key.getCanonicalURI().equals(id);
|
return key.getCanonicalURI().equals(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String ID = InlineJsonSchemaProbe.class.getSimpleName();
|
public static final String ID = InlineJsonSchemaProbe.class.getSimpleName();
|
||||||
}
|
}
|
||||||
|
@ -5,39 +5,39 @@ import java.time.ZonedDateTime;
|
|||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Probe that verifies a credential's issuance status
|
* A Probe that verifies a credential's issuance status
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class IssuanceProbe extends Probe<Credential> {
|
public class IssuanceProbe extends Probe<VerifiableCredential> {
|
||||||
|
|
||||||
public IssuanceProbe() {
|
public IssuanceProbe() {
|
||||||
super(ID);
|
super(ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception {
|
||||||
/*
|
/*
|
||||||
* If the AchievementCredential or EndorsementCredential “issuanceDate”
|
* If the AchievementCredential or EndorsementCredential “issuanceDate”
|
||||||
* property after the current date, the credential is not yet valid.
|
* property after the current date, the credential is not yet valid.
|
||||||
*/
|
*/
|
||||||
JsonNode node = crd.getJson().get("issuanceDate");
|
JsonNode node = crd.getJson().get("issuanceDate");
|
||||||
if(node != null) {
|
if(node != null) {
|
||||||
try {
|
try {
|
||||||
ZonedDateTime issuanceDate = ZonedDateTime.parse(node.textValue());
|
ZonedDateTime issuanceDate = ZonedDateTime.parse(node.textValue());
|
||||||
if (issuanceDate.isAfter(ZonedDateTime.now())) {
|
if (issuanceDate.isAfter(ZonedDateTime.now())) {
|
||||||
return fatal("The credential is not yet issued (issuance date is " + node.asText() + ").", ctx);
|
return fatal("The credential is not yet issued (issuance date is " + node.asText() + ").", ctx);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return exception("Error while checking issuanceDate: " + e.getMessage(), ctx.getResource());
|
return exception("Error while checking issuanceDate: " + e.getMessage(), ctx.getResource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String ID = IssuanceProbe.class.getSimpleName();
|
public static final String ID = IssuanceProbe.class.getSimpleName();
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
@ -20,24 +20,24 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
* A Probe that verifies a credential's revocation status.
|
* A Probe that verifies a credential's revocation status.
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class RevocationListProbe extends Probe<Credential> {
|
public class RevocationListProbe extends Probe<VerifiableCredential> {
|
||||||
|
|
||||||
public RevocationListProbe() {
|
public RevocationListProbe() {
|
||||||
super(ID);
|
super(ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReportItems run(Credential crd, RunContext ctx) throws Exception {
|
public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the AchievementCredential or EndorsementCredential has a “credentialStatus” property
|
* If the AchievementCredential or EndorsementCredential has a “credentialStatus” property
|
||||||
* and the type of the CredentialStatus object is “1EdTechRevocationList”, fetch the
|
* and the type of the CredentialStatus object is “1EdTechRevocationList”, fetch the
|
||||||
* credential status from the URL provided. If the request is unsuccessful,
|
* credential status from the URL provided. If the request is unsuccessful,
|
||||||
* report a warning, not an error.
|
* report a warning, not an error.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JsonNode credentialStatus = crd.getJson().get("credentialStatus");
|
JsonNode credentialStatus = crd.getJson().get("credentialStatus");
|
||||||
if(credentialStatus != null) {
|
if(credentialStatus != null) {
|
||||||
JsonNode type = credentialStatus.get("type");
|
JsonNode type = credentialStatus.get("type");
|
||||||
if(type != null && type.asText().strip().equals("1EdTechRevocationList")) {
|
if(type != null && type.asText().strip().equals("1EdTechRevocationList")) {
|
||||||
JsonNode listID = credentialStatus.get("id");
|
JsonNode listID = credentialStatus.get("id");
|
||||||
@ -46,34 +46,34 @@ public class RevocationListProbe extends Probe<Credential> {
|
|||||||
URL url = new URI(listID.asText().strip()).toURL();
|
URL url = new URI(listID.asText().strip()).toURL();
|
||||||
try (InputStream is = url.openStream()) {
|
try (InputStream is = url.openStream()) {
|
||||||
JsonNode revocList = ((ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER)).readTree(is.readAllBytes());
|
JsonNode revocList = ((ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER)).readTree(is.readAllBytes());
|
||||||
|
|
||||||
/* To check if a credential has been revoked, the verifier issues a GET request
|
/* To check if a credential has been revoked, the verifier issues a GET request
|
||||||
* to the URL of the issuer's 1EdTech Revocation List Status Method. If the
|
* to the URL of the issuer's 1EdTech Revocation List Status Method. If the
|
||||||
* credential's id is in the list of revokedCredentials and the value of
|
* credential's id is in the list of revokedCredentials and the value of
|
||||||
* revoked is true or ommitted, the issuer has revoked the credential. */
|
* revoked is true or ommitted, the issuer has revoked the credential. */
|
||||||
|
|
||||||
JsonNode crdID = crd.getJson().get("id"); //TODO these != checks sb removed (trigger warning)
|
JsonNode crdID = crd.getJson().get("id"); //TODO these != checks sb removed (trigger warning)
|
||||||
if(crdID != null) {
|
if(crdID != null) {
|
||||||
List<JsonNode> list = JsonNodeUtil.asNodeList(revocList.get("revokedCredentials"));
|
List<JsonNode> list = JsonNodeUtil.asNodeList(revocList.get("revokedCredentials"));
|
||||||
if(list != null) {
|
if(list != null) {
|
||||||
for(JsonNode item : list) {
|
for(JsonNode item : list) {
|
||||||
JsonNode revID = item.get("id");
|
JsonNode revID = item.get("id");
|
||||||
JsonNode revoked = item.get("revoked");
|
JsonNode revoked = item.get("revoked");
|
||||||
if(revID != null && revID.equals(crdID) && (revoked == null || revoked.asBoolean())) {
|
if(revID != null && revID.equals(crdID) && (revoked == null || revoked.asBoolean())) {
|
||||||
return fatal("Credential has been revoked", ctx);
|
return fatal("Credential has been revoked", ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return warning("Error when fetching credentialStatus resource " + e.getMessage(), ctx);
|
return warning("Error when fetching credentialStatus resource " + e.getMessage(), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success(ctx);
|
return success(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String ID = RevocationListProbe.class.getSimpleName();
|
public static final String ID = RevocationListProbe.class.getSimpleName();
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ import java.util.List;
|
|||||||
import org.oneedtech.inspect.core.probe.Probe;
|
import org.oneedtech.inspect.core.probe.Probe;
|
||||||
import org.oneedtech.inspect.core.probe.RunContext;
|
import org.oneedtech.inspect.core.probe.RunContext;
|
||||||
import org.oneedtech.inspect.core.report.ReportItems;
|
import org.oneedtech.inspect.core.report.ReportItems;
|
||||||
import org.oneedtech.inspect.vc.Credential;
|
import org.oneedtech.inspect.vc.VerifiableCredential;
|
||||||
import org.oneedtech.inspect.vc.Credential.Type;
|
import org.oneedtech.inspect.vc.VerifiableCredential.Type;
|
||||||
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
@ -16,13 +16,13 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A Probe that verifies a credential's type property.
|
* A Probe that verifies a credential's type property.
|
||||||
*
|
*
|
||||||
* @author mgylling
|
* @author mgylling
|
||||||
*/
|
*/
|
||||||
public class TypePropertyProbe extends Probe<JsonNode> {
|
public class TypePropertyProbe extends Probe<JsonNode> {
|
||||||
private final Credential.Type expected;
|
private final VerifiableCredential.Type expected;
|
||||||
|
|
||||||
public TypePropertyProbe(Credential.Type expected) {
|
public TypePropertyProbe(VerifiableCredential.Type expected) {
|
||||||
super(ID);
|
super(ID);
|
||||||
this.expected = checkNotNull(expected);
|
this.expected = checkNotNull(expected);
|
||||||
}
|
}
|
||||||
@ -40,15 +40,15 @@ public class TypePropertyProbe extends Probe<JsonNode> {
|
|||||||
return fatal("The type property does not contain the entry 'VerifiableCredential'", ctx);
|
return fatal("The type property does not contain the entry 'VerifiableCredential'", ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected == Credential.Type.OpenBadgeCredential) {
|
if (expected == VerifiableCredential.Type.OpenBadgeCredential) {
|
||||||
if (!values.contains("OpenBadgeCredential") && !values.contains("AchievementCredential")) {
|
if (!values.contains("OpenBadgeCredential") && !values.contains("AchievementCredential")) {
|
||||||
return fatal("The type property does not contain one of 'OpenBadgeCredential' or 'AchievementCredential'", ctx);
|
return fatal("The type property does not contain one of 'OpenBadgeCredential' or 'AchievementCredential'", ctx);
|
||||||
}
|
}
|
||||||
} else if (expected == Credential.Type.ClrCredential) {
|
} else if (expected == VerifiableCredential.Type.ClrCredential) {
|
||||||
if (!values.contains("ClrCredential")) {
|
if (!values.contains("ClrCredential")) {
|
||||||
return fatal("The type property does not contain the entry 'ClrCredential'", ctx);
|
return fatal("The type property does not contain the entry 'ClrCredential'", ctx);
|
||||||
}
|
}
|
||||||
} else if (expected == Credential.Type.EndorsementCredential) {
|
} else if (expected == VerifiableCredential.Type.EndorsementCredential) {
|
||||||
if (!values.contains("EndorsementCredential")) {
|
if (!values.contains("EndorsementCredential")) {
|
||||||
return fatal("The type property does not contain the entry 'EndorsementCredential'", ctx);
|
return fatal("The type property does not contain the entry 'EndorsementCredential'", ctx);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user