github.com/koko1123/flow-go-1@v0.29.6/network/validator/authorized_sender_validator_test.go (about)

     1  package validator
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/libp2p/go-libp2p/core/peer"
     8  	"github.com/rs/zerolog"
     9  	"github.com/stretchr/testify/require"
    10  	"github.com/stretchr/testify/suite"
    11  
    12  	"github.com/koko1123/flow-go-1/model/flow"
    13  	"github.com/koko1123/flow-go-1/model/messages"
    14  	"github.com/koko1123/flow-go-1/module/metrics"
    15  	"github.com/koko1123/flow-go-1/network/channels"
    16  	"github.com/koko1123/flow-go-1/network/message"
    17  	"github.com/koko1123/flow-go-1/network/p2p"
    18  	"github.com/koko1123/flow-go-1/network/slashing"
    19  	"github.com/koko1123/flow-go-1/utils/unittest"
    20  )
    21  
    22  type TestCase struct {
    23  	Identity    *flow.Identity
    24  	GetIdentity func(pid peer.ID) (*flow.Identity, bool)
    25  	Channel     channels.Channel
    26  	Message     interface{}
    27  	MessageStr  string
    28  	Protocols   message.Protocols
    29  }
    30  
    31  func TestIsAuthorizedSender(t *testing.T) {
    32  	suite.Run(t, new(TestAuthorizedSenderValidatorSuite))
    33  }
    34  
    35  type TestAuthorizedSenderValidatorSuite struct {
    36  	suite.Suite
    37  	authorizedSenderTestCases             []TestCase
    38  	unauthorizedSenderTestCases           []TestCase
    39  	unauthorizedMessageOnChannelTestCases []TestCase
    40  	unauthorizedUnicastOnChannel          []TestCase
    41  	authorizedUnicastOnChannel            []TestCase
    42  	log                                   zerolog.Logger
    43  	slashingViolationsConsumer            slashing.ViolationsConsumer
    44  	allMsgConfigs                         []message.MsgAuthConfig
    45  }
    46  
    47  func (s *TestAuthorizedSenderValidatorSuite) SetupTest() {
    48  	s.allMsgConfigs = message.GetAllMessageAuthConfigs()
    49  	s.initializeAuthorizationTestCases()
    50  	s.initializeInvalidMessageOnChannelTestCases()
    51  	s.initializeUnicastOnChannelTestCases()
    52  	s.log = unittest.Logger()
    53  	s.slashingViolationsConsumer = slashing.NewSlashingViolationsConsumer(s.log, metrics.NewNoopCollector())
    54  }
    55  
    56  // TestValidatorCallback_AuthorizedSender checks that AuthorizedSenderValidator.Validate does not return false positive
    57  // validation errors for all possible valid combinations (authorized sender role, message type).
    58  func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_AuthorizedSender() {
    59  	for _, c := range s.authorizedSenderTestCases {
    60  		str := fmt.Sprintf("role (%s) should be authorized to send message type (%s) on channel (%s)", c.Identity.Role, c.MessageStr, c.Channel)
    61  		s.Run(str, func() {
    62  			authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity)
    63  
    64  			pid, err := unittest.PeerIDFromFlowID(c.Identity)
    65  			require.NoError(s.T(), err)
    66  
    67  			// ensure according to the message auth config, if a message is authorized to be sent via unicast it
    68  			// is accepted or rejected.
    69  			msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolUnicast)
    70  			if c.Protocols.Contains(message.ProtocolUnicast) {
    71  				require.NoError(s.T(), err)
    72  				require.Equal(s.T(), c.MessageStr, msgType)
    73  			} else {
    74  				require.ErrorIs(s.T(), err, message.ErrUnauthorizedUnicastOnChannel)
    75  				require.Equal(s.T(), c.MessageStr, msgType)
    76  			}
    77  
    78  			validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel)
    79  			pubsubResult := validatePubsub(pid, c.Message)
    80  			if !c.Protocols.Contains(message.ProtocolPublish) {
    81  				require.Equal(s.T(), p2p.ValidationReject, pubsubResult)
    82  			} else {
    83  				require.Equal(s.T(), p2p.ValidationAccept, pubsubResult)
    84  			}
    85  		})
    86  	}
    87  }
    88  
    89  // TestValidatorCallback_UnAuthorizedSender checks that AuthorizedSenderValidator.Validate return's p2p.ValidationReject
    90  // validation error for all possible invalid combinations (unauthorized sender role, message type).
    91  func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedSender() {
    92  	for _, c := range s.unauthorizedSenderTestCases {
    93  		str := fmt.Sprintf("role (%s) should not be authorized to send message type (%s) on channel (%s)", c.Identity.Role, c.MessageStr, c.Channel)
    94  		s.Run(str, func() {
    95  			pid, err := unittest.PeerIDFromFlowID(c.Identity)
    96  			require.NoError(s.T(), err)
    97  
    98  			authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity)
    99  
   100  			validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel)
   101  			pubsubResult := validatePubsub(pid, c.Message)
   102  			require.Equal(s.T(), p2p.ValidationReject, pubsubResult)
   103  		})
   104  	}
   105  }
   106  
   107  // TestValidatorCallback_AuthorizedUnicastOnChannel checks that AuthorizedSenderValidator.Validate does not return an error
   108  // for messages sent via unicast that are authorized to be sent via unicast.
   109  func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_AuthorizedUnicastOnChannel() {
   110  	for _, c := range s.authorizedUnicastOnChannel {
   111  		str := fmt.Sprintf("role (%s) should be authorized to send message type (%s) on channel (%s) via unicast", c.Identity.Role, c.MessageStr, c.Channel)
   112  		s.Run(str, func() {
   113  			pid, err := unittest.PeerIDFromFlowID(c.Identity)
   114  			require.NoError(s.T(), err)
   115  
   116  			authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity)
   117  
   118  			msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolUnicast)
   119  			require.NoError(s.T(), err)
   120  			require.Equal(s.T(), c.MessageStr, msgType)
   121  		})
   122  	}
   123  }
   124  
   125  // TestValidatorCallback_UnAuthorizedUnicastOnChannel checks that AuthorizedSenderValidator.Validate returns message.ErrUnauthorizedUnicastOnChannel
   126  // when a message not authorized to be sent via unicast is sent via unicast.
   127  func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedUnicastOnChannel() {
   128  	for _, c := range s.unauthorizedUnicastOnChannel {
   129  		str := fmt.Sprintf("role (%s) should not be authorized to send message type (%s) on channel (%s) via unicast", c.Identity.Role, c.MessageStr, c.Channel)
   130  		s.Run(str, func() {
   131  			pid, err := unittest.PeerIDFromFlowID(c.Identity)
   132  			require.NoError(s.T(), err)
   133  
   134  			authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity)
   135  
   136  			msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolUnicast)
   137  			require.ErrorIs(s.T(), err, message.ErrUnauthorizedUnicastOnChannel)
   138  			require.Equal(s.T(), c.MessageStr, msgType)
   139  		})
   140  	}
   141  }
   142  
   143  // TestValidatorCallback_UnAuthorizedMessageOnChannel checks that for each invalid combination of message type and channel
   144  // AuthorizedSenderValidator.Validate returns the appropriate error message.ErrUnauthorizedMessageOnChannel.
   145  func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedMessageOnChannel() {
   146  	for _, c := range s.unauthorizedMessageOnChannelTestCases {
   147  		str := fmt.Sprintf("message type (%s) should not be authorized to be sent on channel (%s)", c.MessageStr, c.Channel)
   148  		s.Run(str, func() {
   149  			pid, err := unittest.PeerIDFromFlowID(c.Identity)
   150  			require.NoError(s.T(), err)
   151  
   152  			authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity)
   153  
   154  			msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolUnicast)
   155  			require.ErrorIs(s.T(), err, message.ErrUnauthorizedMessageOnChannel)
   156  			require.Equal(s.T(), c.MessageStr, msgType)
   157  
   158  			validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel)
   159  			pubsubResult := validatePubsub(pid, c.Message)
   160  			require.Equal(s.T(), p2p.ValidationReject, pubsubResult)
   161  		})
   162  	}
   163  }
   164  
   165  // TestValidatorCallback_ClusterPrefixedChannels checks that AuthorizedSenderValidator.Validate correctly
   166  // handles cluster prefixed channels during validation.
   167  func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ClusterPrefixedChannels() {
   168  	identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(flow.RoleCollection))
   169  	clusterID := flow.Localnet
   170  
   171  	getIdentityFunc := s.getIdentity(identity)
   172  	pid, err := unittest.PeerIDFromFlowID(identity)
   173  	require.NoError(s.T(), err)
   174  
   175  	authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, getIdentityFunc)
   176  
   177  	// ensure ClusterBlockProposal not allowed to be sent on channel via unicast
   178  	msgType, err := authorizedSenderValidator.Validate(pid, &messages.ClusterBlockProposal{}, channels.ConsensusCluster(clusterID), message.ProtocolUnicast)
   179  	require.ErrorIs(s.T(), err, message.ErrUnauthorizedUnicastOnChannel)
   180  	require.Equal(s.T(), message.ClusterBlockProposal, msgType)
   181  
   182  	// ensure ClusterBlockProposal is allowed to be sent via pubsub by authorized sender
   183  	validateCollConsensusPubsub := authorizedSenderValidator.PubSubMessageValidator(channels.ConsensusCluster(clusterID))
   184  	pubsubResult := validateCollConsensusPubsub(pid, &messages.ClusterBlockProposal{})
   185  	require.Equal(s.T(), p2p.ValidationAccept, pubsubResult)
   186  
   187  	// validate collection sync cluster SyncRequest is not allowed to be sent on channel via unicast
   188  	msgType, err = authorizedSenderValidator.Validate(pid, &messages.SyncRequest{}, channels.SyncCluster(clusterID), message.ProtocolUnicast)
   189  	require.ErrorIs(s.T(), err, message.ErrUnauthorizedUnicastOnChannel)
   190  	require.Equal(s.T(), message.SyncRequest, msgType)
   191  
   192  	// ensure SyncRequest is allowed to be sent via pubsub by authorized sender
   193  	validateSyncClusterPubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCluster(clusterID))
   194  	pubsubResult = validateSyncClusterPubsub(pid, &messages.SyncRequest{})
   195  	require.Equal(s.T(), p2p.ValidationAccept, pubsubResult)
   196  }
   197  
   198  // TestValidatorCallback_ValidationFailure checks that AuthorizedSenderValidator.Validate returns the expected validation error.
   199  func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ValidationFailure() {
   200  	s.Run("sender is ejected", func() {
   201  		identity, _ := unittest.IdentityWithNetworkingKeyFixture()
   202  		identity.Ejected = true
   203  		getIdentityFunc := s.getIdentity(identity)
   204  		pid, err := unittest.PeerIDFromFlowID(identity)
   205  		require.NoError(s.T(), err)
   206  
   207  		authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, getIdentityFunc)
   208  
   209  		msgType, err := authorizedSenderValidator.Validate(pid, &messages.SyncRequest{}, channels.SyncCommittee, message.ProtocolUnicast)
   210  		require.ErrorIs(s.T(), err, ErrSenderEjected)
   211  		require.Equal(s.T(), "", msgType)
   212  
   213  		validatePubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCommittee)
   214  		pubsubResult := validatePubsub(pid, &messages.SyncRequest{})
   215  		require.Equal(s.T(), p2p.ValidationReject, pubsubResult)
   216  	})
   217  
   218  	s.Run("unknown message type", func() {
   219  		identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(flow.RoleConsensus))
   220  		type msg struct {
   221  			*messages.BlockProposal
   222  		}
   223  
   224  		// *validator.msg is not a known message type, but embeds *messages.BlockProposal which is
   225  		m := &msg{unittest.ProposalFixture()}
   226  
   227  		getIdentityFunc := s.getIdentity(identity)
   228  		pid, err := unittest.PeerIDFromFlowID(identity)
   229  		require.NoError(s.T(), err)
   230  
   231  		authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, getIdentityFunc)
   232  		validatePubsub := authorizedSenderValidator.PubSubMessageValidator(channels.ConsensusCommittee)
   233  
   234  		// unknown message types are rejected
   235  		msgType, err := authorizedSenderValidator.Validate(pid, m, channels.ConsensusCommittee, message.ProtocolUnicast)
   236  		require.True(s.T(), message.IsUnknownMsgTypeErr(err))
   237  		require.Equal(s.T(), "", msgType)
   238  		pubsubResult := validatePubsub(pid, m)
   239  		require.Equal(s.T(), p2p.ValidationReject, pubsubResult)
   240  
   241  		// nil messages are rejected
   242  		msgType, err = authorizedSenderValidator.Validate(pid, nil, channels.ConsensusCommittee, message.ProtocolUnicast)
   243  		require.True(s.T(), message.IsUnknownMsgTypeErr(err))
   244  		require.Equal(s.T(), "", msgType)
   245  		pubsubResult = validatePubsub(pid, nil)
   246  		require.Equal(s.T(), p2p.ValidationReject, pubsubResult)
   247  	})
   248  
   249  	s.Run("sender is not staked getIdentityFunc does not return identity ", func() {
   250  		identity, _ := unittest.IdentityWithNetworkingKeyFixture()
   251  
   252  		// getIdentityFunc simulates unstaked node not found in participant list
   253  		getIdentityFunc := func(id peer.ID) (*flow.Identity, bool) { return nil, false }
   254  
   255  		pid, err := unittest.PeerIDFromFlowID(identity)
   256  		require.NoError(s.T(), err)
   257  
   258  		authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, getIdentityFunc)
   259  
   260  		msgType, err := authorizedSenderValidator.Validate(pid, &messages.SyncRequest{}, channels.SyncCommittee, message.ProtocolUnicast)
   261  		require.ErrorIs(s.T(), err, ErrIdentityUnverified)
   262  		require.Equal(s.T(), "", msgType)
   263  
   264  		validatePubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCommittee)
   265  		pubsubResult := validatePubsub(pid, &messages.SyncRequest{})
   266  		require.Equal(s.T(), p2p.ValidationReject, pubsubResult)
   267  	})
   268  }
   269  
   270  // TestValidatorCallback_ValidationFailure checks that AuthorizedSenderValidator returns the expected validation error when a unicast-only message is published.
   271  func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnauthorizedPublishOnChannel() {
   272  	for _, c := range s.authorizedUnicastOnChannel {
   273  		str := fmt.Sprintf("message type (%s) is not authorized to be sent via libp2p publish", c.MessageStr)
   274  		s.Run(str, func() {
   275  			pid, err := unittest.PeerIDFromFlowID(c.Identity)
   276  			require.NoError(s.T(), err)
   277  
   278  			authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity)
   279  
   280  			msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolPublish)
   281  			if c.MessageStr == message.TestMessage {
   282  				require.NoError(s.T(), err)
   283  			} else {
   284  				require.ErrorIs(s.T(), err, message.ErrUnauthorizedPublishOnChannel)
   285  				require.Equal(s.T(), c.MessageStr, msgType)
   286  			}
   287  		})
   288  	}
   289  }
   290  
   291  // initializeAuthorizationTestCases initializes happy and sad path test cases for checking authorized and unauthorized role message combinations.
   292  func (s *TestAuthorizedSenderValidatorSuite) initializeAuthorizationTestCases() {
   293  	for _, c := range s.allMsgConfigs {
   294  		for channel, channelAuthConfig := range c.Config {
   295  			for _, role := range flow.Roles() {
   296  				identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(role))
   297  				tc := TestCase{
   298  					Identity:    identity,
   299  					GetIdentity: s.getIdentity(identity),
   300  					Channel:     channel,
   301  					Message:     c.Type(),
   302  					MessageStr:  c.Name,
   303  					Protocols:   channelAuthConfig.AllowedProtocols,
   304  				}
   305  				if channelAuthConfig.AuthorizedRoles.Contains(role) {
   306  					// test cases for validation success happy path
   307  					s.authorizedSenderTestCases = append(s.authorizedSenderTestCases, tc)
   308  				} else {
   309  					// test cases for validation unsuccessful sad path
   310  					s.unauthorizedSenderTestCases = append(s.unauthorizedSenderTestCases, tc)
   311  				}
   312  			}
   313  		}
   314  	}
   315  }
   316  
   317  // initializeInvalidMessageOnChannelTestCases initializes test cases for all possible combinations of invalid message types on channel.
   318  // NOTE: the role in the test case does not matter since ErrUnauthorizedMessageOnChannel will be returned before the role is checked.
   319  func (s *TestAuthorizedSenderValidatorSuite) initializeInvalidMessageOnChannelTestCases() {
   320  	// iterate all channels
   321  	for _, c := range s.allMsgConfigs {
   322  		for channel, channelAuthConfig := range c.Config {
   323  			identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(channelAuthConfig.AuthorizedRoles[0]))
   324  
   325  			// iterate all message types
   326  			for _, config := range s.allMsgConfigs {
   327  				// include test if message type is not authorized on channel
   328  				_, ok := config.Config[channel]
   329  				if config.Name != c.Name && !ok {
   330  					tc := TestCase{
   331  						Identity:    identity,
   332  						GetIdentity: s.getIdentity(identity),
   333  						Channel:     channel,
   334  						Message:     config.Type(),
   335  						MessageStr:  config.Name,
   336  						Protocols:   channelAuthConfig.AllowedProtocols,
   337  					}
   338  					s.unauthorizedMessageOnChannelTestCases = append(s.unauthorizedMessageOnChannelTestCases, tc)
   339  				}
   340  			}
   341  		}
   342  	}
   343  }
   344  
   345  // initializeUnicastOnChannelTestCases initializes happy and sad path test cases for unicast on channel message combinations.
   346  func (s *TestAuthorizedSenderValidatorSuite) initializeUnicastOnChannelTestCases() {
   347  	for _, c := range s.allMsgConfigs {
   348  		for channel, channelAuthConfig := range c.Config {
   349  			identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(channelAuthConfig.AuthorizedRoles[0]))
   350  			tc := TestCase{
   351  				Identity:    identity,
   352  				GetIdentity: s.getIdentity(identity),
   353  				Channel:     channel,
   354  				Message:     c.Type(),
   355  				MessageStr:  c.Name,
   356  				Protocols:   channelAuthConfig.AllowedProtocols,
   357  			}
   358  			if channelAuthConfig.AllowedProtocols.Contains(message.ProtocolUnicast) {
   359  				s.authorizedUnicastOnChannel = append(s.authorizedUnicastOnChannel, tc)
   360  			} else {
   361  				s.unauthorizedUnicastOnChannel = append(s.unauthorizedUnicastOnChannel, tc)
   362  			}
   363  		}
   364  	}
   365  }
   366  
   367  // getIdentity returns a callback that simply returns the provided identity.
   368  func (s *TestAuthorizedSenderValidatorSuite) getIdentity(id *flow.Identity) func(pid peer.ID) (*flow.Identity, bool) {
   369  	return func(pid peer.ID) (*flow.Identity, bool) {
   370  		return id, true
   371  	}
   372  }