github.com/hyperledger/aries-framework-go@v0.3.2/pkg/store/verifiable/store.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package verifiable 8 9 import ( 10 "encoding/json" 11 "errors" 12 "fmt" 13 14 "github.com/google/uuid" 15 "github.com/piprate/json-gold/ld" 16 17 "github.com/hyperledger/aries-framework-go/pkg/common/log" 18 "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" 19 "github.com/hyperledger/aries-framework-go/pkg/store/verifiable/internal" 20 "github.com/hyperledger/aries-framework-go/spi/storage" 21 ) 22 23 // NameSpace for vc store. 24 const NameSpace = "verifiable" 25 26 var logger = log.New("aries-framework/store/verifiable") 27 28 // Opt represents option function. 29 type Opt func(o *options) 30 31 type options struct { 32 MyDID string 33 TheirDID string 34 } 35 36 // WithMyDID allows specifying MyDID for credential or presentation that is being issued. 37 func WithMyDID(val string) Opt { 38 return func(o *options) { 39 o.MyDID = val 40 } 41 } 42 43 // WithTheirDID allows specifying TheirDID for credential or presentation that is being issued. 44 func WithTheirDID(val string) Opt { 45 return func(o *options) { 46 o.TheirDID = val 47 } 48 } 49 50 // Store provides interface for storing and managing verifiable credentials. 51 type Store interface { 52 SaveCredential(name string, vc *verifiable.Credential, opts ...Opt) error 53 SavePresentation(name string, vp *verifiable.Presentation, opts ...Opt) error 54 GetCredential(id string) (*verifiable.Credential, error) 55 GetPresentation(id string) (*verifiable.Presentation, error) 56 GetCredentialIDByName(name string) (string, error) 57 GetPresentationIDByName(name string) (string, error) 58 GetCredentials() ([]*Record, error) 59 GetPresentations() ([]*Record, error) 60 RemoveCredentialByName(name string) error 61 RemovePresentationByName(name string) error 62 } 63 64 // StoreImplementation stores vc. 65 type StoreImplementation struct { 66 store storage.Store 67 documentLoader ld.DocumentLoader 68 } 69 70 type provider interface { 71 StorageProvider() storage.Provider 72 JSONLDDocumentLoader() ld.DocumentLoader 73 } 74 75 // New returns a new vc store. 76 func New(ctx provider) (*StoreImplementation, error) { 77 store, err := ctx.StorageProvider().OpenStore(NameSpace) 78 if err != nil { 79 return nil, fmt.Errorf("failed to open vc store: %w", err) 80 } 81 82 err = ctx.StorageProvider().SetStoreConfig(NameSpace, 83 storage.StoreConfiguration{TagNames: []string{internal.CredentialNameKey, internal.PresentationNameKey}}) 84 if err != nil { 85 return nil, fmt.Errorf("failed to set store configuration: %w", err) 86 } 87 88 return &StoreImplementation{ 89 store: store, 90 documentLoader: ctx.JSONLDDocumentLoader(), 91 }, nil 92 } 93 94 // SaveCredential saves a verifiable credential. 95 func (s *StoreImplementation) SaveCredential(name string, vc *verifiable.Credential, opts ...Opt) error { 96 if name == "" { 97 return errors.New("credential name is mandatory") 98 } 99 100 id, err := s.GetCredentialIDByName(name) 101 if err != nil && !errors.Is(err, storage.ErrDataNotFound) { 102 return fmt.Errorf("get credential id using name : %w", err) 103 } 104 105 if id != "" { 106 return errors.New("credential name already exists") 107 } 108 109 vcBytes, err := vc.MarshalJSON() 110 if err != nil { 111 return fmt.Errorf("failed to marshal vc: %w", err) 112 } 113 114 id = vc.ID 115 if id == "" { 116 // ID in VCs are not mandatory, use uuid to save in DB if id missing. 117 id = uuid.New().String() 118 } 119 120 if e := s.store.Put(id, vcBytes); e != nil { 121 return fmt.Errorf("failed to put vc: %w", e) 122 } 123 124 o := &options{} 125 126 for _, opt := range opts { 127 opt(o) 128 } 129 130 recordBytes, err := json.Marshal(&Record{ 131 ID: id, 132 Name: name, 133 Context: vc.Context, 134 Type: vc.Types, 135 MyDID: o.MyDID, 136 TheirDID: o.TheirDID, 137 SubjectID: getVCSubjectID(vc), 138 }) 139 if err != nil { 140 return fmt.Errorf("failed to marshal record: %w", err) 141 } 142 143 return s.store.Put(internal.CredentialNameDataKey(name), recordBytes, storage.Tag{Name: internal.CredentialNameKey}) 144 } 145 146 // SavePresentation saves a verifiable presentation. 147 func (s *StoreImplementation) SavePresentation(name string, vp *verifiable.Presentation, opts ...Opt) error { 148 if name == "" { 149 return errors.New("presentation name is mandatory") 150 } 151 152 id, err := s.GetPresentationIDByName(name) 153 if err != nil && !errors.Is(err, storage.ErrDataNotFound) { 154 return fmt.Errorf("get presentation id using name : %w", err) 155 } 156 157 if id != "" { 158 return errors.New("presentation name already exists") 159 } 160 161 vpBytes, err := vp.MarshalJSON() 162 if err != nil { 163 return fmt.Errorf("failed to marshal vp: %w", err) 164 } 165 166 id = vp.ID 167 if id == "" { 168 // ID in VPs are not mandatory, use uuid to save in DB. 169 id = uuid.New().String() 170 } 171 172 o := &options{} 173 174 for _, opt := range opts { 175 opt(o) 176 } 177 178 recordBytes, err := json.Marshal(&Record{ 179 ID: id, 180 Name: name, 181 Context: vp.Context, 182 Type: vp.Type, 183 MyDID: o.MyDID, 184 TheirDID: o.TheirDID, 185 SubjectID: vp.Holder, 186 }) 187 if err != nil { 188 return fmt.Errorf("failed to marshal record: %w", err) 189 } 190 191 if err := s.store.Put(id, vpBytes); err != nil { 192 return fmt.Errorf("failed to put vp: %w", err) 193 } 194 195 return s.store.Put(internal.PresentationNameDataKey(name), recordBytes, 196 storage.Tag{Name: internal.PresentationNameKey}) 197 } 198 199 // GetCredential retrieves a verifiable credential based on ID. 200 func (s *StoreImplementation) GetCredential(id string) (*verifiable.Credential, error) { 201 vcBytes, err := s.store.Get(id) 202 if err != nil { 203 return nil, fmt.Errorf("failed to get vc: %w", err) 204 } 205 206 vc, err := verifiable.ParseCredential(vcBytes, verifiable.WithDisabledProofCheck(), 207 verifiable.WithJSONLDDocumentLoader(s.documentLoader)) 208 if err != nil { 209 return nil, fmt.Errorf("new credential failed: %w", err) 210 } 211 212 return vc, nil 213 } 214 215 // GetPresentation retrieves a verifiable presentation based on ID. 216 func (s *StoreImplementation) GetPresentation(id string) (*verifiable.Presentation, error) { 217 vpBytes, err := s.store.Get(id) 218 if err != nil { 219 return nil, fmt.Errorf("failed to get vc: %w", err) 220 } 221 222 vp, err := verifiable.ParsePresentation(vpBytes, 223 verifiable.WithPresDisabledProofCheck(), 224 verifiable.WithPresJSONLDDocumentLoader(s.documentLoader), 225 ) 226 if err != nil { 227 return nil, fmt.Errorf("new presentation failed: %w", err) 228 } 229 230 return vp, nil 231 } 232 233 // GetCredentialIDByName retrieves verifiable credential id based on name. 234 func (s *StoreImplementation) GetCredentialIDByName(name string) (string, error) { 235 recordBytes, err := s.store.Get(internal.CredentialNameDataKey(name)) 236 if err != nil { 237 return "", fmt.Errorf("fetch credential id based on name : %w", err) 238 } 239 240 var r Record 241 242 err = json.Unmarshal(recordBytes, &r) 243 if err != nil { 244 return "", fmt.Errorf("failed unmarshal record : %w", err) 245 } 246 247 return r.ID, nil 248 } 249 250 // GetPresentationIDByName retrieves verifiable presentation id based on name. 251 func (s *StoreImplementation) GetPresentationIDByName(name string) (string, error) { 252 recordBytes, err := s.store.Get(internal.PresentationNameDataKey(name)) 253 if err != nil { 254 return "", fmt.Errorf("fetch presentation id based on name : %w", err) 255 } 256 257 var r Record 258 259 err = json.Unmarshal(recordBytes, &r) 260 if err != nil { 261 return "", fmt.Errorf("failed unmarshal record : %w", err) 262 } 263 264 return r.ID, nil 265 } 266 267 // GetCredentials retrieves the verifiable credential records containing name and fields of interest. 268 func (s *StoreImplementation) GetCredentials() ([]*Record, error) { 269 return s.getAllRecords(internal.CredentialNameDataKey("")) 270 } 271 272 // GetPresentations retrieves the verifiable presentations records containing name and fields of interest. 273 func (s *StoreImplementation) GetPresentations() ([]*Record, error) { 274 return s.getAllRecords(internal.PresentationNameDataKey("")) 275 } 276 277 // RemoveCredentialByName removes the verifiable credential and its records containing given name. 278 func (s *StoreImplementation) RemoveCredentialByName(name string) error { 279 if name == "" { 280 return errors.New("credential name is mandatory") 281 } 282 283 id, err := s.GetCredentialIDByName(name) 284 if err != nil { 285 return fmt.Errorf("get credential id using name : %w", err) 286 } 287 288 err = s.remove(id, internal.CredentialNameDataKey(name)) 289 if err != nil { 290 return fmt.Errorf("unable to delete credential : %w", err) 291 } 292 293 return nil 294 } 295 296 // RemovePresentationByName removes the verifiable presentation and its records containing given name. 297 func (s *StoreImplementation) RemovePresentationByName(name string) error { 298 if name == "" { 299 return errors.New("presentation name is mandatory") 300 } 301 302 id, err := s.GetPresentationIDByName(name) 303 if err != nil { 304 return fmt.Errorf("get presentation id using name : %w", err) 305 } 306 307 err = s.remove(id, internal.PresentationNameDataKey(name)) 308 if err != nil { 309 return fmt.Errorf("unable to delete presentation : %w", err) 310 } 311 312 return nil 313 } 314 315 func (s *StoreImplementation) remove(id, recordKey string) error { 316 err := s.store.Delete(id) 317 if err != nil { 318 return fmt.Errorf("unable to delete from store : %w", err) 319 } 320 321 err = s.store.Delete(recordKey) 322 if err != nil { 323 return fmt.Errorf("unable to delete record : %w", err) 324 } 325 326 return nil 327 } 328 329 func (s *StoreImplementation) getAllRecords(searchKey string) ([]*Record, error) { 330 itr, err := s.store.Query(searchKey) 331 if err != nil { 332 return nil, fmt.Errorf("failed to query store: %w", err) 333 } 334 335 defer func() { 336 errClose := itr.Close() 337 if errClose != nil { 338 logger.Errorf("failed to close iterator: %s", errClose.Error()) 339 } 340 }() 341 342 var records []*Record 343 344 more, err := itr.Next() 345 if err != nil { 346 return nil, fmt.Errorf("failed to get next set of data from iterator") 347 } 348 349 for more { 350 var r *Record 351 352 value, err := itr.Value() 353 if err != nil { 354 return nil, fmt.Errorf("failed to get value from iterator: %w", err) 355 } 356 357 err = json.Unmarshal(value, &r) 358 if err != nil { 359 return nil, fmt.Errorf("failed to unmarshal record : %w", err) 360 } 361 362 records = append(records, r) 363 364 more, err = itr.Next() 365 if err != nil { 366 return nil, fmt.Errorf("failed to get next set of data from iterator") 367 } 368 } 369 370 return records, nil 371 } 372 373 func getVCSubjectID(vc *verifiable.Credential) string { 374 if subjectID, err := verifiable.SubjectID(vc.Subject); err == nil { 375 return subjectID 376 } 377 378 return "" 379 }