Skip to content

Commit

Permalink
feat: vcwallet presentproof - wait for done
Browse files Browse the repository at this point in the history
- wait for acknowledgement
- wait for problem-report
- support for web redirect info
- closes hyperledger-archives#3021

Signed-off-by: sudesh.shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed Oct 20, 2021
1 parent d7c58d6 commit 376ab69
Show file tree
Hide file tree
Showing 19 changed files with 663 additions and 51 deletions.
8 changes: 4 additions & 4 deletions pkg/client/vcwallet/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,14 +408,14 @@ func (c *Client) ProposePresentation(invitation *outofband.Invitation, options .
// - presentation: presentation to be sent.
//
// Returns:
// - present proof status including web redirect info.
// - error if operation fails.
//
// TODO: wait for acknowledgement option to be added.
func (c *Client) PresentProof(thID string, presentProofFrom wallet.PresentProofFrom) error {
func (c *Client) PresentProof(thID string, presentProofFrom ...wallet.PresentProofOptions) (*wallet.PresentProofStatus, error) { //nolint: lll
auth, err := c.auth()
if err != nil {
return err
return nil, err
}

return c.wallet.PresentProof(auth, thID, presentProofFrom)
return c.wallet.PresentProof(auth, thID, presentProofFrom...)
}
58 changes: 56 additions & 2 deletions pkg/client/vcwallet/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/hyperledger/aries-framework-go/pkg/client/outofband"
"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"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
Expand Down Expand Up @@ -342,6 +343,9 @@ const (
],
"required": true
}`
webRedirectStatusKey = "status"
webRedirectURLKey = "url"
exampleWebRedirect = "http://example.com/sample"
)

func TestCreateProfile(t *testing.T) {
Expand Down Expand Up @@ -1744,17 +1748,57 @@ func TestClient_PresentProof(t *testing.T) {
require.NoError(t, err)
defer vcWallet.Close()

err = vcWallet.PresentProof(uuid.New().String(), wallet.FromPresentation(&verifiable.Presentation{}))
response, err := vcWallet.PresentProof(uuid.New().String(), wallet.FromPresentation(&verifiable.Presentation{}))
require.NoError(t, err)
require.NotEmpty(t, response)
require.Equal(t, model.AckStatusPENDING, response.Status)
})

t.Run("test present proof success - 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

vcWallet, err := New(sampleUser, mockctx)
require.NoError(t, err)
require.NotEmpty(t, vcWallet)

err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
require.NoError(t, err)
defer vcWallet.Close()

response, err := vcWallet.PresentProof(thID, wallet.FromPresentation(&verifiable.Presentation{}),
wallet.WaitForDone(1*time.Millisecond))
require.NoError(t, err)
require.NotEmpty(t, response)
require.Equal(t, model.AckStatusOK, response.Status)
require.Equal(t, exampleWebRedirect, response.RedirectURL)
})

t.Run("test present proof failure - auth error", func(t *testing.T) {
vcWallet, err := New(sampleUser, mockctx)
require.NoError(t, err)
require.NotEmpty(t, vcWallet)

err = vcWallet.PresentProof(uuid.New().String(), wallet.FromPresentation(&verifiable.Presentation{}))
response, err := vcWallet.PresentProof(uuid.New().String(), wallet.FromPresentation(&verifiable.Presentation{}))
require.True(t, errors.Is(err, ErrWalletLocked))
require.Empty(t, response)
})
}

Expand Down Expand Up @@ -1805,3 +1849,13 @@ func (s *mockStorageProvider) SetStoreConfig(name string, config storage.StoreCo
func (s *mockStorageProvider) GetStoreConfig(name string) (storage.StoreConfiguration, error) {
return s.config, nil
}

// mockMsg containing custom parent thread ID.
type mockMsg struct {
*service.DIDCommMsgMap
thID string
}

func (m *mockMsg) ParentThreadID() string {
return m.thID
}
14 changes: 13 additions & 1 deletion pkg/controller/command/vcwallet/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,13 +824,15 @@ func (o *Command) PresentProof(rw io.Writer, req io.Reader) command.Error {
return command.NewExecuteError(PresentProofErrorCode, err)
}

err = vcWallet.PresentProof(request.Auth, request.ThreadID, wallet.FromRawPresentation(request.Presentation))
status, err := vcWallet.PresentProof(request.Auth, request.ThreadID, preparePresentProofOpts(request)...)
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))

Expand Down Expand Up @@ -986,3 +988,13 @@ func prepareDeriveOption(rqst *DeriveRequest) wallet.CredentialToDerive {

return wallet.FromRawCredential(rqst.RawCredential)
}

func preparePresentProofOpts(rqst *PresentProofRequest) []wallet.PresentProofOptions {
var options []wallet.PresentProofOptions

if rqst.WaitForDone {
options = append(options, wallet.WaitForDone(rqst.Timeout))
}

return append(options, wallet.FromRawPresentation(rqst.Presentation))
}
56 changes: 56 additions & 0 deletions pkg/controller/command/vcwallet/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
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"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
Expand Down Expand Up @@ -244,6 +245,9 @@ const (
]
}
}`
webRedirectStatusKey = "status"
webRedirectURLKey = "url"
exampleWebRedirect = "http://example.com/sample"
)

func TestNew(t *testing.T) {
Expand Down Expand Up @@ -2130,6 +2134,48 @@ func TestCommand_PresentProof(t *testing.T) {
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.PresentProofStatus
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 {
Expand Down Expand Up @@ -2332,3 +2378,13 @@ 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
}
15 changes: 15 additions & 0 deletions pkg/controller/command/vcwallet/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,19 @@ type PresentProofRequest struct {

// 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.PresentProofStatus
}
11 changes: 11 additions & 0 deletions pkg/controller/rest/vcwallet/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"encoding/json"

"github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
"github.com/hyperledger/aries-framework-go/pkg/wallet"
)

// createProfileRequest is request model for creating a new wallet profile.
Expand Down Expand Up @@ -312,6 +313,16 @@ type presentProofRequest struct { // nolint: unused,deadcode
Params *vcwallet.PresentProofRequest
}

// presentProofResponse is response model from wallet present proof operation.
//
// swagger:response presentProofRes
type presentProofResponse struct {
// response containing status of present proof operation.
//
// in: body
Response *wallet.PresentProofStatus `json:"response"`
}

// emptyRes model
//
// swagger:response emptyRes
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/rest/vcwallet/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func (o *Operation) ProposePresentation(rw http.ResponseWriter, req *http.Reques
//
// Responses:
// default: genericError
// 200: emptyRes
// 200: presentProofRes
func (o *Operation) PresentProof(rw http.ResponseWriter, req *http.Request) {
rest.Execute(o.command.PresentProof, rw, req.Body)
}
Expand Down
59 changes: 59 additions & 0 deletions pkg/controller/rest/vcwallet/operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"net/http/httptest"
"strings"
"testing"
"time"

"github.com/google/uuid"
"github.com/gorilla/mux"
Expand All @@ -24,6 +25,7 @@ import (
outofbandClient "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/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"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
Expand Down Expand Up @@ -244,6 +246,9 @@ const (
]
}
}`
webRedirectStatusKey = "status"
webRedirectURLKey = "url"
exampleWebRedirect = "http://example.com/sample"
)

func TestNew(t *testing.T) {
Expand Down Expand Up @@ -1614,6 +1619,50 @@ func TestOperation_PresentProof(t *testing.T) {
defer lock()

t.Run("wallet present proof success", 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

request := &vcwallet.PresentProofRequest{
WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
ThreadID: thID,
Presentation: json.RawMessage{},
WaitForDone: true,
Timeout: 1 * time.Millisecond,
}

rq := httptest.NewRequest(http.MethodPost, PresentProofPath, getReader(t, request))
rw := httptest.NewRecorder()

cmd := New(mockctx, &vcwallet.Config{})
cmd.PresentProof(rw, rq)
require.Equal(t, rw.Code, http.StatusOK)

var r presentProofResponse
require.NoError(t, json.NewDecoder(rw.Body).Decode(&r.Response))
require.NotEmpty(t, r)
require.NotEmpty(t, r.Response)
require.Equal(t, model.AckStatusOK, r.Response.Status)
require.NotEmpty(t, exampleWebRedirect, r.Response.RedirectURL)
})

t.Run("wallet present proof success - wait for done", func(t *testing.T) {
request := &vcwallet.PresentProofRequest{
WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
ThreadID: uuid.New().String(),
Expand Down Expand Up @@ -1776,3 +1825,13 @@ func parsePresentation(t *testing.T, b *bytes.Buffer) *verifiable.Presentation {

return vp
}

// mockMsg containing custom parent thread ID.
type mockMsg struct {
*service.DIDCommMsgMap
thID string
}

func (m *mockMsg) ParentThreadID() string {
return m.thID
}
8 changes: 8 additions & 0 deletions pkg/didcomm/common/model/ack.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ package model

import "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"

// acknowledgement status constants.
// Refer https://github.com/hyperledger/aries-rfcs/blob/main/features/0015-acks/README.md#ack-status.
const (
AckStatusOK = "OK"
AckStatusFAIL = "FAIL"
AckStatusPENDING = "PENDING"
)

// Ack acknowledgement struct.
type Ack struct {
Type string `json:"@type,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions pkg/didcomm/protocol/presentproof/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,9 +522,9 @@ func stateFromName(name, v string) state {
switch name {
case stateNameStart:
return &start{}
case stateNameAbandoned:
case StateNameAbandoned:
return &abandoned{V: v}
case stateNameDone:
case StateNameDone:
return &done{V: v}
case stateNameRequestSent:
return &requestSent{V: v}
Expand Down

0 comments on commit 376ab69

Please sign in to comment.