diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/EndorsementInspector.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/EndorsementInspector.java index 1e8efab..ce1b10a 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/EndorsementInspector.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/EndorsementInspector.java @@ -188,8 +188,8 @@ 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++; if(endorsement.getProofType() == EXTERNAL){ diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java index 163b9ec..a2dde88 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/OB30Inspector.java @@ -170,7 +170,7 @@ 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)); //signatures, proofs probeCount++; diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/CredentialSubjectProbe.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/CredentialSubjectProbe.java index 2932563..b2c3494 100644 --- a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/CredentialSubjectProbe.java +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/CredentialSubjectProbe.java @@ -1,21 +1,36 @@ 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; /** * A Probe that checks credential subject specifics not capturable by schemata. - * + * * @author mgylling */ public class CredentialSubjectProbe extends Probe { - - 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 subject = root.get("credentialSubject"); if(subject == null) return notRun("no credentialSubject node found", ctx); //error reported by schema - /* - * Check that we have either .id or .identifier populated + /** + * 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 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 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 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); - - 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); - + 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 false; + + return true; } public static final String ID = CredentialSubjectProbe.class.getSimpleName(); diff --git a/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/IssuerProbe.java b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/IssuerProbe.java new file mode 100644 index 0000000..bb9238b --- /dev/null +++ b/inspector-vc/src/main/java/org/oneedtech/inspect/vc/probe/IssuerProbe.java @@ -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 { + 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 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(); + +} diff --git a/inspector-vc/src/test/java/org/oneedtech/inspect/vc/OB30Tests.java b/inspector-vc/src/test/java/org/oneedtech/inspect/vc/OB30Tests.java index 036eb9e..e764640 100644 --- a/inspector-vc/src/test/java/org/oneedtech/inspect/vc/OB30Tests.java +++ b/inspector-vc/src/test/java/org/oneedtech/inspect/vc/OB30Tests.java @@ -11,6 +11,7 @@ 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; @@ -20,51 +21,51 @@ import org.oneedtech.inspect.vc.probe.TypePropertyProbe; import com.google.common.collect.Iterables; public class OB30Tests { - private static OB30Inspector validator; + private static OB30Inspector validator; private static boolean verbose = true; - - @BeforeAll - static void setup() { - validator = new OB30Inspector.Builder() - .set(Behavior.TEST_INCLUDE_SUCCESS, true) + + @BeforeAll + static void setup() { + validator = new OB30Inspector.Builder() + .set(Behavior.TEST_INCLUDE_SUCCESS, true) .set(Behavior.VALIDATOR_FAIL_FAST, true) - .build(); + .build(); } - + @Test void testSimpleJsonValid() { assertDoesNotThrow(()->{ Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON.asFileResource()); if(verbose) PrintHelper.print(report, true); - assertValid(report); - }); + assertValid(report); + }); } - + @Test void testSimpleDidMethodJsonValid() { assertDoesNotThrow(()->{ Report report = validator.run(Samples.OB30.JSON.SIMPLE_DID_METHOD_JSON.asFileResource()); if(verbose) PrintHelper.print(report, true); - assertValid(report); - }); + assertValid(report); + }); } - + @Test void testSimplePNGPlainValid() { assertDoesNotThrow(()->{ Report report = validator.run(Samples.OB30.PNG.SIMPLE_JSON_PNG.asFileResource()); if(verbose) PrintHelper.print(report, true); - assertValid(report); - }); + assertValid(report); + }); } - + @Test void testSimplePNGJWTValid() { assertDoesNotThrow(()->{ Report report = validator.run(Samples.OB30.PNG.SIMPLE_JWT_PNG.asFileResource()); if(verbose) PrintHelper.print(report, true); - assertValid(report); - }); + assertValid(report); + }); } @Test @@ -72,17 +73,17 @@ public class OB30Tests { assertDoesNotThrow(()->{ Report report = validator.run(Samples.OB30.SVG.SIMPLE_JSON_SVG.asFileResource()); if(verbose) PrintHelper.print(report, true); - assertValid(report); - }); + assertValid(report); + }); } - + @Test void testSimpleJsonSVGJWTValid() { assertDoesNotThrow(()->{ Report report = validator.run(Samples.OB30.SVG.SIMPLE_JWT_SVG.asFileResource()); if(verbose) PrintHelper.print(report, true); - assertValid(report); - }); + assertValid(report); + }); } @Test @@ -94,9 +95,9 @@ public class OB30Tests { assertInvalid(report); assertFatalCount(report, 1); assertHasProbeID(report, TypePropertyProbe.ID, true); - }); + }); } - + @Test void testSimpleJsonInvalidProofMethod() { // add some garbage chars to the verification method fragment @@ -107,9 +108,9 @@ public class OB30Tests { assertInvalid(report); assertErrorCount(report, 1); assertHasProbeID(report, EmbeddedProofProbe.ID, true); - }); + }); } - + @Test void testSimpleJsonInvalidProofMethodNoScheme() { // The verificationMethod is not a URI (no scheme) @@ -119,9 +120,9 @@ public class OB30Tests { assertInvalid(report); assertErrorCount(report, 1); assertHasProbeID(report, EmbeddedProofProbe.ID, true); - }); + }); } - + @Test void testSimpleJsonInvalidProofMethodUnknownScheme() { // The verificationMethod is not a URI (no scheme) @@ -131,9 +132,9 @@ public class OB30Tests { assertInvalid(report); assertErrorCount(report, 1); assertHasProbeID(report, EmbeddedProofProbe.ID, true); - }); + }); } - + @Test void testSimpleJsonInvalidProofMethodUnknownDidMethod() { // The verificationMethod is an unknown DID Method @@ -143,9 +144,9 @@ public class OB30Tests { assertInvalid(report); assertErrorCount(report, 1); assertHasProbeID(report, EmbeddedProofProbe.ID, true); - }); + }); } - + @Test void testSimpleJsonInvalidProofValue() { //add some garbage chars to proofValue @@ -155,9 +156,9 @@ public class OB30Tests { assertInvalid(report); assertErrorCount(report, 1); assertHasProbeID(report, EmbeddedProofProbe.ID, true); - }); + }); } - + @Test void testSimpleJsonExpired() { //"expirationDate": "2020-01-20T00:00:00Z", @@ -166,20 +167,20 @@ public class OB30Tests { if(verbose) PrintHelper.print(report, true); assertInvalid(report); assertHasProbeID(report, ExpirationProbe.ID, true); - }); + }); } - + @Test - void testSimpleJsonContextError() { + void testSimpleJsonContextError() { //removed one of the reqd context uris assertDoesNotThrow(()->{ Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_ERR_CONTEXT.asFileResource()); if(verbose) PrintHelper.print(report, true); assertInvalid(report); assertHasProbeID(report, ContextPropertyProbe.ID, true); - }); + }); } - + @Test void testSimpleJsonSchemaError() throws Exception { //issuer removed @@ -187,10 +188,70 @@ public class OB30Tests { Report report = validator.run(Samples.OB30.JSON.SIMPLE_JSON_ISSUER.asFileResource()); if(verbose) PrintHelper.print(report, true); assertInvalid(report); - assertHasProbeID(report, JsonSchemaProbe.ID, true); - }); + assertHasProbeID(report, JsonSchemaProbe.ID, true); + }); } - + + @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); + }); + } + @Disabled //TODO IssuanceVerifierProbe is not run because FATAL: InvalidSignature terminates @Test void testSimpleJsonNotIssued() { @@ -201,9 +262,9 @@ public class OB30Tests { if(verbose) PrintHelper.print(report, true); assertInvalid(report); assertHasProbeID(report, IssuanceProbe.ID, true); - }); + }); } - + @Test void testCompleteJsonInvalidInlineSchemaRef() throws Exception { //404 inline schema ref, and 404 refresh uri @@ -212,9 +273,9 @@ public class OB30Tests { if(verbose) PrintHelper.print(report, true); assertFalse(report.asBoolean()); assertTrue(Iterables.size(report.getErrors()) > 0); - assertTrue(Iterables.size(report.getExceptions()) > 0); - assertHasProbeID(report, InlineJsonSchemaProbe.ID, true); - }); + assertTrue(Iterables.size(report.getExceptions()) > 0); + assertHasProbeID(report, InlineJsonSchemaProbe.ID, true); + }); } } diff --git a/inspector-vc/src/test/java/org/oneedtech/inspect/vc/Samples.java b/inspector-vc/src/test/java/org/oneedtech/inspect/vc/Samples.java index b484f56..2b44942 100644 --- a/inspector-vc/src/test/java/org/oneedtech/inspect/vc/Samples.java +++ b/inspector-vc/src/test/java/org/oneedtech/inspect/vc/Samples.java @@ -14,6 +14,11 @@ public class Samples { public final static Sample SIMPLE_DID_METHOD_JSON = new Sample("ob30/simple-did-method.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_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); diff --git a/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-achievement-result-description-type.json b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-achievement-result-description-type.json new file mode 100644 index 0000000..dd6b7ae --- /dev/null +++ b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-achievement-result-description-type.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-identifier-type.json b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-identifier-type.json new file mode 100644 index 0000000..64960bd --- /dev/null +++ b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-identifier-type.json @@ -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" + } + ] + } \ No newline at end of file diff --git a/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-profile-type.json b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-profile-type.json new file mode 100644 index 0000000..57646c7 --- /dev/null +++ b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-profile-type.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-result-type.json b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-result-type.json new file mode 100644 index 0000000..5275a9a --- /dev/null +++ b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-result-type.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-type.json b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-type.json new file mode 100644 index 0000000..37712c5 --- /dev/null +++ b/inspector-vc/src/test/resources/ob30/simple-err-credential-subject-type.json @@ -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" + } + ] + } \ No newline at end of file