Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: expand type #42

Merged
merged 6 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"Mother": "ex:Mother",
"RelationshipCredential": "ex:RelationshipCredential",
"UniversityDegreeCredential": "ex:UniversityDegreeCredential",
"AlumniCredential": "ex:AlumniCredential",
"DisputeCredential": "ex:DisputeCredential",
"PrescriptionCredential": "ex:PrescriptionCredential",
"ZkpExampleSchema2018": "ex:ZkpExampleSchema2018",

"issuerData": "ex:issuerData",
Expand All @@ -42,6 +45,9 @@
"evidenceDocument": "ex:evidenceDocument",
"spouse": "schema:spouse",
"subjectPresence": "ex:subjectPresence",
"verifier": {"@id": "ex:verifier", "@type": "@id"}
"verifier": {"@id": "ex:verifier", "@type": "@id"},
"currentStatus": "ex:currentStatus",
"statusReason": "ex:statusReason",
"prescription": "ex:prescription"
}]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"@context": {
"id": "@id",
"type": "@type",
"@protected": true,
"proof": {
"@id": "https://w3id.org/security#proof",
"@type": "@id",
"@container": "@graph"
},
"DataIntegrityProof": {
"@id": "https://w3id.org/security#DataIntegrityProof",
"@context": {
"@protected": true,
"id": "@id",
"type": "@type",
"challenge": "https://w3id.org/security#challenge",
"created": {
"@id": "http://purl.org/dc/terms/created",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"domain": "https://w3id.org/security#domain",
"expires": {
"@id": "https://w3id.org/security#expiration",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"nonce": "https://w3id.org/security#nonce",
"previousProof": {
"@id": "https://w3id.org/security#previousProof",
"@type": "@id"
},
"proofPurpose": {
"@id": "https://w3id.org/security#proofPurpose",
"@type": "@vocab",
"@context": {
"@protected": true,
"id": "@id",
"type": "@type",
"assertionMethod": {
"@id": "https://w3id.org/security#assertionMethod",
"@type": "@id",
"@container": "@set"
},
"authentication": {
"@id": "https://w3id.org/security#authenticationMethod",
"@type": "@id",
"@container": "@set"
},
"capabilityInvocation": {
"@id": "https://w3id.org/security#capabilityInvocationMethod",
"@type": "@id",
"@container": "@set"
},
"capabilityDelegation": {
"@id": "https://w3id.org/security#capabilityDelegationMethod",
"@type": "@id",
"@container": "@set"
},
"keyAgreement": {
"@id": "https://w3id.org/security#keyAgreementMethod",
"@type": "@id",
"@container": "@set"
}
}
},
"cryptosuite": {
"@id": "https://w3id.org/security#cryptosuite",
"@type": "https://w3id.org/security#cryptosuiteString"
},
"proofValue": {
"@id": "https://w3id.org/security#proofValue",
"@type": "https://w3id.org/security#multibase"
},
"verificationMethod": {
"@id": "https://w3id.org/security#verificationMethod",
"@type": "@id"
}
}
}
}
}
6 changes: 6 additions & 0 deletions doc/ld/testutil/document_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ var (
authCred []byte
//go:embed contexts/third_party/w3id.org/data-integrity-v1.jsonld
dataIntegrity []byte
//go:embed contexts/third_party/w3id.org/data-integrity-v2.jsonld
dataIntegrityV2 []byte
)

var testContexts = []ldcontext.Document{ //nolint:gochecknoglobals // embedded test contexts
Expand Down Expand Up @@ -75,6 +77,10 @@ var testContexts = []ldcontext.Document{ //nolint:gochecknoglobals // embedded t
URL: "https://w3id.org/security/data-integrity/v1",
Content: dataIntegrity,
},
{
URL: "https://w3id.org/security/data-integrity/v2",
Content: dataIntegrityV2,
},
}

// WithDocumentLoader returns an option with a custom JSON-LD document loader preloaded with embedded contexts.
Expand Down
22 changes: 12 additions & 10 deletions doc/ld/validator/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,18 @@ func ValidateJSONLDTypes(

types, typesOk := typesObj.([]interface{})
if !typesOk {
return errors.New("type must be an array")
typeStr, typeStrOk := typesObj.(string)
if typeStrOk {
types = []interface{}{typeStr}
} else {
return errors.New("type must be an array or string")
}
}

if len(types) == 0 {
return nil
}

documentTypes := map[string]struct{}{}
for _, t := range types {
documentTypes[fmt.Sprint(t)] = struct{}{}
}

jsonldProc := processor.Default()
opts := getValidateOpts(options)

Expand All @@ -166,12 +166,11 @@ func ValidateJSONLDTypes(
return errors.Join(err, errors.New("expand JSON-LD document"))
}

return validateTypesInExpandedDoc(docExpanded, documentTypes)
return validateTypesInExpandedDoc(docExpanded)
}

func validateTypesInExpandedDoc(
docExpanded []any,
types map[string]struct{},
) error {
if len(docExpanded) != 1 {
return fmt.Errorf("expanded document must contain only one element, got %d", len(docExpanded))
Expand All @@ -197,11 +196,14 @@ func validateTypesInExpandedDoc(
}

for _, t := range expandedTypes {
if _, typeOk := types[fmt.Sprint(t)]; typeOk {
typeStr := fmt.Sprint(t)

if !strings.HasPrefix(typeStr, "http://") && !strings.HasPrefix(typeStr, "https://") &&
!strings.HasPrefix(typeStr, "urn:") {
// expand should change types to full URIs.
// example "VerifiableCredential" -> "https://www.w3.org/2018/credentials#VerifiableCredential".
return fmt.Errorf("expanded document contains unexpanded type %s. "+
"All types should be declared in contexts", t)
"All types should be declared in contexts", typeStr)
}
}

Expand Down
33 changes: 28 additions & 5 deletions doc/ld/validator/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,41 +638,64 @@ func TestValidateType(t *testing.T) {
assert.NoError(t, ValidateJSONLDTypes(parsed, WithDocumentLoader(loader)))
})

t.Run("valid 2", func(t *testing.T) {
var parsed map[string]interface{}
assert.NoError(t, json.Unmarshal([]byte(`{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.edu/credentials/58473",
"type": ["VerifiableCredential", "AlumniCredential"],
"issuer": "https://example.edu/issuers/14",
"issuanceDate": "2010-01-01T19:23:24Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"alumniOf": "Example University"
},
"proof": {
"type": "RsaSignature2018"
}
}`), &parsed))

assert.NoError(t, ValidateJSONLDTypes(parsed, WithDocumentLoader(loader)))
})

t.Run("invalid length", func(t *testing.T) {
assert.ErrorContains(t, validateTypesInExpandedDoc([]any{
1,
2,
}, nil), "expanded document must contain only one element")
}), "expanded document must contain only one element")
})

t.Run("invalid type", func(t *testing.T) {
assert.ErrorContains(t, validateTypesInExpandedDoc([]any{
1,
}, nil), "document must be a map")
}), "document must be a map")
})

t.Run("no type", func(t *testing.T) {
assert.ErrorContains(t, validateTypesInExpandedDoc([]any{
map[string]any{
"xx": "yy",
},
}, nil), "expanded document does not contain")
}), "expanded document does not contain")
})

t.Run("no type", func(t *testing.T) {
assert.ErrorContains(t, validateTypesInExpandedDoc([]any{
map[string]any{
"@type": "yy",
},
}, nil), "expanded @type must be an array")
}), "expanded @type must be an array")
})

t.Run("no records", func(t *testing.T) {
assert.NoError(t, validateTypesInExpandedDoc([]any{
map[string]any{
"@type": []any{},
},
}, nil))
}))
})
}

Expand Down
Loading