From 55bfc8224961558cfd820317808e83eadf84969e Mon Sep 17 00:00:00 2001 From: Volodymyr Kubiv Date: Thu, 14 Jul 2022 14:07:18 +0300 Subject: [PATCH] feat: factor walletlite.Command form vcwallet.Command. Signed-off-by: Volodymyr Kubiv --- .../pkg/wrappers/command/vcwallet.go | 13 +- .../pkg/wrappers/rest/endpoints.go | 7 +- .../pkg/wrappers/rest/vcwallet.go | 7 +- .../command/didcommwallet/command.go | 442 +++++++ .../command/didcommwallet/command_test.go | 991 +++++++++++++++ .../command/didcommwallet/models.go | 178 +++ pkg/controller/command/vcwallet/command.go | 386 +----- .../command/vcwallet/command_test.go | 1122 +++-------------- pkg/controller/command/vcwallet/models.go | 163 --- pkg/controller/controller_test.go | 4 +- pkg/controller/rest/vcwallet/models.go | 21 +- pkg/controller/rest/vcwallet/operation.go | 6 +- .../rest/vcwallet/operation_test.go | 33 +- 13 files changed, 1823 insertions(+), 1550 deletions(-) create mode 100644 pkg/controller/command/didcommwallet/command.go create mode 100644 pkg/controller/command/didcommwallet/command_test.go create mode 100644 pkg/controller/command/didcommwallet/models.go diff --git a/cmd/aries-agent-mobile/pkg/wrappers/command/vcwallet.go b/cmd/aries-agent-mobile/pkg/wrappers/command/vcwallet.go index 8e9f9d245..eabf24b71 100644 --- a/cmd/aries-agent-mobile/pkg/wrappers/command/vcwallet.go +++ b/cmd/aries-agent-mobile/pkg/wrappers/command/vcwallet.go @@ -11,6 +11,7 @@ import ( "github.com/hyperledger/aries-framework-go/cmd/aries-agent-mobile/pkg/wrappers/models" "github.com/hyperledger/aries-framework-go/pkg/controller/command" + cmddidcommwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet" cmdvcwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" ) @@ -261,13 +262,13 @@ func (v *VCWallet) CreateKeyPair(request *models.RequestEnvelope) *models.Respon // Connect accepts out-of-band invitations and performs DID exchange. func (v *VCWallet) Connect(request *models.RequestEnvelope) *models.ResponseEnvelope { - args := cmdvcwallet.ConnectRequest{} + args := cmddidcommwallet.ConnectRequest{} if err := json.Unmarshal(request.Payload, &args); err != nil { return &models.ResponseEnvelope{Error: &models.CommandError{Message: err.Error()}} } - response, cmdErr := exec(v.handlers[cmdvcwallet.ConnectMethod], args) + response, cmdErr := exec(v.handlers[cmddidcommwallet.ConnectMethod], args) if cmdErr != nil { return &models.ResponseEnvelope{Error: cmdErr} } @@ -278,13 +279,13 @@ func (v *VCWallet) Connect(request *models.RequestEnvelope) *models.ResponseEnve // ProposePresentation accepts out-of-band invitation and sends message proposing presentation // from wallet to relying party. func (v *VCWallet) ProposePresentation(request *models.RequestEnvelope) *models.ResponseEnvelope { - args := cmdvcwallet.ProposePresentationRequest{} + args := cmddidcommwallet.ProposePresentationRequest{} if err := json.Unmarshal(request.Payload, &args); err != nil { return &models.ResponseEnvelope{Error: &models.CommandError{Message: err.Error()}} } - response, cmdErr := exec(v.handlers[cmdvcwallet.ProposePresentationMethod], args) + response, cmdErr := exec(v.handlers[cmddidcommwallet.ProposePresentationMethod], args) if cmdErr != nil { return &models.ResponseEnvelope{Error: cmdErr} } @@ -294,13 +295,13 @@ func (v *VCWallet) ProposePresentation(request *models.RequestEnvelope) *models. // PresentProof sends present proof message from wallet to relying party. func (v *VCWallet) PresentProof(request *models.RequestEnvelope) *models.ResponseEnvelope { - args := cmdvcwallet.PresentProofRequest{} + args := cmddidcommwallet.PresentProofRequest{} if err := json.Unmarshal(request.Payload, &args); err != nil { return &models.ResponseEnvelope{Error: &models.CommandError{Message: err.Error()}} } - response, cmdErr := exec(v.handlers[cmdvcwallet.PresentProofMethod], args) + response, cmdErr := exec(v.handlers[cmddidcommwallet.PresentProofMethod], args) if cmdErr != nil { return &models.ResponseEnvelope{Error: cmdErr} } diff --git a/cmd/aries-agent-mobile/pkg/wrappers/rest/endpoints.go b/cmd/aries-agent-mobile/pkg/wrappers/rest/endpoints.go index 2ca880fbe..5bc5de8c1 100644 --- a/cmd/aries-agent-mobile/pkg/wrappers/rest/endpoints.go +++ b/cmd/aries-agent-mobile/pkg/wrappers/rest/endpoints.go @@ -9,6 +9,7 @@ package rest import ( "net/http" + cmddidcommwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet" cmddidexch "github.com/hyperledger/aries-framework-go/pkg/controller/command/didexchange" cmdintroduce "github.com/hyperledger/aries-framework-go/pkg/controller/command/introduce" cmdisscred "github.com/hyperledger/aries-framework-go/pkg/controller/command/issuecredential" @@ -518,13 +519,13 @@ func getVCWalletEndpoints() map[string]*endpoint { cmdvcwallet.CreateKeyPairMethod: { Path: opvcwallet.CreateKeyPairPath, Method: http.MethodPost, }, - cmdvcwallet.ConnectMethod: { + cmddidcommwallet.ConnectMethod: { Path: opvcwallet.ConnectPath, Method: http.MethodPost, }, - cmdvcwallet.ProposePresentationMethod: { + cmddidcommwallet.ProposePresentationMethod: { Path: opvcwallet.ProposePresentationPath, Method: http.MethodPost, }, - cmdvcwallet.PresentProofMethod: { + cmddidcommwallet.PresentProofMethod: { Path: opvcwallet.PresentProofPath, Method: http.MethodPost, }, } diff --git a/cmd/aries-agent-mobile/pkg/wrappers/rest/vcwallet.go b/cmd/aries-agent-mobile/pkg/wrappers/rest/vcwallet.go index 8816ea0a2..64b467df8 100644 --- a/cmd/aries-agent-mobile/pkg/wrappers/rest/vcwallet.go +++ b/cmd/aries-agent-mobile/pkg/wrappers/rest/vcwallet.go @@ -8,6 +8,7 @@ package rest import ( "github.com/hyperledger/aries-framework-go/cmd/aries-agent-mobile/pkg/wrappers/models" + cmddidcommwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet" cmdvcwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" ) @@ -97,18 +98,18 @@ func (wallet *VCWallet) CreateKeyPair(request *models.RequestEnvelope) *models.R // Connect accepts out-of-band invitations and performs DID exchange. func (wallet *VCWallet) Connect(request *models.RequestEnvelope) *models.ResponseEnvelope { - return wallet.createRespEnvelope(request, cmdvcwallet.ConnectMethod) + return wallet.createRespEnvelope(request, cmddidcommwallet.ConnectMethod) } // ProposePresentation accepts out-of-band invitation and sends message proposing presentation // from wallet to relying party. func (wallet *VCWallet) ProposePresentation(request *models.RequestEnvelope) *models.ResponseEnvelope { - return wallet.createRespEnvelope(request, cmdvcwallet.ProposePresentationMethod) + return wallet.createRespEnvelope(request, cmddidcommwallet.ProposePresentationMethod) } // PresentProof sends message present proof message from wallet to relying party. func (wallet *VCWallet) PresentProof(request *models.RequestEnvelope) *models.ResponseEnvelope { - return wallet.createRespEnvelope(request, cmdvcwallet.PresentProofMethod) + return wallet.createRespEnvelope(request, cmddidcommwallet.PresentProofMethod) } func (wallet *VCWallet) createRespEnvelope(request *models.RequestEnvelope, endpoint string) *models.ResponseEnvelope { diff --git a/pkg/controller/command/didcommwallet/command.go b/pkg/controller/command/didcommwallet/command.go new file mode 100644 index 000000000..8317fac81 --- /dev/null +++ b/pkg/controller/command/didcommwallet/command.go @@ -0,0 +1,442 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package didcommwallet + +import ( + "encoding/json" + "io" + "time" + + "github.com/piprate/json-gold/ld" + + "github.com/hyperledger/aries-framework-go/pkg/common/log" + "github.com/hyperledger/aries-framework-go/pkg/controller/command" + "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" + "github.com/hyperledger/aries-framework-go/pkg/controller/internal/cmdutil" + "github.com/hyperledger/aries-framework-go/pkg/crypto" + "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" + "github.com/hyperledger/aries-framework-go/pkg/internal/logutil" + "github.com/hyperledger/aries-framework-go/pkg/kms" + "github.com/hyperledger/aries-framework-go/pkg/wallet" + "github.com/hyperledger/aries-framework-go/spi/storage" +) + +var logger = log.New("aries-framework/command/didcommwallet") + +// Error codes. +const ( + // InvalidRequestErrorCode is typically a code for invalid requests. + InvalidRequestErrorCode = command.Code(iota + command.VCWallet) + + // DIDConnectErrorCode for errors while performing DID connect in wallet. + DIDConnectErrorCode + + // ProposePresentationErrorCode for errors while proposing presentation. + ProposePresentationErrorCode + + // PresentProofErrorCode for errors while presenting proof from wallet. + PresentProofErrorCode + + // ProposeCredentialErrorCode for errors while proposing credential from wallet. + ProposeCredentialErrorCode + + // RequestCredentialErrorCode for errors while request credential from wallet for issue credential protocol. + RequestCredentialErrorCode + + // ResolveCredentialManifestErrorCode for errors while resolving credential manifest from wallet. + ResolveCredentialManifestErrorCode +) + +// All command operations. +const ( + // command methods. + ConnectMethod = "Connect" + ProposePresentationMethod = "ProposePresentation" + PresentProofMethod = "PresentProof" + ProposeCredentialMethod = "ProposeCredential" + RequestCredentialMethod = "RequestCredential" + ResolveCredentialManifestMethod = "ResolveCredentialManifest" +) + +// miscellaneous constants for the vc wallet command controller. +const ( + // log constants. + logSuccess = "success" + logUserIDKey = "userID" + connectionIDString = "connectionID" + invitationIDString = "invitationID" + LabelString = "label" + + emptyRawLength = 4 +) + +// AuthCapabilityProvider is for providing Authorization Capabilities (ZCAP-LD) feature for +// wallet's EDV and WebKMS components. +type AuthCapabilityProvider = vcwallet.AuthCapabilityProvider + +// HTTPHeaderSigner is for http header signing, typically used for zcapld functionality. +type HTTPHeaderSigner = vcwallet.HTTPHeaderSigner + +// Config contains properties to customize verifiable credential wallet controller. +// All properties of this config are optional, but they can be used to customize wallet's webkms and edv client's. +type Config = vcwallet.Config + +// provider contains dependencies for the verifiable credential wallet command controller +// and is typically created by using aries.Context(). +type provider interface { + StorageProvider() storage.Provider + VDRegistry() vdr.Registry + Crypto() crypto.Crypto + JSONLDDocumentLoader() ld.DocumentLoader + MediaTypeProfiles() []string + didCommProvider // to be used only if wallet needs to be participated in DIDComm. +} + +// didCommProvider to be used only if wallet needs to be participated in DIDComm operation. +// TODO: using wallet KMS instead of provider KMS. +// TODO: reconcile Protocol storage with wallet store. +type didCommProvider interface { + KMS() kms.KeyManager + ServiceEndpoint() string + ProtocolStateStorageProvider() storage.Provider + Service(id string) (interface{}, error) + KeyType() kms.KeyType + KeyAgreementType() kms.KeyType +} + +// Command extends vcwallet.Command to add didComm functionality. +type Command struct { + *vcwallet.Command + ctx provider +} + +// New returns new verifiable credential wallet controller command instance. +func New(p provider, config *Config) *Command { + cmd := &Command{ + Command: vcwallet.New(p, config), + ctx: p, + } + + return cmd +} + +// GetHandlers returns list of all commands supported by this controller command. +func (o *Command) GetHandlers() []command.Handler { + return append(o.Command.GetHandlers(), + cmdutil.NewCommandHandler(vcwallet.CommandName, ConnectMethod, o.Connect), + cmdutil.NewCommandHandler(vcwallet.CommandName, ProposePresentationMethod, o.ProposePresentation), + cmdutil.NewCommandHandler(vcwallet.CommandName, PresentProofMethod, o.PresentProof), + cmdutil.NewCommandHandler(vcwallet.CommandName, ProposeCredentialMethod, o.ProposeCredential), + cmdutil.NewCommandHandler(vcwallet.CommandName, RequestCredentialMethod, o.RequestCredential), + cmdutil.NewCommandHandler(vcwallet.CommandName, ResolveCredentialManifestMethod, o.ResolveCredentialManifest), + ) +} + +// Connect accepts out-of-band invitations and performs DID exchange. +func (o *Command) Connect(rw io.Writer, req io.Reader) command.Error { + request := &ConnectRequest{} + + err := json.NewDecoder(req).Decode(&request) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ConnectMethod, err.Error()) + + return command.NewValidationError(InvalidRequestErrorCode, err) + } + + vcWallet, err := wallet.New(request.UserID, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ConnectMethod, err.Error()) + + return command.NewExecuteError(DIDConnectErrorCode, err) + } + + didComm, err := wallet.NewDidComm(vcWallet, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ConnectMethod, err.Error()) + + return command.NewExecuteError(DIDConnectErrorCode, err) + } + + connectionID, err := didComm.Connect(request.Auth, request.Invitation, + wallet.WithConnectTimeout(request.Timeout), wallet.WithReuseDID(request.ReuseConnection), + wallet.WithReuseAnyConnection(request.ReuseAnyConnection), wallet.WithMyLabel(request.MyLabel), + wallet.WithRouterConnections(request.RouterConnections...)) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ConnectMethod, err.Error()) + + return command.NewExecuteError(DIDConnectErrorCode, err) + } + + command.WriteNillableResponse(rw, &ConnectResponse{ConnectionID: connectionID}, logger) + + logutil.LogDebug(logger, vcwallet.CommandName, ConnectMethod, logSuccess, + logutil.CreateKeyValueString(logUserIDKey, request.UserID), + logutil.CreateKeyValueString(invitationIDString, request.Invitation.ID), + logutil.CreateKeyValueString(LabelString, request.MyLabel), + logutil.CreateKeyValueString(connectionIDString, connectionID)) + + return nil +} + +// ProposePresentation accepts out-of-band invitation and sends message proposing presentation +// from wallet to relying party. +// https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposepresentation +// +// Currently Supporting +// [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2) +func (o *Command) ProposePresentation(rw io.Writer, req io.Reader) command.Error { + request := &ProposePresentationRequest{} + + err := json.NewDecoder(req).Decode(&request) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ProposePresentationMethod, err.Error()) + + return command.NewValidationError(InvalidRequestErrorCode, err) + } + + vcWallet, err := wallet.New(request.UserID, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ProposePresentationMethod, err.Error()) + + return command.NewExecuteError(ProposePresentationErrorCode, err) + } + + didComm, err := wallet.NewDidComm(vcWallet, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ProposePresentationMethod, err.Error()) + + return command.NewExecuteError(ProposePresentationErrorCode, err) + } + + msg, err := didComm.ProposePresentation(request.Auth, request.Invitation, + wallet.WithFromDID(request.FromDID), wallet.WithInitiateTimeout(request.Timeout), + wallet.WithConnectOptions(wallet.WithConnectTimeout(request.ConnectionOpts.Timeout), + wallet.WithReuseDID(request.ConnectionOpts.ReuseConnection), + wallet.WithReuseAnyConnection(request.ConnectionOpts.ReuseAnyConnection), + wallet.WithMyLabel(request.ConnectionOpts.MyLabel), + wallet.WithRouterConnections(request.ConnectionOpts.RouterConnections...))) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ProposePresentationMethod, err.Error()) + + return command.NewExecuteError(ProposePresentationErrorCode, err) + } + + command.WriteNillableResponse(rw, &ProposePresentationResponse{PresentationRequest: msg}, logger) + + logutil.LogDebug(logger, vcwallet.CommandName, ProposePresentationMethod, logSuccess, + logutil.CreateKeyValueString(logUserIDKey, request.UserID)) + + return nil +} + +// PresentProof sends present proof message from wallet to relying party. +// https://w3c-ccg.github.io/universal-wallet-interop-spec/#presentproof +// +// Currently Supporting +// [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2) +// +func (o *Command) PresentProof(rw io.Writer, req io.Reader) command.Error { + request := &PresentProofRequest{} + + err := json.NewDecoder(req).Decode(&request) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, PresentProofMethod, err.Error()) + + return command.NewValidationError(InvalidRequestErrorCode, err) + } + + vcWallet, err := wallet.New(request.UserID, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, PresentProofMethod, err.Error()) + + return command.NewExecuteError(PresentProofErrorCode, err) + } + + didComm, err := wallet.NewDidComm(vcWallet, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, PresentProofMethod, err.Error()) + + return command.NewExecuteError(PresentProofErrorCode, err) + } + + status, err := didComm.PresentProof(request.Auth, request.ThreadID, + prepareConcludeInteractionOpts(request.WaitForDone, request.Timeout, request.Presentation)...) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, PresentProofMethod, err.Error()) + + return command.NewExecuteError(PresentProofErrorCode, err) + } + + command.WriteNillableResponse(rw, status, logger) + + logutil.LogDebug(logger, vcwallet.CommandName, PresentProofMethod, logSuccess, + logutil.CreateKeyValueString(logUserIDKey, request.UserID)) + + return nil +} + +// ProposeCredential sends propose credential message from wallet to issuer. +// https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposecredential +// +// Currently Supporting : 0453-issueCredentialV2 +// https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md +// +func (o *Command) ProposeCredential(rw io.Writer, req io.Reader) command.Error { + request := &ProposeCredentialRequest{} + + err := json.NewDecoder(req).Decode(&request) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ProposeCredentialMethod, err.Error()) + + return command.NewValidationError(InvalidRequestErrorCode, err) + } + + vcWallet, err := wallet.New(request.UserID, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ProposeCredentialMethod, err.Error()) + + return command.NewExecuteError(ProposeCredentialErrorCode, err) + } + + didComm, err := wallet.NewDidComm(vcWallet, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ProposeCredentialMethod, err.Error()) + + return command.NewExecuteError(ProposeCredentialErrorCode, err) + } + + msg, err := didComm.ProposeCredential(request.Auth, request.Invitation, + wallet.WithFromDID(request.FromDID), wallet.WithInitiateTimeout(request.Timeout), + wallet.WithConnectOptions(wallet.WithConnectTimeout(request.ConnectionOpts.Timeout), + wallet.WithReuseDID(request.ConnectionOpts.ReuseConnection), + wallet.WithReuseAnyConnection(request.ConnectionOpts.ReuseAnyConnection), + wallet.WithMyLabel(request.ConnectionOpts.MyLabel), + wallet.WithRouterConnections(request.ConnectionOpts.RouterConnections...))) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ProposeCredentialMethod, err.Error()) + + return command.NewExecuteError(ProposeCredentialErrorCode, err) + } + + command.WriteNillableResponse(rw, &ProposeCredentialResponse{OfferCredential: msg}, logger) + + logutil.LogDebug(logger, vcwallet.CommandName, ProposeCredentialMethod, logSuccess, + logutil.CreateKeyValueString(logUserIDKey, request.UserID)) + + return nil +} + +// RequestCredential sends request credential message from wallet to issuer and +// optionally waits for credential fulfillment. +// https://w3c-ccg.github.io/universal-wallet-interop-spec/#requestcredential +// +// Currently Supporting : 0453-issueCredentialV2 +// https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md +// +func (o *Command) RequestCredential(rw io.Writer, req io.Reader) command.Error { + request := &RequestCredentialRequest{} + + err := json.NewDecoder(req).Decode(&request) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, RequestCredentialMethod, err.Error()) + + return command.NewValidationError(InvalidRequestErrorCode, err) + } + + vcWallet, err := wallet.New(request.UserID, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, RequestCredentialMethod, err.Error()) + + return command.NewExecuteError(RequestCredentialErrorCode, err) + } + + didComm, err := wallet.NewDidComm(vcWallet, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, RequestCredentialMethod, err.Error()) + + return command.NewExecuteError(RequestCredentialErrorCode, err) + } + + status, err := didComm.RequestCredential(request.Auth, request.ThreadID, + prepareConcludeInteractionOpts(request.WaitForDone, request.Timeout, request.Presentation)...) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, RequestCredentialMethod, err.Error()) + + return command.NewExecuteError(RequestCredentialErrorCode, err) + } + + command.WriteNillableResponse(rw, status, logger) + + logutil.LogDebug(logger, vcwallet.CommandName, RequestCredentialMethod, logSuccess, + logutil.CreateKeyValueString(logUserIDKey, request.UserID)) + + return nil +} + +// ResolveCredentialManifest resolves given credential manifest by credential fulfillment or credential. +// Supports: https://identity.foundation/credential-manifest/ +// +// Writes list of resolved descriptors to writer or returns error if operation fails. +// +func (o *Command) ResolveCredentialManifest(rw io.Writer, req io.Reader) command.Error { + request := &ResolveCredentialManifestRequest{} + + err := json.NewDecoder(req).Decode(&request) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ResolveCredentialManifestMethod, err.Error()) + + return command.NewValidationError(InvalidRequestErrorCode, err) + } + + vcWallet, err := wallet.New(request.UserID, o.ctx) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ResolveCredentialManifestMethod, err.Error()) + + return command.NewExecuteError(ResolveCredentialManifestErrorCode, err) + } + + resolved, err := vcWallet.ResolveCredentialManifest(request.Auth, request.Manifest, + prepareResolveManifestOption(request)) + if err != nil { + logutil.LogInfo(logger, vcwallet.CommandName, ResolveCredentialManifestMethod, err.Error()) + + return command.NewExecuteError(ResolveCredentialManifestErrorCode, err) + } + + command.WriteNillableResponse(rw, &ResolveCredentialManifestResponse{Resolved: resolved}, logger) + + logutil.LogDebug(logger, vcwallet.CommandName, ResolveCredentialManifestMethod, logSuccess, + logutil.CreateKeyValueString(logUserIDKey, request.UserID)) + + return nil +} + +func prepareConcludeInteractionOpts(waitForDone bool, timeout time.Duration, presentation json.RawMessage) []wallet.ConcludeInteractionOptions { //nolint: lll + var options []wallet.ConcludeInteractionOptions + + if waitForDone { + options = append(options, wallet.WaitForDone(timeout)) + } + + return append(options, wallet.FromRawPresentation(presentation)) +} + +func prepareResolveManifestOption(rqst *ResolveCredentialManifestRequest) wallet.ResolveManifestOption { + if len(rqst.Fulfillment) > emptyRawLength { + return wallet.ResolveRawFulfillment(rqst.Fulfillment) + } + + if len(rqst.Credential) > emptyRawLength { + return wallet.ResolveRawCredential(rqst.DescriptorID, rqst.Credential) + } + + if rqst.CredentialID != "" { + return wallet.ResolveCredentialID(rqst.DescriptorID, rqst.CredentialID) + } + + return nil +} diff --git a/pkg/controller/command/didcommwallet/command_test.go b/pkg/controller/command/didcommwallet/command_test.go new file mode 100644 index 000000000..d774b26af --- /dev/null +++ b/pkg/controller/command/didcommwallet/command_test.go @@ -0,0 +1,991 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package didcommwallet + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "strings" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + "github.com/hyperledger/aries-framework-go/internal/testdata" + outofbandClient "github.com/hyperledger/aries-framework-go/pkg/client/outofband" + "github.com/hyperledger/aries-framework-go/pkg/controller/command" + "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" + "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model" + "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" + "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange" + issuecredentialsvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential" + "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator" + outofbandSvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofband" + oobv2 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofbandv2" + presentproofSvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/presentproof" + "github.com/hyperledger/aries-framework-go/pkg/doc/did" + vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" + mockoutofbandv2 "github.com/hyperledger/aries-framework-go/pkg/internal/gomocks/client/outofbandv2" + "github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil" + mockdidexchange "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/didexchange" + mockissuecredential "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/issuecredential" + mockmediator "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/mediator" + mockoutofband "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/outofband" + mockpresentproof "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/presentproof" + mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider" + mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage" + mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr" + "github.com/hyperledger/aries-framework-go/pkg/store/connection" + "github.com/hyperledger/aries-framework-go/pkg/vdr/key" + "github.com/hyperledger/aries-framework-go/pkg/wallet" +) + +const ( + sampleUserID = "sample-user01" + samplePassPhrase = "fakepassphrase" + sampleCommandError = "sample-command-error-01" + sampleFakeTkn = "sample-fake-token-01" + webRedirectStatusKey = "status" + webRedirectURLKey = "url" + exampleWebRedirect = "http://example.com/sample" +) + +func TestNew(t *testing.T) { + t.Run("successfully create new command instance", func(t *testing.T) { + cmd := New(newMockProvider(t), &Config{}) + require.NotNil(t, cmd) + + require.Len(t, cmd.GetHandlers(), 21) + }) +} + +func TestCommand_Connect(t *testing.T) { + const sampleDIDCommUser = "sample-didcomm-user01" + + mockctx := newMockProvider(t) + + createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + defer lock() + + t.Run("successfully perform DID connect", func(t *testing.T) { + sampleConnID := uuid.New().String() + + oobSvc := &mockoutofband.MockOobService{ + AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { + return sampleConnID, nil + }, + } + mockctx.ServiceMap[outofbandSvc.Name] = oobSvc + + didexSvc := &mockdidexchange.MockDIDExchangeSvc{ + RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { + ch <- service.StateMsg{ + Type: service.PostState, + StateID: didexchange.StateIDCompleted, + Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID}, + } + + return nil + }, + } + mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc + + cmdDidComm := New(mockctx, &Config{}) + + request := &ConnectRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + Invitation: &outofbandClient.Invitation{}, + ConnectOpts: ConnectOpts{ + MyLabel: "sample-label", + }, + } + + var b bytes.Buffer + cmdErr := cmdDidComm.Connect(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + + var response ConnectResponse + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + require.NotEmpty(t, response) + require.Equal(t, sampleConnID, response.ConnectionID) + }) + + t.Run("did connect failure", func(t *testing.T) { + oobSvc := &mockoutofband.MockOobService{ + AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { + return "", fmt.Errorf(sampleCommandError) + }, + } + mockctx.ServiceMap[outofbandSvc.Name] = oobSvc + + cmd := New(mockctx, &Config{}) + + request := &ConnectRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + Invitation: &outofbandClient.Invitation{}, + ConnectOpts: ConnectOpts{ + MyLabel: "sample-label", + }, + } + + var b bytes.Buffer + cmdErr := cmd.Connect(&b, getReader(t, &request)) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, sampleCommandError) + validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, "failed to accept invitation") + require.Empty(t, b.Bytes()) + }) + + t.Run("did connect failure - invalid request", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + var b bytes.Buffer + cmdErr := cmd.Connect(&b, bytes.NewBufferString("--")) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") + require.Empty(t, b.Bytes()) + }) + + t.Run("attempt to didconnect with invalid profile", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &ConnectRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn}, + Invitation: &outofbandClient.Invitation{}, + ConnectOpts: ConnectOpts{ + MyLabel: "sample-label", + }, + } + + var b bytes.Buffer + cmdErr := cmd.Connect(&b, getReader(t, &request)) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, "failed to get VC wallet profile") + require.Empty(t, b.Bytes()) + }) +} + +func TestCommand_ProposePresentation(t *testing.T) { + const sampleDIDCommUser = "sample-didcomm-user02" + + mockctx := newMockProvider(t) + + createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + defer lock() + + const ( + myDID = "did:mydid:123" + theirDID = "did:theirdid:123" + ) + + t.Run("successfully send propose presentation", func(t *testing.T) { + sampleConnID := uuid.New().String() + + oobSvc := &mockoutofband.MockOobService{ + AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { + return sampleConnID, nil + }, + } + mockctx.ServiceMap[outofbandSvc.Name] = oobSvc + + didexSvc := &mockdidexchange.MockDIDExchangeSvc{ + RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { + ch <- service.StateMsg{ + Type: service.PostState, + StateID: didexchange.StateIDCompleted, + Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID}, + } + + return nil + }, + } + mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc + + thID := uuid.New().String() + ppSvc := &mockpresentproof.MockPresentProofSvc{ + ActionsFunc: func() ([]presentproofSvc.Action, error) { + return []presentproofSvc.Action{ + { + PIID: thID, + Msg: service.NewDIDCommMsgMap(&presentproofSvc.RequestPresentationV2{ + Comment: "mock msg", + }), + MyDID: myDID, + TheirDID: theirDID, + }, + }, nil + }, + HandleFunc: func(service.DIDCommMsg) (string, error) { + return thID, nil + }, + } + mockctx.ServiceMap[presentproofSvc.Name] = ppSvc + + store, err := mockctx.StorageProvider().OpenStore(connection.Namespace) + require.NoError(t, err) + + record := &connection.Record{ + ConnectionID: sampleConnID, + MyDID: myDID, + TheirDID: theirDID, + } + recordBytes, err := json.Marshal(record) + require.NoError(t, err) + require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes)) + + cmd := New(mockctx, &Config{}) + + request := &ProposePresentationRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + Invitation: &wallet.GenericInvitation{}, + } + + var b bytes.Buffer + cmdErr := cmd.ProposePresentation(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + + var response ProposePresentationResponse + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + require.NotEmpty(t, response) + require.NotEmpty(t, response.PresentationRequest) + }) + + t.Run("failed to send propose presentation", func(t *testing.T) { + oobSvc := &mockoutofband.MockOobService{ + AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { + return "", fmt.Errorf(sampleCommandError) + }, + } + + mockctx.ServiceMap[outofbandSvc.Name] = oobSvc + + cmd := New(mockctx, &Config{}) + + request := &ConnectRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + Invitation: &outofbandClient.Invitation{}, + ConnectOpts: ConnectOpts{ + MyLabel: "sample-label", + }, + } + + var b bytes.Buffer + cmdErr := cmd.ProposePresentation(&b, getReader(t, &request)) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, sampleCommandError) + validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, "failed to accept invitation") + require.Empty(t, b.Bytes()) + }) + + t.Run("failed to send propose presentation - invalid request", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + var b bytes.Buffer + cmdErr := cmd.ProposePresentation(&b, bytes.NewBufferString("--")) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") + require.Empty(t, b.Bytes()) + }) + + t.Run("failed to send propose presentation - invalid profile", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &ConnectRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn}, + Invitation: &outofbandClient.Invitation{}, + ConnectOpts: ConnectOpts{ + MyLabel: "sample-label", + }, + } + + var b bytes.Buffer + cmdErr := cmd.ProposePresentation(&b, getReader(t, &request)) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, "failed to get VC wallet profile") + require.Empty(t, b.Bytes()) + }) +} + +func TestCommand_PresentProof(t *testing.T) { + const sampleDIDCommUser = "sample-didcomm-user03" + + mockctx := newMockProvider(t) + + createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + defer lock() + + t.Run("successfully present proof", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &PresentProofRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + ThreadID: uuid.New().String(), + Presentation: json.RawMessage{}, + } + + var b bytes.Buffer + cmdErr := cmd.PresentProof(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + }) + + t.Run("successfully present proof - wait for done", func(t *testing.T) { + thID := uuid.New().String() + mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{ + RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { + ch <- service.StateMsg{ + Type: service.PostState, + StateID: presentproofSvc.StateNameDone, + Properties: &mockdidexchange.MockEventProperties{ + Properties: map[string]interface{}{ + webRedirectStatusKey: model.AckStatusOK, + webRedirectURLKey: exampleWebRedirect, + }, + }, + Msg: &mockMsg{thID: thID}, + } + + return nil + }, + } + mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc + + cmd := New(mockctx, &Config{}) + + request := &PresentProofRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + ThreadID: thID, + Presentation: json.RawMessage{}, + WaitForDone: true, + Timeout: 1 * time.Millisecond, + } + + var b bytes.Buffer + cmdErr := cmd.PresentProof(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + + var response wallet.CredentialInteractionStatus + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + require.NotEmpty(t, response) + require.Equal(t, exampleWebRedirect, response.RedirectURL) + require.Equal(t, model.AckStatusOK, response.Status) + }) + + t.Run("failed to present proof", func(t *testing.T) { + ppSvc := &mockpresentproof.MockPresentProofSvc{ + ActionContinueFunc: func(string, ...presentproofSvc.Opt) error { + return fmt.Errorf(sampleCommandError) + }, + } + + mockctx.ServiceMap[presentproofSvc.Name] = ppSvc + + cmd := New(mockctx, &Config{}) + + request := &PresentProofRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + ThreadID: uuid.New().String(), + Presentation: json.RawMessage{}, + } + + var b bytes.Buffer + cmdErr := cmd.PresentProof(&b, getReader(t, &request)) + validateError(t, cmdErr, command.ExecuteError, PresentProofErrorCode, sampleCommandError) + require.Empty(t, b.Bytes()) + }) + + t.Run("failed to present proof - invalid request", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + var b bytes.Buffer + cmdErr := cmd.PresentProof(&b, bytes.NewBufferString("--")) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") + require.Empty(t, b.Bytes()) + }) + + t.Run("failed to present proof - invalid profile", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &PresentProofRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUserID, Auth: token}, + ThreadID: uuid.New().String(), + Presentation: json.RawMessage{}, + } + + var b bytes.Buffer + cmdErr := cmd.PresentProof(&b, getReader(t, &request)) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ExecuteError, PresentProofErrorCode, "failed to get VC wallet profile") + require.Empty(t, b.Bytes()) + }) +} + +func TestCommand_ProposeCredential(t *testing.T) { + const ( + sampleDIDCommUser = "sample-didcomm-user02" + sampleMsgComment = "sample mock msg" + myDID = "did:mydid:123" + theirDID = "did:theirdid:123" + ) + + mockctx := newMockProvider(t) + + createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + defer lock() + + t.Run("successfully send propose credential", func(t *testing.T) { + sampleConnID := uuid.New().String() + + oobSvc := &mockoutofband.MockOobService{ + AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { + return sampleConnID, nil + }, + } + mockctx.ServiceMap[outofbandSvc.Name] = oobSvc + + didexSvc := &mockdidexchange.MockDIDExchangeSvc{ + RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { + ch <- service.StateMsg{ + Type: service.PostState, + StateID: didexchange.StateIDCompleted, + Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID}, + } + + return nil + }, + } + mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc + + thID := uuid.New().String() + icSvc := &mockissuecredential.MockIssueCredentialSvc{ + ActionsFunc: func() ([]issuecredentialsvc.Action, error) { + return []issuecredentialsvc.Action{ + { + PIID: thID, + Msg: service.NewDIDCommMsgMap(&issuecredentialsvc.OfferCredentialV2{ + Comment: sampleMsgComment, + }), + MyDID: myDID, + TheirDID: theirDID, + }, + }, nil + }, + HandleFunc: func(service.DIDCommMsg) (string, error) { + return thID, nil + }, + } + mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc + + store, err := mockctx.StorageProvider().OpenStore(connection.Namespace) + require.NoError(t, err) + + record := &connection.Record{ + ConnectionID: sampleConnID, + MyDID: myDID, + TheirDID: theirDID, + } + recordBytes, err := json.Marshal(record) + require.NoError(t, err) + require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes)) + + cmd := New(mockctx, &Config{}) + + request := &ProposeCredentialRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + Invitation: &wallet.GenericInvitation{}, + } + + var b bytes.Buffer + cmdErr := cmd.ProposeCredential(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + + var response ProposeCredentialResponse + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + require.NotEmpty(t, response) + require.NotEmpty(t, response.OfferCredential) + + offer := &issuecredentialsvc.OfferCredentialV2{} + + err = response.OfferCredential.Decode(offer) + require.NoError(t, err) + require.NotEmpty(t, offer) + require.Equal(t, sampleMsgComment, offer.Comment) + }) + + t.Run("failed to send propose credential", func(t *testing.T) { + oobSvc := &mockoutofband.MockOobService{ + AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { + return "", fmt.Errorf(sampleCommandError) + }, + } + + mockctx.ServiceMap[outofbandSvc.Name] = oobSvc + + cmd := New(mockctx, &Config{}) + + request := &ConnectRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + Invitation: &outofbandClient.Invitation{}, + ConnectOpts: ConnectOpts{ + MyLabel: "sample-label", + }, + } + + var b bytes.Buffer + cmdErr := cmd.ProposeCredential(&b, getReader(t, &request)) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, sampleCommandError) + validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, "failed to accept invitation") + require.Empty(t, b.Bytes()) + }) + + t.Run("failed to send propose credential - invalid request", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + var b bytes.Buffer + cmdErr := cmd.ProposeCredential(&b, bytes.NewBufferString("--")) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") + require.Empty(t, b.Bytes()) + }) + + t.Run("failed to send propose credential - invalid profile", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &ConnectRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn}, + Invitation: &outofbandClient.Invitation{}, + ConnectOpts: ConnectOpts{ + MyLabel: "sample-label", + }, + } + + var b bytes.Buffer + cmdErr := cmd.ProposeCredential(&b, getReader(t, &request)) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, "failed to get VC wallet profile") + require.Empty(t, b.Bytes()) + }) +} + +func TestCommand_RequestCredential(t *testing.T) { + const sampleDIDCommUser = "sample-didcomm-user03" + + mockctx := newMockProvider(t) + + createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{ + UserID: sampleDIDCommUser, + LocalKMSPassphrase: samplePassPhrase, + }) + + defer lock() + + t.Run("successfully request credential", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &RequestCredentialRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + ThreadID: uuid.New().String(), + Presentation: json.RawMessage{}, + } + + var b bytes.Buffer + cmdErr := cmd.RequestCredential(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + }) + + t.Run("successfully request credential - wait for done", func(t *testing.T) { + thID := uuid.New().String() + + icSvc := &mockissuecredential.MockIssueCredentialSvc{ + RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { + ch <- service.StateMsg{ + Type: service.PostState, + StateID: "done", + Properties: &mockdidexchange.MockEventProperties{ + Properties: map[string]interface{}{ + webRedirectStatusKey: model.AckStatusOK, + webRedirectURLKey: exampleWebRedirect, + }, + }, + Msg: &mockMsg{thID: thID}, + } + + return nil + }, + } + mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc + + cmd := New(mockctx, &Config{}) + + request := &RequestCredentialRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + ThreadID: thID, + Presentation: json.RawMessage{}, + WaitForDone: true, + Timeout: 600 * time.Millisecond, + } + + var b bytes.Buffer + cmdErr := cmd.RequestCredential(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + + var response wallet.CredentialInteractionStatus + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + require.NotEmpty(t, response) + require.Equal(t, exampleWebRedirect, response.RedirectURL) + require.Equal(t, model.AckStatusOK, response.Status) + }) + + t.Run("failed to request credential", func(t *testing.T) { + icSvc := &mockissuecredential.MockIssueCredentialSvc{ + ActionContinueFunc: func(string, ...issuecredentialsvc.Opt) error { + return fmt.Errorf(sampleCommandError) + }, + } + mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc + + cmd := New(mockctx, &Config{}) + + request := &RequestCredentialRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, + ThreadID: uuid.New().String(), + Presentation: json.RawMessage{}, + } + + var b bytes.Buffer + cmdErr := cmd.RequestCredential(&b, getReader(t, &request)) + validateError(t, cmdErr, command.ExecuteError, RequestCredentialErrorCode, sampleCommandError) + require.Empty(t, b.Bytes()) + }) + + t.Run("failed to request credential - invalid request", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + var b bytes.Buffer + cmdErr := cmd.RequestCredential(&b, bytes.NewBufferString("--")) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") + require.Empty(t, b.Bytes()) + }) + + t.Run("failed to request credential - invalid profile", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &RequestCredentialRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUserID, Auth: token}, + ThreadID: uuid.New().String(), + Presentation: json.RawMessage{}, + } + + var b bytes.Buffer + cmdErr := cmd.RequestCredential(&b, getReader(t, &request)) + require.Error(t, cmdErr) + + validateError(t, cmdErr, command.ExecuteError, RequestCredentialErrorCode, "failed to get VC wallet profile") + require.Empty(t, b.Bytes()) + }) +} + +func TestCommand_ResolveCredentialManifest(t *testing.T) { + const sampleUser1 = "sample-user-r01" + + mockctx := newMockProvider(t) + mockctx.VDRegistryValue = getMockDIDKeyVDR() + + createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{ + UserID: sampleUser1, + LocalKMSPassphrase: samplePassPhrase, + }) + + token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{ + UserID: sampleUser1, + LocalKMSPassphrase: samplePassPhrase, + }) + + defer lock() + + addContent(t, mockctx, &vcwallet.AddContentRequest{ + Content: testdata.SampleUDCVC, + ContentType: "credential", + WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, + }) + + t.Run("successfully resolve credential fulfillment", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &ResolveCredentialManifestRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, + Manifest: testdata.CredentialManifestMultipleVCs, + Fulfillment: testdata.CredentialFulfillmentWithMultipleVCs, + } + + var b bytes.Buffer + cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, request)) + require.NoError(t, cmdErr) + + var response ResolveCredentialManifestResponse + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + require.NotEmpty(t, response) + require.Len(t, response.Resolved, 2) + }) + + t.Run("successfully resolve credential", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &ResolveCredentialManifestRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, + Manifest: testdata.CredentialManifestMultipleVCs, + Credential: testdata.SampleUDCVC, + DescriptorID: "udc_output", + } + + var b bytes.Buffer + cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + + var response ResolveCredentialManifestResponse + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + require.NotEmpty(t, response) + require.Len(t, response.Resolved, 1) + }) + + t.Run("successfully resolve credential ID", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + request := &ResolveCredentialManifestRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, + Manifest: testdata.CredentialManifestMultipleVCs, + CredentialID: "http://example.edu/credentials/1872", + DescriptorID: "udc_output", + } + + var b bytes.Buffer + cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + + var response ResolveCredentialManifestResponse + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + require.NotEmpty(t, response) + require.Len(t, response.Resolved, 1) + }) + + t.Run("failed to resolve - invalid requests", func(t *testing.T) { + cmd := New(mockctx, &Config{}) + + // missing manifest + request := &ResolveCredentialManifestRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, + Credential: testdata.SampleUDCVC, + DescriptorID: "udc_output", + } + + var b bytes.Buffer + cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request)) + validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode, + "failed to read credential manifest") + + // missing descriptor ID + request = &ResolveCredentialManifestRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, + Manifest: testdata.CredentialManifestMultipleVCs, + Credential: testdata.SampleUDCVC, + } + + b.Reset() + cmdErr = cmd.ResolveCredentialManifest(&b, getReader(t, &request)) + validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode, + "unable to find matching descriptor") + + // missing resolve option + request = &ResolveCredentialManifestRequest{ + WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, + Manifest: testdata.CredentialManifestMultipleVCs, + } + + b.Reset() + cmdErr = cmd.ResolveCredentialManifest(&b, getReader(t, &request)) + validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode, "invalid option") + + b.Reset() + cmdErr = cmd.ResolveCredentialManifest(&b, bytes.NewBufferString("==")) + validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") + }) +} + +func createSampleUserProfile(t *testing.T, ctx *mockprovider.Provider, request *vcwallet.CreateOrUpdateProfileRequest) { + cmd := vcwallet.New(ctx, &Config{}) + require.NotNil(t, cmd) + + var l bytes.Buffer + cmdErr := cmd.CreateProfile(&l, getReader(t, request)) + require.NoError(t, cmdErr) +} + +func getReader(t *testing.T, v interface{}) io.Reader { + vcReqBytes, err := json.Marshal(v) + require.NoError(t, err) + + return bytes.NewBuffer(vcReqBytes) +} + +func getUnlockToken(t *testing.T, b bytes.Buffer) string { + var response vcwallet.UnlockWalletResponse + + require.NoError(t, json.NewDecoder(&b).Decode(&response)) + + return response.Token +} + +func unlockWallet(t *testing.T, ctx *mockprovider.Provider, request *vcwallet.UnlockWalletRequest) (string, func()) { + cmd := vcwallet.New(ctx, nil) + + var b bytes.Buffer + + cmdErr := cmd.Open(&b, getReader(t, &request)) + require.NoError(t, cmdErr) + + return getUnlockToken(t, b), func() { + cmdErr = cmd.Close(&b, getReader(t, &vcwallet.LockWalletRequest{UserID: request.UserID})) + if cmdErr != nil { + t.Log(t, cmdErr) + } + } +} + +func addContent(t *testing.T, ctx *mockprovider.Provider, request *vcwallet.AddContentRequest) { + cmd := vcwallet.New(ctx, &Config{}) + + var b bytes.Buffer + defer b.Reset() + + cmdErr := cmd.Add(&b, getReader(t, &request)) + require.NoError(t, cmdErr) +} + +func validateError(t *testing.T, err command.Error, + expectedType command.Type, expectedCode command.Code, contains string) { + require.Error(t, err) + require.Equal(t, err.Type(), expectedType) + require.Equal(t, err.Code(), expectedCode) + + if contains != "" { + require.Contains(t, err.Error(), contains) + } +} + +func newMockProvider(t *testing.T) *mockprovider.Provider { + t.Helper() + + loader, err := ldtestutil.DocumentLoader() + require.NoError(t, err) + + serviceMap := map[string]interface{}{ + presentproofSvc.Name: &mockpresentproof.MockPresentProofSvc{}, + outofbandSvc.Name: &mockoutofband.MockOobService{}, + didexchange.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{}, + mediator.Coordination: &mockmediator.MockMediatorSvc{}, + issuecredentialsvc.Name: &mockissuecredential.MockIssueCredentialSvc{}, + oobv2.Name: &mockoutofbandv2.MockOobService{}, + } + + return &mockprovider.Provider{ + StorageProviderValue: mockstorage.NewMockStoreProvider(), + ProtocolStateStorageProviderValue: mockstorage.NewMockStoreProvider(), + DocumentLoaderValue: loader, + ServiceMap: serviceMap, + } +} + +func getMockDIDKeyVDR() *mockvdr.MockVDRegistry { + return &mockvdr.MockVDRegistry{ + ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { + if strings.HasPrefix(didID, "did:key:") { + k := key.New() + + d, e := k.Read(didID) + if e != nil { + return nil, e + } + + return d, nil + } + + return nil, fmt.Errorf("did not found") + }, + } +} + +// mockMsg containing custom parent thread ID. +type mockMsg struct { + *service.DIDCommMsgMap + thID string +} + +func (m *mockMsg) ParentThreadID() string { + return m.thID +} + +func (m *mockMsg) ThreadID() (string, error) { + return m.thID, nil +} diff --git a/pkg/controller/command/didcommwallet/models.go b/pkg/controller/command/didcommwallet/models.go new file mode 100644 index 000000000..3e537a71f --- /dev/null +++ b/pkg/controller/command/didcommwallet/models.go @@ -0,0 +1,178 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package didcommwallet + +import ( + "encoding/json" + "time" + + "github.com/hyperledger/aries-framework-go/pkg/client/outofband" + "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" + "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" + "github.com/hyperledger/aries-framework-go/pkg/doc/cm" + "github.com/hyperledger/aries-framework-go/pkg/wallet" +) + +// ConnectRequest is request model for wallet DID connect operation. +type ConnectRequest struct { + vcwallet.WalletAuth + + // out-of-band invitation to establish connection. + Invitation *outofband.Invitation `json:"invitation"` + + ConnectOpts +} + +// ConnectOpts is option for accepting out-of-band invitation and to perform DID exchange. +type ConnectOpts struct { + // Label to be shared with the other agent during the subsequent DID exchange. + MyLabel string `json:"myLabel,omitempty"` + + // router connections to be used to establish connection. + RouterConnections []string `json:"routerConnections,omitempty"` + + // DID to be used when reusing a connection. + ReuseConnection string `json:"reuseConnection,omitempty"` + + // To use any recognized DID in the services array for a reusable connection. + ReuseAnyConnection bool `json:"reuseAnyConnection,omitempty"` + + // Timeout (in milliseconds) waiting for connection status to be completed. + Timeout time.Duration `json:"timeout,omitempty"` +} + +// ConnectResponse is response model from wallet DID connection operation. +type ConnectResponse struct { + // connection ID of the connection established. + ConnectionID string `json:"connectionID"` +} + +// ProposePresentationRequest is request model for performing propose presentation operation from wallet. +type ProposePresentationRequest struct { + vcwallet.WalletAuth + + // out-of-band invitation to establish connection and send propose presentation message. + Invitation *wallet.GenericInvitation `json:"invitation"` + + // Optional From DID option to customize sender DID. + FromDID string `json:"from,omitempty"` + + // Timeout (in milliseconds) waiting for operation to be completed. + Timeout time.Duration `json:"timeout,omitempty"` + + // Options for accepting out-of-band invitation and to perform DID exchange (for DIDComm V1). + ConnectionOpts ConnectOpts `json:"connectOptions,omitempty"` +} + +// ProposePresentationResponse is response model from wallet propose presentation operation. +type ProposePresentationResponse struct { + // response request presentation message from relying party. + PresentationRequest *service.DIDCommMsgMap `json:"presentationRequest,omitempty"` +} + +// PresentProofRequest is request model from wallet present proof operation. +// Supported attachment MIME type "application/ld+json". +type PresentProofRequest struct { + vcwallet.WalletAuth + + // Thread ID from request presentation response + ThreadID string `json:"threadID,omitempty"` + + // presentation to be sent as part of present proof message. + Presentation json.RawMessage `json:"presentation,omitempty"` + + // If true then wallet will wait for present proof protocol status to be + // done or abandoned till given Timeout. + // Also, will return web redirect info if found in acknowledgment message or problem-report. + WaitForDone bool `json:"waitForDone,omitempty"` + + // Optional timeout (in milliseconds) waiting for present proof operation to be done. + // will be taken into account only when WaitForDone is enabled. + // If not provided then wallet will use its default timeout. + Timeout time.Duration `json:"WaitForDoneTimeout,omitempty"` +} + +// PresentProofResponse is response model from wallet present proof operation. +type PresentProofResponse struct { + wallet.CredentialInteractionStatus +} + +// ProposeCredentialRequest is request model for performing propose credential operation from wallet. +type ProposeCredentialRequest struct { + vcwallet.WalletAuth + + // out-of-band invitation to establish connection and send propose credential message. + Invitation *wallet.GenericInvitation `json:"invitation"` + + // Optional From DID option to customize sender DID. + FromDID string `json:"from,omitempty"` + + // Timeout (in milliseconds) waiting for operation to be completed. + Timeout time.Duration `json:"timeout,omitempty"` + + // Options for accepting out-of-band invitation and to perform DID exchange (for DIDComm V1). + ConnectionOpts ConnectOpts `json:"connectOptions,omitempty"` +} + +// ProposeCredentialResponse is response model from wallet propose credential operation. +type ProposeCredentialResponse struct { + // response offer credential message from issuer. + OfferCredential *service.DIDCommMsgMap `json:"offerCredential,omitempty"` +} + +// RequestCredentialRequest is request model from wallet request credential operation. +// Supported attachment MIME type "application/ld+json". +type RequestCredentialRequest struct { + vcwallet.WalletAuth + + // Thread ID from offer credential response previously received during propose credential interaction. + ThreadID string `json:"threadID,omitempty"` + + // presentation to be sent as part of request credential message. + Presentation json.RawMessage `json:"presentation,omitempty"` + + // If true then wallet will wait till it receives credential fulfillment response from issuer for given Timeout. + // Also, will return web redirect info if found in fulfillment message or problem-report. + WaitForDone bool `json:"waitForDone,omitempty"` + + // Optional timeout (in milliseconds) waiting for credential fulfillment to arrive. + // will be taken into account only when WaitForDone is enabled. + // If not provided then wallet will use its default timeout. + Timeout time.Duration `json:"WaitForDoneTimeout,omitempty"` +} + +// RequestCredentialResponse is response model from wallet request credential operation. +type RequestCredentialResponse struct { + wallet.CredentialInteractionStatus +} + +// ResolveCredentialManifestRequest is request model for resolving credential manifest from wallet. +type ResolveCredentialManifestRequest struct { + vcwallet.WalletAuth + + // Credential Manifest on which given credential fulfillment or credential needs to be resolved. + Manifest json.RawMessage `json:"manifest,omitempty"` + + // Fulfillment to be be resolved. + // If provided, then this option takes precedence over credential resolve option. + Fulfillment json.RawMessage `json:"fulfillment,omitempty"` + + // Credential to be be resolved, to be provided along with 'DescriptorID' to be used for resolving. + Credential json.RawMessage `json:"credential,omitempty"` + + // ID of the Credential from wallet content to be be resolved, to be provided along with 'DescriptorID'. + CredentialID string `json:"credentialID,omitempty"` + + // ID of the output descriptor to be used for resolving given credential. + DescriptorID string `json:"descriptorID,omitempty"` +} + +// ResolveCredentialManifestResponse is response model from wallet credential manifest resolve operation. +type ResolveCredentialManifestResponse struct { + // List of Resolved Descriptor results. + Resolved []*cm.ResolvedDescriptor `json:"resolved,omitempty"` +} diff --git a/pkg/controller/command/vcwallet/command.go b/pkg/controller/command/vcwallet/command.go index beb9345e0..9c377a9d0 100644 --- a/pkg/controller/command/vcwallet/command.go +++ b/pkg/controller/command/vcwallet/command.go @@ -23,7 +23,6 @@ import ( "github.com/hyperledger/aries-framework-go/pkg/crypto" "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" "github.com/hyperledger/aries-framework-go/pkg/internal/logutil" - "github.com/hyperledger/aries-framework-go/pkg/kms" "github.com/hyperledger/aries-framework-go/pkg/kms/webkms" "github.com/hyperledger/aries-framework-go/pkg/wallet" "github.com/hyperledger/aries-framework-go/spi/storage" @@ -80,24 +79,6 @@ const ( // ProfileExistsErrorCode for errors while checking if profile exists for a wallet user. ProfileExistsErrorCode - - // DIDConnectErrorCode for errors while performing DID connect in wallet. - DIDConnectErrorCode - - // ProposePresentationErrorCode for errors while proposing presentation. - ProposePresentationErrorCode - - // PresentProofErrorCode for errors while presenting proof from wallet. - PresentProofErrorCode - - // ProposeCredentialErrorCode for errors while proposing credential from wallet. - ProposeCredentialErrorCode - - // RequestCredentialErrorCode for errors while request credential from wallet for issue credential protocol. - RequestCredentialErrorCode - - // ResolveCredentialManifestErrorCode for errors while resolving credential manifest from wallet. - ResolveCredentialManifestErrorCode ) // All command operations. @@ -105,37 +86,28 @@ const ( CommandName = "vcwallet" // command methods. - CreateProfileMethod = "CreateProfile" - UpdateProfileMethod = "UpdateProfile" - ProfileExistsMethod = "ProfileExists" - OpenMethod = "Open" - CloseMethod = "Close" - AddMethod = "Add" - RemoveMethod = "Remove" - GetMethod = "Get" - GetAllMethod = "GetAll" - QueryMethod = "Query" - IssueMethod = "Issue" - ProveMethod = "Prove" - VerifyMethod = "Verify" - DeriveMethod = "Derive" - CreateKeyPairMethod = "CreateKeyPair" - ConnectMethod = "Connect" - ProposePresentationMethod = "ProposePresentation" - PresentProofMethod = "PresentProof" - ProposeCredentialMethod = "ProposeCredential" - RequestCredentialMethod = "RequestCredential" - ResolveCredentialManifestMethod = "ResolveCredentialManifest" + CreateProfileMethod = "CreateProfile" + UpdateProfileMethod = "UpdateProfile" + ProfileExistsMethod = "ProfileExists" + OpenMethod = "Open" + CloseMethod = "Close" + AddMethod = "Add" + RemoveMethod = "Remove" + GetMethod = "Get" + GetAllMethod = "GetAll" + QueryMethod = "Query" + IssueMethod = "Issue" + ProveMethod = "Prove" + VerifyMethod = "Verify" + DeriveMethod = "Derive" + CreateKeyPairMethod = "CreateKeyPair" ) // miscellaneous constants for the vc wallet command controller. const ( // log constants. - logSuccess = "success" - logUserIDKey = "userID" - connectionIDString = "connectionID" - invitationIDString = "invitationID" - LabelString = "label" + logSuccess = "success" + logUserIDKey = "userID" emptyRawLength = 4 @@ -191,19 +163,6 @@ type provider interface { Crypto() crypto.Crypto JSONLDDocumentLoader() ld.DocumentLoader MediaTypeProfiles() []string - didCommProvider // to be used only if wallet needs to be participated in DIDComm. -} - -// didCommProvider to be used only if wallet needs to be participated in DIDComm operation. -// TODO: using wallet KMS instead of provider KMS. -// TODO: reconcile Protocol storage with wallet store. -type didCommProvider interface { - KMS() kms.KeyManager - ServiceEndpoint() string - ProtocolStateStorageProvider() storage.Provider - Service(id string) (interface{}, error) - KeyType() kms.KeyType - KeyAgreementType() kms.KeyType } // Command contains operations provided by verifiable credential wallet controller. @@ -245,12 +204,6 @@ func (o *Command) GetHandlers() []command.Handler { cmdutil.NewCommandHandler(CommandName, VerifyMethod, o.Verify), cmdutil.NewCommandHandler(CommandName, DeriveMethod, o.Derive), cmdutil.NewCommandHandler(CommandName, CreateKeyPairMethod, o.CreateKeyPair), - cmdutil.NewCommandHandler(CommandName, ConnectMethod, o.Connect), - cmdutil.NewCommandHandler(CommandName, ProposePresentationMethod, o.ProposePresentation), - cmdutil.NewCommandHandler(CommandName, PresentProofMethod, o.PresentProof), - cmdutil.NewCommandHandler(CommandName, ProposeCredentialMethod, o.ProposeCredential), - cmdutil.NewCommandHandler(CommandName, RequestCredentialMethod, o.RequestCredential), - cmdutil.NewCommandHandler(CommandName, ResolveCredentialManifestMethod, o.ResolveCredentialManifest), } } @@ -750,285 +703,6 @@ func (o *Command) CreateKeyPair(rw io.Writer, req io.Reader) command.Error { return nil } -// Connect accepts out-of-band invitations and performs DID exchange. -func (o *Command) Connect(rw io.Writer, req io.Reader) command.Error { - request := &ConnectRequest{} - - err := json.NewDecoder(req).Decode(&request) - if err != nil { - logutil.LogInfo(logger, CommandName, ConnectMethod, err.Error()) - - return command.NewValidationError(InvalidRequestErrorCode, err) - } - - vcWallet, err := wallet.New(request.UserID, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, ConnectMethod, err.Error()) - - return command.NewExecuteError(DIDConnectErrorCode, err) - } - - didComm, err := wallet.NewDidComm(vcWallet, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, ConnectMethod, err.Error()) - - return command.NewExecuteError(DIDConnectErrorCode, err) - } - - connectionID, err := didComm.Connect(request.Auth, request.Invitation, - wallet.WithConnectTimeout(request.Timeout), wallet.WithReuseDID(request.ReuseConnection), - wallet.WithReuseAnyConnection(request.ReuseAnyConnection), wallet.WithMyLabel(request.MyLabel), - wallet.WithRouterConnections(request.RouterConnections...)) - if err != nil { - logutil.LogInfo(logger, CommandName, ConnectMethod, err.Error()) - - return command.NewExecuteError(DIDConnectErrorCode, err) - } - - command.WriteNillableResponse(rw, &ConnectResponse{ConnectionID: connectionID}, logger) - - logutil.LogDebug(logger, CommandName, ConnectMethod, logSuccess, - logutil.CreateKeyValueString(logUserIDKey, request.UserID), - logutil.CreateKeyValueString(invitationIDString, request.Invitation.ID), - logutil.CreateKeyValueString(LabelString, request.MyLabel), - logutil.CreateKeyValueString(connectionIDString, connectionID)) - - return nil -} - -// ProposePresentation accepts out-of-band invitation and sends message proposing presentation -// from wallet to relying party. -// https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposepresentation -// -// Currently Supporting -// [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2) -func (o *Command) ProposePresentation(rw io.Writer, req io.Reader) command.Error { - request := &ProposePresentationRequest{} - - err := json.NewDecoder(req).Decode(&request) - if err != nil { - logutil.LogInfo(logger, CommandName, ProposePresentationMethod, err.Error()) - - return command.NewValidationError(InvalidRequestErrorCode, err) - } - - vcWallet, err := wallet.New(request.UserID, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, ProposePresentationMethod, err.Error()) - - return command.NewExecuteError(ProposePresentationErrorCode, err) - } - - didComm, err := wallet.NewDidComm(vcWallet, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, ProposePresentationMethod, err.Error()) - - return command.NewExecuteError(ProposePresentationErrorCode, err) - } - - msg, err := didComm.ProposePresentation(request.Auth, request.Invitation, - wallet.WithFromDID(request.FromDID), wallet.WithInitiateTimeout(request.Timeout), - wallet.WithConnectOptions(wallet.WithConnectTimeout(request.ConnectionOpts.Timeout), - wallet.WithReuseDID(request.ConnectionOpts.ReuseConnection), - wallet.WithReuseAnyConnection(request.ConnectionOpts.ReuseAnyConnection), - wallet.WithMyLabel(request.ConnectionOpts.MyLabel), - wallet.WithRouterConnections(request.ConnectionOpts.RouterConnections...))) - if err != nil { - logutil.LogInfo(logger, CommandName, ProposePresentationMethod, err.Error()) - - return command.NewExecuteError(ProposePresentationErrorCode, err) - } - - command.WriteNillableResponse(rw, &ProposePresentationResponse{PresentationRequest: msg}, logger) - - logutil.LogDebug(logger, CommandName, ProposePresentationMethod, logSuccess, - logutil.CreateKeyValueString(logUserIDKey, request.UserID)) - - return nil -} - -// PresentProof sends present proof message from wallet to relying party. -// https://w3c-ccg.github.io/universal-wallet-interop-spec/#presentproof -// -// Currently Supporting -// [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2) -// -func (o *Command) PresentProof(rw io.Writer, req io.Reader) command.Error { - request := &PresentProofRequest{} - - err := json.NewDecoder(req).Decode(&request) - if err != nil { - logutil.LogInfo(logger, CommandName, PresentProofMethod, err.Error()) - - return command.NewValidationError(InvalidRequestErrorCode, err) - } - - vcWallet, err := wallet.New(request.UserID, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, PresentProofMethod, err.Error()) - - return command.NewExecuteError(PresentProofErrorCode, err) - } - - didComm, err := wallet.NewDidComm(vcWallet, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, PresentProofMethod, err.Error()) - - return command.NewExecuteError(PresentProofErrorCode, err) - } - - status, err := didComm.PresentProof(request.Auth, request.ThreadID, - prepareConcludeInteractionOpts(request.WaitForDone, request.Timeout, request.Presentation)...) - if err != nil { - logutil.LogInfo(logger, CommandName, PresentProofMethod, err.Error()) - - return command.NewExecuteError(PresentProofErrorCode, err) - } - - command.WriteNillableResponse(rw, status, logger) - - logutil.LogDebug(logger, CommandName, PresentProofMethod, logSuccess, - logutil.CreateKeyValueString(logUserIDKey, request.UserID)) - - return nil -} - -// ProposeCredential sends propose credential message from wallet to issuer. -// https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposecredential -// -// Currently Supporting : 0453-issueCredentialV2 -// https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md -// -func (o *Command) ProposeCredential(rw io.Writer, req io.Reader) command.Error { - request := &ProposeCredentialRequest{} - - err := json.NewDecoder(req).Decode(&request) - if err != nil { - logutil.LogInfo(logger, CommandName, ProposeCredentialMethod, err.Error()) - - return command.NewValidationError(InvalidRequestErrorCode, err) - } - - vcWallet, err := wallet.New(request.UserID, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, ProposeCredentialMethod, err.Error()) - - return command.NewExecuteError(ProposeCredentialErrorCode, err) - } - - didComm, err := wallet.NewDidComm(vcWallet, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, ProposeCredentialMethod, err.Error()) - - return command.NewExecuteError(ProposeCredentialErrorCode, err) - } - - msg, err := didComm.ProposeCredential(request.Auth, request.Invitation, - wallet.WithFromDID(request.FromDID), wallet.WithInitiateTimeout(request.Timeout), - wallet.WithConnectOptions(wallet.WithConnectTimeout(request.ConnectionOpts.Timeout), - wallet.WithReuseDID(request.ConnectionOpts.ReuseConnection), - wallet.WithReuseAnyConnection(request.ConnectionOpts.ReuseAnyConnection), - wallet.WithMyLabel(request.ConnectionOpts.MyLabel), - wallet.WithRouterConnections(request.ConnectionOpts.RouterConnections...))) - if err != nil { - logutil.LogInfo(logger, CommandName, ProposeCredentialMethod, err.Error()) - - return command.NewExecuteError(ProposeCredentialErrorCode, err) - } - - command.WriteNillableResponse(rw, &ProposeCredentialResponse{OfferCredential: msg}, logger) - - logutil.LogDebug(logger, CommandName, ProposeCredentialMethod, logSuccess, - logutil.CreateKeyValueString(logUserIDKey, request.UserID)) - - return nil -} - -// RequestCredential sends request credential message from wallet to issuer and -// optionally waits for credential fulfillment. -// https://w3c-ccg.github.io/universal-wallet-interop-spec/#requestcredential -// -// Currently Supporting : 0453-issueCredentialV2 -// https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md -// -func (o *Command) RequestCredential(rw io.Writer, req io.Reader) command.Error { - request := &RequestCredentialRequest{} - - err := json.NewDecoder(req).Decode(&request) - if err != nil { - logutil.LogInfo(logger, CommandName, RequestCredentialMethod, err.Error()) - - return command.NewValidationError(InvalidRequestErrorCode, err) - } - - vcWallet, err := wallet.New(request.UserID, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, RequestCredentialMethod, err.Error()) - - return command.NewExecuteError(RequestCredentialErrorCode, err) - } - - didComm, err := wallet.NewDidComm(vcWallet, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, RequestCredentialMethod, err.Error()) - - return command.NewExecuteError(RequestCredentialErrorCode, err) - } - - status, err := didComm.RequestCredential(request.Auth, request.ThreadID, - prepareConcludeInteractionOpts(request.WaitForDone, request.Timeout, request.Presentation)...) - if err != nil { - logutil.LogInfo(logger, CommandName, RequestCredentialMethod, err.Error()) - - return command.NewExecuteError(RequestCredentialErrorCode, err) - } - - command.WriteNillableResponse(rw, status, logger) - - logutil.LogDebug(logger, CommandName, RequestCredentialMethod, logSuccess, - logutil.CreateKeyValueString(logUserIDKey, request.UserID)) - - return nil -} - -// ResolveCredentialManifest resolves given credential manifest by credential fulfillment or credential. -// Supports: https://identity.foundation/credential-manifest/ -// -// Writes list of resolved descriptors to writer or returns error if operation fails. -// -func (o *Command) ResolveCredentialManifest(rw io.Writer, req io.Reader) command.Error { - request := &ResolveCredentialManifestRequest{} - - err := json.NewDecoder(req).Decode(&request) - if err != nil { - logutil.LogInfo(logger, CommandName, ResolveCredentialManifestMethod, err.Error()) - - return command.NewValidationError(InvalidRequestErrorCode, err) - } - - vcWallet, err := wallet.New(request.UserID, o.ctx) - if err != nil { - logutil.LogInfo(logger, CommandName, ResolveCredentialManifestMethod, err.Error()) - - return command.NewExecuteError(ResolveCredentialManifestErrorCode, err) - } - - resolved, err := vcWallet.ResolveCredentialManifest(request.Auth, request.Manifest, - prepareResolveManifestOption(request)) - if err != nil { - logutil.LogInfo(logger, CommandName, ResolveCredentialManifestMethod, err.Error()) - - return command.NewExecuteError(ResolveCredentialManifestErrorCode, err) - } - - command.WriteNillableResponse(rw, &ResolveCredentialManifestResponse{Resolved: resolved}, logger) - - logutil.LogDebug(logger, CommandName, ResolveCredentialManifestMethod, logSuccess, - logutil.CreateKeyValueString(logUserIDKey, request.UserID)) - - return nil -} - // prepareProfileOptions prepares options for creating wallet profile. func prepareProfileOptions(rqst *CreateOrUpdateProfileRequest) []wallet.ProfileOptions { var options []wallet.ProfileOptions @@ -1200,29 +874,3 @@ func prepareDeriveOption(rqst *DeriveRequest) wallet.CredentialToDerive { return wallet.FromRawCredential(rqst.RawCredential) } - -func prepareConcludeInteractionOpts(waitForDone bool, timeout time.Duration, presentation json.RawMessage) []wallet.ConcludeInteractionOptions { //nolint: lll - var options []wallet.ConcludeInteractionOptions - - if waitForDone { - options = append(options, wallet.WaitForDone(timeout)) - } - - return append(options, wallet.FromRawPresentation(presentation)) -} - -func prepareResolveManifestOption(rqst *ResolveCredentialManifestRequest) wallet.ResolveManifestOption { - if len(rqst.Fulfillment) > emptyRawLength { - return wallet.ResolveRawFulfillment(rqst.Fulfillment) - } - - if len(rqst.Credential) > emptyRawLength { - return wallet.ResolveRawCredential(rqst.DescriptorID, rqst.Credential) - } - - if rqst.CredentialID != "" { - return wallet.ResolveCredentialID(rqst.DescriptorID, rqst.CredentialID) - } - - return nil -} diff --git a/pkg/controller/command/vcwallet/command_test.go b/pkg/controller/command/vcwallet/command_test.go index e546c09b1..ada7e26a4 100644 --- a/pkg/controller/command/vcwallet/command_test.go +++ b/pkg/controller/command/vcwallet/command_test.go @@ -21,11 +21,8 @@ import ( "github.com/stretchr/testify/require" "github.com/hyperledger/aries-framework-go/internal/testdata" - outofbandClient "github.com/hyperledger/aries-framework-go/pkg/client/outofband" "github.com/hyperledger/aries-framework-go/pkg/controller/command" "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto" - "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model" - "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange" issuecredentialsvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential" "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator" @@ -46,7 +43,6 @@ import ( mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider" mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage" mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr" - "github.com/hyperledger/aries-framework-go/pkg/store/connection" "github.com/hyperledger/aries-framework-go/pkg/vdr/key" "github.com/hyperledger/aries-framework-go/pkg/wallet" ) @@ -63,9 +59,6 @@ const ( sampleFakeTkn = "sample-fake-token-01" sampleFakeCapability = "sample-fake-capability-01" sampleDIDKey = "did:key:z6MknC1wwS6DEYwtGbZZo2QvjQjkh2qSBjb4GYmbye8dv4S5" - webRedirectStatusKey = "status" - webRedirectURLKey = "url" - exampleWebRedirect = "http://example.com/sample" ) func TestNew(t *testing.T) { @@ -73,7 +66,7 @@ func TestNew(t *testing.T) { cmd := New(newMockProvider(t), &Config{}) require.NotNil(t, cmd) - require.Len(t, cmd.GetHandlers(), 21) + require.Len(t, cmd.GetHandlers(), 15) }) } @@ -404,12 +397,20 @@ func TestCommand_OpenAndClose(t *testing.T) { }, }) - t.Run("successfully unlock & lock wallet (local kms)", func(t *testing.T) { - cmd := New(mockctx, &Config{}) + t.Run("successfully unlock & lock wallet (gnap auth, no custom header func)", func(t *testing.T) { + cmd := New(mockctx, &Config{ + WebKMSCacheSize: 99, + }) request := &UnlockWalletRequest{ - UserID: sampleUser1, - LocalKMSPassphrase: samplePassPhrase, + UserID: sampleUser2, + WebKMSAuth: &UnlockAuth{ + GNAPToken: sampleFakeTkn, + }, + EDVUnlock: &UnlockAuth{ + GNAPToken: sampleFakeTkn, + }, + Expiry: 10 * time.Second, } // unlock wallet @@ -427,7 +428,7 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() // lock wallet - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser1})) + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2})) require.NoError(t, cmdErr) var lockResponse LockWalletResponse require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse)) @@ -435,19 +436,33 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() // lock wallet again - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser1})) + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2})) require.NoError(t, cmdErr) require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse)) require.False(t, lockResponse.Closed) b.Reset() }) - t.Run("successfully unlock & lock wallet (remote kms)", func(t *testing.T) { - cmd := New(mockctx, &Config{}) + t.Run("successfully unlock & lock wallet (gnap auth, with custom header func)", func(t *testing.T) { + headerFunc := func(r *http.Request) (*http.Header, error) { + return &r.Header, nil + } + + cmd := New(mockctx, &Config{ + WebKMSCacheSize: 99, + WebKMSGNAPSigner: headerFunc, + EDVGNAPSigner: headerFunc, + }) request := &UnlockWalletRequest{ - UserID: sampleUser2, - WebKMSAuth: &UnlockAuth{AuthToken: sampleFakeTkn}, + UserID: sampleUser2, + WebKMSAuth: &UnlockAuth{ + GNAPToken: sampleFakeTkn, + }, + EDVUnlock: &UnlockAuth{ + GNAPToken: sampleFakeTkn, + }, + Expiry: 10 * time.Second, } // unlock wallet @@ -480,16 +495,12 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() }) - t.Run("successfully unlock & lock wallet (remote kms, capability)", func(t *testing.T) { - cmd := New(mockctx, &Config{ - WebKMSCacheSize: 99, - WebKMSAuthzProvider: &mockAuthZCapability{}, - }) + t.Run("successfully unlock & lock wallet (local kms)", func(t *testing.T) { + cmd := New(mockctx, &Config{}) request := &UnlockWalletRequest{ - UserID: sampleUser2, - WebKMSAuth: &UnlockAuth{Capability: sampleFakeCapability}, - Expiry: 10 * time.Second, + UserID: sampleUser1, + LocalKMSPassphrase: samplePassPhrase, } // unlock wallet @@ -507,7 +518,7 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() // lock wallet - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2})) + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser1})) require.NoError(t, cmdErr) var lockResponse LockWalletResponse require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse)) @@ -515,22 +526,19 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() // lock wallet again - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2})) + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser1})) require.NoError(t, cmdErr) require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse)) require.False(t, lockResponse.Closed) b.Reset() }) - t.Run("successfully unlock & lock wallet (local kms, edv user)", func(t *testing.T) { + t.Run("successfully unlock & lock wallet (remote kms)", func(t *testing.T) { cmd := New(mockctx, &Config{}) request := &UnlockWalletRequest{ - UserID: sampleUser3, - LocalKMSPassphrase: samplePassPhrase, - EDVUnlock: &UnlockAuth{ - AuthToken: sampleFakeTkn, - }, + UserID: sampleUser2, + WebKMSAuth: &UnlockAuth{AuthToken: sampleFakeTkn}, } // unlock wallet @@ -548,7 +556,7 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() // lock wallet - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser3})) + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2})) require.NoError(t, cmdErr) var lockResponse LockWalletResponse require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse)) @@ -556,27 +564,23 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() // lock wallet again - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser3})) + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2})) require.NoError(t, cmdErr) require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse)) require.False(t, lockResponse.Closed) b.Reset() }) - t.Run("successfully unlock & lock wallet (gnap auth, no custom header func)", func(t *testing.T) { + t.Run("successfully unlock & lock wallet (remote kms, capability)", func(t *testing.T) { cmd := New(mockctx, &Config{ - WebKMSCacheSize: 99, + WebKMSCacheSize: 99, + WebKMSAuthzProvider: &mockAuthZCapability{}, }) request := &UnlockWalletRequest{ - UserID: sampleUser2, - WebKMSAuth: &UnlockAuth{ - GNAPToken: sampleFakeTkn, - }, - EDVUnlock: &UnlockAuth{ - GNAPToken: sampleFakeTkn, - }, - Expiry: 10 * time.Second, + UserID: sampleUser2, + WebKMSAuth: &UnlockAuth{Capability: sampleFakeCapability}, + Expiry: 10 * time.Second, } // unlock wallet @@ -609,26 +613,15 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() }) - t.Run("successfully unlock & lock wallet (gnap auth, with custom header func)", func(t *testing.T) { - headerFunc := func(r *http.Request) (*http.Header, error) { - return &r.Header, nil - } - - cmd := New(mockctx, &Config{ - WebKMSCacheSize: 99, - WebKMSGNAPSigner: headerFunc, - EDVGNAPSigner: headerFunc, - }) + t.Run("successfully unlock & lock wallet (local kms, edv user)", func(t *testing.T) { + cmd := New(mockctx, &Config{}) request := &UnlockWalletRequest{ - UserID: sampleUser2, - WebKMSAuth: &UnlockAuth{ - GNAPToken: sampleFakeTkn, - }, + UserID: sampleUser3, + LocalKMSPassphrase: samplePassPhrase, EDVUnlock: &UnlockAuth{ - GNAPToken: sampleFakeTkn, + AuthToken: sampleFakeTkn, }, - Expiry: 10 * time.Second, } // unlock wallet @@ -646,7 +639,7 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() // lock wallet - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2})) + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser3})) require.NoError(t, cmdErr) var lockResponse LockWalletResponse require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse)) @@ -654,7 +647,7 @@ func TestCommand_OpenAndClose(t *testing.T) { b.Reset() // lock wallet again - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2})) + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser3})) require.NoError(t, cmdErr) require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse)) require.False(t, lockResponse.Closed) @@ -1776,932 +1769,125 @@ func TestCommand_CreateKeyPair(t *testing.T) { }) } -func TestCommand_Connect(t *testing.T) { - const sampleDIDCommUser = "sample-didcomm-user01" +func createSampleUserProfile(t *testing.T, ctx *mockprovider.Provider, request *CreateOrUpdateProfileRequest) { + cmd := New(ctx, &Config{}) + require.NotNil(t, cmd) - mockctx := newMockProvider(t) + var l bytes.Buffer + cmdErr := cmd.CreateProfile(&l, getReader(t, request)) + require.NoError(t, cmdErr) +} - createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) +func getReader(t *testing.T, v interface{}) io.Reader { + vcReqBytes, err := json.Marshal(v) + require.NoError(t, err) - token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) + return bytes.NewBuffer(vcReqBytes) +} - defer lock() +func getUnlockToken(t *testing.T, b bytes.Buffer) string { + var response UnlockWalletResponse - t.Run("successfully perform DID connect", func(t *testing.T) { - sampleConnID := uuid.New().String() + require.NoError(t, json.NewDecoder(&b).Decode(&response)) - oobSvc := &mockoutofband.MockOobService{ - AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { - return sampleConnID, nil - }, - } - mockctx.ServiceMap[outofbandSvc.Name] = oobSvc - - didexSvc := &mockdidexchange.MockDIDExchangeSvc{ - RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { - ch <- service.StateMsg{ - Type: service.PostState, - StateID: didexchange.StateIDCompleted, - Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID}, - } + return response.Token +} - return nil - }, - } - mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc +func unlockWallet(t *testing.T, ctx *mockprovider.Provider, request *UnlockWalletRequest) (string, func()) { + cmd := New(ctx, nil) - cmd := New(mockctx, &Config{}) + var b bytes.Buffer - request := &ConnectRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - Invitation: &outofbandClient.Invitation{}, - ConnectOpts: ConnectOpts{ - MyLabel: "sample-label", - }, - } + cmdErr := cmd.Open(&b, getReader(t, &request)) + require.NoError(t, cmdErr) - var b bytes.Buffer - cmdErr := cmd.Connect(&b, getReader(t, &request)) - require.NoError(t, cmdErr) + return getUnlockToken(t, b), func() { + cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: request.UserID})) + if cmdErr != nil { + t.Log(t, cmdErr) + } + } +} - var response ConnectResponse - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - require.NotEmpty(t, response) - require.Equal(t, sampleConnID, response.ConnectionID) - }) +func addContent(t *testing.T, ctx *mockprovider.Provider, request *AddContentRequest) { + cmd := New(ctx, &Config{}) - t.Run("did connect failure", func(t *testing.T) { - oobSvc := &mockoutofband.MockOobService{ - AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { - return "", fmt.Errorf(sampleCommandError) - }, - } - mockctx.ServiceMap[outofbandSvc.Name] = oobSvc + var b bytes.Buffer + defer b.Reset() - cmd := New(mockctx, &Config{}) + cmdErr := cmd.Add(&b, getReader(t, &request)) + require.NoError(t, cmdErr) +} - request := &ConnectRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - Invitation: &outofbandClient.Invitation{}, - ConnectOpts: ConnectOpts{ - MyLabel: "sample-label", - }, - } +func validateError(t *testing.T, err command.Error, + expectedType command.Type, expectedCode command.Code, contains string) { + require.Error(t, err) + require.Equal(t, err.Type(), expectedType) + require.Equal(t, err.Code(), expectedCode) - var b bytes.Buffer - cmdErr := cmd.Connect(&b, getReader(t, &request)) - require.Error(t, cmdErr) + if contains != "" { + require.Contains(t, err.Error(), contains) + } +} - validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, sampleCommandError) - validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, "failed to accept invitation") - require.Empty(t, b.Bytes()) - }) +func newMockProvider(t *testing.T) *mockprovider.Provider { + t.Helper() - t.Run("did connect failure - invalid request", func(t *testing.T) { - cmd := New(mockctx, &Config{}) + loader, err := ldtestutil.DocumentLoader() + require.NoError(t, err) - var b bytes.Buffer - cmdErr := cmd.Connect(&b, bytes.NewBufferString("--")) - require.Error(t, cmdErr) + serviceMap := map[string]interface{}{ + presentproofSvc.Name: &mockpresentproof.MockPresentProofSvc{}, + outofbandSvc.Name: &mockoutofband.MockOobService{}, + didexchange.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{}, + mediator.Coordination: &mockmediator.MockMediatorSvc{}, + issuecredentialsvc.Name: &mockissuecredential.MockIssueCredentialSvc{}, + oobv2.Name: &mockoutofbandv2.MockOobService{}, + } - validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") - require.Empty(t, b.Bytes()) - }) + return &mockprovider.Provider{ + StorageProviderValue: mockstorage.NewMockStoreProvider(), + ProtocolStateStorageProviderValue: mockstorage.NewMockStoreProvider(), + DocumentLoaderValue: loader, + ServiceMap: serviceMap, + } +} - t.Run("attempt to didconnect with invalid profile", func(t *testing.T) { - cmd := New(mockctx, &Config{}) +func getMockDIDKeyVDR() *mockvdr.MockVDRegistry { + return &mockvdr.MockVDRegistry{ + ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { + if strings.HasPrefix(didID, "did:key:") { + k := key.New() - request := &ConnectRequest{ - WalletAuth: WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn}, - Invitation: &outofbandClient.Invitation{}, - ConnectOpts: ConnectOpts{ - MyLabel: "sample-label", - }, - } + d, e := k.Read(didID) + if e != nil { + return nil, e + } - var b bytes.Buffer - cmdErr := cmd.Connect(&b, getReader(t, &request)) - require.Error(t, cmdErr) + return d, nil + } - validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, "failed to get VC wallet profile") - require.Empty(t, b.Bytes()) - }) + return nil, fmt.Errorf("did not found") + }, + } } -func TestCommand_ProposePresentation(t *testing.T) { - const sampleDIDCommUser = "sample-didcomm-user02" +func parseCredential(t *testing.T, b bytes.Buffer) *verifiable.Credential { + var response struct { + Credential json.RawMessage + } - mockctx := newMockProvider(t) + require.NoError(t, json.NewDecoder(&b).Decode(&response)) - createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) + loader, err := ldtestutil.DocumentLoader() + require.NoError(t, err) - token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) + vc, err := verifiable.ParseCredential(response.Credential, verifiable.WithDisabledProofCheck(), + verifiable.WithJSONLDDocumentLoader(loader)) + require.NoError(t, err) - defer lock() - - const ( - myDID = "did:mydid:123" - theirDID = "did:theirdid:123" - ) - - t.Run("successfully send propose presentation", func(t *testing.T) { - sampleConnID := uuid.New().String() - - oobSvc := &mockoutofband.MockOobService{ - AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { - return sampleConnID, nil - }, - } - mockctx.ServiceMap[outofbandSvc.Name] = oobSvc - - didexSvc := &mockdidexchange.MockDIDExchangeSvc{ - RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { - ch <- service.StateMsg{ - Type: service.PostState, - StateID: didexchange.StateIDCompleted, - Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID}, - } - - return nil - }, - } - mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc - - thID := uuid.New().String() - ppSvc := &mockpresentproof.MockPresentProofSvc{ - ActionsFunc: func() ([]presentproofSvc.Action, error) { - return []presentproofSvc.Action{ - { - PIID: thID, - Msg: service.NewDIDCommMsgMap(&presentproofSvc.RequestPresentationV2{ - Comment: "mock msg", - }), - MyDID: myDID, - TheirDID: theirDID, - }, - }, nil - }, - HandleFunc: func(service.DIDCommMsg) (string, error) { - return thID, nil - }, - } - mockctx.ServiceMap[presentproofSvc.Name] = ppSvc - - store, err := mockctx.StorageProvider().OpenStore(connection.Namespace) - require.NoError(t, err) - - record := &connection.Record{ - ConnectionID: sampleConnID, - MyDID: myDID, - TheirDID: theirDID, - } - recordBytes, err := json.Marshal(record) - require.NoError(t, err) - require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes)) - - cmd := New(mockctx, &Config{}) - - request := &ProposePresentationRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - Invitation: &wallet.GenericInvitation{}, - } - - var b bytes.Buffer - cmdErr := cmd.ProposePresentation(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - - var response ProposePresentationResponse - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - require.NotEmpty(t, response) - require.NotEmpty(t, response.PresentationRequest) - }) - - t.Run("failed to send propose presentation", func(t *testing.T) { - oobSvc := &mockoutofband.MockOobService{ - AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { - return "", fmt.Errorf(sampleCommandError) - }, - } - - mockctx.ServiceMap[outofbandSvc.Name] = oobSvc - - cmd := New(mockctx, &Config{}) - - request := &ConnectRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - Invitation: &outofbandClient.Invitation{}, - ConnectOpts: ConnectOpts{ - MyLabel: "sample-label", - }, - } - - var b bytes.Buffer - cmdErr := cmd.ProposePresentation(&b, getReader(t, &request)) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, sampleCommandError) - validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, "failed to accept invitation") - require.Empty(t, b.Bytes()) - }) - - t.Run("failed to send propose presentation - invalid request", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - var b bytes.Buffer - cmdErr := cmd.ProposePresentation(&b, bytes.NewBufferString("--")) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") - require.Empty(t, b.Bytes()) - }) - - t.Run("failed to send propose presentation - invalid profile", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &ConnectRequest{ - WalletAuth: WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn}, - Invitation: &outofbandClient.Invitation{}, - ConnectOpts: ConnectOpts{ - MyLabel: "sample-label", - }, - } - - var b bytes.Buffer - cmdErr := cmd.ProposePresentation(&b, getReader(t, &request)) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, "failed to get VC wallet profile") - require.Empty(t, b.Bytes()) - }) -} - -func TestCommand_PresentProof(t *testing.T) { - const sampleDIDCommUser = "sample-didcomm-user03" - - mockctx := newMockProvider(t) - - createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) - - token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) - - defer lock() - - t.Run("successfully present proof", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &PresentProofRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - ThreadID: uuid.New().String(), - Presentation: json.RawMessage{}, - } - - var b bytes.Buffer - cmdErr := cmd.PresentProof(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - }) - - t.Run("successfully present proof - wait for done", func(t *testing.T) { - thID := uuid.New().String() - mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{ - RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { - ch <- service.StateMsg{ - Type: service.PostState, - StateID: presentproofSvc.StateNameDone, - Properties: &mockdidexchange.MockEventProperties{ - Properties: map[string]interface{}{ - webRedirectStatusKey: model.AckStatusOK, - webRedirectURLKey: exampleWebRedirect, - }, - }, - Msg: &mockMsg{thID: thID}, - } - - return nil - }, - } - mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc - - cmd := New(mockctx, &Config{}) - - request := &PresentProofRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - ThreadID: thID, - Presentation: json.RawMessage{}, - WaitForDone: true, - Timeout: 1 * time.Millisecond, - } - - var b bytes.Buffer - cmdErr := cmd.PresentProof(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - - var response wallet.CredentialInteractionStatus - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - require.NotEmpty(t, response) - require.Equal(t, exampleWebRedirect, response.RedirectURL) - require.Equal(t, model.AckStatusOK, response.Status) - }) - - t.Run("failed to present proof", func(t *testing.T) { - ppSvc := &mockpresentproof.MockPresentProofSvc{ - ActionContinueFunc: func(string, ...presentproofSvc.Opt) error { - return fmt.Errorf(sampleCommandError) - }, - } - - mockctx.ServiceMap[presentproofSvc.Name] = ppSvc - - cmd := New(mockctx, &Config{}) - - request := &PresentProofRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - ThreadID: uuid.New().String(), - Presentation: json.RawMessage{}, - } - - var b bytes.Buffer - cmdErr := cmd.PresentProof(&b, getReader(t, &request)) - validateError(t, cmdErr, command.ExecuteError, PresentProofErrorCode, sampleCommandError) - require.Empty(t, b.Bytes()) - }) - - t.Run("failed to present proof - invalid request", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - var b bytes.Buffer - cmdErr := cmd.PresentProof(&b, bytes.NewBufferString("--")) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") - require.Empty(t, b.Bytes()) - }) - - t.Run("failed to present proof - invalid profile", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &PresentProofRequest{ - WalletAuth: WalletAuth{UserID: sampleUserID, Auth: token}, - ThreadID: uuid.New().String(), - Presentation: json.RawMessage{}, - } - - var b bytes.Buffer - cmdErr := cmd.PresentProof(&b, getReader(t, &request)) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ExecuteError, PresentProofErrorCode, "failed to get VC wallet profile") - require.Empty(t, b.Bytes()) - }) -} - -func TestCommand_ProposeCredential(t *testing.T) { - const ( - sampleDIDCommUser = "sample-didcomm-user02" - sampleMsgComment = "sample mock msg" - myDID = "did:mydid:123" - theirDID = "did:theirdid:123" - ) - - mockctx := newMockProvider(t) - - createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) - - token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) - - defer lock() - - t.Run("successfully send propose credential", func(t *testing.T) { - sampleConnID := uuid.New().String() - - oobSvc := &mockoutofband.MockOobService{ - AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { - return sampleConnID, nil - }, - } - mockctx.ServiceMap[outofbandSvc.Name] = oobSvc - - didexSvc := &mockdidexchange.MockDIDExchangeSvc{ - RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { - ch <- service.StateMsg{ - Type: service.PostState, - StateID: didexchange.StateIDCompleted, - Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID}, - } - - return nil - }, - } - mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc - - thID := uuid.New().String() - icSvc := &mockissuecredential.MockIssueCredentialSvc{ - ActionsFunc: func() ([]issuecredentialsvc.Action, error) { - return []issuecredentialsvc.Action{ - { - PIID: thID, - Msg: service.NewDIDCommMsgMap(&issuecredentialsvc.OfferCredentialV2{ - Comment: sampleMsgComment, - }), - MyDID: myDID, - TheirDID: theirDID, - }, - }, nil - }, - HandleFunc: func(service.DIDCommMsg) (string, error) { - return thID, nil - }, - } - mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc - - store, err := mockctx.StorageProvider().OpenStore(connection.Namespace) - require.NoError(t, err) - - record := &connection.Record{ - ConnectionID: sampleConnID, - MyDID: myDID, - TheirDID: theirDID, - } - recordBytes, err := json.Marshal(record) - require.NoError(t, err) - require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes)) - - cmd := New(mockctx, &Config{}) - - request := &ProposeCredentialRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - Invitation: &wallet.GenericInvitation{}, - } - - var b bytes.Buffer - cmdErr := cmd.ProposeCredential(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - - var response ProposeCredentialResponse - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - require.NotEmpty(t, response) - require.NotEmpty(t, response.OfferCredential) - - offer := &issuecredentialsvc.OfferCredentialV2{} - - err = response.OfferCredential.Decode(offer) - require.NoError(t, err) - require.NotEmpty(t, offer) - require.Equal(t, sampleMsgComment, offer.Comment) - }) - - t.Run("failed to send propose credential", func(t *testing.T) { - oobSvc := &mockoutofband.MockOobService{ - AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) { - return "", fmt.Errorf(sampleCommandError) - }, - } - - mockctx.ServiceMap[outofbandSvc.Name] = oobSvc - - cmd := New(mockctx, &Config{}) - - request := &ConnectRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - Invitation: &outofbandClient.Invitation{}, - ConnectOpts: ConnectOpts{ - MyLabel: "sample-label", - }, - } - - var b bytes.Buffer - cmdErr := cmd.ProposeCredential(&b, getReader(t, &request)) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, sampleCommandError) - validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, "failed to accept invitation") - require.Empty(t, b.Bytes()) - }) - - t.Run("failed to send propose credential - invalid request", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - var b bytes.Buffer - cmdErr := cmd.ProposeCredential(&b, bytes.NewBufferString("--")) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") - require.Empty(t, b.Bytes()) - }) - - t.Run("failed to send propose credential - invalid profile", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &ConnectRequest{ - WalletAuth: WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn}, - Invitation: &outofbandClient.Invitation{}, - ConnectOpts: ConnectOpts{ - MyLabel: "sample-label", - }, - } - - var b bytes.Buffer - cmdErr := cmd.ProposeCredential(&b, getReader(t, &request)) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, "failed to get VC wallet profile") - require.Empty(t, b.Bytes()) - }) -} - -func TestCommand_RequestCredential(t *testing.T) { - const sampleDIDCommUser = "sample-didcomm-user03" - - mockctx := newMockProvider(t) - - createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) - - token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{ - UserID: sampleDIDCommUser, - LocalKMSPassphrase: samplePassPhrase, - }) - - defer lock() - - t.Run("successfully request credential", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &RequestCredentialRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - ThreadID: uuid.New().String(), - Presentation: json.RawMessage{}, - } - - var b bytes.Buffer - cmdErr := cmd.RequestCredential(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - }) - - t.Run("successfully request credential - wait for done", func(t *testing.T) { - thID := uuid.New().String() - - icSvc := &mockissuecredential.MockIssueCredentialSvc{ - RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error { - ch <- service.StateMsg{ - Type: service.PostState, - StateID: "done", - Properties: &mockdidexchange.MockEventProperties{ - Properties: map[string]interface{}{ - webRedirectStatusKey: model.AckStatusOK, - webRedirectURLKey: exampleWebRedirect, - }, - }, - Msg: &mockMsg{thID: thID}, - } - - return nil - }, - } - mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc - - cmd := New(mockctx, &Config{}) - - request := &RequestCredentialRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - ThreadID: thID, - Presentation: json.RawMessage{}, - WaitForDone: true, - Timeout: 600 * time.Millisecond, - } - - var b bytes.Buffer - cmdErr := cmd.RequestCredential(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - - var response wallet.CredentialInteractionStatus - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - require.NotEmpty(t, response) - require.Equal(t, exampleWebRedirect, response.RedirectURL) - require.Equal(t, model.AckStatusOK, response.Status) - }) - - t.Run("failed to request credential", func(t *testing.T) { - icSvc := &mockissuecredential.MockIssueCredentialSvc{ - ActionContinueFunc: func(string, ...issuecredentialsvc.Opt) error { - return fmt.Errorf(sampleCommandError) - }, - } - mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc - - cmd := New(mockctx, &Config{}) - - request := &RequestCredentialRequest{ - WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token}, - ThreadID: uuid.New().String(), - Presentation: json.RawMessage{}, - } - - var b bytes.Buffer - cmdErr := cmd.RequestCredential(&b, getReader(t, &request)) - validateError(t, cmdErr, command.ExecuteError, RequestCredentialErrorCode, sampleCommandError) - require.Empty(t, b.Bytes()) - }) - - t.Run("failed to request credential - invalid request", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - var b bytes.Buffer - cmdErr := cmd.RequestCredential(&b, bytes.NewBufferString("--")) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") - require.Empty(t, b.Bytes()) - }) - - t.Run("failed to request credential - invalid profile", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &RequestCredentialRequest{ - WalletAuth: WalletAuth{UserID: sampleUserID, Auth: token}, - ThreadID: uuid.New().String(), - Presentation: json.RawMessage{}, - } - - var b bytes.Buffer - cmdErr := cmd.RequestCredential(&b, getReader(t, &request)) - require.Error(t, cmdErr) - - validateError(t, cmdErr, command.ExecuteError, RequestCredentialErrorCode, "failed to get VC wallet profile") - require.Empty(t, b.Bytes()) - }) -} - -func TestCommand_ResolveCredentialManifest(t *testing.T) { - const sampleUser1 = "sample-user-r01" - - mockctx := newMockProvider(t) - mockctx.VDRegistryValue = getMockDIDKeyVDR() - - createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{ - UserID: sampleUser1, - LocalKMSPassphrase: samplePassPhrase, - }) - - token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{ - UserID: sampleUser1, - LocalKMSPassphrase: samplePassPhrase, - }) - - defer lock() - - addContent(t, mockctx, &AddContentRequest{ - Content: testdata.SampleUDCVC, - ContentType: "credential", - WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token}, - }) - - t.Run("successfully resolve credential fulfillment", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &ResolveCredentialManifestRequest{ - WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token}, - Manifest: testdata.CredentialManifestMultipleVCs, - Fulfillment: testdata.CredentialFulfillmentWithMultipleVCs, - } - - var b bytes.Buffer - cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, request)) - require.NoError(t, cmdErr) - - var response ResolveCredentialManifestResponse - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - require.NotEmpty(t, response) - require.Len(t, response.Resolved, 2) - }) - - t.Run("successfully resolve credential", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &ResolveCredentialManifestRequest{ - WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token}, - Manifest: testdata.CredentialManifestMultipleVCs, - Credential: testdata.SampleUDCVC, - DescriptorID: "udc_output", - } - - var b bytes.Buffer - cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - - var response ResolveCredentialManifestResponse - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - require.NotEmpty(t, response) - require.Len(t, response.Resolved, 1) - }) - - t.Run("successfully resolve credential ID", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - request := &ResolveCredentialManifestRequest{ - WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token}, - Manifest: testdata.CredentialManifestMultipleVCs, - CredentialID: "http://example.edu/credentials/1872", - DescriptorID: "udc_output", - } - - var b bytes.Buffer - cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - - var response ResolveCredentialManifestResponse - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - require.NotEmpty(t, response) - require.Len(t, response.Resolved, 1) - }) - - t.Run("failed to resolve - invalid requests", func(t *testing.T) { - cmd := New(mockctx, &Config{}) - - // missing manifest - request := &ResolveCredentialManifestRequest{ - WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token}, - Credential: testdata.SampleUDCVC, - DescriptorID: "udc_output", - } - - var b bytes.Buffer - cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request)) - validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode, - "failed to read credential manifest") - - // missing descriptor ID - request = &ResolveCredentialManifestRequest{ - WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token}, - Manifest: testdata.CredentialManifestMultipleVCs, - Credential: testdata.SampleUDCVC, - } - - b.Reset() - cmdErr = cmd.ResolveCredentialManifest(&b, getReader(t, &request)) - validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode, - "unable to find matching descriptor") - - // missing resolve option - request = &ResolveCredentialManifestRequest{ - WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token}, - Manifest: testdata.CredentialManifestMultipleVCs, - } - - b.Reset() - cmdErr = cmd.ResolveCredentialManifest(&b, getReader(t, &request)) - validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode, "invalid option") - - b.Reset() - cmdErr = cmd.ResolveCredentialManifest(&b, bytes.NewBufferString("==")) - validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character") - }) -} - -func createSampleUserProfile(t *testing.T, ctx *mockprovider.Provider, request *CreateOrUpdateProfileRequest) { - cmd := New(ctx, &Config{}) - require.NotNil(t, cmd) - - var l bytes.Buffer - cmdErr := cmd.CreateProfile(&l, getReader(t, request)) - require.NoError(t, cmdErr) -} - -func getReader(t *testing.T, v interface{}) io.Reader { - vcReqBytes, err := json.Marshal(v) - require.NoError(t, err) - - return bytes.NewBuffer(vcReqBytes) -} - -func getUnlockToken(t *testing.T, b bytes.Buffer) string { - var response UnlockWalletResponse - - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - - return response.Token -} - -func unlockWallet(t *testing.T, ctx *mockprovider.Provider, request *UnlockWalletRequest) (string, func()) { - cmd := New(ctx, nil) - - var b bytes.Buffer - - cmdErr := cmd.Open(&b, getReader(t, &request)) - require.NoError(t, cmdErr) - - return getUnlockToken(t, b), func() { - cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: request.UserID})) - if cmdErr != nil { - t.Log(t, cmdErr) - } - } -} - -func addContent(t *testing.T, ctx *mockprovider.Provider, request *AddContentRequest) { - cmd := New(ctx, &Config{}) - - var b bytes.Buffer - defer b.Reset() - - cmdErr := cmd.Add(&b, getReader(t, &request)) - require.NoError(t, cmdErr) -} - -func validateError(t *testing.T, err command.Error, - expectedType command.Type, expectedCode command.Code, contains string) { - require.Error(t, err) - require.Equal(t, err.Type(), expectedType) - require.Equal(t, err.Code(), expectedCode) - - if contains != "" { - require.Contains(t, err.Error(), contains) - } -} - -func newMockProvider(t *testing.T) *mockprovider.Provider { - t.Helper() - - loader, err := ldtestutil.DocumentLoader() - require.NoError(t, err) - - serviceMap := map[string]interface{}{ - presentproofSvc.Name: &mockpresentproof.MockPresentProofSvc{}, - outofbandSvc.Name: &mockoutofband.MockOobService{}, - didexchange.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{}, - mediator.Coordination: &mockmediator.MockMediatorSvc{}, - issuecredentialsvc.Name: &mockissuecredential.MockIssueCredentialSvc{}, - oobv2.Name: &mockoutofbandv2.MockOobService{}, - } - - return &mockprovider.Provider{ - StorageProviderValue: mockstorage.NewMockStoreProvider(), - ProtocolStateStorageProviderValue: mockstorage.NewMockStoreProvider(), - DocumentLoaderValue: loader, - ServiceMap: serviceMap, - } -} - -func getMockDIDKeyVDR() *mockvdr.MockVDRegistry { - return &mockvdr.MockVDRegistry{ - ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) { - if strings.HasPrefix(didID, "did:key:") { - k := key.New() - - d, e := k.Read(didID) - if e != nil { - return nil, e - } - - return d, nil - } - - return nil, fmt.Errorf("did not found") - }, - } -} - -func parseCredential(t *testing.T, b bytes.Buffer) *verifiable.Credential { - var response struct { - Credential json.RawMessage - } - - require.NoError(t, json.NewDecoder(&b).Decode(&response)) - - loader, err := ldtestutil.DocumentLoader() - require.NoError(t, err) - - vc, err := verifiable.ParseCredential(response.Credential, verifiable.WithDisabledProofCheck(), - verifiable.WithJSONLDDocumentLoader(loader)) - require.NoError(t, err) - - return vc -} + return vc +} func parsePresentation(t *testing.T, b bytes.Buffer) *verifiable.Presentation { var response struct { @@ -2735,17 +1921,3 @@ type mockHeaderSigner struct{} func (s *mockHeaderSigner) SignHeader(req *http.Request, capabilityBytes []byte) (*http.Header, error) { return &http.Header{}, nil } - -// mockMsg containing custom parent thread ID. -type mockMsg struct { - *service.DIDCommMsgMap - thID string -} - -func (m *mockMsg) ParentThreadID() string { - return m.thID -} - -func (m *mockMsg) ThreadID() (string, error) { - return m.thID, nil -} diff --git a/pkg/controller/command/vcwallet/models.go b/pkg/controller/command/vcwallet/models.go index 030948d0b..b8011e385 100644 --- a/pkg/controller/command/vcwallet/models.go +++ b/pkg/controller/command/vcwallet/models.go @@ -10,9 +10,6 @@ import ( "encoding/json" "time" - "github.com/hyperledger/aries-framework-go/pkg/client/outofband" - "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" - "github.com/hyperledger/aries-framework-go/pkg/doc/cm" "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" "github.com/hyperledger/aries-framework-go/pkg/kms" "github.com/hyperledger/aries-framework-go/pkg/wallet" @@ -313,163 +310,3 @@ type CreateKeyPairRequest struct { type CreateKeyPairResponse struct { *wallet.KeyPair } - -// ConnectRequest is request model for wallet DID connect operation. -type ConnectRequest struct { - WalletAuth - - // out-of-band invitation to establish connection. - Invitation *outofband.Invitation `json:"invitation"` - - ConnectOpts -} - -// ConnectOpts is option for accepting out-of-band invitation and to perform DID exchange. -type ConnectOpts struct { - // Label to be shared with the other agent during the subsequent DID exchange. - MyLabel string `json:"myLabel,omitempty"` - - // router connections to be used to establish connection. - RouterConnections []string `json:"routerConnections,omitempty"` - - // DID to be used when reusing a connection. - ReuseConnection string `json:"reuseConnection,omitempty"` - - // To use any recognized DID in the services array for a reusable connection. - ReuseAnyConnection bool `json:"reuseAnyConnection,omitempty"` - - // Timeout (in milliseconds) waiting for connection status to be completed. - Timeout time.Duration `json:"timeout,omitempty"` -} - -// ConnectResponse is response model from wallet DID connection operation. -type ConnectResponse struct { - // connection ID of the connection established. - ConnectionID string `json:"connectionID"` -} - -// ProposePresentationRequest is request model for performing propose presentation operation from wallet. -type ProposePresentationRequest struct { - WalletAuth - - // out-of-band invitation to establish connection and send propose presentation message. - Invitation *wallet.GenericInvitation `json:"invitation"` - - // Optional From DID option to customize sender DID. - FromDID string `json:"from,omitempty"` - - // Timeout (in milliseconds) waiting for operation to be completed. - Timeout time.Duration `json:"timeout,omitempty"` - - // Options for accepting out-of-band invitation and to perform DID exchange (for DIDComm V1). - ConnectionOpts ConnectOpts `json:"connectOptions,omitempty"` -} - -// ProposePresentationResponse is response model from wallet propose presentation operation. -type ProposePresentationResponse struct { - // response request presentation message from relying party. - PresentationRequest *service.DIDCommMsgMap `json:"presentationRequest,omitempty"` -} - -// PresentProofRequest is request model from wallet present proof operation. -// Supported attachment MIME type "application/ld+json". -type PresentProofRequest struct { - WalletAuth - - // Thread ID from request presentation response - ThreadID string `json:"threadID,omitempty"` - - // presentation to be sent as part of present proof message. - Presentation json.RawMessage `json:"presentation,omitempty"` - - // If true then wallet will wait for present proof protocol status to be - // done or abandoned till given Timeout. - // Also, will return web redirect info if found in acknowledgment message or problem-report. - WaitForDone bool `json:"waitForDone,omitempty"` - - // Optional timeout (in milliseconds) waiting for present proof operation to be done. - // will be taken into account only when WaitForDone is enabled. - // If not provided then wallet will use its default timeout. - Timeout time.Duration `json:"WaitForDoneTimeout,omitempty"` -} - -// PresentProofResponse is response model from wallet present proof operation. -type PresentProofResponse struct { - wallet.CredentialInteractionStatus -} - -// ProposeCredentialRequest is request model for performing propose credential operation from wallet. -type ProposeCredentialRequest struct { - WalletAuth - - // out-of-band invitation to establish connection and send propose credential message. - Invitation *wallet.GenericInvitation `json:"invitation"` - - // Optional From DID option to customize sender DID. - FromDID string `json:"from,omitempty"` - - // Timeout (in milliseconds) waiting for operation to be completed. - Timeout time.Duration `json:"timeout,omitempty"` - - // Options for accepting out-of-band invitation and to perform DID exchange (for DIDComm V1). - ConnectionOpts ConnectOpts `json:"connectOptions,omitempty"` -} - -// ProposeCredentialResponse is response model from wallet propose credential operation. -type ProposeCredentialResponse struct { - // response offer credential message from issuer. - OfferCredential *service.DIDCommMsgMap `json:"offerCredential,omitempty"` -} - -// RequestCredentialRequest is request model from wallet request credential operation. -// Supported attachment MIME type "application/ld+json". -type RequestCredentialRequest struct { - WalletAuth - - // Thread ID from offer credential response previously received during propose credential interaction. - ThreadID string `json:"threadID,omitempty"` - - // presentation to be sent as part of request credential message. - Presentation json.RawMessage `json:"presentation,omitempty"` - - // If true then wallet will wait till it receives credential fulfillment response from issuer for given Timeout. - // Also, will return web redirect info if found in fulfillment message or problem-report. - WaitForDone bool `json:"waitForDone,omitempty"` - - // Optional timeout (in milliseconds) waiting for credential fulfillment to arrive. - // will be taken into account only when WaitForDone is enabled. - // If not provided then wallet will use its default timeout. - Timeout time.Duration `json:"WaitForDoneTimeout,omitempty"` -} - -// RequestCredentialResponse is response model from wallet request credential operation. -type RequestCredentialResponse struct { - wallet.CredentialInteractionStatus -} - -// ResolveCredentialManifestRequest is request model for resolving credential manifest from wallet. -type ResolveCredentialManifestRequest struct { - WalletAuth - - // Credential Manifest on which given credential fulfillment or credential needs to be resolved. - Manifest json.RawMessage `json:"manifest,omitempty"` - - // Fulfillment to be be resolved. - // If provided, then this option takes precedence over credential resolve option. - Fulfillment json.RawMessage `json:"fulfillment,omitempty"` - - // Credential to be be resolved, to be provided along with 'DescriptorID' to be used for resolving. - Credential json.RawMessage `json:"credential,omitempty"` - - // ID of the Credential from wallet content to be be resolved, to be provided along with 'DescriptorID'. - CredentialID string `json:"credentialID,omitempty"` - - // ID of the output descriptor to be used for resolving given credential. - DescriptorID string `json:"descriptorID,omitempty"` -} - -// ResolveCredentialManifestResponse is response model from wallet credential manifest resolve operation. -type ResolveCredentialManifestResponse struct { - // List of Resolved Descriptor results. - Resolved []*cm.ResolvedDescriptor `json:"resolved,omitempty"` -} diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go index 8e0d001ed..6fc45ff51 100644 --- a/pkg/controller/controller_test.go +++ b/pkg/controller/controller_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" + "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet" "github.com/hyperledger/aries-framework-go/pkg/controller/internal/mocks/webhook" "github.com/hyperledger/aries-framework-go/pkg/framework/aries" "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api" @@ -159,7 +159,7 @@ func TestWithMessageHandler(t *testing.T) { func TestWithWalletConfiguration(t *testing.T) { controllerOpts := &allOpts{} - opt := WithWalletConfiguration(&vcwallet.Config{WebKMSCacheSize: 99}) + opt := WithWalletConfiguration(&didcommwallet.Config{WebKMSCacheSize: 99}) opt(controllerOpts) diff --git a/pkg/controller/rest/vcwallet/models.go b/pkg/controller/rest/vcwallet/models.go index 7df30891a..29441d1e7 100644 --- a/pkg/controller/rest/vcwallet/models.go +++ b/pkg/controller/rest/vcwallet/models.go @@ -9,6 +9,7 @@ package vcwallet import ( "encoding/json" + "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet" "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" "github.com/hyperledger/aries-framework-go/pkg/wallet" ) @@ -270,7 +271,7 @@ type connectRequest struct { // nolint: unused,deadcode // Params for connecting to wallet for DIDComm. // // in: body - Params *vcwallet.ConnectRequest + Params *didcommwallet.ConnectRequest } // connectResponse is response model from wallet DID connect operation. @@ -280,7 +281,7 @@ type connectResponse struct { // wallet connect response. // // in: body - Response *vcwallet.ConnectResponse `json:"response"` + Response *didcommwallet.ConnectResponse `json:"response"` } // proposePresentationRequest is request model for performing propose presentation operation from wallet. @@ -290,7 +291,7 @@ type proposePresentationRequest struct { // nolint: unused,deadcode // Params for proposing presentation from wallet. // // in: body - Params *vcwallet.ProposePresentationRequest + Params *didcommwallet.ProposePresentationRequest } // proposePresentationResponse is response model from wallet propose presentation operation. @@ -300,7 +301,7 @@ type proposePresentationResponse struct { // response containing request presentation message from relyinig party. // // in: body - Response *vcwallet.ProposePresentationResponse `json:"response"` + Response *didcommwallet.ProposePresentationResponse `json:"response"` } // presentProofRequest is request model for performing present proof operation from wallet. @@ -310,7 +311,7 @@ type presentProofRequest struct { // nolint: unused,deadcode // Params for accepting presentation request and sending present proof message to relying party. // // in: body - Params *vcwallet.PresentProofRequest + Params *didcommwallet.PresentProofRequest } // presentProofResponse is response model from wallet present proof operation. @@ -331,7 +332,7 @@ type proposeCredentialRequest struct { // nolint: unused,deadcode // Params for proposing credential from wallet. // // in: body - Params *vcwallet.ProposeCredentialRequest + Params *didcommwallet.ProposeCredentialRequest } // proposePresentationResponse is response model from wallet propose credential operation. @@ -341,7 +342,7 @@ type proposeCredentialResponse struct { // response containing offer credential response from issuer. // // in: body - Response *vcwallet.ProposeCredentialResponse `json:"response"` + Response *didcommwallet.ProposeCredentialResponse `json:"response"` } // requestCredentialRequest is request model for performing request credential operation from wallet to conclude @@ -352,7 +353,7 @@ type requestCredentialRequest struct { // nolint: unused,deadcode // Params for sending request credential message from wallet and optionally wait for credential fulfillment. // // in: body - Params *vcwallet.RequestCredentialRequest + Params *didcommwallet.RequestCredentialRequest } // requestCredentialResponse is response model from wallet request credential operation which may contain @@ -378,7 +379,7 @@ type resolveCredentialManifestRequest struct { // nolint: unused,deadcode // Params for resolving credential manifests from wallet. // // in: body - Params *vcwallet.ResolveCredentialManifestRequest + Params *didcommwallet.ResolveCredentialManifestRequest } // resolveCredentialManifestResponse is response model for resolving credential manifests from wallet. @@ -388,5 +389,5 @@ type resolveCredentialManifestResponse struct { // Response containing resolve credential manifest descriptors. // // in: body - Response *vcwallet.ResolveCredentialManifestResponse `json:"response"` + Response *didcommwallet.ResolveCredentialManifestResponse `json:"response"` } diff --git a/pkg/controller/rest/vcwallet/operation.go b/pkg/controller/rest/vcwallet/operation.go index ee2a56117..4a0600a88 100644 --- a/pkg/controller/rest/vcwallet/operation.go +++ b/pkg/controller/rest/vcwallet/operation.go @@ -14,6 +14,7 @@ import ( "github.com/gorilla/mux" "github.com/piprate/json-gold/ld" + "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet" "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" "github.com/hyperledger/aries-framework-go/pkg/controller/internal/cmdutil" "github.com/hyperledger/aries-framework-go/pkg/controller/rest" @@ -77,13 +78,12 @@ type didCommProvider interface { // Operation contains REST operations provided by verifiable credential wallet. type Operation struct { handlers []rest.Handler - command *vcwallet.Command + command *didcommwallet.Command } // New returns new verfiable credential wallet REST controller. func New(p provider, config *vcwallet.Config) *Operation { - cmd := vcwallet.New(p, config) - + cmd := didcommwallet.New(p, config) o := &Operation{command: cmd} o.registerHandler() diff --git a/pkg/controller/rest/vcwallet/operation_test.go b/pkg/controller/rest/vcwallet/operation_test.go index e711916c0..e87dd9f36 100644 --- a/pkg/controller/rest/vcwallet/operation_test.go +++ b/pkg/controller/rest/vcwallet/operation_test.go @@ -24,6 +24,7 @@ import ( "github.com/hyperledger/aries-framework-go/internal/testdata" outofbandClient "github.com/hyperledger/aries-framework-go/pkg/client/outofband" + "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet" "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet" "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto" "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model" @@ -1253,10 +1254,10 @@ func TestOperation_Connect(t *testing.T) { } mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc - request := &vcwallet.ConnectRequest{ + request := &didcommwallet.ConnectRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, Invitation: &outofbandClient.Invitation{}, - ConnectOpts: vcwallet.ConnectOpts{ + ConnectOpts: didcommwallet.ConnectOpts{ MyLabel: "sample-label", }, } @@ -1283,10 +1284,10 @@ func TestOperation_Connect(t *testing.T) { } mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc - request := &vcwallet.ConnectRequest{ + request := &didcommwallet.ConnectRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, Invitation: &outofbandClient.Invitation{}, - ConnectOpts: vcwallet.ConnectOpts{ + ConnectOpts: didcommwallet.ConnectOpts{ MyLabel: "sample-label", }, } @@ -1379,7 +1380,7 @@ func TestOperation_ProposePresentation(t *testing.T) { require.NoError(t, err) require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes)) - request := &vcwallet.ProposePresentationRequest{ + request := &didcommwallet.ProposePresentationRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, Invitation: &wallet.GenericInvitation{}, } @@ -1407,7 +1408,7 @@ func TestOperation_ProposePresentation(t *testing.T) { mockctx.ServiceMap[outofbandSvc.Name] = oobSvc - request := &vcwallet.ProposePresentationRequest{ + request := &didcommwallet.ProposePresentationRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, Invitation: &wallet.GenericInvitation{}, } @@ -1460,7 +1461,7 @@ func TestOperation_PresentProof(t *testing.T) { } mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc - request := &vcwallet.PresentProofRequest{ + request := &didcommwallet.PresentProofRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, ThreadID: thID, Presentation: json.RawMessage{}, @@ -1484,7 +1485,7 @@ func TestOperation_PresentProof(t *testing.T) { }) t.Run("wallet present proof success", func(t *testing.T) { - request := &vcwallet.PresentProofRequest{ + request := &didcommwallet.PresentProofRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, ThreadID: uuid.New().String(), Presentation: json.RawMessage{}, @@ -1507,7 +1508,7 @@ func TestOperation_PresentProof(t *testing.T) { mockctx.ServiceMap[presentproofSvc.Name] = ppSvc - request := &vcwallet.PresentProofRequest{ + request := &didcommwallet.PresentProofRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, ThreadID: uuid.New().String(), Presentation: json.RawMessage{}, @@ -1599,7 +1600,7 @@ func TestOperation_ProposeCredential(t *testing.T) { require.NoError(t, err) require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes)) - request := &vcwallet.ProposeCredentialRequest{ + request := &didcommwallet.ProposeCredentialRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, Invitation: &wallet.GenericInvitation{}, } @@ -1634,7 +1635,7 @@ func TestOperation_ProposeCredential(t *testing.T) { mockctx.ServiceMap[outofbandSvc.Name] = oobSvc - request := &vcwallet.ProposeCredentialRequest{ + request := &didcommwallet.ProposeCredentialRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, Invitation: &wallet.GenericInvitation{}, } @@ -1690,7 +1691,7 @@ func TestOperation_RequestCredential(t *testing.T) { } mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc - request := &vcwallet.RequestCredentialRequest{ + request := &didcommwallet.RequestCredentialRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, ThreadID: thID, Presentation: json.RawMessage{}, @@ -1714,7 +1715,7 @@ func TestOperation_RequestCredential(t *testing.T) { }) t.Run("wallet request credential success", func(t *testing.T) { - request := &vcwallet.RequestCredentialRequest{ + request := &didcommwallet.RequestCredentialRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, ThreadID: uuid.New().String(), Presentation: json.RawMessage{}, @@ -1736,7 +1737,7 @@ func TestOperation_RequestCredential(t *testing.T) { } mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc - request := &vcwallet.PresentProofRequest{ + request := &didcommwallet.PresentProofRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token}, ThreadID: uuid.New().String(), Presentation: json.RawMessage{}, @@ -1771,7 +1772,7 @@ func TestOperation_ResolveCredentialManifest(t *testing.T) { defer lock() t.Run("resolve credential manifest", func(t *testing.T) { - request := &vcwallet.ResolveCredentialManifestRequest{ + request := &didcommwallet.ResolveCredentialManifestRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, Manifest: testdata.CredentialManifestMultipleVCs, Fulfillment: testdata.CredentialFulfillmentWithMultipleVCs, @@ -1792,7 +1793,7 @@ func TestOperation_ResolveCredentialManifest(t *testing.T) { }) t.Run("resolve credential manifest failure", func(t *testing.T) { - request := &vcwallet.ResolveCredentialManifestRequest{ + request := &didcommwallet.ResolveCredentialManifestRequest{ WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token}, Manifest: testdata.CredentialManifestMultipleVCs, Credential: testdata.SampleUDCVC,