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 af1833b..3886f84 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 @@ -5,162 +5,207 @@ import static org.oneedtech.inspect.vc.VerifiableCredential.Type.ClrCredential; import static org.oneedtech.inspect.vc.VerifiableCredential.Type.EndorsementCredential; import static org.oneedtech.inspect.vc.VerifiableCredential.Type.VerifiablePresentation; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableMap; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; - import org.oneedtech.inspect.schema.Catalog; import org.oneedtech.inspect.schema.SchemaKey; import org.oneedtech.inspect.util.resource.MimeType; import org.oneedtech.inspect.util.resource.Resource; import org.oneedtech.inspect.vc.util.JsonNodeUtil; -import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.base.MoreObjects; -import com.google.common.collect.ImmutableMap; - /** - * A wrapper object for a verifiable credential. This contains e.g. the origin resource - * and the extracted JSON data plus any other stuff Probes need. + * A wrapper object for a verifiable credential. This contains e.g. the origin resource and the + * extracted JSON data plus any other stuff Probes need. + * * @author mgylling */ -public class VerifiableCredential extends Credential { - final VerifiableCredential.Type credentialType; +public class VerifiableCredential extends Credential { + final VerifiableCredential.Type credentialType; - protected VerifiableCredential(Resource resource, JsonNode data, String jwt, Map schemas, String issuedOnPropertyName, String expiresAtPropertyName) { - super(ID, resource, data, jwt, schemas, issuedOnPropertyName, expiresAtPropertyName); + protected VerifiableCredential( + Resource resource, + JsonNode data, + String jwt, + Map schemas, + String issuedOnPropertyName, + String expiresAtPropertyName) { + super(ID, resource, data, jwt, schemas, issuedOnPropertyName, expiresAtPropertyName); - JsonNode typeNode = jsonData.get("type"); - this.credentialType = VerifiableCredential.Type.valueOf(typeNode); + JsonNode typeNode = jsonData.get("type"); + this.credentialType = VerifiableCredential.Type.valueOf(typeNode); + } + + public CredentialEnum getCredentialType() { + return credentialType; + } + + public ProofType getProofType() { + return jwt == null ? ProofType.EMBEDDED : ProofType.EXTERNAL; + } + + private static final Map schemas = + new ImmutableMap.Builder() + .put(AchievementCredential, Catalog.OB_30_ANY_ACHIEVEMENTCREDENTIAL_JSON) + .put(ClrCredential, Catalog.CLR_20_ANY_CLRCREDENTIAL_JSON) + .put(VerifiablePresentation, Catalog.CLR_20_ANY_CLRCREDENTIAL_JSON) + .put(EndorsementCredential, Catalog.OB_30_ANY_ENDORSEMENTCREDENTIAL_JSON) + .build(); + + private static final Map, List> contextMap = + new ImmutableMap.Builder, List>() + .put( + Set.of(Type.OpenBadgeCredential, AchievementCredential, EndorsementCredential), + List.of( + "https://www.w3.org/ns/credentials/v2", + "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json")) + .put( + Set.of(ClrCredential), + List.of( + "https://www.w3.org/ns/credentials/v2", + "https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1.json", + "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json")) + .build(); + + private static final Map> contextAliasesMap = + new ImmutableMap.Builder>() + .put( + "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json", + List.of( + "https://purl.imsglobal.org/spec/ob/v3p0/context/ob_v3p0.jsonld", + "https://purl.imsglobal.org/spec/ob/v3p0/context.json")) + .put( + "https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1.json", + List.of("https://purl.imsglobal.org/spec/clr/v2p0/context.json")) + .put( + "https://www.w3.org/ns/credentials/v2", + List.of("https://www.w3.org/2018/credentials/v1")) + .build(); + + private static final Map> contextVersioningPatternMap = + new ImmutableMap.Builder>() + .put( + "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json", + List.of( + "https:\\/\\/purl\\.imsglobal\\.org\\/spec\\/ob\\/v3p0\\/context(-\\d+\\.\\d+\\.\\d+)*\\.json")) + .put( + "https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1.json", + List.of( + "https:\\/\\/purl\\.imsglobal\\.org\\/spec\\/clr\\/v2p0\\/context(-\\d+\\.\\d+\\.\\d+)*\\.json")) + .build(); + + public enum Type implements CredentialEnum { + AchievementCredential(Collections.emptyList()), + OpenBadgeCredential( + List.of( + "OpenBadgeCredential", + "AchievementCredential")), // treated as an alias of AchievementCredential + ClrCredential(List.of("ClrCredential")), + EndorsementCredential(List.of("EndorsementCredential")), + VerifiablePresentation(Collections.emptyList()), + VerifiableCredential( + List.of("VerifiableCredential")), // this is an underspecifier in our context + Unknown(Collections.emptyList()); + + private final List allowedTypeValues; + + Type(List allowedTypeValues) { + this.allowedTypeValues = allowedTypeValues; } - public CredentialEnum getCredentialType() { - return credentialType; - } - - public ProofType getProofType() { - return jwt == null ? ProofType.EMBEDDED : ProofType.EXTERNAL; - } - - private static final Map schemas = new ImmutableMap.Builder() - .put(AchievementCredential, Catalog.OB_30_ACHIEVEMENTCREDENTIAL_JSON) - .put(ClrCredential, Catalog.CLR_20_CLRCREDENTIAL_JSON) - .put(VerifiablePresentation, Catalog.CLR_20_CLRCREDENTIAL_JSON) - .put(EndorsementCredential, Catalog.OB_30_ENDORSEMENTCREDENTIAL_JSON) - .build(); - - private static final Map, List> contextMap = new ImmutableMap.Builder, List>() - .put(Set.of(Type.OpenBadgeCredential, AchievementCredential, EndorsementCredential), - List.of("https://www.w3.org/2018/credentials/v1", - "https://purl.imsglobal.org/spec/ob/v3p0/context.json")) - .put(Set.of(ClrCredential), - List.of("https://www.w3.org/2018/credentials/v1", - "https://purl.imsglobal.org/spec/clr/v2p0/context.json", - "https://purl.imsglobal.org/spec/ob/v3p0/context.json")) - - .build(); - - private static final Map> contextAliasesMap = new ImmutableMap.Builder>() - .put("https://purl.imsglobal.org/spec/ob/v3p0/context.json", - List.of("https://purl.imsglobal.org/spec/ob/v3p0/context/ob_v3p0.jsonld")) - .build(); - - private static final Map> contextVersioningPatternMap = new ImmutableMap.Builder>() - .put("https://purl.imsglobal.org/spec/ob/v3p0/context.json", - List.of("https:\\/\\/purl\\.imsglobal\\.org\\/spec\\/ob\\/v3p0\\/context(-\\d+\\.\\d+\\.\\d+)*\\.json")) - .put("https://purl.imsglobal.org/spec/clr/v2p0/context.json", - List.of("https:\\/\\/purl\\.imsglobal\\.org\\/spec\\/clr\\/v2p0\\/context(-\\d+\\.\\d+\\.\\d+)*\\.json")) - .build(); - - public enum Type implements CredentialEnum { - AchievementCredential(Collections.emptyList()), - OpenBadgeCredential(List.of("OpenBadgeCredential", "AchievementCredential")), //treated as an alias of AchievementCredential - ClrCredential(List.of("ClrCredential")), - EndorsementCredential(List.of("EndorsementCredential")), - VerifiablePresentation(Collections.emptyList()), - VerifiableCredential(List.of("VerifiableCredential")), //this is an underspecifier in our context - Unknown(Collections.emptyList()); - - private final List allowedTypeValues; - - Type(List allowedTypeValues) { - this.allowedTypeValues = allowedTypeValues; - } - - public static VerifiableCredential.Type valueOf (JsonNode typeNode) { - if(typeNode != null) { - List values = JsonNodeUtil.asStringList(typeNode); - for (String value : values) { - if(value.equals("AchievementCredential") || value.equals("OpenBadgeCredential")) { - return AchievementCredential; - } else if(value.equals("ClrCredential")) { - return ClrCredential; - } else if(value.equals("VerifiablePresentation")) { - return VerifiablePresentation; - } else if(value.equals("EndorsementCredential")) { - return EndorsementCredential; - } - } - } - return Unknown; - } - - @Override - public List getRequiredTypeValues() { - return List.of("VerifiableCredential"); - } - - @Override - public List getAllowedTypeValues() { - return allowedTypeValues; - } - - @Override - public boolean isAllowedTypeValuesRequired() { - return true; - } - @Override - public List getContextUris() { - return contextMap.get(contextMap.keySet() - .stream() - .filter(s->s.contains(this)) - .findFirst() - .orElseThrow(()-> new IllegalArgumentException(this.name() + " not recognized"))); - } - @Override - public Map> getContextAliases() { - return contextAliasesMap; - } - @Override - public Map> getContextVersionPatterns() { - return contextVersioningPatternMap; - } - } - - public enum ProofType { - EXTERNAL, - EMBEDDED - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("super", super.toString()) - .add("credentialType", credentialType) - .toString(); - } - - public static class Builder extends Credential.Builder { - @Override - public VerifiableCredential build() { - return new VerifiableCredential(getResource(), getJsonData(), getJwt(), schemas, ISSUED_ON_PROPERTY_NAME, EXPIRES_AT_PROPERTY_NAME); + public static VerifiableCredential.Type valueOf(JsonNode typeNode) { + if (typeNode != null) { + List values = JsonNodeUtil.asStringList(typeNode); + for (String value : values) { + if (value.equals("AchievementCredential") || value.equals("OpenBadgeCredential")) { + return AchievementCredential; + } else if (value.equals("ClrCredential")) { + return ClrCredential; + } else if (value.equals("VerifiablePresentation")) { + return VerifiablePresentation; + } else if (value.equals("EndorsementCredential")) { + return EndorsementCredential; + } } + } + return Unknown; } - public static final String ID = VerifiableCredential.class.getCanonicalName(); - private static final String ISSUED_ON_PROPERTY_NAME = "issuanceDate"; - private static final String EXPIRES_AT_PROPERTY_NAME = "expirationDate"; - public static final String JWT_NODE_NAME = "vc"; - public static final List REFRESH_SERVICE_MIME_TYPES = List.of(MimeType.JSON, MimeType.JSON_LD, MimeType.TEXT_PLAIN); + @Override + public List getRequiredTypeValues() { + return List.of("VerifiableCredential"); + } + + @Override + public List getAllowedTypeValues() { + return allowedTypeValues; + } + + @Override + public boolean isAllowedTypeValuesRequired() { + return true; + } + + @Override + public List getContextUris() { + return contextMap.get( + contextMap.keySet().stream() + .filter(s -> s.contains(this)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(this.name() + " not recognized"))); + } + + @Override + public Map> getContextAliases() { + return contextAliasesMap; + } + + @Override + public Map> getContextVersionPatterns() { + return contextVersioningPatternMap; + } + } + + public enum ProofType { + EXTERNAL, + EMBEDDED + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("super", super.toString()) + .add("credentialType", credentialType) + .toString(); + } + + public static class Builder extends Credential.Builder { + @Override + public VerifiableCredential build() { + boolean is2p0VC = JsonNodeUtil.asNodeList(getJsonData().get("@context")) + .stream() + .anyMatch(node -> node.isTextual() && node.asText().equals("https://www.w3.org/ns/credentials/v2")); + + return new VerifiableCredential( + getResource(), + getJsonData(), + getJwt(), + schemas, + is2p0VC ? ISSUED_ON_PROPERTY_NAME_V20 : ISSUED_ON_PROPERTY_NAME_V11, + is2p0VC ? EXPIRES_AT_PROPERTY_NAME_V20 : EXPIRES_AT_PROPERTY_NAME_V11); + } + } + + public static final String ID = VerifiableCredential.class.getCanonicalName(); + private static final String ISSUED_ON_PROPERTY_NAME_V11 = "issuanceDate"; + private static final String ISSUED_ON_PROPERTY_NAME_V20 = "validFrom"; + private static final String EXPIRES_AT_PROPERTY_NAME_V11 = "expirationDate"; + private static final String EXPIRES_AT_PROPERTY_NAME_V20 = "validUntil"; + public static final String JWT_NODE_NAME = "vc"; + public static final List REFRESH_SERVICE_MIME_TYPES = + List.of(MimeType.JSON, MimeType.JSON_LD, MimeType.TEXT_PLAIN); }