From bc178ce2b87ce467f7f4fadb635c7d847f9aae13 Mon Sep 17 00:00:00 2001 From: Scott Leggett Date: Fri, 13 Dec 2024 10:31:11 +0800 Subject: [PATCH] chore: add tests for sshserver authhandler --- internal/sshserver/authhandler_test.go | 77 +++++++++++++++++++ internal/sshserver/helper_test.go | 20 ++--- ...er_mock_test.go => sshserver_mock_test.go} | 44 ++++++++++- 3 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 internal/sshserver/authhandler_test.go rename internal/sshserver/{sessionhandler_mock_test.go => sshserver_mock_test.go} (67%) diff --git a/internal/sshserver/authhandler_test.go b/internal/sshserver/authhandler_test.go new file mode 100644 index 0000000..b77815c --- /dev/null +++ b/internal/sshserver/authhandler_test.go @@ -0,0 +1,77 @@ +package sshserver_test + +import ( + "crypto/ed25519" + "log/slog" + "os" + "testing" + + "github.com/alecthomas/assert/v2" + "github.com/gliderlabs/ssh" + "github.com/uselagoon/ssh-portal/internal/sshserver" + gomock "go.uber.org/mock/gomock" + gossh "golang.org/x/crypto/ssh" +) + +func TestPubKeyHandler(t *testing.T) { + log := slog.New(slog.NewJSONHandler(os.Stderr, nil)) + var testCases = map[string]struct { + keyCanAccessEnv bool + }{ + "access granted": { + keyCanAccessEnv: true, + }, + "access denied": { + keyCanAccessEnv: false, + }, + } + for name, tc := range testCases { + t.Run(name, func(tt *testing.T) { + ctrl := gomock.NewController(tt) + k8sService := NewMockK8SAPIService(ctrl) + natsService := NewMockNATSService(ctrl) + sshContext := NewMockContext(ctrl) + // configure callback + callback := sshserver.PubKeyHandler( + log, + natsService, + k8sService, + ) + // configure mocks + namespaceName := "my-project-master" + sessionID := "abc123" + projectID := 1 + environmentID := 2 + sshContext.EXPECT().User().Return(namespaceName).AnyTimes() + sshContext.EXPECT().SessionID().Return(sessionID).AnyTimes() + k8sService.EXPECT().NamespaceDetails(sshContext, namespaceName). + Return(environmentID, projectID, "master", "my-project", nil) + // set up public key mock + publicKey, _, err := ed25519.GenerateKey(nil) + if err != nil { + tt.Fatal(err) + } + sshPublicKey, err := gossh.NewPublicKey(publicKey) + if err != nil { + tt.Fatal(err) + } + fingerprint := gossh.FingerprintSHA256(sshPublicKey) + natsService.EXPECT().KeyCanAccessEnvironment( + sessionID, + fingerprint, + namespaceName, + projectID, + environmentID, + ).Return(tc.keyCanAccessEnv, nil) + // set up permissions mock + sshPermissions := ssh.Permissions{Permissions: &gossh.Permissions{}} + // permissions are not touched if access is denied + if tc.keyCanAccessEnv { + sshContext.EXPECT().Permissions().Return(&sshPermissions) + } + // execute callback + assert.Equal( + tt, tc.keyCanAccessEnv, callback(sshContext, sshPublicKey), name) + }) + } +} diff --git a/internal/sshserver/helper_test.go b/internal/sshserver/helper_test.go index b591699..c762ea0 100644 --- a/internal/sshserver/helper_test.go +++ b/internal/sshserver/helper_test.go @@ -1,17 +1,13 @@ package sshserver -// ParseConnectionParams exposes the private parseConnectionParams for testing -// only. -var ParseConnectionParams = parseConnectionParams - -// ParseLogsArg exposes the private parseLogsArg for testing only. -var ParseLogsArg = parseLogsArg - -// SessionHandler exposes the private sessionHandler for testing only. -var SessionHandler = sessionHandler - -// PermissionsMarshal exposes the private permissionsMarshal for testing only. -var PermissionsMarshal = permissionsMarshal +// These variables are exposed for testing only. +var ( + ParseConnectionParams = parseConnectionParams + ParseLogsArg = parseLogsArg + PermissionsMarshal = permissionsMarshal + SessionHandler = sessionHandler + PubKeyHandler = pubKeyHandler +) // Exposes the private ctxKey constants for testing only. const ( diff --git a/internal/sshserver/sessionhandler_mock_test.go b/internal/sshserver/sshserver_mock_test.go similarity index 67% rename from internal/sshserver/sessionhandler_mock_test.go rename to internal/sshserver/sshserver_mock_test.go index a51b99f..9a83eac 100644 --- a/internal/sshserver/sessionhandler_mock_test.go +++ b/internal/sshserver/sshserver_mock_test.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: sessionhandler.go +// Source: github.com/uselagoon/ssh-portal/internal/sshserver (interfaces: K8SAPIService,NATSService) // // Generated by this command: // -// mockgen -source=sessionhandler.go -package=sshserver_test -destination=sessionhandler_mock_test.go -write_generate_directive +// mockgen -package=sshserver_test -destination=sshserver_mock_test.go -write_generate_directive . K8SAPIService,NATSService // // Package sshserver_test is a generated GoMock package. @@ -18,7 +18,7 @@ import ( gomock "go.uber.org/mock/gomock" ) -//go:generate mockgen -source=sessionhandler.go -package=sshserver_test -destination=sessionhandler_mock_test.go -write_generate_directive +//go:generate mockgen -package=sshserver_test -destination=sshserver_mock_test.go -write_generate_directive . K8SAPIService,NATSService // MockK8SAPIService is a mock of K8SAPIService interface. type MockK8SAPIService struct { @@ -103,3 +103,41 @@ func (mr *MockK8SAPIServiceMockRecorder) NamespaceDetails(arg0, arg1 any) *gomoc mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamespaceDetails", reflect.TypeOf((*MockK8SAPIService)(nil).NamespaceDetails), arg0, arg1) } + +// MockNATSService is a mock of NATSService interface. +type MockNATSService struct { + ctrl *gomock.Controller + recorder *MockNATSServiceMockRecorder +} + +// MockNATSServiceMockRecorder is the mock recorder for MockNATSService. +type MockNATSServiceMockRecorder struct { + mock *MockNATSService +} + +// NewMockNATSService creates a new mock instance. +func NewMockNATSService(ctrl *gomock.Controller) *MockNATSService { + mock := &MockNATSService{ctrl: ctrl} + mock.recorder = &MockNATSServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockNATSService) EXPECT() *MockNATSServiceMockRecorder { + return m.recorder +} + +// KeyCanAccessEnvironment mocks base method. +func (m *MockNATSService) KeyCanAccessEnvironment(arg0, arg1, arg2 string, arg3, arg4 int) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "KeyCanAccessEnvironment", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// KeyCanAccessEnvironment indicates an expected call of KeyCanAccessEnvironment. +func (mr *MockNATSServiceMockRecorder) KeyCanAccessEnvironment(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeyCanAccessEnvironment", reflect.TypeOf((*MockNATSService)(nil).KeyCanAccessEnvironment), arg0, arg1, arg2, arg3, arg4) +}