github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/protocol/middleware/issuecredential/middlewares.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package issuecredential 8 9 import ( 10 "errors" 11 "fmt" 12 "strings" 13 14 "github.com/google/uuid" 15 "github.com/piprate/json-gold/ld" 16 17 "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" 18 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator" 19 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential" 20 "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" 21 vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" 22 storeverifiable "github.com/hyperledger/aries-framework-go/pkg/store/verifiable" 23 ) 24 25 const ( 26 // SkipCredentialSaveKey is present in metadata properties as `true` then accepted credential will not be saved in 27 // verifiable store by middleware. 28 SkipCredentialSaveKey = "skip-credential-save" 29 30 stateNameCredentialReceived = "credential-received" 31 myDIDKey = "myDID" 32 theirDIDKey = "theirDID" 33 namesKey = "names" 34 mimeTypeAll = "*" 35 ) 36 37 // Metadata is an alias to the original Metadata. 38 type Metadata issuecredential.Metadata 39 40 // Provider contains dependencies for the SaveCredentials middleware function. 41 type Provider interface { 42 VerifiableStore() storeverifiable.Store 43 VDRegistry() vdrapi.Registry 44 JSONLDDocumentLoader() ld.DocumentLoader 45 } 46 47 // SaveCredentials the helper function for the issue credential protocol which saves credentials. 48 func SaveCredentials(p Provider) issuecredential.Middleware { //nolint: funlen,gocognit,gocyclo 49 vdr := p.VDRegistry() 50 store := p.VerifiableStore() 51 documentLoader := p.JSONLDDocumentLoader() 52 53 return func(next issuecredential.Handler) issuecredential.Handler { 54 return issuecredential.HandlerFunc(func(metadata issuecredential.Metadata) error { 55 if metadata.StateName() != stateNameCredentialReceived { 56 return next.Handle(metadata) 57 } 58 59 properties := metadata.Properties() 60 61 // skip storage if SkipCredentialSaveKey is enabled 62 if val, ok := properties[SkipCredentialSaveKey]; ok { 63 if skip, ok := val.(bool); ok && skip { 64 return next.Handle(metadata) 65 } 66 } 67 68 msg := metadata.Message() 69 70 attachments, err := getAttachments(msg) 71 if err != nil { 72 return fmt.Errorf("get attachments: %w", err) 73 } 74 75 credentials, err := toVerifiableCredentials(vdr, attachments, documentLoader) 76 if err != nil { 77 return fmt.Errorf("to verifiable credentials: %w", err) 78 } 79 80 if len(credentials) == 0 { 81 return errors.New("credentials were not provided") 82 } 83 84 var names []string 85 86 // nolint: errcheck 87 myDID, _ := properties[myDIDKey].(string) 88 // nolint: errcheck 89 theirDID, _ := properties[theirDIDKey].(string) 90 if myDID == "" || theirDID == "" { 91 return errors.New("myDID or theirDID is absent") 92 } 93 94 for i, credential := range credentials { 95 names = append(names, getName(i, credential.ID, metadata)) 96 97 err := store.SaveCredential(names[i], credential, 98 storeverifiable.WithMyDID(myDID), 99 storeverifiable.WithTheirDID(theirDID), 100 ) 101 if err != nil { 102 return fmt.Errorf("save credential: %w", err) 103 } 104 } 105 106 properties[namesKey] = names 107 108 return next.Handle(metadata) 109 }) 110 } 111 } 112 113 func getAttachments(msg service.DIDCommMsg) ([]decorator.AttachmentData, error) { 114 if strings.HasPrefix(msg.Type(), issuecredential.SpecV3) { 115 cred := issuecredential.IssueCredentialV3{} 116 if err := msg.Decode(&cred); err != nil { 117 return nil, fmt.Errorf("decode: %w", err) 118 } 119 120 return filterByMediaType(cred.Attachments, mimeTypeAll), nil 121 } 122 123 cred := issuecredential.IssueCredentialV2{} 124 if err := msg.Decode(&cred); err != nil { 125 return nil, fmt.Errorf("decode: %w", err) 126 } 127 128 return filterByMimeType(cred.CredentialsAttach, mimeTypeAll), nil 129 } 130 131 func getName(idx int, id string, metadata issuecredential.Metadata) string { 132 name := id 133 if len(metadata.CredentialNames()) > idx { 134 name = metadata.CredentialNames()[idx] 135 } 136 137 if name != "" { 138 return name 139 } 140 141 return uuid.New().String() 142 } 143 144 func toVerifiableCredentials(v vdrapi.Registry, attachments []decorator.AttachmentData, 145 documentLoader ld.DocumentLoader) ([]*verifiable.Credential, error) { 146 var credentials []*verifiable.Credential 147 148 for i := range attachments { 149 rawVC, err := attachments[i].Fetch() 150 if err != nil { 151 return nil, fmt.Errorf("fetch: %w", err) 152 } 153 154 vc, err := verifiable.ParseCredential(rawVC, 155 verifiable.WithPublicKeyFetcher(verifiable.NewVDRKeyResolver(v).PublicKeyFetcher()), 156 verifiable.WithJSONLDDocumentLoader(documentLoader)) 157 if err != nil { 158 return nil, fmt.Errorf("new credential: %w", err) 159 } 160 161 credentials = append(credentials, vc) 162 } 163 164 return credentials, nil 165 } 166 167 func filterByMimeType(attachments []decorator.Attachment, mimeType string) []decorator.AttachmentData { 168 var result []decorator.AttachmentData 169 170 for i := range attachments { 171 if attachments[i].MimeType != mimeType && mimeType != mimeTypeAll { 172 continue 173 } 174 175 result = append(result, attachments[i].Data) 176 } 177 178 return result 179 } 180 181 func filterByMediaType(attachments []decorator.AttachmentV2, mediaType string) []decorator.AttachmentData { 182 var result []decorator.AttachmentData 183 184 for i := range attachments { 185 if attachments[i].MediaType != mediaType && mediaType != mimeTypeAll { 186 continue 187 } 188 189 result = append(result, attachments[i].Data) 190 } 191 192 return result 193 }