diff options
author | feistel <6742251-feistel@users.noreply.gitlab.com> | 2021-09-08 14:40:35 +0000 |
---|---|---|
committer | feistel <6742251-feistel@users.noreply.gitlab.com> | 2021-09-08 14:41:57 +0000 |
commit | 67415dc4f6f293460517d4281b5e4e80e66ffb91 (patch) | |
tree | f3c3e9162a39ddc8fcfcf6f659ab5cdf362871d6 /cmd | |
parent | 7884a4420ac8ffd3ee34589c0f8e0d25ca0fd076 (diff) | |
download | gitlab-shell-67415dc4f6f293460517d4281b5e4e80e66ffb91.tar.gz |
refactor: rearchitect command and executable Go modules
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/check/command/command.go | 22 | ||||
-rw-r--r-- | cmd/check/command/command_test.go | 42 | ||||
-rw-r--r-- | cmd/check/main.go | 6 | ||||
-rw-r--r-- | cmd/gitlab-shell-authorized-keys-check/command/command.go | 39 | ||||
-rw-r--r-- | cmd/gitlab-shell-authorized-keys-check/command/command_test.go | 120 | ||||
-rw-r--r-- | cmd/gitlab-shell-authorized-keys-check/main.go | 5 | ||||
-rw-r--r-- | cmd/gitlab-shell-authorized-principals-check/command/command.go | 39 | ||||
-rw-r--r-- | cmd/gitlab-shell-authorized-principals-check/command/command_test.go | 114 | ||||
-rw-r--r-- | cmd/gitlab-shell-authorized-principals-check/main.go | 5 | ||||
-rw-r--r-- | cmd/gitlab-shell/command/command.go | 65 | ||||
-rw-r--r-- | cmd/gitlab-shell/command/command_test.go | 281 | ||||
-rw-r--r-- | cmd/gitlab-shell/main.go | 5 |
12 files changed, 734 insertions, 9 deletions
diff --git a/cmd/check/command/command.go b/cmd/check/command/command.go new file mode 100644 index 0000000..e72f792 --- /dev/null +++ b/cmd/check/command/command.go @@ -0,0 +1,22 @@ +package command + +import ( + "gitlab.com/gitlab-org/gitlab-shell/internal/command" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/healthcheck" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/shared/disallowedcommand" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" +) + + +func New(config *config.Config, readWriter *readwriter.ReadWriter) (command.Command, error) { + if cmd := build(config, readWriter); cmd != nil { + return cmd, nil + } + + return nil, disallowedcommand.Error +} + +func build(config *config.Config, readWriter *readwriter.ReadWriter) command.Command { + return &healthcheck.Command{Config: config, ReadWriter: readWriter} +} diff --git a/cmd/check/command/command_test.go b/cmd/check/command/command_test.go new file mode 100644 index 0000000..cd06456 --- /dev/null +++ b/cmd/check/command/command_test.go @@ -0,0 +1,42 @@ +package command_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitlab-shell/cmd/check/command" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/healthcheck" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/executable" + "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" +) + +var ( + basicConfig = &config.Config{GitlabUrl: "http+unix://gitlab.socket"} +) + +func TestNew(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + config *config.Config + expectedType interface{} + }{ + { + desc: "it returns a Healthcheck command", + config: basicConfig, + expectedType: &healthcheck.Command{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + command, err := command.New(tc.config, nil) + + require.NoError(t, err) + require.IsType(t, tc.expectedType, command) + }) + } +} diff --git a/cmd/check/main.go b/cmd/check/main.go index 44d8175..e4bcdf2 100644 --- a/cmd/check/main.go +++ b/cmd/check/main.go @@ -4,12 +4,12 @@ import ( "fmt" "os" + checkCmd "gitlab.com/gitlab-org/gitlab-shell/cmd/check/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/internal/config" "gitlab.com/gitlab-org/gitlab-shell/internal/executable" "gitlab.com/gitlab-org/gitlab-shell/internal/logger" - "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" ) func main() { @@ -19,7 +19,7 @@ func main() { ErrOut: os.Stderr, } - executable, err := executable.New(executable.Healthcheck, false) + executable, err := executable.New(executable.Healthcheck) if err != nil { fmt.Fprintln(readWriter.ErrOut, "Failed to determine executable, exiting") os.Exit(1) @@ -34,7 +34,7 @@ func main() { logCloser := logger.Configure(config) defer logCloser.Close() - cmd, err := command.New(executable, os.Args[1:], sshenv.Env{}, config, readWriter) + cmd, err := checkCmd.New(config, readWriter) if err != nil { fmt.Fprintf(readWriter.ErrOut, "%v\n", err) os.Exit(1) diff --git a/cmd/gitlab-shell-authorized-keys-check/command/command.go b/cmd/gitlab-shell-authorized-keys-check/command/command.go new file mode 100644 index 0000000..3db8605 --- /dev/null +++ b/cmd/gitlab-shell-authorized-keys-check/command/command.go @@ -0,0 +1,39 @@ +package command + +import ( + "gitlab.com/gitlab-org/gitlab-shell/internal/command" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/authorizedkeys" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/shared/disallowedcommand" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/executable" + "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" +) + +func New(e *executable.Executable, arguments []string, env sshenv.Env, config *config.Config, readWriter *readwriter.ReadWriter) (command.Command, error) { + args, err := Parse(e, arguments, env) + if err != nil { + return nil, err + } + + if cmd := build(args, config, readWriter); cmd != nil { + return cmd, nil + } + + return nil, disallowedcommand.Error +} + +func Parse(e *executable.Executable, arguments []string, env sshenv.Env) (*commandargs.AuthorizedKeys, error) { + args := &commandargs.AuthorizedKeys{Arguments: arguments} + + if err := args.Parse(); err != nil { + return nil, err + } + + return args, nil +} + +func build(args *commandargs.AuthorizedKeys, config *config.Config, readWriter *readwriter.ReadWriter) command.Command { + return &authorizedkeys.Command{Config: config, Args: args, ReadWriter: readWriter} +} diff --git a/cmd/gitlab-shell-authorized-keys-check/command/command_test.go b/cmd/gitlab-shell-authorized-keys-check/command/command_test.go new file mode 100644 index 0000000..5c5419e --- /dev/null +++ b/cmd/gitlab-shell-authorized-keys-check/command/command_test.go @@ -0,0 +1,120 @@ +package command_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitlab-shell/cmd/gitlab-shell-authorized-keys-check/command" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/authorizedkeys" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/executable" + "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" +) + +var ( + authorizedKeysExec = &executable.Executable{Name: executable.AuthorizedKeysCheck} + basicConfig = &config.Config{GitlabUrl: "http+unix://gitlab.socket"} +) + +func TestNew(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + config *config.Config + expectedType interface{} + }{ + { + desc: "it returns a AuthorizedKeys command", + executable: authorizedKeysExec, + arguments: []string{"git", "git", "key"}, + config: basicConfig, + expectedType: &authorizedkeys.Command{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + command, err := command.New(tc.executable, tc.arguments, tc.env, tc.config, nil) + + require.NoError(t, err) + require.IsType(t, tc.expectedType, command) + }) + } +} + +func TestParseSuccess(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + expectedArgs commandargs.CommandArgs + expectError bool + }{ + { + desc: "It parses authorized-keys command", + executable: &executable.Executable{Name: executable.AuthorizedKeysCheck}, + arguments: []string{"git", "git", "key"}, + expectedArgs: &commandargs.AuthorizedKeys{Arguments: []string{"git", "git", "key"}, ExpectedUser: "git", ActualUser: "git", Key: "key"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + result, err := command.Parse(tc.executable, tc.arguments, tc.env) + + if !tc.expectError { + require.NoError(t, err) + require.Equal(t, tc.expectedArgs, result) + } else { + require.Error(t, err) + } + }) + } +} + +func TestParseFailure(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + expectedError string + }{ + { + desc: "With not enough arguments for the AuthorizedKeysCheck", + executable: &executable.Executable{Name: executable.AuthorizedKeysCheck}, + arguments: []string{"user"}, + expectedError: "# Insufficient arguments. 1. Usage\n#\tgitlab-shell-authorized-keys-check <expected-username> <actual-username> <key>", + }, + { + desc: "With too many arguments for the AuthorizedKeysCheck", + executable: &executable.Executable{Name: executable.AuthorizedKeysCheck}, + arguments: []string{"user", "user", "key", "something-else"}, + expectedError: "# Insufficient arguments. 4. Usage\n#\tgitlab-shell-authorized-keys-check <expected-username> <actual-username> <key>", + }, + { + desc: "With missing username for the AuthorizedKeysCheck", + executable: &executable.Executable{Name: executable.AuthorizedKeysCheck}, + arguments: []string{"user", "", "key"}, + expectedError: "# No username provided", + }, + { + desc: "With missing key for the AuthorizedKeysCheck", + executable: &executable.Executable{Name: executable.AuthorizedKeysCheck}, + arguments: []string{"user", "user", ""}, + expectedError: "# No key provided", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + _, err := command.Parse(tc.executable, tc.arguments, tc.env) + + require.EqualError(t, err, tc.expectedError) + }) + } +} diff --git a/cmd/gitlab-shell-authorized-keys-check/main.go b/cmd/gitlab-shell-authorized-keys-check/main.go index cda3e0b..bfaeca7 100644 --- a/cmd/gitlab-shell-authorized-keys-check/main.go +++ b/cmd/gitlab-shell-authorized-keys-check/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + cmd "gitlab.com/gitlab-org/gitlab-shell/cmd/gitlab-shell-authorized-keys-check/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/internal/config" @@ -20,7 +21,7 @@ func main() { ErrOut: os.Stderr, } - executable, err := executable.New(executable.AuthorizedKeysCheck, true) + executable, err := executable.New(executable.AuthorizedKeysCheck) if err != nil { fmt.Fprintln(readWriter.ErrOut, "Failed to determine executable, exiting") os.Exit(1) @@ -35,7 +36,7 @@ func main() { logCloser := logger.Configure(config) defer logCloser.Close() - cmd, err := command.New(executable, os.Args[1:], sshenv.Env{}, config, readWriter) + cmd, err := cmd.New(executable, os.Args[1:], sshenv.Env{}, config, readWriter) if err != nil { // For now this could happen if `SSH_CONNECTION` is not set on // the environment diff --git a/cmd/gitlab-shell-authorized-principals-check/command/command.go b/cmd/gitlab-shell-authorized-principals-check/command/command.go new file mode 100644 index 0000000..b5ded54 --- /dev/null +++ b/cmd/gitlab-shell-authorized-principals-check/command/command.go @@ -0,0 +1,39 @@ +package command + +import ( + "gitlab.com/gitlab-org/gitlab-shell/internal/command" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/authorizedprincipals" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/shared/disallowedcommand" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/executable" + "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" +) + +func New(e *executable.Executable, arguments []string, env sshenv.Env, config *config.Config, readWriter *readwriter.ReadWriter) (command.Command, error) { + args, err := Parse(e, arguments, env) + if err != nil { + return nil, err + } + + if cmd := build(args, config, readWriter); cmd != nil { + return cmd, nil + } + + return nil, disallowedcommand.Error +} + +func Parse(e *executable.Executable, arguments []string, env sshenv.Env) (*commandargs.AuthorizedPrincipals, error) { + args := &commandargs.AuthorizedPrincipals{Arguments: arguments} + + if err := args.Parse(); err != nil { + return nil, err + } + + return args, nil +} + +func build(args *commandargs.AuthorizedPrincipals, config *config.Config, readWriter *readwriter.ReadWriter) command.Command { + return &authorizedprincipals.Command{Config: config, Args: args, ReadWriter: readWriter} +} diff --git a/cmd/gitlab-shell-authorized-principals-check/command/command_test.go b/cmd/gitlab-shell-authorized-principals-check/command/command_test.go new file mode 100644 index 0000000..b6a89f5 --- /dev/null +++ b/cmd/gitlab-shell-authorized-principals-check/command/command_test.go @@ -0,0 +1,114 @@ +package command_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + cmd "gitlab.com/gitlab-org/gitlab-shell/cmd/gitlab-shell-authorized-principals-check/command" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/authorizedprincipals" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/executable" + "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" +) + +var ( + authorizedPrincipalsExec = &executable.Executable{Name: executable.AuthorizedPrincipalsCheck} + basicConfig = &config.Config{GitlabUrl: "http+unix://gitlab.socket"} +) + +func TestNew(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + config *config.Config + expectedType interface{} + }{ + { + desc: "it returns a AuthorizedPrincipals command", + executable: authorizedPrincipalsExec, + arguments: []string{"key", "principal"}, + config: basicConfig, + expectedType: &authorizedprincipals.Command{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + command, err := cmd.New(tc.executable, tc.arguments, tc.env, tc.config, nil) + + require.NoError(t, err) + require.IsType(t, tc.expectedType, command) + }) + } +} + +func TestParseSuccess(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + expectedArgs commandargs.CommandArgs + expectError bool + }{ + { + desc: "It parses authorized-principals command", + executable: &executable.Executable{Name: executable.AuthorizedPrincipalsCheck}, + arguments: []string{"key", "principal-1", "principal-2"}, + expectedArgs: &commandargs.AuthorizedPrincipals{Arguments: []string{"key", "principal-1", "principal-2"}, KeyId: "key", Principals: []string{"principal-1", "principal-2"}}, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + result, err := cmd.Parse(tc.executable, tc.arguments, tc.env) + + if !tc.expectError { + require.NoError(t, err) + require.Equal(t, tc.expectedArgs, result) + } else { + require.Error(t, err) + } + }) + } +} + +func TestParseFailure(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + expectedError string + }{ + { + desc: "With not enough arguments for the AuthorizedPrincipalsCheck", + executable: &executable.Executable{Name: executable.AuthorizedPrincipalsCheck}, + arguments: []string{"key"}, + expectedError: "# Insufficient arguments. 1. Usage\n#\tgitlab-shell-authorized-principals-check <key-id> <principal1> [<principal2>...]", + }, + { + desc: "With missing key_id for the AuthorizedPrincipalsCheck", + executable: &executable.Executable{Name: executable.AuthorizedPrincipalsCheck}, + arguments: []string{"", "principal"}, + expectedError: "# No key_id provided", + }, + { + desc: "With blank principal for the AuthorizedPrincipalsCheck", + executable: &executable.Executable{Name: executable.AuthorizedPrincipalsCheck}, + arguments: []string{"key", "principal", ""}, + expectedError: "# An invalid principal was provided", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + _, err := cmd.Parse(tc.executable, tc.arguments, tc.env) + + require.EqualError(t, err, tc.expectedError) + }) + } +} diff --git a/cmd/gitlab-shell-authorized-principals-check/main.go b/cmd/gitlab-shell-authorized-principals-check/main.go index 87f7fa3..d0e709c 100644 --- a/cmd/gitlab-shell-authorized-principals-check/main.go +++ b/cmd/gitlab-shell-authorized-principals-check/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + cmd "gitlab.com/gitlab-org/gitlab-shell/cmd/gitlab-shell-authorized-principals-check/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/internal/config" @@ -20,7 +21,7 @@ func main() { ErrOut: os.Stderr, } - executable, err := executable.New(executable.AuthorizedPrincipalsCheck, true) + executable, err := executable.New(executable.AuthorizedPrincipalsCheck) if err != nil { fmt.Fprintln(readWriter.ErrOut, "Failed to determine executable, exiting") os.Exit(1) @@ -35,7 +36,7 @@ func main() { logCloser := logger.Configure(config) defer logCloser.Close() - cmd, err := command.New(executable, os.Args[1:], sshenv.Env{}, config, readWriter) + cmd, err := cmd.New(executable, os.Args[1:], sshenv.Env{}, config, readWriter) if err != nil { // For now this could happen if `SSH_CONNECTION` is not set on // the environment diff --git a/cmd/gitlab-shell/command/command.go b/cmd/gitlab-shell/command/command.go new file mode 100644 index 0000000..98bfdff --- /dev/null +++ b/cmd/gitlab-shell/command/command.go @@ -0,0 +1,65 @@ +package command + +import ( + "gitlab.com/gitlab-org/gitlab-shell/internal/command" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/discover" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/lfsauthenticate" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/personalaccesstoken" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/receivepack" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/shared/disallowedcommand" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/twofactorrecover" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/twofactorverify" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/uploadarchive" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/uploadpack" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/executable" + "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" +) + +func New(e *executable.Executable, arguments []string, env sshenv.Env, config *config.Config, readWriter *readwriter.ReadWriter) (command.Command, error) { + args, err := Parse(e, arguments, env) + if err != nil { + return nil, err + } + + if cmd := Build(args, config, readWriter); cmd != nil { + return cmd, nil + } + + return nil, disallowedcommand.Error +} + +func Parse(e *executable.Executable, arguments []string, env sshenv.Env) (*commandargs.Shell, error) { + args := &commandargs.Shell{Arguments: arguments, Env: env} + + if err := args.Parse(); err != nil { + return nil, err + } + + return args, nil +} + +func Build(args *commandargs.Shell, config *config.Config, readWriter *readwriter.ReadWriter) command.Command { + switch args.CommandType { + case commandargs.Discover: + return &discover.Command{Config: config, Args: args, ReadWriter: readWriter} + case commandargs.TwoFactorRecover: + return &twofactorrecover.Command{Config: config, Args: args, ReadWriter: readWriter} + case commandargs.TwoFactorVerify: + return &twofactorverify.Command{Config: config, Args: args, ReadWriter: readWriter} + case commandargs.LfsAuthenticate: + return &lfsauthenticate.Command{Config: config, Args: args, ReadWriter: readWriter} + case commandargs.ReceivePack: + return &receivepack.Command{Config: config, Args: args, ReadWriter: readWriter} + case commandargs.UploadPack: + return &uploadpack.Command{Config: config, Args: args, ReadWriter: readWriter} + case commandargs.UploadArchive: + return &uploadarchive.Command{Config: config, Args: args, ReadWriter: readWriter} + case commandargs.PersonalAccessToken: + return &personalaccesstoken.Command{Config: config, Args: args, ReadWriter: readWriter} + } + + return nil +} diff --git a/cmd/gitlab-shell/command/command_test.go b/cmd/gitlab-shell/command/command_test.go new file mode 100644 index 0000000..5dacb67 --- /dev/null +++ b/cmd/gitlab-shell/command/command_test.go @@ -0,0 +1,281 @@ +package command_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + cmd "gitlab.com/gitlab-org/gitlab-shell/cmd/gitlab-shell/command" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/discover" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/lfsauthenticate" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/personalaccesstoken" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/receivepack" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/shared/disallowedcommand" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/twofactorrecover" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/twofactorverify" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/uploadarchive" + "gitlab.com/gitlab-org/gitlab-shell/internal/command/uploadpack" + "gitlab.com/gitlab-org/gitlab-shell/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/internal/executable" + "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" +) + +var ( + gitlabShellExec = &executable.Executable{Name: executable.GitlabShell} + basicConfig = &config.Config{GitlabUrl: "http+unix://gitlab.socket"} +) + +func TestNew(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + config *config.Config + expectedType interface{} + }{ + { + desc: "it returns a Discover command", + executable: gitlabShellExec, + env: buildEnv(""), + config: basicConfig, + expectedType: &discover.Command{}, + }, + { + desc: "it returns a TwoFactorRecover command", + executable: gitlabShellExec, + env: buildEnv("2fa_recovery_codes"), + config: basicConfig, + expectedType: &twofactorrecover.Command{}, + }, + { + desc: "it returns a TwoFactorVerify command", + executable: gitlabShellExec, + env: buildEnv("2fa_verify"), + config: basicConfig, + expectedType: &twofactorverify.Command{}, + }, + { + desc: "it returns an LfsAuthenticate command", + executable: gitlabShellExec, + env: buildEnv("git-lfs-authenticate"), + config: basicConfig, + expectedType: &lfsauthenticate.Command{}, + }, + { + desc: "it returns a ReceivePack command", + executable: gitlabShellExec, + env: buildEnv("git-receive-pack"), + config: basicConfig, + expectedType: &receivepack.Command{}, + }, + { + desc: "it returns an UploadPack command", + executable: gitlabShellExec, + env: buildEnv("git-upload-pack"), + config: basicConfig, + expectedType: &uploadpack.Command{}, + }, + { + desc: "it returns an UploadArchive command", + executable: gitlabShellExec, + env: buildEnv("git-upload-archive"), + config: basicConfig, + expectedType: &uploadarchive.Command{}, + }, + { + desc: "it returns a PersonalAccessToken command", + executable: gitlabShellExec, + env: buildEnv("personal_access_token"), + config: basicConfig, + expectedType: &personalaccesstoken.Command{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + command, err := cmd.New(tc.executable, tc.arguments, tc.env, tc.config, nil) + + require.NoError(t, err) + require.IsType(t, tc.expectedType, command) + }) + } +} + +func TestFailingNew(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + expectedError error + }{ + { + desc: "Parsing environment failed", + executable: gitlabShellExec, + expectedError: errors.New("Only SSH allowed"), + }, + { + desc: "Unknown command given", + executable: gitlabShellExec, + env: buildEnv("unknown"), + expectedError: disallowedcommand.Error, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + command, err := cmd.New(tc.executable, []string{}, tc.env, basicConfig, nil) + require.Nil(t, command) + require.Equal(t, tc.expectedError, err) + }) + } +} + +func buildEnv(command string) sshenv.Env { + return sshenv.Env{ + IsSSHConnection: true, + OriginalCommand: command, + } +} + +func TestParseSuccess(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + expectedArgs commandargs.CommandArgs + expectError bool + }{ + { + desc: "It sets discover as the command when the command string was empty", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, RemoteAddr: "1"}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{}, CommandType: commandargs.Discover, Env: sshenv.Env{IsSSHConnection: true, RemoteAddr: "1"}}, + }, + { + desc: "It finds the key id in any passed arguments", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, RemoteAddr: "1"}, + arguments: []string{"hello", "key-123"}, + expectedArgs: &commandargs.Shell{Arguments: []string{"hello", "key-123"}, SshArgs: []string{}, CommandType: commandargs.Discover, GitlabKeyId: "123", Env: sshenv.Env{IsSSHConnection: true, RemoteAddr: "1"}}, + }, + { + desc: "It finds the key id only if the argument is of <key-id> format", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, RemoteAddr: "1"}, + arguments: []string{"hello", "username-key-123"}, + expectedArgs: &commandargs.Shell{Arguments: []string{"hello", "username-key-123"}, SshArgs: []string{}, CommandType: commandargs.Discover, GitlabUsername: "key-123", Env: sshenv.Env{IsSSHConnection: true, RemoteAddr: "1"}}, + }, + { + desc: "It finds the username in any passed arguments", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, RemoteAddr: "1"}, + arguments: []string{"hello", "username-jane-doe"}, + expectedArgs: &commandargs.Shell{Arguments: []string{"hello", "username-jane-doe"}, SshArgs: []string{}, CommandType: commandargs.Discover, GitlabUsername: "jane-doe", Env: sshenv.Env{IsSSHConnection: true, RemoteAddr: "1"}}, + }, + { + desc: "It parses 2fa_recovery_codes command", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "2fa_recovery_codes"}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{"2fa_recovery_codes"}, CommandType: commandargs.TwoFactorRecover, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "2fa_recovery_codes"}}, + }, + { + desc: "It parses git-receive-pack command", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-receive-pack group/repo"}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: commandargs.ReceivePack, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-receive-pack group/repo"}}, + }, + { + desc: "It parses git-receive-pack command and a project with single quotes", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-receive-pack 'group/repo'"}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: commandargs.ReceivePack, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-receive-pack 'group/repo'"}}, + }, + { + desc: `It parses "git receive-pack" command`, + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: `git-receive-pack "group/repo"`}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: commandargs.ReceivePack, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: `git-receive-pack "group/repo"`}}, + }, + { + desc: `It parses a command followed by control characters`, + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: `git-receive-pack group/repo; any command`}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: commandargs.ReceivePack, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: `git-receive-pack group/repo; any command`}}, + }, + { + desc: "It parses git-upload-pack command", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: `git upload-pack "group/repo"`}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{"git-upload-pack", "group/repo"}, CommandType: commandargs.UploadPack, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: `git upload-pack "group/repo"`}}, + }, + { + desc: "It parses git-upload-archive command", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-upload-archive 'group/repo'"}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{"git-upload-archive", "group/repo"}, CommandType: commandargs.UploadArchive, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-upload-archive 'group/repo'"}}, + }, + { + desc: "It parses git-lfs-authenticate command", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-lfs-authenticate 'group/repo' download"}, + arguments: []string{}, + expectedArgs: &commandargs.Shell{Arguments: []string{}, SshArgs: []string{"git-lfs-authenticate", "group/repo", "download"}, CommandType: commandargs.LfsAuthenticate, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-lfs-authenticate 'group/repo' download"}}, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + result, err := cmd.Parse(tc.executable, tc.arguments, tc.env) + + if !tc.expectError { + require.NoError(t, err) + require.Equal(t, tc.expectedArgs, result) + } else { + require.Error(t, err) + } + }) + } +} + +func TestParseFailure(t *testing.T) { + testCases := []struct { + desc string + executable *executable.Executable + env sshenv.Env + arguments []string + expectedError string + }{ + { + desc: "It fails if SSH connection is not set", + executable: &executable.Executable{Name: executable.GitlabShell}, + arguments: []string{}, + expectedError: "Only SSH allowed", + }, + { + desc: "It fails if SSH command is invalid", + executable: &executable.Executable{Name: executable.GitlabShell}, + env: sshenv.Env{IsSSHConnection: true, OriginalCommand: `git receive-pack "`}, + arguments: []string{}, + expectedError: "Invalid SSH command", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + _, err := cmd.Parse(tc.executable, tc.arguments, tc.env) + + require.EqualError(t, err, tc.expectedError) + }) + } +} diff --git a/cmd/gitlab-shell/main.go b/cmd/gitlab-shell/main.go index fe52bfc..14bd457 100644 --- a/cmd/gitlab-shell/main.go +++ b/cmd/gitlab-shell/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + shellCmd "gitlab.com/gitlab-org/gitlab-shell/cmd/gitlab-shell/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/internal/config" @@ -34,7 +35,7 @@ func main() { ErrOut: os.Stderr, } - executable, err := executable.New(executable.GitlabShell, true) + executable, err := executable.New(executable.GitlabShell) if err != nil { fmt.Fprintln(readWriter.ErrOut, "Failed to determine executable, exiting") os.Exit(1) @@ -50,7 +51,7 @@ func main() { defer logCloser.Close() env := sshenv.NewFromEnv() - cmd, err := command.New(executable, os.Args[1:], env, config, readWriter) + cmd, err := shellCmd.New(executable, os.Args[1:], env, config, readWriter) if err != nil { // For now this could happen if `SSH_CONNECTION` is not set on // the environment |