github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/choria/protocol.go (about)

     1  // Copyright (c) 2017-2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package choria
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  
    11  	"github.com/choria-io/go-choria/inter"
    12  	"github.com/choria-io/go-choria/message"
    13  	"github.com/choria-io/go-choria/protocol"
    14  	v1 "github.com/choria-io/go-choria/protocol/v1"
    15  	v2 "github.com/choria-io/go-choria/protocol/v2"
    16  )
    17  
    18  // NewMessage creates a new Message associated with this Choria instance
    19  func (fw *Framework) NewMessage(payload []byte, agent string, collective string, msgType string, request inter.Message) (msg inter.Message, err error) {
    20  	return message.NewMessage(payload, agent, collective, msgType, request, fw)
    21  }
    22  
    23  // RequestProtocol determines the protocol version to use based on security provider technology
    24  func (fw *Framework) RequestProtocol() protocol.ProtocolVersion {
    25  	switch fw.security.BackingTechnology() {
    26  	case inter.SecurityTechnologyX509:
    27  		return protocol.RequestV1
    28  
    29  	case inter.SecurityTechnologyED25519JWT:
    30  		return protocol.RequestV2
    31  
    32  	default:
    33  		return protocol.Unknown
    34  	}
    35  }
    36  
    37  // NewRequestMessageFromTransportJSON creates a Message from a Transport JSON that holds a Request
    38  func (fw *Framework) NewRequestMessageFromTransportJSON(payload []byte) (inter.Message, error) {
    39  	transport, err := fw.NewTransportFromJSON(payload)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	srequest, err := fw.NewSecureRequestFromTransport(transport, false)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	request, err := fw.NewRequestFromSecureRequest(srequest)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	protocol.CopyFederationData(transport, request)
    55  
    56  	msg, err := message.NewMessageFromRequest(request, transport.ReplyTo(), fw)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	return msg, nil
    62  }
    63  
    64  func (fw *Framework) NewMessageFromRequest(req protocol.Request, replyto string) (inter.Message, error) {
    65  	return message.NewMessageFromRequest(req, replyto, fw)
    66  }
    67  
    68  // NewReplyFromTransportJSON creates a new Reply from a transport JSON
    69  func (fw *Framework) NewReplyFromTransportJSON(payload []byte, skipvalidate bool) (msg protocol.Reply, err error) {
    70  	transport, err := fw.NewTransportFromJSON(payload)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	sreply, err := fw.NewSecureReplyFromTransport(transport, skipvalidate)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	reply, err := fw.NewReplyFromSecureReply(sreply)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	protocol.CopyFederationData(transport, reply)
    86  
    87  	return reply, nil
    88  }
    89  
    90  // NewRequestFromTransportJSON creates a new Request from transport JSON
    91  func (fw *Framework) NewRequestFromTransportJSON(payload []byte, skipvalidate bool) (msg protocol.Request, err error) {
    92  	transport, err := fw.NewTransportFromJSON(payload)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	sreq, err := fw.NewSecureRequestFromTransport(transport, skipvalidate)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	req, err := fw.NewRequestFromSecureRequest(sreq)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	protocol.CopyFederationData(transport, req)
   108  
   109  	return req, nil
   110  }
   111  
   112  // NewRequest creates a new Request complying with a specific protocol version like protocol.RequestV1
   113  func (fw *Framework) NewRequest(version protocol.ProtocolVersion, agent string, senderid string, callerid string, ttl int, requestid string, collective string) (request protocol.Request, err error) {
   114  	switch version {
   115  	case protocol.RequestV1:
   116  		request, err = v1.NewRequest(agent, senderid, callerid, ttl, requestid, collective)
   117  	case protocol.RequestV2:
   118  		request, err = v2.NewRequest(agent, senderid, callerid, ttl, requestid, collective)
   119  	default:
   120  		err = fmt.Errorf("do not know how to create a Request version %s", version)
   121  	}
   122  
   123  	return request, err
   124  }
   125  
   126  // NewRequestFromMessage creates a new Request with the Message settings preloaded complying with a specific protocol version like protocol.RequestV1
   127  func (fw *Framework) NewRequestFromMessage(version protocol.ProtocolVersion, msg inter.Message) (req protocol.Request, err error) {
   128  	if !(msg.Type() == inter.RequestMessageType || msg.Type() == inter.DirectRequestMessageType || msg.Type() == inter.ServiceRequestMessageType) {
   129  		err = fmt.Errorf("cannot use '%s' message to construct a Request", msg.Type())
   130  		return nil, err
   131  	}
   132  
   133  	req, err = fw.NewRequest(version, msg.Agent(), msg.SenderID(), msg.CallerID(), msg.TTL(), msg.RequestID(), msg.Collective())
   134  	if err != nil {
   135  		return nil, fmt.Errorf("could not create a Request from a Message: %s", err)
   136  	}
   137  
   138  	req.SetMessage(msg.Payload())
   139  
   140  	if msg.Filter() == nil {
   141  		req.NewFilter()
   142  	} else {
   143  		req.SetFilter(msg.Filter())
   144  	}
   145  
   146  	return req, nil
   147  }
   148  
   149  // NewReply creates a new Reply, the version will match that of the given request
   150  func (fw *Framework) NewReply(request protocol.Request) (reply protocol.Reply, err error) {
   151  	switch request.Version() {
   152  	case protocol.RequestV1:
   153  		return v1.NewReply(request, fw.Config.Identity)
   154  	case protocol.RequestV2:
   155  		return v2.NewReply(request, fw.Config.Identity)
   156  	default:
   157  		return nil, fmt.Errorf("do not know how to create a Reply version %s", request.Version())
   158  	}
   159  }
   160  
   161  // NewReplyFromMessage creates a new Reply with the Message settings preloaded complying with a specific protocol version like protocol.ReplyV1
   162  func (fw *Framework) NewReplyFromMessage(version protocol.ProtocolVersion, msg inter.Message) (rep protocol.Reply, err error) {
   163  	if msg.Type() != "reply" {
   164  		return nil, fmt.Errorf("cannot use '%s' message to construct a Reply", msg.Type())
   165  	}
   166  
   167  	if msg.Request() == nil {
   168  		return nil, fmt.Errorf("cannot create a Reply from Messages without Requests")
   169  	}
   170  
   171  	req, err := fw.NewRequestFromMessage(version, msg.Request())
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	rep, err = fw.NewReply(req)
   177  	rep.SetMessage(msg.Payload())
   178  
   179  	return rep, err
   180  }
   181  
   182  // NewReplyFromSecureReply creates a new Reply from the JSON payload of SecureReply, the version will match what is in the JSON payload
   183  func (fw *Framework) NewReplyFromSecureReply(sr protocol.SecureReply) (reply protocol.Reply, err error) {
   184  	switch sr.Version() {
   185  	case protocol.SecureReplyV1:
   186  		return v1.NewReplyFromSecureReply(sr)
   187  	case protocol.SecureReplyV2:
   188  		return v2.NewReplyFromSecureReply(sr)
   189  	default:
   190  		return nil, fmt.Errorf("do not know how to create a Reply version %s", sr.Version())
   191  	}
   192  }
   193  
   194  // NewRequestFromSecureRequest creates a new Request from a SecureRequest, the version will match what is in the JSON payload
   195  func (fw *Framework) NewRequestFromSecureRequest(sr protocol.SecureRequest) (request protocol.Request, err error) {
   196  	switch sr.Version() {
   197  	case protocol.SecureRequestV1:
   198  		return v1.NewRequestFromSecureRequest(sr)
   199  	case protocol.SecureRequestV2:
   200  		return v2.NewRequestFromSecureRequest(sr)
   201  	default:
   202  		return nil, fmt.Errorf("do not know how to create a Reply version %s", sr.Version())
   203  	}
   204  
   205  }
   206  
   207  // NewSecureReply creates a new SecureReply with the given Reply message as payload
   208  func (fw *Framework) NewSecureReply(reply protocol.Reply) (secure protocol.SecureReply, err error) {
   209  	switch reply.Version() {
   210  	case protocol.ReplyV1:
   211  		return v1.NewSecureReply(reply, fw.security)
   212  	case protocol.ReplyV2:
   213  		return v2.NewSecureReply(reply, fw.security)
   214  	default:
   215  		return nil, fmt.Errorf("do not know how to create a SecureReply based on a Reply version %s", reply.Version())
   216  	}
   217  
   218  }
   219  
   220  // NewSecureReplyFromTransport creates a new SecureReply from the JSON payload of TransportMessage, the version SecureReply will be the same as the TransportMessage
   221  func (fw *Framework) NewSecureReplyFromTransport(message protocol.TransportMessage, skipvalidate bool) (secure protocol.SecureReply, err error) {
   222  	switch message.Version() {
   223  	case protocol.TransportV1:
   224  		return v1.NewSecureReplyFromTransport(message, fw.security, skipvalidate)
   225  	case protocol.TransportV2:
   226  		return v2.NewSecureReplyFromTransport(message, fw.security, skipvalidate)
   227  	default:
   228  		return nil, fmt.Errorf("do not know how to create a SecureReply version %s", message.Version())
   229  	}
   230  }
   231  
   232  // NewSecureRequest creates a new SecureRequest with the given Request message as payload
   233  func (fw *Framework) NewSecureRequest(ctx context.Context, request protocol.Request) (secure protocol.SecureRequest, err error) {
   234  	switch request.Version() {
   235  	case protocol.RequestV1:
   236  		if fw.security.IsRemoteSigning() {
   237  			return v1.NewRemoteSignedSecureRequest(ctx, request, fw.security)
   238  		}
   239  
   240  		return v1.NewSecureRequest(request, fw.security)
   241  	case protocol.RequestV2:
   242  		if fw.security.IsRemoteSigning() {
   243  			return v2.NewRemoteSignedSecureRequest(ctx, request, fw.security)
   244  		}
   245  
   246  		return v2.NewSecureRequest(request, fw.security)
   247  	default:
   248  		return nil, fmt.Errorf("do not know how to create a SecureReply from a Request with version %s", request.Version())
   249  	}
   250  }
   251  
   252  // NewSecureRequestFromTransport creates a new SecureRequest from the JSON payload of TransportMessage, the version SecureRequest will be the same as the TransportMessage
   253  func (fw *Framework) NewSecureRequestFromTransport(message protocol.TransportMessage, skipvalidate bool) (secure protocol.SecureRequest, err error) {
   254  	switch message.Version() {
   255  	case protocol.TransportV1:
   256  		return v1.NewSecureRequestFromTransport(message, fw.security, skipvalidate)
   257  	case protocol.TransportV2:
   258  		return v2.NewSecureRequestFromTransport(message, fw.security, skipvalidate)
   259  	default:
   260  		return nil, fmt.Errorf("do not know how to create a SecureReply from a TransportMessage version %s", message.Version())
   261  	}
   262  }
   263  
   264  // NewTransportForSecureRequest creates a new TransportMessage with a SecureRequest as payload.  The Transport will be the same version as the SecureRequest
   265  func (fw *Framework) NewTransportForSecureRequest(request protocol.SecureRequest) (message protocol.TransportMessage, err error) {
   266  	switch request.Version() {
   267  	case protocol.SecureRequestV1:
   268  		message, err = v1.NewTransportMessage(fw.Config.Identity)
   269  	case protocol.SecureRequestV2:
   270  		message, err = v2.NewTransportMessage(fw.Config.Identity)
   271  	default:
   272  		return nil, fmt.Errorf("co not know how to create a Transport message for SecureRequest version %s", request.Version())
   273  	}
   274  
   275  	if err != nil {
   276  		fw.log.Errorf("Failed to create transport from secure request: %s", err)
   277  		return nil, err
   278  	}
   279  
   280  	err = message.SetRequestData(request)
   281  	if err != nil {
   282  		fw.log.Errorf("Failed to create transport from secure request: %s", err)
   283  		return nil, err
   284  	}
   285  
   286  	return message, nil
   287  }
   288  
   289  // NewTransportForSecureReply creates a new TransportMessage with a SecureReply as payload.  The Transport will be the same version as the SecureRequest
   290  func (fw *Framework) NewTransportForSecureReply(reply protocol.SecureReply) (message protocol.TransportMessage, err error) {
   291  	switch reply.Version() {
   292  	case protocol.SecureReplyV1:
   293  		message, err = v1.NewTransportMessage(fw.Config.Identity)
   294  	case protocol.SecureReplyV2:
   295  		message, err = v2.NewTransportMessage(fw.Config.Identity)
   296  	default:
   297  		return nil, fmt.Errorf("do not know how to create a Transport message for SecureRequest version %s", reply.Version())
   298  	}
   299  
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	message.SetReplyData(reply)
   305  
   306  	return message, nil
   307  }
   308  
   309  // NewReplyTransportForMessage creates a new Transport message based on a Message and the request its a reply to
   310  //
   311  // The new transport message will have the same version as the request its based on
   312  func (fw *Framework) NewReplyTransportForMessage(msg inter.Message, request protocol.Request) (protocol.TransportMessage, error) {
   313  	reply, err := fw.NewReply(request)
   314  	if err != nil {
   315  		return nil, fmt.Errorf("could not create Reply: %s", err)
   316  	}
   317  
   318  	reply.SetMessage(msg.Payload())
   319  
   320  	sreply, err := fw.NewSecureReply(reply)
   321  	if err != nil {
   322  		return nil, fmt.Errorf("could not create Secure Reply: %s", err)
   323  	}
   324  
   325  	transport, err := fw.NewTransportForSecureReply(sreply)
   326  	if err != nil {
   327  		return nil, fmt.Errorf("could not create Transport: %s", err)
   328  	}
   329  
   330  	protocol.CopyFederationData(request, transport)
   331  
   332  	return transport, nil
   333  }
   334  
   335  // NewRequestTransportForMessage creates a new versioned Transport message based on a Message
   336  func (fw *Framework) NewRequestTransportForMessage(ctx context.Context, msg inter.Message, version protocol.ProtocolVersion) (protocol.TransportMessage, error) {
   337  	req, err := fw.NewRequestFromMessage(version, msg)
   338  	if err != nil {
   339  		return nil, fmt.Errorf("could not create Request: %s", err)
   340  	}
   341  
   342  	sr, err := fw.NewSecureRequest(ctx, req)
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  
   347  	transport, err := fw.NewTransportForSecureRequest(sr)
   348  	if err != nil {
   349  		return nil, fmt.Errorf("could not create Transport: %s", err)
   350  	}
   351  
   352  	return transport, nil
   353  }
   354  
   355  // NewTransportMessage creates a new TransportMessage complying with a specific protocol version like protocol.TransportV1
   356  func (fw *Framework) NewTransportMessage(version protocol.ProtocolVersion) (message protocol.TransportMessage, err error) {
   357  	switch version {
   358  	case protocol.TransportV1:
   359  		return v1.NewTransportMessage(fw.Config.Identity)
   360  	case protocol.TransportV2:
   361  		return v2.NewTransportMessage(fw.Config.Identity)
   362  	default:
   363  		return nil, fmt.Errorf("so not know how to create a Transport version '%s'", version)
   364  	}
   365  }
   366  
   367  // NewTransportFromJSON creates a new TransportMessage from a JSON payload.  The version will match what is in the payload
   368  func (fw *Framework) NewTransportFromJSON(data []byte) (message protocol.TransportMessage, err error) {
   369  	switch protocol.VersionFromJSON(data) {
   370  	case protocol.TransportV1:
   371  		return v1.NewTransportFromJSON(data)
   372  	case protocol.TransportV2:
   373  		return v2.NewTransportFromJSON(data)
   374  	default:
   375  		return nil, fmt.Errorf("do not know how to create a TransportMessage from an expected JSON format message with content: %s", data)
   376  	}
   377  }