Check required values for type attribute

added attributes for required type and achievement

return error instead of notRun
This commit is contained in:
Xavi Aracil 2023-01-05 10:48:15 +01:00
parent 677e547add
commit 1fe23b7278
11 changed files with 711 additions and 65 deletions

View File

@ -188,7 +188,7 @@ public class EndorsementInspector extends VCInspector implements SubInspector {
//credentialSubject //credentialSubject
probeCount++; probeCount++;
accumulator.add(new CredentialSubjectProbe().run(endorsement.getJson(), ctx)); accumulator.add(new CredentialSubjectProbe("EndorsementSubject").run(endorsement.getJson(), ctx));
//signatures, proofs //signatures, proofs
probeCount++; probeCount++;

View File

@ -170,7 +170,7 @@ public class OB30Inspector extends VCInspector implements SubInspector {
//credentialSubject //credentialSubject
probeCount++; probeCount++;
accumulator.add(new CredentialSubjectProbe().run(ob.getJson(), ctx)); accumulator.add(new CredentialSubjectProbe("AchievementSubject", true).run(ob.getJson(), ctx));
//signatures, proofs //signatures, proofs
probeCount++; probeCount++;

View File

@ -1,8 +1,11 @@
package org.oneedtech.inspect.vc.probe; package org.oneedtech.inspect.vc.probe;
import java.util.List;
import org.oneedtech.inspect.core.probe.Probe; import org.oneedtech.inspect.core.probe.Probe;
import org.oneedtech.inspect.core.probe.RunContext; import org.oneedtech.inspect.core.probe.RunContext;
import org.oneedtech.inspect.core.report.ReportItems; import org.oneedtech.inspect.core.report.ReportItems;
import org.oneedtech.inspect.vc.util.JsonNodeUtil;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
@ -14,8 +17,20 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
*/ */
public class CredentialSubjectProbe extends Probe<JsonNode> { 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); super(ID);
this.requiredType = requiredType;
this.achivementRequired = achivementRequired;
} }
@Override @Override
@ -24,18 +39,86 @@ public class CredentialSubjectProbe extends Probe<JsonNode> {
JsonNode subject = root.get("credentialSubject"); JsonNode subject = root.get("credentialSubject");
if(subject == null) return notRun("no credentialSubject node found", ctx); //error reported by schema 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 * 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"); 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"); JsonNode identifier = root.get("identifier");
if(identifier != null && identifier instanceof ArrayNode if(identifier != null && identifier instanceof ArrayNode
&& ((ArrayNode)identifier).size() > 0) return success(ctx); && ((ArrayNode)identifier).size() > 0) return false;
return error("no id in credentialSubject", ctx);
return true;
} }
public static final String ID = CredentialSubjectProbe.class.getSimpleName(); public static final String ID = CredentialSubjectProbe.class.getSimpleName();

View File

@ -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();
}

View File

@ -11,6 +11,7 @@ import org.oneedtech.inspect.core.probe.json.JsonSchemaProbe;
import org.oneedtech.inspect.core.report.Report; import org.oneedtech.inspect.core.report.Report;
import org.oneedtech.inspect.test.PrintHelper; import org.oneedtech.inspect.test.PrintHelper;
import org.oneedtech.inspect.vc.probe.ContextPropertyProbe; 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.ExpirationProbe;
import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe; import org.oneedtech.inspect.vc.probe.InlineJsonSchemaProbe;
import org.oneedtech.inspect.vc.probe.IssuanceProbe; import org.oneedtech.inspect.vc.probe.IssuanceProbe;
@ -191,6 +192,66 @@ 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);
});
}
@Disabled //TODO IssuanceVerifierProbe is not run because FATAL: InvalidSignature terminates @Disabled //TODO IssuanceVerifierProbe is not run because FATAL: InvalidSignature terminates
@Test @Test
void testSimpleJsonNotIssued() { void testSimpleJsonNotIssued() {

View File

@ -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_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_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_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_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_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); public final static Sample SIMPLE_JSON_PROOF_METHOD_UNKNOWN_SCHEME_ERROR = new Sample("ob30/simple-err-proof-method-unknown-scheme.json", false);

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}