summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfeistel <6742251-feistel@users.noreply.gitlab.com>2021-09-08 14:40:35 +0000
committerfeistel <6742251-feistel@users.noreply.gitlab.com>2021-09-08 14:41:57 +0000
commit67415dc4f6f293460517d4281b5e4e80e66ffb91 (patch)
treef3c3e9162a39ddc8fcfcf6f659ab5cdf362871d6
parent7884a4420ac8ffd3ee34589c0f8e0d25ca0fd076 (diff)
downloadgitlab-shell-67415dc4f6f293460517d4281b5e4e80e66ffb91.tar.gz
refactor: rearchitect command and executable Go modules
-rw-r--r--cmd/check/command/command.go22
-rw-r--r--cmd/check/command/command_test.go42
-rw-r--r--cmd/check/main.go6
-rw-r--r--cmd/gitlab-shell-authorized-keys-check/command/command.go39
-rw-r--r--cmd/gitlab-shell-authorized-keys-check/command/command_test.go120
-rw-r--r--cmd/gitlab-shell-authorized-keys-check/main.go5
-rw-r--r--cmd/gitlab-shell-authorized-principals-check/command/command.go39
-rw-r--r--cmd/gitlab-shell-authorized-principals-check/command/command_test.go114
-rw-r--r--cmd/gitlab-shell-authorized-principals-check/main.go5
-rw-r--r--cmd/gitlab-shell/command/command.go65
-rw-r--r--cmd/gitlab-shell/command/command_test.go281
-rw-r--r--cmd/gitlab-shell/main.go5
-rw-r--r--internal/command/command.go83
-rw-r--r--internal/command/command_test.go158
-rw-r--r--internal/command/commandargs/command_args.go29
-rw-r--r--internal/command/commandargs/command_args_test.go197
-rw-r--r--internal/executable/executable.go4
-rw-r--r--internal/executable/executable_test.go4
-rw-r--r--internal/sshd/session.go4
19 files changed, 739 insertions, 483 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
diff --git a/internal/command/command.go b/internal/command/command.go
index dadf41a..4ee568e 100644
--- a/internal/command/command.go
+++ b/internal/command/command.go
@@ -3,23 +3,7 @@ package command
import (
"context"
- "gitlab.com/gitlab-org/gitlab-shell/internal/command/authorizedkeys"
- "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/discover"
- "gitlab.com/gitlab-org/gitlab-shell/internal/command/healthcheck"
- "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"
"gitlab.com/gitlab-org/labkit/correlation"
"gitlab.com/gitlab-org/labkit/tracing"
)
@@ -28,23 +12,6 @@ type Command interface {
Execute(ctx context.Context) error
}
-func New(e *executable.Executable, arguments []string, env sshenv.Env, config *config.Config, readWriter *readwriter.ReadWriter) (Command, error) {
- var args commandargs.CommandArgs
- if e.AcceptArgs {
- var err error
- args, err = commandargs.Parse(e, arguments, env)
- if err != nil {
- return nil, err
- }
- }
-
- if cmd := buildCommand(e, args, config, readWriter); cmd != nil {
- return cmd, nil
- }
-
- return nil, disallowedcommand.Error
-}
-
// Setup() initializes tracing from the configuration file and generates a
// background context from which all other contexts in the process should derive
// from, as it has a service name and initial correlation ID set.
@@ -80,53 +47,3 @@ func Setup(serviceName string, config *config.Config) (context.Context, func())
closer.Close()
}
}
-
-func buildCommand(e *executable.Executable, args commandargs.CommandArgs, config *config.Config, readWriter *readwriter.ReadWriter) Command {
- switch e.Name {
- case executable.GitlabShell:
- return BuildShellCommand(args.(*commandargs.Shell), config, readWriter)
- case executable.AuthorizedKeysCheck:
- return buildAuthorizedKeysCommand(args.(*commandargs.AuthorizedKeys), config, readWriter)
- case executable.AuthorizedPrincipalsCheck:
- return buildAuthorizedPrincipalsCommand(args.(*commandargs.AuthorizedPrincipals), config, readWriter)
- case executable.Healthcheck:
- return buildHealthcheckCommand(config, readWriter)
- }
-
- return nil
-}
-
-func BuildShellCommand(args *commandargs.Shell, config *config.Config, readWriter *readwriter.ReadWriter) 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
-}
-
-func buildAuthorizedKeysCommand(args *commandargs.AuthorizedKeys, config *config.Config, readWriter *readwriter.ReadWriter) Command {
- return &authorizedkeys.Command{Config: config, Args: args, ReadWriter: readWriter}
-}
-
-func buildAuthorizedPrincipalsCommand(args *commandargs.AuthorizedPrincipals, config *config.Config, readWriter *readwriter.ReadWriter) Command {
- return &authorizedprincipals.Command{Config: config, Args: args, ReadWriter: readWriter}
-}
-
-func buildHealthcheckCommand(config *config.Config, readWriter *readwriter.ReadWriter) Command {
- return &healthcheck.Command{Config: config, ReadWriter: readWriter}
-}
diff --git a/internal/command/command_test.go b/internal/command/command_test.go
index a538745..2fc6655 100644
--- a/internal/command/command_test.go
+++ b/internal/command/command_test.go
@@ -1,173 +1,15 @@
package command
import (
- "errors"
"os"
"testing"
"github.com/stretchr/testify/require"
- "gitlab.com/gitlab-org/gitlab-shell/internal/command/authorizedkeys"
- "gitlab.com/gitlab-org/gitlab-shell/internal/command/authorizedprincipals"
- "gitlab.com/gitlab-org/gitlab-shell/internal/command/discover"
- "gitlab.com/gitlab-org/gitlab-shell/internal/command/healthcheck"
- "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"
"gitlab.com/gitlab-org/labkit/correlation"
)
-var (
- authorizedKeysExec = &executable.Executable{Name: executable.AuthorizedKeysCheck, AcceptArgs: true}
- authorizedPrincipalsExec = &executable.Executable{Name: executable.AuthorizedPrincipalsCheck, AcceptArgs: true}
- checkExec = &executable.Executable{Name: executable.Healthcheck, AcceptArgs: false}
- gitlabShellExec = &executable.Executable{Name: executable.GitlabShell, AcceptArgs: true}
-
- basicConfig = &config.Config{GitlabUrl: "http+unix://gitlab.socket"}
- advancedConfig = &config.Config{GitlabUrl: "http+unix://gitlab.socket", SslCertDir: "/tmp/certs"}
-)
-
-func buildEnv(command string) sshenv.Env {
- return sshenv.Env{
- IsSSHConnection: true,
- OriginalCommand: command,
- }
-}
-
-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 Healthcheck command",
- executable: checkExec,
- config: basicConfig,
- expectedType: &healthcheck.Command{},
- },
- {
- desc: "it returns a AuthorizedKeys command",
- executable: authorizedKeysExec,
- arguments: []string{"git", "git", "key"},
- config: basicConfig,
- expectedType: &authorizedkeys.Command{},
- },
- {
- desc: "it returns a AuthorizedPrincipals command",
- executable: authorizedPrincipalsExec,
- arguments: []string{"key", "principal"},
- config: basicConfig,
- expectedType: &authorizedprincipals.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 := 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 := New(tc.executable, []string{}, tc.env, basicConfig, nil)
- require.Nil(t, command)
- require.Equal(t, tc.expectedError, err)
- })
- }
-}
-
func TestSetup(t *testing.T) {
testCases := []struct {
name string
diff --git a/internal/command/commandargs/command_args.go b/internal/command/commandargs/command_args.go
index a01b8b2..f23ba18 100644
--- a/internal/command/commandargs/command_args.go
+++ b/internal/command/commandargs/command_args.go
@@ -1,37 +1,8 @@
package commandargs
-import (
- "errors"
- "fmt"
-
- "gitlab.com/gitlab-org/gitlab-shell/internal/executable"
- "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv"
-)
-
type CommandType string
type CommandArgs interface {
Parse() error
GetArguments() []string
}
-
-func Parse(e *executable.Executable, arguments []string, env sshenv.Env) (CommandArgs, error) {
- var args CommandArgs
-
- switch e.Name {
- case executable.GitlabShell:
- args = &Shell{Arguments: arguments, Env: env}
- case executable.AuthorizedKeysCheck:
- args = &AuthorizedKeys{Arguments: arguments}
- case executable.AuthorizedPrincipalsCheck:
- args = &AuthorizedPrincipals{Arguments: arguments}
- default:
- return nil, errors.New(fmt.Sprintf("unknown executable: %s", e.Name))
- }
-
- if err := args.Parse(); err != nil {
- return nil, err
- }
-
- return args, nil
-}
diff --git a/internal/command/commandargs/command_args_test.go b/internal/command/commandargs/command_args_test.go
deleted file mode 100644
index 119ecd4..0000000
--- a/internal/command/commandargs/command_args_test.go
+++ /dev/null
@@ -1,197 +0,0 @@
-package commandargs
-
-import (
- "testing"
-
- "gitlab.com/gitlab-org/gitlab-shell/internal/executable"
- "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestParseSuccess(t *testing.T) {
- testCases := []struct {
- desc string
- executable *executable.Executable
- env sshenv.Env
- arguments []string
- expectedArgs 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: &Shell{Arguments: []string{}, SshArgs: []string{}, CommandType: 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: &Shell{Arguments: []string{"hello", "key-123"}, SshArgs: []string{}, CommandType: 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: &Shell{Arguments: []string{"hello", "username-key-123"}, SshArgs: []string{}, CommandType: 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: &Shell{Arguments: []string{"hello", "username-jane-doe"}, SshArgs: []string{}, CommandType: 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: &Shell{Arguments: []string{}, SshArgs: []string{"2fa_recovery_codes"}, CommandType: 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: &Shell{Arguments: []string{}, SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: 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: &Shell{Arguments: []string{}, SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: 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: &Shell{Arguments: []string{}, SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: 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: &Shell{Arguments: []string{}, SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: 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: &Shell{Arguments: []string{}, SshArgs: []string{"git-upload-pack", "group/repo"}, CommandType: 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: &Shell{Arguments: []string{}, SshArgs: []string{"git-upload-archive", "group/repo"}, CommandType: 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: &Shell{Arguments: []string{}, SshArgs: []string{"git-lfs-authenticate", "group/repo", "download"}, CommandType: LfsAuthenticate, Env: sshenv.Env{IsSSHConnection: true, OriginalCommand: "git-lfs-authenticate 'group/repo' download"}},
- }, {
- desc: "It parses authorized-keys command",
- executable: &executable.Executable{Name: executable.AuthorizedKeysCheck},
- arguments: []string{"git", "git", "key"},
- expectedArgs: &AuthorizedKeys{Arguments: []string{"git", "git", "key"}, ExpectedUser: "git", ActualUser: "git", Key: "key"},
- }, {
- desc: "It parses authorized-principals command",
- executable: &executable.Executable{Name: executable.AuthorizedPrincipalsCheck},
- arguments: []string{"key", "principal-1", "principal-2"},
- expectedArgs: &AuthorizedPrincipals{Arguments: []string{"key", "principal-1", "principal-2"}, KeyId: "key", Principals: []string{"principal-1", "principal-2"}},
- }, {
- desc: "Unknown executable",
- executable: &executable.Executable{Name: "unknown"},
- arguments: []string{},
- expectError: true,
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.desc, func(t *testing.T) {
- result, err := 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",
- },
- {
- 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",
- },
- {
- 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 := Parse(tc.executable, tc.arguments, tc.env)
-
- require.EqualError(t, err, tc.expectedError)
- })
- }
-}
diff --git a/internal/executable/executable.go b/internal/executable/executable.go
index 8b6b586..108c3ea 100644
--- a/internal/executable/executable.go
+++ b/internal/executable/executable.go
@@ -16,7 +16,6 @@ const (
type Executable struct {
Name string
RootDir string
- AcceptArgs bool
}
var (
@@ -24,7 +23,7 @@ var (
osExecutable = os.Executable
)
-func New(name string, acceptArgs bool) (*Executable, error) {
+func New(name string) (*Executable, error) {
path, err := osExecutable()
if err != nil {
return nil, err
@@ -38,7 +37,6 @@ func New(name string, acceptArgs bool) (*Executable, error) {
executable := &Executable{
Name: name,
RootDir: rootDir,
- AcceptArgs: acceptArgs,
}
return executable, nil
diff --git a/internal/executable/executable_test.go b/internal/executable/executable_test.go
index 71984c3..3915f1a 100644
--- a/internal/executable/executable_test.go
+++ b/internal/executable/executable_test.go
@@ -59,7 +59,7 @@ func TestNewSuccess(t *testing.T) {
fake.Setup()
defer fake.Cleanup()
- result, err := New("gitlab-shell", true)
+ result, err := New("gitlab-shell")
require.NoError(t, err)
require.Equal(t, result.Name, "gitlab-shell")
@@ -96,7 +96,7 @@ func TestNewFailure(t *testing.T) {
fake.Setup()
defer fake.Cleanup()
- _, err := New("gitlab-shell", true)
+ _, err := New("gitlab-shell")
require.Error(t, err)
})
diff --git a/internal/sshd/session.go b/internal/sshd/session.go
index 22cb715..b58598e 100644
--- a/internal/sshd/session.go
+++ b/internal/sshd/session.go
@@ -6,7 +6,7 @@ import (
"golang.org/x/crypto/ssh"
- "gitlab.com/gitlab-org/gitlab-shell/internal/command"
+ shellCmd "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/readwriter"
"gitlab.com/gitlab-org/gitlab-shell/internal/config"
@@ -125,7 +125,7 @@ func (s *session) handleShell(ctx context.Context, req *ssh.Request) uint32 {
ErrOut: s.channel.Stderr(),
}
- cmd := command.BuildShellCommand(args, s.cfg, rw)
+ cmd := shellCmd.Build(args, s.cfg, rw)
if cmd == nil {
s.toStderr("Unknown command: %v\n", args.CommandType)
return 128