github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/verifiable/credential_extension_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 SPDX-License-Identifier: Apache-2.0 4 */ 5 6 package verifiable 7 8 import ( 9 "encoding/json" 10 "errors" 11 "fmt" 12 "testing" 13 14 "github.com/stretchr/testify/require" 15 ) 16 17 // UniversityDegree university degree. 18 type UniversityDegree struct { 19 Type string `json:"type,omitempty"` 20 Name string `json:"name,omitempty"` 21 College string `json:"college,omitempty"` 22 University string `json:"university,omitempty"` 23 } 24 25 // UniversityDegreeSubject subject of university degree. 26 type UniversityDegreeSubject struct { 27 ID string `json:"id,omitempty"` 28 Name string `json:"name,omitempty"` 29 Spouse string `json:"spouse,omitempty"` 30 31 Degree UniversityDegree `json:"degree,omitempty"` 32 } 33 34 func mapUniversityDegreeSubject(subjects []Subject) *UniversityDegreeSubject { 35 if len(subjects) != 1 { 36 return nil 37 } 38 39 subject := &subjects[0] 40 customFields := subject.CustomFields 41 udSubject := &UniversityDegreeSubject{ 42 ID: subject.ID, 43 Name: safeStringValue(customFields["name"]), 44 Spouse: safeStringValue(customFields["spouse"]), 45 } 46 47 degreeValue, ok := customFields["degree"] 48 if !ok { 49 return udSubject 50 } 51 52 degreeMap, ok := degreeValue.(map[string]interface{}) 53 if !ok { 54 return udSubject 55 } 56 57 udDegree := &udSubject.Degree 58 udDegree.Type = safeStringValue(degreeMap["type"]) 59 udDegree.Name = safeStringValue(degreeMap["name"]) 60 udDegree.College = safeStringValue(degreeMap["college"]) 61 udDegree.University = safeStringValue(degreeMap["university"]) 62 63 return udSubject 64 } 65 66 // UniversityDegreeCredential University Degree credential, from examples of https://w3c.github.io/vc-data-model 67 type UniversityDegreeCredential struct { 68 Base Credential `json:"-"` 69 70 Subject *UniversityDegreeSubject `json:"credentialSubject,omitempty"` 71 } 72 73 func NewUniversityDegreeCredential(t *testing.T, vcData []byte, 74 opts ...CredentialOpt) (*UniversityDegreeCredential, error) { 75 cred, err := parseTestCredential(t, vcData, opts...) 76 if err != nil { 77 return nil, fmt.Errorf("new university degree credential: %w", err) 78 } 79 80 // One way to build custom credential subject is to convert []Subject to the custom credential. 81 udc := UniversityDegreeCredential{ 82 Base: *cred, 83 Subject: mapUniversityDegreeSubject(credSubjects(cred)), 84 } 85 86 // The other way is to marshal credential subject and unmarshal back to custom subject structure. 87 subjects, ok := cred.Subject.([]Subject) 88 if !ok { 89 return nil, errors.New("expected subject of []Subject type") 90 } 91 92 if len(subjects) != 1 { 93 return nil, errors.New("expected a single subject") 94 } 95 96 subjectBytes, err := json.Marshal(subjects[0]) 97 if err != nil { 98 return nil, fmt.Errorf("new university degree credential subject: %w", err) 99 } 100 101 err = json.Unmarshal(subjectBytes, &udc.Subject) 102 if err != nil { 103 return nil, fmt.Errorf("new university degree credential subject: %w", err) 104 } 105 106 return &udc, nil 107 } 108 109 func credSubjects(vc *Credential) []Subject { 110 if vc.Subject == nil { 111 return nil 112 } 113 114 if subjects, ok := vc.Subject.([]Subject); ok { 115 return subjects 116 } 117 118 return nil 119 } 120 121 func TestCredentialExtensibility(t *testing.T) { 122 udCredential := ` 123 124 { 125 "@context": [ 126 "https://www.w3.org/2018/credentials/v1", 127 "https://www.w3.org/2018/credentials/examples/v1" 128 ], 129 "id": "http://example.edu/credentials/1872", 130 "type": [ 131 "VerifiableCredential", 132 "UniversityDegreeCredential" 133 ], 134 "credentialSubject": { 135 "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", 136 "degree": { 137 "type": "BachelorDegree" 138 }, 139 "name": "Jayden Doe", 140 "spouse": "did:example:c276e12ec21ebfeb1f712ebc6f1" 141 }, 142 143 "issuer": { 144 "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 145 "name": "Example University" 146 }, 147 148 "issuanceDate": "2010-01-01T19:23:24Z", 149 150 "expirationDate": "2020-01-01T19:23:24Z", 151 152 "credentialStatus": { 153 "id": "https://example.edu/status/24", 154 "type": "CredentialStatusList2017" 155 }, 156 157 "evidence": [{ 158 "id": "https://example.edu/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d4231", 159 "type": ["DocumentVerification"], 160 "verifier": "https://example.edu/issuers/14", 161 "evidenceDocument": "DriversLicense", 162 "subjectPresence": "Physical", 163 "documentPresence": "Physical" 164 },{ 165 "id": "https://example.edu/evidence/f2aeec97-fc0d-42bf-8ca7-0548192dxyzab", 166 "type": ["SupportingActivity"], 167 "verifier": "https://example.edu/issuers/14", 168 "evidenceDocument": "Fluid Dynamics Focus", 169 "subjectPresence": "Digital", 170 "documentPresence": "Digital" 171 }], 172 173 "termsOfUse": [ 174 { 175 "type": "IssuerPolicy", 176 "id": "http://example.com/policies/credential/4", 177 "profile": "http://example.com/profiles/credential", 178 "prohibition": [ 179 { 180 "assigner": "https://example.edu/issuers/14", 181 "assignee": "AllVerifiers", 182 "target": "http://example.edu/credentials/3732", 183 "action": [ 184 "Archival" 185 ] 186 } 187 ] 188 } 189 ], 190 191 "refreshService": { 192 "id": "https://example.edu/refresh/3732", 193 "type": "ManualRefreshService2018" 194 } 195 } 196 ` 197 198 cred, err := parseTestCredential(t, []byte(udCredential)) 199 require.NoError(t, err) 200 require.NotNil(t, cred) 201 202 udc, err := NewUniversityDegreeCredential(t, []byte(udCredential)) 203 require.NoError(t, err) 204 205 // base Credential part is the same 206 require.Equal(t, *cred, udc.Base) 207 208 // default issuer credential decoder is applied (i.e. not re-written by new custom decoder) 209 require.NotNil(t, cred.Issuer) 210 require.Equal(t, cred.Issuer.ID, "did:example:76e12ec712ebc6f1c221ebfeb1f") 211 require.Equal(t, cred.Issuer.CustomFields["name"], "Example University") 212 213 // new mapping is applied 214 subj := udc.Subject 215 require.Equal(t, "did:example:ebfeb1f712ebc6f1c276e12ec21", subj.ID) 216 require.Equal(t, "BachelorDegree", subj.Degree.Type) 217 require.Equal(t, "Jayden Doe", subj.Name) 218 require.Equal(t, "did:example:c276e12ec21ebfeb1f712ebc6f1", subj.Spouse) 219 }