Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
Merge pull request #990 from rolsonquadras/issue-957
Browse files Browse the repository at this point in the history
feat: Forward Message - Base structure implementation
  • Loading branch information
fqutishat authored Dec 16, 2019
2 parents 92ac19f + 859e3db commit 9da989c
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 6 deletions.
6 changes: 6 additions & 0 deletions pkg/didcomm/dispatcher/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ type Service interface {

// Outbound interface
type Outbound interface {
// Sends the message after packing with the sender key and recipient keys.
Send(interface{}, string, *service.Destination) error

// Sends the message after packing with the keys derived from DIDs.
SendToDID(msg interface{}, myDID, theirDID string) error

// Forward forwards the message without packing to the destination.
Forward(interface{}, *service.Destination) error
}
49 changes: 48 additions & 1 deletion pkg/didcomm/dispatcher/outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (o *OutboundDispatcher) SendToDID(msg interface{}, myDID, theirDID string)
return nil
}

// Send msg
// Send sends the message after packing with the sender key and recipient keys.
func (o *OutboundDispatcher) Send(msg interface{}, senderVerKey string, des *service.Destination) error {
for _, v := range o.outboundTransports {
if !v.AcceptRecipient(des.RecipientKeys) {
Expand Down Expand Up @@ -87,6 +87,8 @@ func (o *OutboundDispatcher) Send(msg interface{}, senderVerKey string, des *ser
// set the return route option
des.TransportReturnRoute = o.transportReturnRoute

packedMsg = createForwardMessage(packedMsg, des)

_, err = v.Send(packedMsg, des)
if err != nil {
return fmt.Errorf("failed to send msg using outbound transport: %w", err)
Expand All @@ -97,3 +99,48 @@ func (o *OutboundDispatcher) Send(msg interface{}, senderVerKey string, des *ser

return fmt.Errorf("no outbound transport found for serviceEndpoint: %s", des.ServiceEndpoint)
}

// Forward forwards the message without packing to the destination.
func (o *OutboundDispatcher) Forward(msg interface{}, des *service.Destination) error {
for _, v := range o.outboundTransports {
if !v.AcceptRecipient(des.RecipientKeys) {
if !v.Accept(des.ServiceEndpoint) {
continue
}
}

req, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("failed marshal to bytes: %w", err)
}

_, err = v.Send(req, des)
if err != nil {
return fmt.Errorf("failed to send msg using outbound transport: %w", err)
}

return nil
}

return fmt.Errorf("no outbound transport found for serviceEndpoint: %s", des.ServiceEndpoint)
}

func createForwardMessage(msg []byte, des *service.Destination) []byte {
// TODO https://github.com/hyperledger/aries-framework-go/issues/807#issuecomment-566126744 message needs to
// be packed with anon crypt if the request needs through routed ie. des.RoutingKeys != nil
// psuedocode:
// if des.RoutingKeys != nil {
// // create forward message:
// forward := &Forward{
// Type: "https://didcomm.org/routing/1.0/forward",
// ID: uuid.New().String(),
// To: "destinationRecKey",
// Msg: packedMsg,
// }
//
// // pack above message using anon crypt
//
// // return the message
// }
return msg
}
27 changes: 27 additions & 0 deletions pkg/didcomm/dispatcher/outbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,33 @@ func TestOutboundDispatcherTransportReturnRoute(t *testing.T) {
})
}

func TestOutboundDispatcher_Forward(t *testing.T) {
t.Run("test forward - success", func(t *testing.T) {
o := NewOutbound(&mockProvider{
packagerValue: &mockpackager.Packager{},
outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: true}},
})
require.NoError(t, o.Forward("data", &service.Destination{ServiceEndpoint: "url"}))
})

t.Run("test forward - no outbound transport found", func(t *testing.T) {
o := NewOutbound(&mockProvider{packagerValue: &mockpackager.Packager{},
outboundTransportsValue: []transport.OutboundTransport{&mockdidcomm.MockOutboundTransport{AcceptValue: false}}})
err := o.Forward("data", &service.Destination{ServiceEndpoint: "url"})
require.Error(t, err)
require.Contains(t, err.Error(), "no outbound transport found for serviceEndpoint: url")
})

t.Run("test forward - outbound send failure", func(t *testing.T) {
o := NewOutbound(&mockProvider{packagerValue: &mockpackager.Packager{},
outboundTransportsValue: []transport.OutboundTransport{
&mockdidcomm.MockOutboundTransport{AcceptValue: true, SendErr: fmt.Errorf("send error")}}})
err := o.Forward("data", &service.Destination{ServiceEndpoint: "url"})
require.Error(t, err)
require.Contains(t, err.Error(), "send error")
})
}

// mockProvider mock provider
type mockProvider struct {
packagerValue commontransport.Packager
Expand Down
10 changes: 10 additions & 0 deletions pkg/didcomm/protocol/route/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,13 @@ type UpdateResponse struct {
Action string `json:"action,omitempty"`
Result string `json:"result,omitempty"`
}

// Forward route forward message.
// nolint lll - url in the next line is long
// https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0094-cross-domain-messaging/README.md#corerouting10forward
type Forward struct {
Type string `json:"@type,omitempty"`
ID string `json:"@id,omitempty"`
To string `json:"@to,omitempty"`
Msg interface{} `json:"@msg,omitempty"`
}
39 changes: 36 additions & 3 deletions pkg/didcomm/protocol/route/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const (

// KeyListUpdateResponseMsgType defines the route coordination key list update message response type.
KeylistUpdateResponseMsgType = CoordinationSpec + "keylist_update_response"

// ForwardMsgType defines the route forward message type.
ForwardMsgType = "https://didcomm.org/routing/1.0/forward"
)

// constants for key list update processing
Expand Down Expand Up @@ -92,7 +95,7 @@ func New(prov provider) (*Service, error) {
}

// HandleInbound handles inbound route coordination messages.
func (s *Service) HandleInbound(msg *service.DIDCommMsg) (string, error) {
func (s *Service) HandleInbound(msg *service.DIDCommMsg) (string, error) { // nolint gocyclo (5 switch cases)
// perform action on inbound message asynchronously
go func() {
switch msg.Header.Type {
Expand All @@ -112,6 +115,10 @@ func (s *Service) HandleInbound(msg *service.DIDCommMsg) (string, error) {
if err := s.handleKeylistUpdateResponse(msg); err != nil {
logger.Errorf("handle route keylist update response error : %s", err)
}
case ForwardMsgType:
if err := s.handleForward(msg); err != nil {
logger.Errorf("handle forward error : %s", err)
}
}
}()

Expand All @@ -126,7 +133,7 @@ func (s *Service) HandleOutbound(msg *service.DIDCommMsg, destination *service.D
// Accept checks whether the service can handle the message type.
func (s *Service) Accept(msgType string) bool {
switch msgType {
case RequestMsgType, GrantMsgType, KeylistUpdateMsgType, KeylistUpdateResponseMsgType:
case RequestMsgType, GrantMsgType, KeylistUpdateMsgType, KeylistUpdateResponseMsgType, ForwardMsgType:
return true
}

Expand Down Expand Up @@ -197,7 +204,7 @@ func (s *Service) handleKeylistUpdate(msg *service.DIDCommMsg) error {
val := ""
result := success

err = s.routeStore.Put(v.RecipientKey, []byte(val))
err = s.routeStore.Put(dataKey(v.RecipientKey), []byte(val))
if err != nil {
logger.Errorf("failed to add the route key to store : %s", err)

Expand Down Expand Up @@ -246,3 +253,29 @@ func (s *Service) handleKeylistUpdateResponse(msg *service.DIDCommMsg) error {

return nil
}

func (s *Service) handleForward(msg *service.DIDCommMsg) error {
// unmarshal the payload
forward := &Forward{}

err := json.Unmarshal(msg.Payload, forward)
if err != nil {
return fmt.Errorf("forward message unmarshal : %w", err)
}

// TODO Open question - https://github.com/hyperledger/aries-framework-go/issues/965 Mismatch between Route
// Coordination and Forward RFC. For now assume, the TO field contains the recipient key.
_, err = s.routeStore.Get(dataKey(forward.To))
if err != nil {
return fmt.Errorf("route key fetch : %w", err)
}

// TODO https://github.com/hyperledger/aries-framework-go/issues/725 get destination details from the
// did retrieved from previous Get call.

return s.outbound.Forward(forward.Msg, nil)
}

func dataKey(id string) string {
return "route-" + id
}
67 changes: 66 additions & 1 deletion pkg/didcomm/protocol/route/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func TestServiceAccept(t *testing.T) {
require.Equal(t, true, s.Accept(GrantMsgType))
require.Equal(t, true, s.Accept(KeylistUpdateMsgType))
require.Equal(t, true, s.Accept(KeylistUpdateResponseMsgType))
require.Equal(t, true, s.Accept(ForwardMsgType))
require.Equal(t, false, s.Accept("unsupported msg type"))
}

Expand Down Expand Up @@ -177,7 +178,7 @@ func TestServiceUpdateKeyListMsg(t *testing.T) {
update := make(map[string]updateResult)
update["ABC"] = updateResult{action: add, result: success}
update["XYZ"] = updateResult{action: remove, result: serverError}
update[""] = updateResult{action: add, result: serverError}
update[""] = updateResult{action: add, result: success}

svc, err := New(&mockProvider{

Expand Down Expand Up @@ -244,3 +245,67 @@ func TestServiceKeylistUpdateResponseMsg(t *testing.T) {
require.Contains(t, err.Error(), "route keylist update response message unmarshal")
})
}

func TestServiceForwardMsg(t *testing.T) {
t.Run("test service handle inbound forward msg - success", func(t *testing.T) {
to := randomID()
svc, err := New(&mockProvider{})
require.NoError(t, err)

err = svc.routeStore.Put(to, []byte("did:example:123"))
require.NoError(t, err)

msgID := randomID()

id, err := svc.HandleInbound(generateForwardMsgPayload(t, msgID, to, nil))
require.NoError(t, err)
require.Equal(t, msgID, id)
})

t.Run("test service handle forward msg - success", func(t *testing.T) {
svc, err := New(&mockProvider{})
require.NoError(t, err)

msg := &service.DIDCommMsg{Payload: []byte("invalid json")}

err = svc.handleForward(msg)
require.Error(t, err)
require.Contains(t, err.Error(), "forward message unmarshal")
})

t.Run("test service handle forward msg - route key fetch fail", func(t *testing.T) {
to := randomID()
msgID := randomID()

svc, err := New(&mockProvider{})
require.NoError(t, err)

err = svc.handleForward(generateForwardMsgPayload(t, msgID, to, nil))
require.Error(t, err)
require.Contains(t, err.Error(), "route key fetch")
})

t.Run("test service handle forward msg - validate forward message content", func(t *testing.T) {
to := randomID()
msgID := randomID()

content := "packed message destined to the recipient through router"
msg := generateForwardMsgPayload(t, msgID, to, content)

svc, err := New(&mockProvider{
outbound: &mockOutbound{validateForward: func(msg interface{}) error {
require.Equal(t, content, msg)

return nil
},
},
})
require.NoError(t, err)

err = svc.routeStore.Put(dataKey(to), []byte("did:example:123"))
require.NoError(t, err)

err = svc.handleForward(msg)
require.NoError(t, err)
})
}
22 changes: 21 additions & 1 deletion pkg/didcomm/protocol/route/support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ func (p *mockProvider) KMS() kms.KeyManager {

// mock outbound
type mockOutbound struct {
validateSend func(msg interface{}) error
validateSend func(msg interface{}) error
validateForward func(msg interface{}) error
}

func (m *mockOutbound) Send(msg interface{}, senderVerKey string, des *service.Destination) error {
Expand All @@ -70,6 +71,10 @@ func (m *mockOutbound) SendToDID(msg interface{}, myDID, theirDID string) error
return nil
}

func (m *mockOutbound) Forward(msg interface{}, des *service.Destination) error {
return m.validateForward(msg)
}

func generateRequestMsgPayload(t *testing.T, id string) *service.DIDCommMsg {
requestBytes, err := json.Marshal(&Request{
Type: RequestMsgType,
Expand Down Expand Up @@ -124,6 +129,21 @@ func generateKeylistUpdateResponseMsgPayload(t *testing.T, id string, updates []
return didMsg
}

func generateForwardMsgPayload(t *testing.T, id, to string, msg interface{}) *service.DIDCommMsg {
requestBytes, err := json.Marshal(&Forward{
Type: ForwardMsgType,
ID: id,
To: to,
Msg: msg,
})
require.NoError(t, err)

didMsg, err := service.NewDIDCommMsg(requestBytes)
require.NoError(t, err)

return didMsg
}

func randomID() string {
return uuid.New().String()
}
5 changes: 5 additions & 0 deletions pkg/internal/mock/didcomm/dispatcher/mock_outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ func (m *MockOutbound) Send(msg interface{}, senderVerKey string, des *service.D
func (m *MockOutbound) SendToDID(msg interface{}, myDID, theirDID string) error {
return nil
}

// Forward msg
func (m *MockOutbound) Forward(msg interface{}, des *service.Destination) error {
return nil
}

0 comments on commit 9da989c

Please sign in to comment.