Merge pull request #69 from imsglc/feature/upload_uri_in_api
OB 3.0 / CLR 2.0 cert suite
This commit is contained in:
		
						commit
						5f9335155c
					
				| @ -5,7 +5,7 @@ | ||||
| 	<parent> | ||||
| 		<groupId>org.1edtech</groupId> | ||||
| 		<artifactId>inspector</artifactId> | ||||
| 		<version>0.9.10</version> | ||||
| 		<version>0.9.11</version> | ||||
| 	</parent> | ||||
| 	<artifactId>inspector-vc</artifactId> | ||||
| 	<dependencies> | ||||
|  | ||||
| @ -148,6 +148,14 @@ public class Assertion extends Credential { | ||||
|         public List<Validation> getValidations() { | ||||
|             return validationMap.get(this); | ||||
|         } | ||||
|         @Override | ||||
|         public Map<String, List<String>> getContextAliases() { | ||||
|             return Collections.emptyMap(); | ||||
|         } | ||||
|         @Override | ||||
|         public Map<String, List<String>> getContextVersionPatterns() { | ||||
|             return Collections.emptyMap(); | ||||
|         } | ||||
| 	} | ||||
| 
 | ||||
|     public enum ValueType { | ||||
|  | ||||
| @ -92,6 +92,8 @@ public abstract class Credential extends GeneratedObject { | ||||
|         List<String> getAllowedTypeValues(); | ||||
|         boolean isAllowedTypeValuesRequired(); | ||||
|         List<String> getContextUris(); | ||||
|         Map<String, List<String>> getContextAliases(); | ||||
|         Map<String, List<String>> getContextVersionPatterns(); | ||||
|         String toString(); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -188,7 +188,7 @@ public class EndorsementInspector extends VCInspector implements SubInspector { | ||||
| 
 | ||||
| 			//credentialSubject | ||||
| 			probeCount++; | ||||
| 			accumulator.add(new CredentialSubjectProbe().run(endorsement.getJson(), ctx)); | ||||
| 			accumulator.add(new CredentialSubjectProbe("EndorsementSubject").run(endorsement.getJson(), ctx)); | ||||
| 
 | ||||
| 			//signatures, proofs | ||||
| 			probeCount++; | ||||
|  | ||||
| @ -6,6 +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.json.ObjectMapperCache.Config.DEFAULT; | ||||
| import static org.oneedtech.inspect.vc.Credential.CREDENTIAL_KEY; | ||||
| import static org.oneedtech.inspect.vc.VerifiableCredential.REFRESH_SERVICE_MIME_TYPES; | ||||
| import static org.oneedtech.inspect.vc.VerifiableCredential.ProofType.EXTERNAL; | ||||
| import static org.oneedtech.inspect.vc.payload.PayloadParser.fromJwt; | ||||
| import static org.oneedtech.inspect.vc.util.JsonNodeUtil.asNodeList; | ||||
| @ -43,7 +44,9 @@ import org.oneedtech.inspect.vc.probe.CredentialSubjectProbe; | ||||
| import org.oneedtech.inspect.vc.probe.ExpirationProbe; | ||||
| import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe; | ||||
| import org.oneedtech.inspect.vc.probe.IssuanceProbe; | ||||
| import org.oneedtech.inspect.vc.probe.IssuerProbe; | ||||
| import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe; | ||||
| import org.oneedtech.inspect.vc.probe.EvidenceProbe; | ||||
| import org.oneedtech.inspect.vc.probe.RevocationListProbe; | ||||
| import org.oneedtech.inspect.vc.probe.ExternalProofProbe; | ||||
| import org.oneedtech.inspect.vc.probe.TypePropertyProbe; | ||||
| @ -170,7 +173,17 @@ public class OB30Inspector extends VCInspector implements SubInspector { | ||||
| 
 | ||||
| 			//credentialSubject | ||||
| 			probeCount++; | ||||
| 			accumulator.add(new CredentialSubjectProbe().run(ob.getJson(), ctx)); | ||||
| 			accumulator.add(new CredentialSubjectProbe("AchievementSubject", true).run(ob.getJson(), ctx)); | ||||
| 
 | ||||
| 			// evidence | ||||
| 			probeCount++; | ||||
| 			accumulator.add(new EvidenceProbe().run(ob.getJson(), ctx)); | ||||
| 			if(broken(accumulator)) return abort(ctx, accumulator, probeCount); | ||||
| 
 | ||||
| 			// issuer | ||||
| 			probeCount++; | ||||
| 			accumulator.add(new IssuerProbe().run(ob.getJson(), ctx)); | ||||
| 			if(broken(accumulator)) return abort(ctx, accumulator, probeCount); | ||||
| 
 | ||||
| 			//signatures, proofs | ||||
| 			probeCount++; | ||||
| @ -188,9 +201,11 @@ public class OB30Inspector extends VCInspector implements SubInspector { | ||||
| 			if(resource.getContext().get(REFRESHED) != TRUE) { | ||||
| 				Optional<String> newID = checkRefreshService(ob, ctx); | ||||
| 				if(newID.isPresent()) { | ||||
| 					return this.run( | ||||
| 						new UriResource(new URI(newID.get())) | ||||
| 							.setContext(new ResourceContext(REFRESHED, TRUE))); | ||||
| 					// If the refresh is not successful, continue the verification process using the original OpenBadgeCredential. | ||||
| 					UriResource uriResource = new UriResource(new URI(newID.get()), null, REFRESH_SERVICE_MIME_TYPES); | ||||
| 					if (uriResource.exists()) { | ||||
| 						return this.run(uriResource.setContext(new ResourceContext(REFRESHED, TRUE))); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
|  | ||||
| @ -9,10 +9,10 @@ import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| @ -53,17 +53,24 @@ public class VerifiableCredential extends Credential  { | ||||
| 	private static final Map<Set<VerifiableCredential.Type>, List<String>> contextMap = new ImmutableMap.Builder<Set<VerifiableCredential.Type>, List<String>>() | ||||
| 			.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")) //dev legacy | ||||
| 							"https://purl.imsglobal.org/spec/ob/v3p0/context.json")) | ||||
| 			.put(Set.of(ClrCredential), | ||||
| 					List.of("https://www.w3.org/2018/credentials/v1", | ||||
| 		//							"https://dc.imsglobal.org/draft/clr/v2p0/context", //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/ob/v3p0/context.json")) | ||||
| 
 | ||||
| 			.build(); | ||||
| 
 | ||||
| 	private static final Map<String, List<String>> contextAliasesMap = new ImmutableMap.Builder<String, List<String>>() | ||||
| 			.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<String, List<String>> contextVersioningPatternMap = new ImmutableMap.Builder<String, List<String>>() | ||||
| 			.put("https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
| 					List.of("https:\\/\\/purl\\.imsglobal\\.org\\/spec\\/ob\\/v3p0\\/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 | ||||
| @ -119,6 +126,14 @@ public class VerifiableCredential extends Credential  { | ||||
| 				.findFirst() | ||||
| 				.orElseThrow(()-> new IllegalArgumentException(this.name() + " not recognized"))); | ||||
| 		} | ||||
| 		@Override | ||||
| 		public Map<String, List<String>> getContextAliases() { | ||||
| 			return contextAliasesMap; | ||||
| 		} | ||||
| 		@Override | ||||
| 		public Map<String, List<String>> getContextVersionPatterns() { | ||||
| 			return contextVersioningPatternMap; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public enum ProofType { | ||||
| @ -145,4 +160,5 @@ public class VerifiableCredential extends Credential  { | ||||
| 	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<MimeType> REFRESH_SERVICE_MIME_TYPES = List.of(MimeType.JSON, MimeType.JSON_LD, MimeType.TEXT_PLAIN); | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,39 @@ | ||||
| package org.oneedtech.inspect.vc; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import org.oneedtech.inspect.vc.jsonld.JsonLDObjectUtils; | ||||
| 
 | ||||
| import com.danubetech.verifiablecredentials.VerifiableCredential; | ||||
| 
 | ||||
| import foundation.identity.jsonld.ConfigurableDocumentLoader; | ||||
| import info.weboftrust.ldsignatures.LdProof; | ||||
| 
 | ||||
| /** | ||||
|  * Holder for W3C's Verifiable Credential | ||||
|  */ | ||||
| public class W3CVCHolder { | ||||
|     private VerifiableCredential credential; | ||||
| 
 | ||||
|     public W3CVCHolder(VerifiableCredential credential) { | ||||
|         this.credential = credential; | ||||
| 		ConfigurableDocumentLoader documentLoader = new ConfigurableDocumentLoader(); | ||||
| 		documentLoader.setEnableHttp(true); | ||||
| 		documentLoader.setEnableHttps(true); | ||||
| 		credential.setDocumentLoader(documentLoader); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the list of proofs in the credential. | ||||
|      * {@link VerifiableCredential} contains the method getLdProof(), but only works with one proof. This methods | ||||
|      * returns a list of all proofs defined in the credential. | ||||
|      * @return proofs defined in the credential | ||||
|      */ | ||||
|     public List<LdProof> getProofs() { | ||||
|         return JsonLDObjectUtils.getListFromJsonLDObject(LdProof.class, credential); | ||||
|     } | ||||
| 
 | ||||
|     public VerifiableCredential getCredential() { | ||||
|         return credential; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,45 @@ | ||||
| package org.oneedtech.inspect.vc.jsonld; | ||||
| 
 | ||||
| import java.lang.reflect.InvocationTargetException; | ||||
| import java.lang.reflect.Method; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import foundation.identity.jsonld.JsonLDObject; | ||||
| 
 | ||||
| public class JsonLDObjectUtils { | ||||
| 	@SuppressWarnings("unchecked") | ||||
|     public static <C extends JsonLDObject> List<C> getListFromJsonLDObject(Class<C> cl, JsonLDObject jsonLdObject) { | ||||
| 		String term = JsonLDObject.getDefaultJsonLDPredicate(cl); | ||||
| 		List<Map<String, Object>> jsonObjects = jsonLdGetJsonObjectList(jsonLdObject.getJsonObject(), term); | ||||
| 		if (jsonObjects == null) return null; | ||||
| 		try { | ||||
| 			Method method = cl.getMethod("fromMap", Map.class); | ||||
| 			return jsonObjects.stream().map(jsonObject ->  { | ||||
| 				try { | ||||
| 					return (C) method.invoke(null, jsonObject); | ||||
| 				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { | ||||
| 					throw new Error(e); | ||||
| 				} | ||||
| 			}).collect(Collectors.toList()); | ||||
| 		} catch (NoSuchMethodException | SecurityException e) { | ||||
| 			throw new Error(e); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@SuppressWarnings("unchecked") | ||||
|     public static List<Map<String, Object>> jsonLdGetJsonObjectList(Map<String, Object> jsonObject, String term) { | ||||
| 		Object entry = jsonObject.get(term); | ||||
| 		if (entry == null) return null; | ||||
| 
 | ||||
| 		if (entry instanceof Map<?, ?>) { | ||||
| 			return Collections.singletonList((Map<String, Object>) entry); | ||||
| 		} else if (entry instanceof List<?> && ((List<Object>) entry).stream().allMatch(e -> e instanceof Map<?, ?>)) { | ||||
| 			return (List<Map<String, Object>>) (List<Map<String,Object>>) entry; | ||||
| 		} else { | ||||
| 			throw new IllegalArgumentException("Cannot get json object '" + term + "' from " + jsonObject); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -36,7 +36,7 @@ public class ContextPropertyProbe extends StringValuePropertyProbe { | ||||
| 
 | ||||
| 			int pos = 0; | ||||
| 			for (String uri : contextUris) { | ||||
| 				if ((nodeValues.size() < pos + 1) || !nodeValues.get(pos).equals(uri)) { | ||||
| 				if ((nodeValues.size() < pos + 1) || !contains(uri, nodeValues.get(pos))) { | ||||
| 					return error("missing required @context uri " + uri + " at position " + (pos + 1), ctx); | ||||
| 				} | ||||
| 				pos++; | ||||
| @ -46,5 +46,25 @@ public class ContextPropertyProbe extends StringValuePropertyProbe { | ||||
| 		return success(ctx); | ||||
| 	} | ||||
| 
 | ||||
| 	private boolean contains(String uri, String nodeValue) { | ||||
| 		// check equal case | ||||
| 		if (nodeValue.equals(uri)) { | ||||
| 			return true; | ||||
| 		} | ||||
| 		// check aliases | ||||
| 		if (type.getContextAliases().containsKey(uri)) { | ||||
| 			if (type.getContextAliases().get(uri).stream().anyMatch(alias -> nodeValue.equals(alias))) { | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 		// check versioning | ||||
| 		if (type.getContextVersionPatterns().containsKey(uri)) { | ||||
| 			if (type.getContextVersionPatterns().get(uri).stream().anyMatch(version -> nodeValue.matches(version))) { | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	public static final String ID = ContextPropertyProbe.class.getSimpleName(); | ||||
| } | ||||
|  | ||||
| @ -1,8 +1,11 @@ | ||||
| package org.oneedtech.inspect.vc.probe; | ||||
| 
 | ||||
| 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.util.JsonNodeUtil; | ||||
| 
 | ||||
| import com.fasterxml.jackson.databind.JsonNode; | ||||
| import com.fasterxml.jackson.databind.node.ArrayNode; | ||||
| @ -14,8 +17,20 @@ import com.fasterxml.jackson.databind.node.ArrayNode; | ||||
|  */ | ||||
| public class CredentialSubjectProbe extends Probe<JsonNode> { | ||||
| 
 | ||||
| 	public CredentialSubjectProbe() { | ||||
| 	/** | ||||
| 	 * Required type to be present. | ||||
| 	 */ | ||||
| 	private final String requiredType; | ||||
| 	private boolean achivementRequired; | ||||
| 
 | ||||
| 	public CredentialSubjectProbe(String requiredType) { | ||||
| 		this(requiredType, false); | ||||
| 	} | ||||
| 
 | ||||
| 	public CredentialSubjectProbe(String requiredType, boolean achivementRequired) { | ||||
| 		super(ID); | ||||
| 		this.requiredType = requiredType; | ||||
| 		this.achivementRequired = achivementRequired; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| @ -24,18 +39,86 @@ public class CredentialSubjectProbe extends Probe<JsonNode> { | ||||
| 		JsonNode subject = root.get("credentialSubject"); | ||||
| 		if(subject == null) return notRun("no credentialSubject node found", ctx); //error reported by schema | ||||
| 
 | ||||
| 		/** | ||||
| 		 * Check that type contains AchievementSubject | ||||
| 		 */ | ||||
| 		if (!JsonNodeUtil.asStringList(subject.get("type")).contains(requiredType)) { | ||||
| 			return error("credentialSubject is not of type \"" + requiredType + "\"", ctx); | ||||
| 		} | ||||
| 
 | ||||
| 		/* | ||||
| 		 * Check that we have either .id or .identifier populated | ||||
| 		 */ | ||||
| 		if (idAndIdentifierEmpty(subject)) { | ||||
| 			return error("no id in credentialSubject", ctx); | ||||
| 		} | ||||
| 
 | ||||
| 		/** | ||||
| 		 * if .identifier is provider, check its type | ||||
| 		 */ | ||||
| 		if (subject.hasNonNull("identifier")) { | ||||
| 			List<JsonNode> identifiers = JsonNodeUtil.asNodeList(subject.get("identifier")); | ||||
| 			for (JsonNode identifier : identifiers) { | ||||
| 				// check that type contains "IdentityObject" | ||||
| 				if (!JsonNodeUtil.asStringList(identifier.get("type")).contains("IdentityObject")) { | ||||
| 					return error("identifier in credentialSubject is not of type \"IdentityObject\"", ctx); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* | ||||
| 		 * Check results | ||||
| 		 */ | ||||
| 		if (subject.hasNonNull("result")) { | ||||
| 			List<JsonNode> results = JsonNodeUtil.asNodeList(subject.get("result")); | ||||
| 			for (JsonNode result : results) { | ||||
| 				// check that type contains "Result" | ||||
| 				if (!JsonNodeUtil.asStringList(result.get("type")).contains("Result")) { | ||||
| 					return error("result in credentialSubject is not of type \"Result\"", ctx); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* | ||||
| 		 * Check achievement result description | ||||
| 		 */ | ||||
| 		if (subject.hasNonNull("achievement")) { | ||||
| 			JsonNode achievement = subject.get("achievement"); | ||||
| 			if (achievement.hasNonNull("resultDescription")) { | ||||
| 				List<JsonNode> resultDescriptions = JsonNodeUtil.asNodeList(achievement.get("resultDescription")); | ||||
| 				for (JsonNode resultDescription : resultDescriptions) { | ||||
| 					// check that type contains "ResultDescription" | ||||
| 					if (!JsonNodeUtil.asStringList(resultDescription.get("type")).contains("ResultDescription")) { | ||||
| 						return error("resultDescription in achievement of credentialSubject is not of type \"ResultDescription\"", ctx); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} else if (achivementRequired) { | ||||
| 			return error("missing required achievement in credentialSubject", ctx); | ||||
| 		} | ||||
| 
 | ||||
| 		/** | ||||
| 		 * Check that source type contains "Profile" | ||||
| 		 */ | ||||
| 		if (subject.hasNonNull("source")) { | ||||
| 			JsonNode source = subject.get("source"); | ||||
| 			// check that type contains "Profile" | ||||
| 			if (!JsonNodeUtil.asStringList(source.get("type")).contains("Profile")) { | ||||
| 				return error("source in credentialSubject is not of type \"Profile\"", ctx); | ||||
| 			} | ||||
| 		} | ||||
| 		return success(ctx); | ||||
| 	} | ||||
| 
 | ||||
| 	private boolean idAndIdentifierEmpty(JsonNode root) { | ||||
| 		JsonNode id = root.get("id"); | ||||
| 		if (id != null && id.textValue().strip().length() > 0) return success(ctx); | ||||
| 		if (id != null && id.textValue().strip().length() > 0) return false; | ||||
| 
 | ||||
| 		JsonNode identifier = root.get("identifier"); | ||||
| 		if(identifier != null && identifier instanceof ArrayNode | ||||
| 				&& ((ArrayNode)identifier).size() > 0) return success(ctx); | ||||
| 					 | ||||
| 		return error("no id in credentialSubject", ctx); | ||||
| 				&& ((ArrayNode)identifier).size() > 0) return false; | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	public static final String ID = CredentialSubjectProbe.class.getSimpleName(); | ||||
|  | ||||
| @ -2,13 +2,14 @@ package org.oneedtech.inspect.vc.probe; | ||||
| 
 | ||||
| import java.io.StringReader; | ||||
| import java.net.URI; | ||||
| import java.util.List; | ||||
| import java.util.Optional; | ||||
| 
 | ||||
| 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.util.CachingDocumentLoader; | ||||
| import org.oneedtech.inspect.vc.W3CVCHolder; | ||||
| 
 | ||||
| import com.apicatalog.jsonld.StringUtils; | ||||
| import com.apicatalog.jsonld.document.Document; | ||||
| @ -17,7 +18,6 @@ import com.apicatalog.multibase.Multibase; | ||||
| import com.apicatalog.multicodec.Multicodec; | ||||
| import com.apicatalog.multicodec.Multicodec.Codec; | ||||
| 
 | ||||
| import foundation.identity.jsonld.ConfigurableDocumentLoader; | ||||
| import info.weboftrust.ldsignatures.LdProof; | ||||
| import info.weboftrust.ldsignatures.verifier.Ed25519Signature2020LdVerifier; | ||||
| import jakarta.json.JsonObject; | ||||
| @ -41,18 +41,23 @@ public class EmbeddedProofProbe extends Probe<VerifiableCredential> { | ||||
| 	@Override | ||||
| 	public ReportItems run(VerifiableCredential crd, RunContext ctx) throws Exception { | ||||
| 
 | ||||
| 		// TODO: What there are multiple proofs? | ||||
| 		W3CVCHolder credentiaHolder = new W3CVCHolder(com.danubetech.verifiablecredentials.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(); | ||||
| 		documentLoader.setEnableHttp(true); | ||||
| 		documentLoader.setEnableHttps(true); | ||||
| 		vc.setDocumentLoader(documentLoader); | ||||
| 
 | ||||
| 		LdProof proof = vc.getLdProof(); | ||||
| 		if (proof == null) { | ||||
| 		List<LdProof> proofs = credentiaHolder.getProofs(); | ||||
| 		if (proofs == null || proofs.size() == 0) { | ||||
| 			return error("The verifiable credential is missing a proof.", ctx); | ||||
| 		} | ||||
| 
 | ||||
| 		// get proof of standard type and purpose | ||||
| 		Optional<LdProof> selectedProof = proofs.stream().filter(proof -> proof.isType("Ed25519Signature2020") && proof.getProofPurpose().equals("assertionMethod")) | ||||
| 			.findFirst(); | ||||
| 
 | ||||
| 		if (!selectedProof.isPresent()) { | ||||
| 			return error("No proof with type \"Ed25519Signature2020\" or proof purpose \"assertionMethod\" found", ctx); | ||||
| 		} | ||||
| 
 | ||||
| 		LdProof proof = selectedProof.get(); | ||||
| 
 | ||||
| 		if (!proof.isType("Ed25519Signature2020")) { | ||||
| 			return error("Unknown proof type: " + proof.getType(), ctx); | ||||
| 		} | ||||
| @ -92,7 +97,7 @@ public class EmbeddedProofProbe extends Probe<VerifiableCredential> { | ||||
| 				} | ||||
| 			} else if (method.getScheme().equals("http") || method.getScheme().equals("https")) { | ||||
| 				try { | ||||
| 					Document keyDocument = vc.getDocumentLoader().loadDocument(method, new DocumentLoaderOptions()); | ||||
| 					Document keyDocument = credentiaHolder.getCredential().getDocumentLoader().loadDocument(method, new DocumentLoaderOptions()); | ||||
| 					Optional<JsonStructure> keyStructure = keyDocument.getJsonContent(); | ||||
| 					if (keyStructure.isEmpty()) { | ||||
| 						return error("Key document not found at " + method, ctx); | ||||
| @ -135,8 +140,8 @@ public class EmbeddedProofProbe extends Probe<VerifiableCredential> { | ||||
| 		} | ||||
| 
 | ||||
| 		if (controller != null) { | ||||
| 			if (!controller.equals(vc.getIssuer().toString())) { | ||||
| 				return error("Key controller does not match issuer: " + vc.getIssuer(), ctx); | ||||
| 			if (!controller.equals(credentiaHolder.getCredential().getIssuer().toString())) { | ||||
| 				return error("Key controller does not match issuer: " + credentiaHolder.getCredential().getIssuer(), ctx); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| @ -146,7 +151,7 @@ public class EmbeddedProofProbe extends Probe<VerifiableCredential> { | ||||
| 		Ed25519Signature2020LdVerifier verifier = new Ed25519Signature2020LdVerifier(publicKey); | ||||
| 
 | ||||
| 		try { | ||||
| 			boolean verify = verifier.verify(vc); | ||||
| 			boolean verify = verifier.verify(credentiaHolder.getCredential(), proof); | ||||
| 			if (!verify) { | ||||
| 				return error("Embedded proof verification failed.", ctx); | ||||
| 			} | ||||
|  | ||||
| @ -0,0 +1,38 @@ | ||||
| package org.oneedtech.inspect.vc.probe; | ||||
| 
 | ||||
| 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.util.JsonNodeUtil; | ||||
| 
 | ||||
| import com.fasterxml.jackson.databind.JsonNode; | ||||
| 
 | ||||
| public class EvidenceProbe extends Probe<JsonNode> { | ||||
|     public EvidenceProbe() { | ||||
| 		super(ID); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public ReportItems run(JsonNode root, RunContext ctx) throws Exception { | ||||
| 
 | ||||
|         if (root.hasNonNull("evidence")) { | ||||
|             /* | ||||
|              * evidence is an array, so check type of each element | ||||
|              */ | ||||
|             List<JsonNode> evidences = JsonNodeUtil.asNodeList(root.get("evidence")); | ||||
|             for (JsonNode evidence : evidences) { | ||||
|                 // check that type contains "Evidence" | ||||
|                 if (!JsonNodeUtil.asStringList(evidence.get("type")).contains("Evidence")) { | ||||
|                     return error("evidence is not of type \"Evidence\"", ctx); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return success(ctx); | ||||
|     } | ||||
| 
 | ||||
|     public static final String ID = EvidenceProbe.class.getSimpleName(); | ||||
| 
 | ||||
| } | ||||
| @ -50,7 +50,7 @@ public class ExternalProofProbe extends Probe<VerifiableCredential> { | ||||
| 		try { | ||||
| 			verifySignature(crd, ctx); | ||||
| 		} catch (Exception e) { | ||||
| 			return fatal("Error verifying jwt signature: " + e.getMessage(), ctx); | ||||
| 			return fatal("Error verifying jwt signature: " + e.getMessage() + (e.getCause() != null ? ". Reason: " + e.getCause().getMessage() : ""), ctx); | ||||
| 		} | ||||
| 		return success(ctx); | ||||
| 	} | ||||
| @ -75,7 +75,9 @@ public class ExternalProofProbe extends Probe<VerifiableCredential> { | ||||
| 		JsonNode alg = headerObj.get("alg"); | ||||
| 		if(alg == null || !alg.textValue().equals("RS256")) { throw new Exception("alg must be present and must be 'RS256'"); } | ||||
| 
 | ||||
| 		//TODO: decoded jwt will check timestamps, but shall we explicitly break these out? | ||||
| 		// decoded jwt will check timestamps, but shall we explicitly break these out? | ||||
| 		// JWT verifier throws and exception with the cause when claims are invalid. Adding that cause | ||||
| 		// to the probe result can avoid having to explicitly check the claims. | ||||
| 
 | ||||
| 		//Option 1, fetch directly from header | ||||
| 		JsonNode jwk = headerObj.get("jwk"); | ||||
|  | ||||
| @ -12,6 +12,7 @@ import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe; | ||||
| import org.oneedtech.inspect.core.report.ReportItems; | ||||
| import org.oneedtech.inspect.schema.SchemaKey; | ||||
| import org.oneedtech.inspect.vc.VerifiableCredential; | ||||
| import org.oneedtech.inspect.vc.util.JsonNodeUtil; | ||||
| 
 | ||||
| import com.fasterxml.jackson.databind.JsonNode; | ||||
| import com.fasterxml.jackson.databind.node.ArrayNode; | ||||
| @ -42,7 +43,7 @@ public class InlineJsonSchemaProbe extends Probe<JsonNode> { | ||||
| 		JsonNode credentialSchemaNode = root.get("credentialSchema"); | ||||
| 		if(credentialSchemaNode == null) return success(ctx); | ||||
| 
 | ||||
| 		ArrayNode schemas = (ArrayNode)	credentialSchemaNode; //TODO guard this cast | ||||
| 		List<JsonNode> schemas = JsonNodeUtil.asNodeList(credentialSchemaNode); | ||||
| 
 | ||||
| 		for(JsonNode schemaNode : schemas) { | ||||
| 			JsonNode typeNode = schemaNode.get("type"); | ||||
|  | ||||
| @ -0,0 +1,66 @@ | ||||
| package org.oneedtech.inspect.vc.probe; | ||||
| 
 | ||||
| import java.net.URI; | ||||
| 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.util.resource.UriResource; | ||||
| import org.oneedtech.inspect.vc.util.JsonNodeUtil; | ||||
| 
 | ||||
| import com.fasterxml.jackson.databind.JsonNode; | ||||
| 
 | ||||
| public class IssuerProbe extends Probe<JsonNode> { | ||||
|     public IssuerProbe() { | ||||
| 		super(ID); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public ReportItems run(JsonNode root, RunContext ctx) throws Exception { | ||||
|         JsonNode issuer = root.get("issuer"); | ||||
|         if(issuer == null) return error("no issuer node found", ctx); | ||||
| 
 | ||||
|         // check that type contains "Profile" | ||||
|         if (!JsonNodeUtil.asStringList(issuer.get("type")).contains("Profile")) { | ||||
|             return error("issuer is not of type \"Profile\"", ctx); | ||||
|         } | ||||
| 
 | ||||
|         // check url is accessible | ||||
|         if (issuer.hasNonNull("url")) { | ||||
|             try { | ||||
|                 UriResource urlResource = new UriResource(new URI(issuer.get("url").asText().strip())); | ||||
|                 if (!urlResource.exists()) { | ||||
|                     return warning("url \"" + issuer.get("url").asText().strip() + "\" in issuer is not accessible", ctx); | ||||
|                 } | ||||
|             } catch (Exception e) { | ||||
|                 return warning("url \"" + issuer.get("url").asText().strip() + "\" in issuer is not accessible", ctx); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // check other identifier | ||||
|         if (issuer.hasNonNull("otherIdentifier")) { | ||||
| 			List<JsonNode> otherIdentifiers = JsonNodeUtil.asNodeList(issuer.get("otherIdentifier")); | ||||
| 			for (JsonNode otherIdentifier : otherIdentifiers) { | ||||
| 				// check that type contains "IdentityObject" | ||||
| 				if (!JsonNodeUtil.asStringList(otherIdentifier.get("type")).contains("IdentityObject")) { | ||||
| 					return error("otherIdentifier in issuer is not of type \"IdentityObject\"", ctx); | ||||
| 				} | ||||
| 			} | ||||
|         } | ||||
| 
 | ||||
|         // check parent issuer | ||||
|         if (issuer.hasNonNull("parentOrg")) { | ||||
| 			JsonNode parentOrg = issuer.get("parentOrg"); | ||||
|             // check that type contains "Profile" | ||||
|             if (!JsonNodeUtil.asStringList(parentOrg.get("type")).contains("Profile")) { | ||||
|                 return error("parentOrg in issuer is not of type \"Profile\"", ctx); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return success(ctx); | ||||
|     } | ||||
| 
 | ||||
|     public static final String ID = IssuerProbe.class.getSimpleName(); | ||||
| 
 | ||||
| } | ||||
| @ -3,6 +3,7 @@ package org.oneedtech.inspect.vc.probe; | ||||
| import static org.oneedtech.inspect.core.probe.RunContext.Key.JACKSON_OBJECTMAPPER; | ||||
| 
 | ||||
| import java.io.InputStream; | ||||
| import java.net.HttpURLConnection; | ||||
| import java.net.URI; | ||||
| import java.net.URL; | ||||
| import java.util.List; | ||||
| @ -10,6 +11,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.util.resource.MimeType; | ||||
| import org.oneedtech.inspect.vc.Credential; | ||||
| import org.oneedtech.inspect.vc.VerifiableCredential; | ||||
| import org.oneedtech.inspect.vc.util.JsonNodeUtil; | ||||
| @ -45,7 +47,9 @@ public class RevocationListProbe extends Probe<Credential> { | ||||
| 				if(listID != null) { | ||||
| 					try { | ||||
| 						URL url = new URI(listID.asText().strip()).toURL(); | ||||
| 						try (InputStream is = url.openStream()) { | ||||
| 						HttpURLConnection connection = (HttpURLConnection) url.openConnection(); | ||||
| 						connection.setRequestProperty("Accept", MimeType.JSON.toString()); | ||||
| 						try (InputStream is = connection.getInputStream()) { | ||||
| 					        JsonNode revocList = ((ObjectMapper)ctx.get(JACKSON_OBJECTMAPPER)).readTree(is.readAllBytes()); | ||||
| 
 | ||||
| 					        /* To check if a credential has been revoked, the verifier issues a GET request | ||||
|  | ||||
| @ -108,6 +108,7 @@ public class CachingDocumentLoader extends ConfigurableDocumentLoader { | ||||
| 
 | ||||
| 	static final ImmutableMap<String, URL> bundled = ImmutableMap.<String, URL>builder() | ||||
| 			.put("https://purl.imsglobal.org/spec/clr/v2p0/context.json",Resources.getResource("contexts/clr-v2p0.json")) | ||||
| 			.put("https://purl.imsglobal.org/spec/ob/v3p0/context/ob_v3p0.jsonld",Resources.getResource("contexts/ob-v3p0.json")) | ||||
| 			.put("https://purl.imsglobal.org/spec/ob/v3p0/context.json",Resources.getResource("contexts/ob-v3p0.json")) | ||||
| 			.put("https://purl.imsglobal.org/spec/ob/v3p0/extensions.json",Resources.getResource("contexts/ob-v3p0-extensions.json")) | ||||
| 			.put("https://www.w3.org/ns/did/v1", Resources.getResource("contexts/did-v1.jsonld")) | ||||
|  | ||||
| @ -11,10 +11,13 @@ import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe; | ||||
| import org.oneedtech.inspect.core.report.Report; | ||||
| import org.oneedtech.inspect.test.PrintHelper; | ||||
| import org.oneedtech.inspect.vc.probe.ContextPropertyProbe; | ||||
| import org.oneedtech.inspect.vc.probe.CredentialSubjectProbe; | ||||
| import org.oneedtech.inspect.vc.probe.ExpirationProbe; | ||||
| import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe; | ||||
| import org.oneedtech.inspect.vc.probe.IssuanceProbe; | ||||
| import org.oneedtech.inspect.vc.probe.IssuerProbe; | ||||
| import org.oneedtech.inspect.vc.probe.EmbeddedProofProbe; | ||||
| import org.oneedtech.inspect.vc.probe.EvidenceProbe; | ||||
| import org.oneedtech.inspect.vc.probe.TypePropertyProbe; | ||||
| 
 | ||||
| import com.google.common.collect.Iterables; | ||||
| @ -49,6 +52,15 @@ public class OB30Tests { | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleMultipleProofsJsonValid() { | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_MULTIPLE_PROOF_JSON.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertValid(report); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimplePNGPlainValid() { | ||||
| 		assertDoesNotThrow(()->{ | ||||
| @ -180,6 +192,26 @@ public class OB30Tests { | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonContextAlias() { | ||||
| 		//removed one of the reqd context uris | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_ALIAS_CONTEXT.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertValid(report); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonContextVersion() { | ||||
| 		//removed one of the reqd context uris | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_VERSION_CONTEXT.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertHasValidProbeID(report, ContextPropertyProbe.ID); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonSchemaError() throws Exception { | ||||
| 		//issuer removed | ||||
| @ -191,6 +223,114 @@ public class OB30Tests { | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidCredentialSubjectType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, CredentialSubjectProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidCredentialSubjectIdentifierType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_IDENTIFIER_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, CredentialSubjectProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidCredentialSubjectResultType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_RESULT_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, CredentialSubjectProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidCredentialSubjectAchievementResultDescriptionType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_ACHIEVEMENT_RESULT_DESCRIPTION_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, CredentialSubjectProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidCredentialSubjectProfileType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_PROFILE_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, CredentialSubjectProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidEvidenceType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_EVIDENCE_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, EvidenceProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidIssuerType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_ISSUER_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, IssuerProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidIssuerParentOrgType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_ISSUER_PARENTORG_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, IssuerProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	void testSimpleJsonInvalidIssuerOtherIdentifierType() { | ||||
| 		//add a dumb value to .type and remove the ob type | ||||
| 		assertDoesNotThrow(()->{ | ||||
| 			Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_UNKNOWN_ISSUER_OTHERIDENTIFIER_TYPE.asFileResource()); | ||||
| 			if(verbose) PrintHelper.print(report, true); | ||||
| 			assertInvalid(report); | ||||
| 			// assertFatalCount(report, 1); | ||||
| 			assertHasProbeID(report, IssuerProbe.ID, true); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	@Disabled //TODO IssuanceVerifierProbe is not run because FATAL: InvalidSignature terminates | ||||
| 	@Test | ||||
| 	void testSimpleJsonNotIssued() { | ||||
|  | ||||
| @ -12,8 +12,18 @@ public class Samples { | ||||
| 			public final static Sample COMPLETE_JSON = new Sample("ob30/complete.json", false); | ||||
| 			public final static Sample SIMPLE_JSON = new Sample("ob30/simple.json", true); | ||||
| 			public final static Sample SIMPLE_DID_METHOD_JSON = new Sample("ob30/simple-did-method.json", true); | ||||
| 			public final static Sample SIMPLE_MULTIPLE_PROOF_JSON = new Sample("ob30/simple-multiple-proofs.json", true); | ||||
| 			public final static Sample SIMPLE_JSON_NOPROOF = new Sample("ob30/simple-noproof.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_TYPE = new Sample("ob30/simple-err-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_TYPE = new Sample("ob30/simple-err-credential-subject-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_IDENTIFIER_TYPE = new Sample("ob30/simple-err-credential-subject-identifier-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_RESULT_TYPE = new Sample("ob30/simple-err-credential-subject-result-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_ACHIEVEMENT_RESULT_DESCRIPTION_TYPE = new Sample("ob30/simple-err-credential-subject-achievement-result-description-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_CREDENTIAL_SUBJECT_PROFILE_TYPE = new Sample("ob30/simple-err-credential-subject-profile-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_EVIDENCE_TYPE = new Sample("ob30/simple-err-evidence-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_ISSUER_TYPE = new Sample("ob30/simple-err-issuer-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_ISSUER_OTHERIDENTIFIER_TYPE = new Sample("ob30/simple-err-issuer-otheridentifier-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_UNKNOWN_ISSUER_PARENTORG_TYPE = new Sample("ob30/simple-err-issuer-parentorg-type.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_PROOF_METHOD_ERROR = new Sample("ob30/simple-err-proof-method.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_PROOF_METHOD_NO_SCHEME_ERROR = new Sample("ob30/simple-err-proof-method-no-scheme.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_PROOF_METHOD_UNKNOWN_SCHEME_ERROR = new Sample("ob30/simple-err-proof-method-unknown-scheme.json", false); | ||||
| @ -25,6 +35,8 @@ public class Samples { | ||||
| 			public final static Sample SIMPLE_JSON_ERR_CONTEXT = new Sample("ob30/simple-err-context.json", false); | ||||
| 			public final static Sample ENDORSEMENT_ERR_SCHEMA_STATUS_REFRESH = new Sample("ob30/endorsement-err-schema-status-refresh.json", false); | ||||
| 			public final static Sample ENDORSEMENT_VALID = new Sample("ob30/endorsement-valid.json", false); | ||||
| 			public final static Sample SIMPLE_JSON_ALIAS_CONTEXT = new Sample("ob30/simple-context-alias.json", true); | ||||
| 			public final static Sample SIMPLE_JSON_VERSION_CONTEXT = new Sample("ob30/simple-context-version.json", true); | ||||
| 		} | ||||
| 		public static final class PNG { | ||||
| 			public final static Sample SIMPLE_JWT_PNG = new Sample("ob30/simple-jwt.png", true); | ||||
|  | ||||
| @ -0,0 +1,47 @@ | ||||
| { | ||||
|   "@context": [ | ||||
|     "https://www.w3.org/2018/credentials/v1", | ||||
|     "https://purl.imsglobal.org/spec/ob/v3p0/context/ob_v3p0.jsonld", | ||||
|     "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|   ], | ||||
|   "id": "http://example.com/credentials/3527", | ||||
|   "type": [ | ||||
|     "VerifiableCredential", | ||||
|     "OpenBadgeCredential" | ||||
|   ], | ||||
|   "issuer": { | ||||
|     "id": "https://example.com/issuers/876543", | ||||
|     "type": [ | ||||
|       "Profile" | ||||
|     ], | ||||
|     "name": "Example Corp" | ||||
|   }, | ||||
|   "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|   "name": "Teamwork Badge", | ||||
|   "credentialSubject": { | ||||
|     "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|     "type": [ | ||||
|       "AchievementSubject" | ||||
|     ], | ||||
|     "achievement": { | ||||
|       "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|       "type": [ | ||||
|         "Achievement" | ||||
|       ], | ||||
|       "criteria": { | ||||
|         "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|       }, | ||||
|       "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|       "name": "Teamwork" | ||||
|     } | ||||
|   }, | ||||
|   "proof": [ | ||||
|     { | ||||
|       "type": "Ed25519Signature2020", | ||||
|       "created": "2022-11-16T18:54:22Z", | ||||
|       "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|       "proofPurpose": "assertionMethod", | ||||
|       "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @ -0,0 +1,47 @@ | ||||
| { | ||||
|   "@context": [ | ||||
|     "https://www.w3.org/2018/credentials/v1", | ||||
|     "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.1.json", | ||||
|     "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|   ], | ||||
|   "id": "http://example.com/credentials/3527", | ||||
|   "type": [ | ||||
|     "VerifiableCredential", | ||||
|     "OpenBadgeCredential" | ||||
|   ], | ||||
|   "issuer": { | ||||
|     "id": "https://example.com/issuers/876543", | ||||
|     "type": [ | ||||
|       "Profile" | ||||
|     ], | ||||
|     "name": "Example Corp" | ||||
|   }, | ||||
|   "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|   "name": "Teamwork Badge", | ||||
|   "credentialSubject": { | ||||
|     "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|     "type": [ | ||||
|       "AchievementSubject" | ||||
|     ], | ||||
|     "achievement": { | ||||
|       "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|       "type": [ | ||||
|         "Achievement" | ||||
|       ], | ||||
|       "criteria": { | ||||
|         "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|       }, | ||||
|       "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|       "name": "Teamwork" | ||||
|     } | ||||
|   }, | ||||
|   "proof": [ | ||||
|     { | ||||
|       "type": "Ed25519Signature2020", | ||||
|       "created": "2022-11-16T18:54:22Z", | ||||
|       "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|       "proofPurpose": "assertionMethod", | ||||
|       "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @ -0,0 +1,188 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|         "https://www.w3.org/2018/credentials/v1", | ||||
|         "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|         "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|         "VerifiableCredential", | ||||
|         "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|         "id": "https://example.com/issuers/876543", | ||||
|         "type": [ | ||||
|             "Profile" | ||||
|         ], | ||||
|         "name": "Example Corp" | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|         "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|         "type": [ | ||||
|             "AchievementSubject" | ||||
|         ], | ||||
|         "achievement": { | ||||
|             "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|             "type": [ | ||||
|                 "Achievement" | ||||
|             ], | ||||
|             "criteria": { | ||||
|                 "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|             }, | ||||
|             "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|             "name": "Teamwork", | ||||
|             "resultDescription": [ | ||||
|                 { | ||||
|                     "id": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c", | ||||
|                     "type": "ResultDescription", | ||||
|                     "alignment": [ | ||||
|                         { | ||||
|                             "type": "Alignment", | ||||
|                             "targetCode": "project", | ||||
|                             "targetDescription": "Project description", | ||||
|                             "targetName": "Final Project", | ||||
|                             "targetFramework": "1EdTech University Program and Course Catalog", | ||||
|                             "targetType": "CFItem", | ||||
|                             "targetUrl": "https://1edtech.edu/catalog/degree/project" | ||||
|                         } | ||||
|                     ], | ||||
|                     "allowedValue": [ | ||||
|                         "D", | ||||
|                         "C", | ||||
|                         "B", | ||||
|                         "A" | ||||
|                     ], | ||||
|                     "name": "Final Project Grade", | ||||
|                     "requiredValue": "C", | ||||
|                     "resultType": "LetterGrade" | ||||
|                 }, | ||||
|                 { | ||||
|                     "id": "urn:uuid:a70ddc6a-4c4a-4bd8-8277-cb97c79f40c5", | ||||
|                     "type": "ResultDescription", | ||||
|                     "alignment": [ | ||||
|                         { | ||||
|                             "type": "Alignment", | ||||
|                             "targetCode": "project", | ||||
|                             "targetDescription": "Project description", | ||||
|                             "targetName": "Final Project", | ||||
|                             "targetFramework": "1EdTech University Program and Course Catalog", | ||||
|                             "targetType": "CFItem", | ||||
|                             "targetUrl": "https://1edtech.edu/catalog/degree/project" | ||||
|                         } | ||||
|                     ], | ||||
|                     "allowedValue": [ | ||||
|                         "D", | ||||
|                         "C", | ||||
|                         "B", | ||||
|                         "A" | ||||
|                     ], | ||||
|                     "name": "Final Project Grade", | ||||
|                     "requiredLevel": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a", | ||||
|                     "resultType": "RubricCriterionLevel", | ||||
|                     "rubricCriterionLevel": [ | ||||
|                         { | ||||
|                             "id": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a", | ||||
|                             "type": "RubricCriterionLevel", | ||||
|                             "alignment": [ | ||||
|                                 { | ||||
|                                     "type": "Alignment", | ||||
|                                     "targetCode": "project", | ||||
|                                     "targetDescription": "Project description", | ||||
|                                     "targetName": "Final Project", | ||||
|                                     "targetFramework": "1EdTech University Program and Course Catalog", | ||||
|                                     "targetType": "CFRubricCriterionLevel", | ||||
|                                     "targetUrl": "https://1edtech.edu/catalog/degree/project/rubric/levels/mastered" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "description": "The author demonstrated...", | ||||
|                             "level": "Mastered", | ||||
|                             "name": "Mastery", | ||||
|                             "points": "4" | ||||
|                         }, | ||||
|                         { | ||||
|                             "id": "urn:uuid:6b84b429-31ee-4dac-9d20-e5c55881f80e", | ||||
|                             "type": "RubricCriterionLevel", | ||||
|                             "alignment": [ | ||||
|                                 { | ||||
|                                     "type": "Alignment", | ||||
|                                     "targetCode": "project", | ||||
|                                     "targetDescription": "Project description", | ||||
|                                     "targetName": "Final Project", | ||||
|                                     "targetFramework": "1EdTech University Program and Course Catalog", | ||||
|                                     "targetType": "CFRubricCriterionLevel", | ||||
|                                     "targetUrl": "https://1edtech.edu/catalog/degree/project/rubric/levels/basic" | ||||
|                                 } | ||||
|                             ], | ||||
|                             "description": "The author demonstrated...", | ||||
|                             "level": "Basic", | ||||
|                             "name": "Basic", | ||||
|                             "points": "4" | ||||
|                         } | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "id": "urn:uuid:b07c0387-f2d6-4b65-a3f4-f4e4302ea8f7", | ||||
|                     "type": "InvalidResultDescription", | ||||
|                     "name": "Project Status", | ||||
|                     "resultType": "Status" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "result": [ | ||||
|             { | ||||
|                 "type": [ | ||||
|                     "Result" | ||||
|                 ], | ||||
|                 "alignment": [ | ||||
|                     { | ||||
|                         "type": "Alignment", | ||||
|                         "targetCode": "project", | ||||
|                         "targetDescription": "Project description", | ||||
|                         "targetName": "Final Project", | ||||
|                         "targetFramework": "1EdTech University Program and Course Catalog", | ||||
|                         "targetType": "CFItem", | ||||
|                         "targetUrl": "https://1edtech.edu/catalog/degree/project/result/1" | ||||
|                     } | ||||
|                 ], | ||||
|                 "resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c", | ||||
|                 "value": "A" | ||||
|             }, | ||||
|             { | ||||
|                 "type": [ | ||||
|                     "Result" | ||||
|                 ], | ||||
|                 "achievedLevel": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a", | ||||
|                 "alignment": [ | ||||
|                     { | ||||
|                         "type": "Alignment", | ||||
|                         "targetCode": "project", | ||||
|                         "targetDescription": "Project description", | ||||
|                         "targetName": "Final Project", | ||||
|                         "targetFramework": "1EdTech University Program and Course Catalog", | ||||
|                         "targetType": "CFItem", | ||||
|                         "targetUrl": "https://1edtech.edu/catalog/degree/project/result/1" | ||||
|                     } | ||||
|                 ], | ||||
|                 "resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c" | ||||
|             }, | ||||
|             { | ||||
|                 "type": [ | ||||
|                     "Result" | ||||
|                 ], | ||||
|                 "resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c", | ||||
|                 "status": "Completed" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     "proof": [ | ||||
|         { | ||||
|             "type": "Ed25519Signature2020", | ||||
|             "created": "2022-11-16T18:54:22Z", | ||||
|             "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|             "proofPurpose": "assertionMethod", | ||||
|             "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1,52 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|       "https://www.w3.org/2018/credentials/v1", | ||||
|       "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|       "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|       "VerifiableCredential", | ||||
|       "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|       "id": "https://example.com/issuers/876543", | ||||
|       "type": [ | ||||
|         "Profile" | ||||
|       ], | ||||
|       "name": "Example Corp" | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|       "identifier": [{ | ||||
|         "type": "InvalidIdentityObject", | ||||
|         "hashed": true, | ||||
|         "identityHash": "asdjhsadas", | ||||
|         "identityType": "lisSourcedId" | ||||
|       }], | ||||
|       "type": [ | ||||
|         "AchievementSubject" | ||||
|       ], | ||||
|       "achievement": { | ||||
|         "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|         "type": [ | ||||
|           "Achievement" | ||||
|         ], | ||||
|         "criteria": { | ||||
|           "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|         }, | ||||
|         "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|         "name": "Teamwork" | ||||
|       } | ||||
|     }, | ||||
|     "proof": [ | ||||
|       { | ||||
|         "type": "Ed25519Signature2020", | ||||
|         "created": "2022-11-16T18:54:22Z", | ||||
|         "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|         "proofPurpose": "assertionMethod", | ||||
|         "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| @ -0,0 +1,52 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|         "https://www.w3.org/2018/credentials/v1", | ||||
|         "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|         "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|         "VerifiableCredential", | ||||
|         "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|         "id": "https://example.com/issuers/876543", | ||||
|         "type": [ | ||||
|             "Profile" | ||||
|         ], | ||||
|         "name": "Example Corp" | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|         "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|         "type": [ | ||||
|             "AchievementSubject" | ||||
|         ], | ||||
|         "achievement": { | ||||
|             "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|             "type": [ | ||||
|                 "Achievement" | ||||
|             ], | ||||
|             "criteria": { | ||||
|                 "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|             }, | ||||
|             "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|             "name": "Teamwork" | ||||
|         }, | ||||
|         "source": { | ||||
|             "id": "https://school.edu/issuers/201234", | ||||
|             "type": "InvalidProfile", | ||||
|             "name": "1EdTech College of Arts" | ||||
|         } | ||||
|     }, | ||||
|     "proof": [ | ||||
|         { | ||||
|             "type": "Ed25519Signature2020", | ||||
|             "created": "2022-11-16T18:54:22Z", | ||||
|             "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|             "proofPurpose": "assertionMethod", | ||||
|             "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1,92 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|         "https://www.w3.org/2018/credentials/v1", | ||||
|         "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|         "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|         "VerifiableCredential", | ||||
|         "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|         "id": "https://example.com/issuers/876543", | ||||
|         "type": [ | ||||
|             "Profile" | ||||
|         ], | ||||
|         "name": "Example Corp" | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|         "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|         "type": [ | ||||
|             "AchievementSubject" | ||||
|         ], | ||||
|         "achievement": { | ||||
|             "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|             "type": [ | ||||
|                 "Achievement" | ||||
|             ], | ||||
|             "criteria": { | ||||
|                 "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|             }, | ||||
|             "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|             "name": "Teamwork" | ||||
|         }, | ||||
|         "result": [ | ||||
|             { | ||||
|                 "type": [ | ||||
|                     "Result" | ||||
|                 ], | ||||
|                 "alignment": [ | ||||
|                     { | ||||
|                         "type": "Alignment", | ||||
|                         "targetCode": "project", | ||||
|                         "targetDescription": "Project description", | ||||
|                         "targetName": "Final Project", | ||||
|                         "targetFramework": "1EdTech University Program and Course Catalog", | ||||
|                         "targetType": "CFItem", | ||||
|                         "targetUrl": "https://1edtech.edu/catalog/degree/project/result/1" | ||||
|                     } | ||||
|                 ], | ||||
|                 "resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c", | ||||
|                 "value": "A" | ||||
|             }, | ||||
|             { | ||||
|                 "type": [ | ||||
|                     "InvalidResult" | ||||
|                 ], | ||||
|                 "achievedLevel": "urn:uuid:d05a0867-d0ad-4b03-bdb5-28fb5d2aab7a", | ||||
|                 "alignment": [ | ||||
|                     { | ||||
|                         "type": "Alignment", | ||||
|                         "targetCode": "project", | ||||
|                         "targetDescription": "Project description", | ||||
|                         "targetName": "Final Project", | ||||
|                         "targetFramework": "1EdTech University Program and Course Catalog", | ||||
|                         "targetType": "CFItem", | ||||
|                         "targetUrl": "https://1edtech.edu/catalog/degree/project/result/1" | ||||
|                     } | ||||
|                 ], | ||||
|                 "resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c" | ||||
|             }, | ||||
|             { | ||||
|                 "type": [ | ||||
|                     "Result" | ||||
|                 ], | ||||
|                 "resultDescription": "urn:uuid:f6ab24cd-86e8-4eaf-b8c6-ded74e8fd41c", | ||||
|                 "status": "Completed" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     "proof": [ | ||||
|         { | ||||
|             "type": "Ed25519Signature2020", | ||||
|             "created": "2022-11-16T18:54:22Z", | ||||
|             "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|             "proofPurpose": "assertionMethod", | ||||
|             "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1,47 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|       "https://www.w3.org/2018/credentials/v1", | ||||
|       "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|       "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|       "VerifiableCredential", | ||||
|       "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|       "id": "https://example.com/issuers/876543", | ||||
|       "type": [ | ||||
|         "Profile" | ||||
|       ], | ||||
|       "name": "Example Corp" | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|       "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|       "type": [ | ||||
|         "InvalidAchievementSubject" | ||||
|       ], | ||||
|       "achievement": { | ||||
|         "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|         "type": [ | ||||
|           "Achievement" | ||||
|         ], | ||||
|         "criteria": { | ||||
|           "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|         }, | ||||
|         "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|         "name": "Teamwork" | ||||
|       } | ||||
|     }, | ||||
|     "proof": [ | ||||
|       { | ||||
|         "type": "Ed25519Signature2020", | ||||
|         "created": "2022-11-16T18:54:22Z", | ||||
|         "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|         "proofPurpose": "assertionMethod", | ||||
|         "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| @ -0,0 +1,66 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|         "https://www.w3.org/2018/credentials/v1", | ||||
|         "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|         "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|         "VerifiableCredential", | ||||
|         "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|         "id": "https://example.com/issuers/876543", | ||||
|         "type": [ | ||||
|             "Profile" | ||||
|         ], | ||||
|         "name": "Example Corp" | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|         "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|         "type": [ | ||||
|             "AchievementSubject" | ||||
|         ], | ||||
|         "achievement": { | ||||
|             "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|             "type": [ | ||||
|                 "Achievement" | ||||
|             ], | ||||
|             "criteria": { | ||||
|                 "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|             }, | ||||
|             "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|             "name": "Teamwork" | ||||
|         } | ||||
|     }, | ||||
|     "evidence": [ | ||||
|         { | ||||
|             "id": "https://1edtech.edu/credentials/3732/evidence/1", | ||||
|             "type": "Evidence", | ||||
|             "narrative": "# Final Project Report \n This project was ...", | ||||
|             "name": "Final Project Report", | ||||
|             "description": "This is the final project report.", | ||||
|             "genre": "Research", | ||||
|             "audience": "Department" | ||||
|         }, | ||||
|         { | ||||
|             "id": "https://github.com/somebody/project", | ||||
|             "type": "InvalidEvidence", | ||||
|             "name": "Final Project Code", | ||||
|             "description": "This is the source code for the final project app.", | ||||
|             "genre": "Research", | ||||
|             "audience": "Department" | ||||
|         } | ||||
|     ], | ||||
|     "proof": [ | ||||
|         { | ||||
|             "type": "Ed25519Signature2020", | ||||
|             "created": "2022-11-16T18:54:22Z", | ||||
|             "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|             "proofPurpose": "assertionMethod", | ||||
|             "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1,59 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|         "https://www.w3.org/2018/credentials/v1", | ||||
|         "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|         "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|         "VerifiableCredential", | ||||
|         "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|         "id": "https://example.com/issuers/876543", | ||||
|         "type": [ | ||||
|             "Profile" | ||||
|         ], | ||||
|         "name": "Example Corp", | ||||
|         "otherIdentifier": [ | ||||
|             { | ||||
|                 "type": "IdentifierEntry", | ||||
|                 "identifier": "12345", | ||||
|                 "identifierType": "sourcedId" | ||||
|             }, | ||||
|             { | ||||
|                 "type": "InvalidIdentifierEntry", | ||||
|                 "identifier": "67890", | ||||
|                 "identifierType": "nationalIdentityNumber" | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|         "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|         "type": [ | ||||
|             "AchievementSubject" | ||||
|         ], | ||||
|         "achievement": { | ||||
|             "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|             "type": [ | ||||
|                 "Achievement" | ||||
|             ], | ||||
|             "criteria": { | ||||
|                 "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|             }, | ||||
|             "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|             "name": "Teamwork" | ||||
|         } | ||||
|     }, | ||||
|     "proof": [ | ||||
|         { | ||||
|             "type": "Ed25519Signature2020", | ||||
|             "created": "2022-11-16T18:54:22Z", | ||||
|             "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|             "proofPurpose": "assertionMethod", | ||||
|             "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1,54 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|         "https://www.w3.org/2018/credentials/v1", | ||||
|         "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|         "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|         "VerifiableCredential", | ||||
|         "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|         "id": "https://example.com/issuers/876543", | ||||
|         "type": [ | ||||
|             "Profile" | ||||
|         ], | ||||
|         "name": "Example Corp", | ||||
|         "parentOrg": { | ||||
|             "id": "https://example.com/issuers/876543", | ||||
|             "type": [ | ||||
|                 "InvalidProfile" | ||||
|             ], | ||||
|             "name": "Example Parent Corp" | ||||
|         } | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|         "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|         "type": [ | ||||
|             "AchievementSubject" | ||||
|         ], | ||||
|         "achievement": { | ||||
|             "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|             "type": [ | ||||
|                 "Achievement" | ||||
|             ], | ||||
|             "criteria": { | ||||
|                 "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|             }, | ||||
|             "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|             "name": "Teamwork" | ||||
|         } | ||||
|     }, | ||||
|     "proof": [ | ||||
|         { | ||||
|             "type": "Ed25519Signature2020", | ||||
|             "created": "2022-11-16T18:54:22Z", | ||||
|             "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|             "proofPurpose": "assertionMethod", | ||||
|             "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1,47 @@ | ||||
| { | ||||
|     "@context": [ | ||||
|         "https://www.w3.org/2018/credentials/v1", | ||||
|         "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|         "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|     ], | ||||
|     "id": "http://example.com/credentials/3527", | ||||
|     "type": [ | ||||
|         "VerifiableCredential", | ||||
|         "OpenBadgeCredential" | ||||
|     ], | ||||
|     "issuer": { | ||||
|         "id": "https://example.com/issuers/876543", | ||||
|         "type": [ | ||||
|             "InvalidProfile" | ||||
|         ], | ||||
|         "name": "Example Corp" | ||||
|     }, | ||||
|     "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|     "name": "Teamwork Badge", | ||||
|     "credentialSubject": { | ||||
|         "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|         "type": [ | ||||
|             "AchievementSubject" | ||||
|         ], | ||||
|         "achievement": { | ||||
|             "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|             "type": [ | ||||
|                 "Achievement" | ||||
|             ], | ||||
|             "criteria": { | ||||
|                 "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|             }, | ||||
|             "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|             "name": "Teamwork" | ||||
|         } | ||||
|     }, | ||||
|     "proof": [ | ||||
|         { | ||||
|             "type": "Ed25519Signature2020", | ||||
|             "created": "2022-11-16T18:54:22Z", | ||||
|             "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|             "proofPurpose": "assertionMethod", | ||||
|             "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1,51 @@ | ||||
| { | ||||
|   "@context": [ | ||||
|     "https://www.w3.org/2018/credentials/v1", | ||||
|     "https://purl.imsglobal.org/spec/ob/v3p0/context.json", | ||||
|     "https://w3id.org/security/suites/ed25519-2020/v1" | ||||
|   ], | ||||
|   "id": "http://example.com/credentials/3527", | ||||
|   "type": [ | ||||
|     "VerifiableCredential", | ||||
|     "OpenBadgeCredential" | ||||
|   ], | ||||
|   "issuer": { | ||||
|     "id": "https://example.com/issuers/876543", | ||||
|     "type": [ | ||||
|       "Profile" | ||||
|     ], | ||||
|     "name": "Example Corp" | ||||
|   }, | ||||
|   "issuanceDate": "2010-01-01T00:00:00Z", | ||||
|   "name": "Teamwork Badge", | ||||
|   "credentialSubject": { | ||||
|     "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||||
|     "type": [ | ||||
|       "AchievementSubject" | ||||
|     ], | ||||
|     "achievement": { | ||||
|       "id": "https://example.com/achievements/21st-century-skills/teamwork", | ||||
|       "type": [ | ||||
|         "Achievement" | ||||
|       ], | ||||
|       "criteria": { | ||||
|         "narrative": "Team members are nominated for this badge by their peers and recognized upon review by Example Corp management." | ||||
|       }, | ||||
|       "description": "This badge recognizes the development of the capacity to collaborate within a group environment.", | ||||
|       "name": "Teamwork" | ||||
|     } | ||||
|   }, | ||||
|   "proof": [{ | ||||
|       "type": "SomeProofType", | ||||
|       "created": "2022-11-16T18:54:22Z", | ||||
|       "proofPurpose": "assertionMethod" | ||||
|     }, | ||||
|     { | ||||
|       "type": "Ed25519Signature2020", | ||||
|       "created": "2022-11-16T18:54:22Z", | ||||
|       "verificationMethod": "https://example.com/issuers/876543#z6MknNHHrBzPytzu6CUBP9Lg7fg4KSBjzimc2Frh693YbMiv", | ||||
|       "proofPurpose": "assertionMethod", | ||||
|       "proofValue": "z5gJZKchSJEYPGeq6bsqiLKuxT6mXqAovPbqYX66CB7u9CSNFdV41vHtysjHFiitvoyhfPxsaZnWftrZZZW2txPQK" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user