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  }