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

feat: Forward Message - Base structure implementation #990

Merged
merged 1 commit into from
Dec 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}