From ce89968837727ed18bbd961c11e7001c0fe113d3 Mon Sep 17 00:00:00 2001 From: Xavi Aracil Date: Thu, 1 Dec 2022 12:30:34 +0100 Subject: [PATCH] generic IssuanceProbe --- .../org/oneedtech/inspect/vc/Assertion.java | 7 +- .../org/oneedtech/inspect/vc/Credential.java | 8 +- .../inspect/vc/EndorsementInspector.java | 2 +- .../oneedtech/inspect/vc/OB30Inspector.java | 2 +- .../inspect/vc/VerifiableCredential.java | 7 +- .../inspect/vc/probe/ExpirationProbe.java | 6 +- .../inspect/vc/probe/IssuanceProbe.java | 8 +- .../inspect/vc/probe/RevocationListProbe.java | 5 +- ...nFlattenEmbeddedResourcePropertyProbe.java | 94 +++++++++++++++++++ 9 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/validation/ValidationFlattenEmbeddedResourcePropertyProbe.java diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/Assertion.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/Assertion.java index 1febb31..1892c4f 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/Assertion.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/Assertion.java @@ -26,8 +26,8 @@ public class Assertion extends Credential { final Assertion.Type assertionType; - protected Assertion(Resource resource, JsonNode data, String jwt, Map schemas) { - super(resource.getID(), resource, data, jwt, schemas); + protected Assertion(Resource resource, JsonNode data, String jwt, Map schemas, String issuedOnPropertyName) { + super(resource.getID(), resource, data, jwt, schemas, issuedOnPropertyName); JsonNode typeNode = jsonData.get("type"); this.assertionType = Assertion.Type.valueOf(typeNode); @@ -60,7 +60,7 @@ public class Assertion extends Credential { public Assertion build() { // 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 - return new Assertion(getResource(), getJsonData(), getJwt(), schemas); + return new Assertion(getResource(), getJsonData(), getJwt(), schemas, ISSUED_ON_PROPERTY_NAME); } } @@ -300,4 +300,5 @@ public class Assertion extends Credential { .build(); public static final String ID = Assertion.class.getCanonicalName(); + private static final String ISSUED_ON_PROPERTY_NAME = "issuedOn"; } diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/Credential.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/Credential.java index 83c19b8..9c7701e 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/Credential.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/Credential.java @@ -29,14 +29,16 @@ public abstract class Credential extends GeneratedObject { final Resource resource; final JsonNode jsonData; final String jwt; + final String issuedOnPropertyName; final Map schemas; - protected Credential(String id, Resource resource, JsonNode data, String jwt, Map schemas) { + protected Credential(String id, Resource resource, JsonNode data, String jwt, Map schemas, String issuedOnPropertyName) { super(id, GeneratedObject.Type.INTERNAL); this.resource = checkNotNull(resource); this.jsonData = checkNotNull(data); this.jwt = jwt; //may be null this.schemas = schemas; + this.issuedOnPropertyName = issuedOnPropertyName; checkTrue(RECOGNIZED_PAYLOAD_TYPES.contains(resource.getType())); } @@ -53,6 +55,10 @@ public abstract class Credential extends GeneratedObject { return Optional.ofNullable(jwt); } + public String getIssuedOnPropertyName() { + return issuedOnPropertyName; + } + /** * Get the canonical schema for this credential if such exists. */ diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/EndorsementInspector.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/EndorsementInspector.java index 6879e79..e21b34f 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/EndorsementInspector.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/EndorsementInspector.java @@ -111,7 +111,7 @@ public class EndorsementInspector extends VCInspector implements SubInspector { } //revocation, expiration and issuance - for(Probe probe : List.of(new RevocationListProbe(), + for(Probe probe : List.of(new RevocationListProbe(), new ExpirationProbe(), new IssuanceProbe())) { probeCount++; accumulator.add(probe.run(endorsement, ctx)); 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 51b48c6..9c29467 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 @@ -194,7 +194,7 @@ public class OB30Inspector extends VCInspector implements SubInspector { } //revocation, expiration and issuance - for(Probe probe : List.of(new RevocationListProbe(), + for(Probe probe : List.of(new RevocationListProbe(), new ExpirationProbe(), new IssuanceProbe())) { probeCount++; accumulator.add(probe.run(ob, ctx)); diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/VerifiableCredential.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/VerifiableCredential.java index e062432..bd3985c 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/VerifiableCredential.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/VerifiableCredential.java @@ -28,8 +28,8 @@ import com.google.common.collect.ImmutableMap; public class VerifiableCredential extends Credential { final VerifiableCredential.Type credentialType; - protected VerifiableCredential(Resource resource, JsonNode data, String jwt, Map schemas) { - super(ID, resource, data, jwt, schemas); + protected VerifiableCredential(Resource resource, JsonNode data, String jwt, Map schemas, String issuedOnPropertyName) { + super(ID, resource, data, jwt, schemas, issuedOnPropertyName); JsonNode typeNode = jsonData.get("type"); this.credentialType = VerifiableCredential.Type.valueOf(typeNode); @@ -133,9 +133,10 @@ public class VerifiableCredential extends Credential { public static class Builder extends Credential.Builder { @Override public VerifiableCredential build() { - return new VerifiableCredential(getResource(), getJsonData(), getJwt(), schemas); + return new VerifiableCredential(getResource(), getJsonData(), getJwt(), schemas, ISSUED_ON_PROPERTY_NAME); } } public static final String ID = VerifiableCredential.class.getCanonicalName(); + private static final String ISSUED_ON_PROPERTY_NAME = "issuanceDate"; } diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/ExpirationProbe.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/ExpirationProbe.java index 0c9092c..b900ad4 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/ExpirationProbe.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/ExpirationProbe.java @@ -5,7 +5,7 @@ import java.time.ZonedDateTime; 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.VerifiableCredential; +import org.oneedtech.inspect.vc.Credential; import com.fasterxml.jackson.databind.JsonNode; @@ -13,14 +13,14 @@ import com.fasterxml.jackson.databind.JsonNode; * A Probe that verifies a credential's expiration status * @author mgylling */ -public class ExpirationProbe extends Probe { +public class ExpirationProbe extends Probe { public ExpirationProbe() { super(ID); } @Override - public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception { + public ReportItems run(Credential crd, RunContext ctx) throws Exception { /* * If the AchievementCredential or EndorsementCredential has an “expirationDate” property * and the expiration date is prior to the current date, the credential has expired. diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/IssuanceProbe.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/IssuanceProbe.java index 7b693ff..2328199 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/IssuanceProbe.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/IssuanceProbe.java @@ -5,7 +5,7 @@ import java.time.ZonedDateTime; 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.VerifiableCredential; +import org.oneedtech.inspect.vc.Credential; import com.fasterxml.jackson.databind.JsonNode; @@ -13,19 +13,19 @@ import com.fasterxml.jackson.databind.JsonNode; * A Probe that verifies a credential's issuance status * @author mgylling */ -public class IssuanceProbe extends Probe { +public class IssuanceProbe extends Probe { public IssuanceProbe() { super(ID); } @Override - public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception { + public ReportItems run(Credential crd, RunContext ctx) throws Exception { /* * If the AchievementCredential or EndorsementCredential “issuanceDate” * property after the current date, the credential is not yet valid. */ - JsonNode node = crd.getJson().get("issuanceDate"); + JsonNode node = crd.getJson().get(crd.getIssuedOnPropertyName()); if(node != null) { try { ZonedDateTime issuanceDate = ZonedDateTime.parse(node.textValue()); diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/RevocationListProbe.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/RevocationListProbe.java index 576c6e4..e083c42 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/RevocationListProbe.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/RevocationListProbe.java @@ -10,6 +10,7 @@ import java.util.List; 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.VerifiableCredential; import org.oneedtech.inspect.vc.util.JsonNodeUtil; @@ -20,14 +21,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; * A Probe that verifies a credential's revocation status. * @author mgylling */ -public class RevocationListProbe extends Probe { +public class RevocationListProbe extends Probe { public RevocationListProbe() { super(ID); } @Override - public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception { + public ReportItems run(Credential crd, RunContext ctx) throws Exception { /* * If the AchievementCredential or EndorsementCredential has a “credentialStatus” property diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/validation/ValidationFlattenEmbeddedResourcePropertyProbe.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/validation/ValidationFlattenEmbeddedResourcePropertyProbe.java new file mode 100644 index 0000000..604f39a --- /dev/null +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/validation/ValidationFlattenEmbeddedResourcePropertyProbe.java @@ -0,0 +1,94 @@ +package org.oneedtech.inspect.vc.probe.validation; + +import java.util.UUID; + +import org.oneedtech.inspect.core.probe.RunContext; +import org.oneedtech.inspect.core.probe.RunContext.Key; +import org.oneedtech.inspect.core.report.ReportItems; +import org.oneedtech.inspect.util.resource.UriResource; +import org.oneedtech.inspect.vc.Validation; +import org.oneedtech.inspect.vc.jsonld.JsonLdGeneratedObject; +import org.oneedtech.inspect.vc.jsonld.probe.JsonLDCompactionProve; +import org.oneedtech.inspect.vc.util.PrimitiveValueValidator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.google.common.io.Resources; + +public class ValidationFlattenEmbeddedResourcePropertyProbe extends ValidationPropertyProbe { + + public ValidationFlattenEmbeddedResourcePropertyProbe(Validation validation) { + super(validation); + } + + public ValidationFlattenEmbeddedResourcePropertyProbe(Validation validation, boolean fullValidate) { + super(validation, fullValidate); + } + + @Override + protected ReportItems reportForNonExistentProperty(JsonNode node, RunContext ctx) { + return notRun("Expected property " + validation.getName() + " was missing in node " + node.toString(), ctx); + } + + @Override + protected ReportItems validate(JsonNode node, RunContext ctx) { + try { + UriResource uriResource = resolveUriResource(ctx, node.asText()); + JsonLdGeneratedObject resolved = (JsonLdGeneratedObject) ctx.getGeneratedObject(JsonLDCompactionProve.getId(uriResource)); + ObjectMapper mapper = (ObjectMapper) ctx.get(Key.JACKSON_OBJECTMAPPER); + JsonNode fetchedNode = mapper.readTree(resolved.getJson()); + + if (fetchedNode.isTextual()) { + return notRun("Property " + validation.getName() + " referenced from " + node.toString() + " is not embedded in need of flattening", ctx); + } + + if (!fetchedNode.isObject()) { + return error("Property " + validation.getName() + " referenced from " + node.toString() + " is not a JSON object or string as expected", ctx); + } + + JsonNode idNode = fetchedNode.get("id"); + if (idNode == null) { + // add a new node to the graph + JsonNode newNode = mapper.readTree(Resources.getResource("contexts/ob-v2p0.json")); + ObjectReader readerForUpdating = mapper.readerForUpdating(newNode); + UUID newId = UUID.randomUUID(); + JsonNode merged = readerForUpdating.readValue("{\"id\": \"_:" + newId + "\"}"); + ctx.addGeneratedObject(new JsonLdGeneratedObject(JsonLDCompactionProve.getId(newId.toString()), merged.toString())); + + return warning("Node id missing at " + node.toString() + ". A blank node ID has been assigned", ctx); + } else if (!idNode.isTextual() && !PrimitiveValueValidator.validateIri(idNode)) { + return error("Embedded JSON object at " + node.asText() + " has no proper assigned id.", ctx); + } else if (/*node_class == Assertion && */ !PrimitiveValueValidator.validateUrl(idNode)) { + /* + if not re.match(URN_REGEX, embedded_node_id, re.IGNORECASE): + actions.append(report_message( + 'ID format for {} at {} not in an expected HTTP or URN:UUID scheme'.format( + embedded_node_id, abv_node(node_path=[node_id, prop_name]) + ))) + new_node = value.copy() + new_node['@context'] = OPENBADGES_CONTEXT_V2_URI + actions.append(add_node(embedded_node_id, data=value)) + actions.append(patch_node(node_id, {prop_name: embedded_node_id})) + */ + + } else { + + /* + actions.append(patch_node(node_id, {prop_name: embedded_node_id})) + + if not node_match_exists(state, embedded_node_id) and not filter_tasks( + state, node_id=embedded_node_id, task_type=FETCH_HTTP_NODE): + # fetch + actions.append(add_task(FETCH_HTTP_NODE, url=embedded_node_id)) + */ + } + } catch (Throwable t) { + return fatal(t.getMessage(), ctx); + } + + return success(ctx); + } + + +}