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  }