From 61bca80e0950c933665f46ab7a9f44808528b1cf Mon Sep 17 00:00:00 2001 From: Parham Alvani Date: Sat, 11 Nov 2023 23:06:36 +0000 Subject: [PATCH] fix: correct tests and their usage --- internal/authenticator/authenticator_test.go | 459 +++++++++++++++++++ 1 file changed, 459 insertions(+) diff --git a/internal/authenticator/authenticator_test.go b/internal/authenticator/authenticator_test.go index 7d777c7..6dd8345 100644 --- a/internal/authenticator/authenticator_test.go +++ b/internal/authenticator/authenticator_test.go @@ -1,5 +1,23 @@ package authenticator_test +import ( + "crypto/rsa" + "errors" + "fmt" + "os" + "testing" + "time" + + "github.com/golang-jwt/jwt/v5" + "github.com/snapp-incubator/soteria/internal/authenticator" + "github.com/snapp-incubator/soteria/internal/config" + "github.com/snapp-incubator/soteria/internal/topics" + "github.com/snapp-incubator/soteria/pkg/acl" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "go.uber.org/zap" +) + const ( // nolint: gosec, lll invalidToken = "ey1JhbGciOiJSUzI1NiIsInR5cCI56kpXVCJ9.eyJzdWIiOiJCRzdScDFkcnpWRE5RcjYiLCJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZSwiaXNzIjowLCJpYXQiOjE1MTYyMzkwMjJ9.1cYXFEhcewOYFjGJYhB8dsaFO9uKEXwlM8954rkt4Tsu0lWMITbRf_hHh1l9QD4MFqD-0LwRPUYaiaemy0OClMu00G2sujLCWaquYDEP37iIt8RoOQAh8Jb5vT8LX5C3PEKvbW_i98u8HHJoFUR9CXJmzrKi48sAcOYvXVYamN0S9KoY38H-Ze37Mdu3o6B58i73krk7QHecsc2_PkCJisvUVAzb0tiInIalBc8-zI3QZSxwNLr_hjlBg1sUxTUvH5SCcRR7hxI8TxJzkOHqAHWDRO84NC_DSAoO2p04vrHpqglN9XPJ8RC2YWpfefvD2ttH554RJWu_0RlR2kAYvQ" @@ -40,3 +58,444 @@ const ( invalidDriverCallOutgoingTopic = "snapp/driver/0596923be632d673560af9adadd2f78a/call/receive" invalidPassengerCallOutgoingTopic = "snapp/passenger/0596923be632d673560af9adadd2f78a/call/receive" ) + +var ( + ErrPrivateKeyNotFound = errors.New("invalid user, private key not found") + ErrPublicKeyNotFound = errors.New("invalid user, public key not found") +) + +type AuthenticatorTestSuite struct { + suite.Suite + + Tokens struct { + Passenger string + Driver string + } + + PublicKeys struct { + Passenger *rsa.PublicKey + Driver *rsa.PublicKey + } + + Authenticator authenticator.Authenticator +} + +func (suite *AuthenticatorTestSuite) SetupSuite() { + require := suite.Require() + + driverToken := suite.getSampleToken(topics.DriverIss) + + suite.Tokens.Driver = driverToken + + passengerToken := suite.getSampleToken(topics.PassengerIss) + + suite.Tokens.Passenger = passengerToken + + pkey0, err := suite.getPublicKey(topics.DriverIss) + require.NoError(err) + + suite.PublicKeys.Driver = pkey0 + + pkey1, err := suite.getPublicKey(topics.PassengerIss) + require.NoError(err) + + suite.PublicKeys.Passenger = pkey1 +} + +func (suite *AuthenticatorTestSuite) TestAuth() { + require := suite.Require() + + suite.Run("testing driver token auth", func() { + require.NoError(suite.Authenticator.Auth(suite.Tokens.Driver)) + }) + + suite.Run("testing passenger token auth", func() { + require.NoError(suite.Authenticator.Auth(suite.Tokens.Passenger)) + }) + + suite.Run("testing invalid token auth", func() { + require.Error(suite.Authenticator.Auth(invalidToken)) + }) +} + +// nolint: dupl +func (suite *AuthenticatorTestSuite) TestACL_Basics() { + require := suite.Require() + + suite.Run("testing acl with invalid access type", func() { + ok, err := suite.Authenticator.ACL("invalid-access", suite.Tokens.Passenger, "test") + require.Error(err) + require.False(ok) + require.ErrorIs(err, authenticator.ErrInvalidAccessType) + }) + + suite.Run("testing acl with invalid token", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, invalidToken, validDriverCabEventTopic) + require.False(ok) + require.Error(err) + require.ErrorIs(err, jwt.ErrTokenMalformed) + }) + + suite.Run("testing acl with valid inputs", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, suite.Tokens.Passenger, validPassengerCabEventTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing acl with invalid topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, suite.Tokens.Passenger, invalidPassengerCabEventTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing acl with invalid access type", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, suite.Tokens.Passenger, validPassengerCabEventTopic) + require.Error(err) + require.False(ok) + }) +} + +// nolint: funlen +func (suite *AuthenticatorTestSuite) TestACL_Passenger() { + require := suite.Require() + token := suite.Tokens.Passenger + + suite.Run("testing passenger subscribe on valid superapp event topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, validPassengerSuperappEventTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing passenger subscribe on invalid superapp event topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidPassengerSuperappEventTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing passenger subscribe on valid shared location topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, validPassengerSharedTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing passenger subscribe on invalid shared location topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidPassengerSharedTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing passenger subscribe on valid chat topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, validPassengerChatTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing passenger subscribe on invalid chat topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidPassengerChatTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing passenger subscribe on valid entry call topic", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, token, validPassengerCallEntryTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing passenger subscribe on invalid call entry topic", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, token, invalidPassengerCallEntryTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing passenger subscribe on valid outgoing call topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, validPassengerCallOutgoingTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing passenger subscribe on valid outgoing call node topic", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, token, validPassengerNodeCallEntryTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing passenger subscribe on invalid call outgoing topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidPassengerCallOutgoingTopic) + require.Error(err) + require.False(ok) + }) +} + +// nolint: funlen +func (suite *AuthenticatorTestSuite) TestACL_Driver() { + require := suite.Require() + token := suite.Tokens.Driver + + suite.Run("testing driver publish on its location topic", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, token, validDriverLocationTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing driver publish on invalid location topic", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, token, invalidDriverLocationTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing driver subscribe on invalid cab event topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidDriverCabEventTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing driver subscribe on valid superapp event topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, validDriverSuperappEventTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing driver subscribe on invalid superapp event topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidDriverSuperappEventTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing driver subscribe on valid shared location topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, validDriverSharedTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing driver subscribe on invalid shared location topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidDriverSharedTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing driver subscribe on valid chat topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, validDriverChatTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing driver subscribe on invalid chat topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidDriverChatTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing driver subscribe on valid call entry topic", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, token, validDriverCallEntryTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing driver subscribe on invalid call entry topic", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, token, invalidDriverCallEntryTopic) + require.Error(err) + require.False(ok) + }) + + suite.Run("testing driver subscribe on valid call outgoing topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, validDriverCallOutgoingTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing driver subscribe on valid call outgoing node topic", func() { + ok, err := suite.Authenticator.ACL(acl.Pub, token, validDriverNodeCallEntryTopic) + require.NoError(err) + require.True(ok) + }) + + suite.Run("testing driver subscribe on invalid call outgoing topic", func() { + ok, err := suite.Authenticator.ACL(acl.Sub, token, invalidDriverCallOutgoingTopic) + require.Error(err) + require.False(ok) + }) +} + +func TestManualAuthenticator_ValidateTopicBySender(t *testing.T) { + t.Parallel() + + cfg := config.SnappVendor() + + hid, err := topics.NewHashIDManager(cfg.HashIDMap) + require.NoError(t, err) + + // nolint: exhaustruct + authenticator := authenticator.ManualAuthenticator{ + AllowedAccessTypes: []acl.AccessType{acl.Pub, acl.Sub}, + Company: "snapp", + TopicManager: topics.NewTopicManager(cfg.Topics, hid, "snapp", cfg.IssEntityMap, cfg.IssPeerMap, zap.NewNop()), + } + + t.Run("testing valid driver cab event", func(t *testing.T) { + t.Parallel() + topicTemplate := authenticator.TopicManager.ParseTopic(validDriverCabEventTopic, topics.DriverIss, "DXKgaNQa7N5Y7bo") + require.NotNil(t, topicTemplate) + }) +} + +// nolint: funlen +func TestManualAuthenticator_validateAccessType(t *testing.T) { + t.Parallel() + + type fields struct { + AllowedAccessTypes []acl.AccessType + } + + type args struct { + accessType acl.AccessType + } + + tests := []struct { + name string + fields fields + args args + want bool + }{ + { + name: "#1 testing with no allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{}}, + args: args{accessType: acl.Sub}, + want: false, + }, + { + name: "#2 testing with no allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{}}, + args: args{accessType: acl.Pub}, + want: false, + }, + { + name: "#3 testing with no allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{}}, + args: args{accessType: acl.PubSub}, + want: false, + }, + { + name: "#4 testing with one allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{acl.Pub}}, + args: args{accessType: acl.Pub}, + want: true, + }, + { + name: "#5 testing with one allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{acl.Pub}}, + args: args{accessType: acl.Sub}, + want: false, + }, + { + name: "#6 testing with two allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{acl.Pub, acl.Sub}}, + args: args{accessType: acl.Sub}, + want: true, + }, + { + name: "#7 testing with two allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{acl.Pub, acl.Sub}}, + args: args{accessType: acl.Pub}, + want: true, + }, + { + name: "#8 testing with two allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{acl.Pub, acl.Sub}}, + args: args{accessType: acl.PubSub}, + want: false, + }, + { + name: "#9 testing with three allowed access type", + fields: fields{AllowedAccessTypes: []acl.AccessType{acl.Pub, acl.Sub, acl.PubSub}}, + args: args{accessType: acl.PubSub}, + want: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + // nolint: exhaustruct + a := authenticator.ManualAuthenticator{ + AllowedAccessTypes: tt.fields.AllowedAccessTypes, + } + if got := a.ValidateAccessType(tt.args.accessType); got != tt.want { + t.Errorf("validateAccessType() = %v, want %v", got, tt.want) + } + }) + } +} + +func (suite *AuthenticatorTestSuite) getPublicKey(u string) (*rsa.PublicKey, error) { + var fileName string + + switch u { + case topics.PassengerIss: + fileName = "../../test/snapp-1.pem" + case topics.DriverIss: + fileName = "../../test/snapp-0.pem" + default: + return nil, ErrPublicKeyNotFound + } + + pem, err := os.ReadFile(fileName) + if err != nil { + return nil, fmt.Errorf("reading public key failed %w", err) + } + + publicKey, err := jwt.ParseRSAPublicKeyFromPEM(pem) + if err != nil { + return nil, fmt.Errorf("paring public key failed %w", err) + } + + return publicKey, nil +} + +func (suite *AuthenticatorTestSuite) getPrivateKey(u string) (*rsa.PrivateKey, error) { + var fileName string + + switch u { + case topics.DriverIss: + fileName = "../../test/snapp-0.private.pem" + case topics.PassengerIss: + fileName = "../../test/snapp-1.private.pem" + default: + return nil, ErrPrivateKeyNotFound + } + + pem, err := os.ReadFile(fileName) + if err != nil { + return nil, fmt.Errorf("reading private key failed %w", err) + } + + privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(pem) + if err != nil { + return nil, fmt.Errorf("paring private key failed %w", err) + } + + return privateKey, nil +} + +func (suite *AuthenticatorTestSuite) getSampleToken(issuer string) string { + key, err := suite.getPrivateKey(issuer) + suite.Require().NoError(err) + + exp := time.Now().Add(time.Hour * 24 * 365 * 10) + sub := "DXKgaNQa7N5Y7bo" + + // nolint: exhaustruct + claims := jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(exp), + Issuer: issuer, + Subject: sub, + } + token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) + + tokenString, err := token.SignedString(key) + suite.Require().NoError(err) + + return tokenString +}