diff options
-rw-r--r-- | go/internal/command/command.go | 3 | ||||
-rw-r--r-- | go/internal/command/command_test.go | 13 | ||||
-rw-r--r-- | go/internal/command/commandargs/command_args.go | 1 | ||||
-rw-r--r-- | go/internal/command/commandargs/command_args_test.go | 7 | ||||
-rw-r--r-- | go/internal/command/receivepack/receivepack.go | 9 | ||||
-rw-r--r-- | go/internal/command/receivepack/receivepack_test.go | 18 | ||||
-rw-r--r-- | go/internal/command/shared/disallowedcommand/disallowedcommand.go | 7 | ||||
-rw-r--r-- | go/internal/command/uploadpack/gitalycall.go | 44 | ||||
-rw-r--r-- | go/internal/command/uploadpack/gitalycall_test.go | 40 | ||||
-rw-r--r-- | go/internal/command/uploadpack/uploadpack.go | 36 | ||||
-rw-r--r-- | go/internal/command/uploadpack/uploadpack_test.go | 31 | ||||
-rw-r--r-- | go/internal/gitlabnet/testserver/gitalyserver.go | 9 | ||||
-rw-r--r-- | go/internal/testhelper/requesthandlers/requesthandlers.go | 18 |
13 files changed, 212 insertions, 24 deletions
diff --git a/go/internal/command/command.go b/go/internal/command/command.go index f55207a..8b02f16 100644 --- a/go/internal/command/command.go +++ b/go/internal/command/command.go @@ -7,6 +7,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/receivepack" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/twofactorrecover" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/uploadpack" "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" ) @@ -38,6 +39,8 @@ func buildCommand(args *commandargs.CommandArgs, config *config.Config, readWrit return &twofactorrecover.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} } return nil diff --git a/go/internal/command/command_test.go b/go/internal/command/command_test.go index cdcda8a..37db89f 100644 --- a/go/internal/command/command_test.go +++ b/go/internal/command/command_test.go @@ -9,6 +9,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/fallback" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/receivepack" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/twofactorrecover" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/uploadpack" "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" "gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper" ) @@ -69,6 +70,18 @@ func TestNew(t *testing.T) { expectedType: &receivepack.Command{}, }, { + desc: "it returns a UploadPack command if the feature is enabled", + config: &config.Config{ + GitlabUrl: "http+unix://gitlab.socket", + Migration: config.MigrationConfig{Enabled: true, Features: []string{"git-upload-pack"}}, + }, + environment: map[string]string{ + "SSH_CONNECTION": "1", + "SSH_ORIGINAL_COMMAND": "git-upload-pack", + }, + expectedType: &uploadpack.Command{}, + }, + { desc: "it returns a Fallback command if the feature is unimplemented", config: &config.Config{ GitlabUrl: "http+unix://gitlab.socket", diff --git a/go/internal/command/commandargs/command_args.go b/go/internal/command/commandargs/command_args.go index 7e241ea..6789c5b 100644 --- a/go/internal/command/commandargs/command_args.go +++ b/go/internal/command/commandargs/command_args.go @@ -14,6 +14,7 @@ const ( Discover CommandType = "discover" TwoFactorRecover CommandType = "2fa_recovery_codes" ReceivePack CommandType = "git-receive-pack" + UploadPack CommandType = "git-upload-pack" ) var ( diff --git a/go/internal/command/commandargs/command_args_test.go b/go/internal/command/commandargs/command_args_test.go index 01202c0..c3c6ff2 100644 --- a/go/internal/command/commandargs/command_args_test.go +++ b/go/internal/command/commandargs/command_args_test.go @@ -76,6 +76,13 @@ func TestParseSuccess(t *testing.T) { "SSH_ORIGINAL_COMMAND": `git-receive-pack group/repo; any command`, }, expectedArgs: &CommandArgs{SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: ReceivePack}, + }, { + desc: "It parses git-upload-pack command", + environment: map[string]string{ + "SSH_CONNECTION": "1", + "SSH_ORIGINAL_COMMAND": `git upload-pack "group/repo"`, + }, + expectedArgs: &CommandArgs{SshArgs: []string{"git-upload-pack", "group/repo"}, CommandType: UploadPack}, }, } diff --git a/go/internal/command/receivepack/receivepack.go b/go/internal/command/receivepack/receivepack.go index d1ff3f8..d6b788c 100644 --- a/go/internal/command/receivepack/receivepack.go +++ b/go/internal/command/receivepack/receivepack.go @@ -1,18 +1,13 @@ package receivepack import ( - "errors" - "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/shared/accessverifier" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/shared/disallowedcommand" "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" ) -var ( - disallowedCommandError = errors.New("> GitLab: Disallowed command") -) - type Command struct { Config *config.Config Args *commandargs.CommandArgs @@ -22,7 +17,7 @@ type Command struct { func (c *Command) Execute() error { args := c.Args.SshArgs if len(args) != 2 { - return disallowedCommandError + return disallowedcommand.Error } repo := args[1] diff --git a/go/internal/command/receivepack/receivepack_test.go b/go/internal/command/receivepack/receivepack_test.go index 874bac3..e5263f5 100644 --- a/go/internal/command/receivepack/receivepack_test.go +++ b/go/internal/command/receivepack/receivepack_test.go @@ -2,8 +2,6 @@ package receivepack import ( "bytes" - "encoding/json" - "net/http" "testing" "github.com/stretchr/testify/require" @@ -12,23 +10,11 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper/requesthandlers" ) func TestForbiddenAccess(t *testing.T) { - requests := []testserver.TestRequestHandler{ - { - Path: "/api/v4/internal/allowed", - Handler: func(w http.ResponseWriter, r *http.Request) { - body := map[string]interface{}{ - "status": false, - "message": "Disallowed by API call", - } - w.WriteHeader(http.StatusForbidden) - require.NoError(t, json.NewEncoder(w).Encode(body)) - }, - }, - } - + requests := requesthandlers.BuildDisallowedByApiHandlers(t) url, cleanup := testserver.StartHttpServer(t, requests) defer cleanup() diff --git a/go/internal/command/shared/disallowedcommand/disallowedcommand.go b/go/internal/command/shared/disallowedcommand/disallowedcommand.go new file mode 100644 index 0000000..3c98bcc --- /dev/null +++ b/go/internal/command/shared/disallowedcommand/disallowedcommand.go @@ -0,0 +1,7 @@ +package disallowedcommand + +import "errors" + +var ( + Error = errors.New("> GitLab: Disallowed command") +) diff --git a/go/internal/command/uploadpack/gitalycall.go b/go/internal/command/uploadpack/gitalycall.go new file mode 100644 index 0000000..bb81114 --- /dev/null +++ b/go/internal/command/uploadpack/gitalycall.go @@ -0,0 +1,44 @@ +package uploadpack + +import ( + "context" + + "google.golang.org/grpc" + + pb "gitlab.com/gitlab-org/gitaly-proto/go/gitalypb" + "gitlab.com/gitlab-org/gitaly/client" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/accessverifier" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/handler" +) + +func (c *Command) performGitalyCall(response *accessverifier.Response) error { + gc := &handler.GitalyCommand{ + Config: c.Config, + ServiceName: string(commandargs.UploadPack), + Address: response.Gitaly.Address, + Token: response.Gitaly.Token, + } + + repo := response.Gitaly.Repo + request := &pb.SSHUploadPackRequest{ + Repository: &pb.Repository{ + StorageName: repo.StorageName, + RelativePath: repo.RelativePath, + GitObjectDirectory: repo.GitObjectDirectory, + GitAlternateObjectDirectories: repo.GitAlternateObjectDirectories, + GlRepository: repo.RepoName, + GlProjectPath: repo.ProjectPath, + }, + GitProtocol: response.GitProtocol, + GitConfigOptions: response.GitConfigOptions, + } + + return gc.RunGitalyCommand(func(ctx context.Context, conn *grpc.ClientConn) (int32, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + rw := c.ReadWriter + return client.UploadPack(ctx, conn, rw.In, rw.Out, rw.ErrOut, request) + }) +} diff --git a/go/internal/command/uploadpack/gitalycall_test.go b/go/internal/command/uploadpack/gitalycall_test.go new file mode 100644 index 0000000..2097964 --- /dev/null +++ b/go/internal/command/uploadpack/gitalycall_test.go @@ -0,0 +1,40 @@ +package uploadpack + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper/requesthandlers" +) + +func TestUploadPack(t *testing.T) { + gitalyAddress, cleanup := testserver.StartGitalyServer(t) + defer cleanup() + + requests := requesthandlers.BuildAllowedWithGitalyHandlers(t, gitalyAddress) + url, cleanup := testserver.StartHttpServer(t, requests) + defer cleanup() + + output := &bytes.Buffer{} + input := &bytes.Buffer{} + + userId := "1" + repo := "group/repo" + + cmd := &Command{ + Config: &config.Config{GitlabUrl: url}, + Args: &commandargs.CommandArgs{GitlabKeyId: userId, CommandType: commandargs.UploadPack, SshArgs: []string{"git-upload-pack", repo}}, + ReadWriter: &readwriter.ReadWriter{ErrOut: output, Out: output, In: input}, + } + + err := cmd.Execute() + require.NoError(t, err) + + require.Equal(t, "UploadPack: "+repo, output.String()) +} diff --git a/go/internal/command/uploadpack/uploadpack.go b/go/internal/command/uploadpack/uploadpack.go new file mode 100644 index 0000000..cff198d --- /dev/null +++ b/go/internal/command/uploadpack/uploadpack.go @@ -0,0 +1,36 @@ +package uploadpack + +import ( + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/shared/accessverifier" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/shared/disallowedcommand" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" +) + +type Command struct { + Config *config.Config + Args *commandargs.CommandArgs + ReadWriter *readwriter.ReadWriter +} + +func (c *Command) Execute() error { + args := c.Args.SshArgs + if len(args) != 2 { + return disallowedcommand.Error + } + + repo := args[1] + response, err := c.verifyAccess(repo) + if err != nil { + return err + } + + return c.performGitalyCall(response) +} + +func (c *Command) verifyAccess(repo string) (*accessverifier.Response, error) { + cmd := accessverifier.Command{c.Config, c.Args, c.ReadWriter} + + return cmd.Verify(c.Args.CommandType, repo) +} diff --git a/go/internal/command/uploadpack/uploadpack_test.go b/go/internal/command/uploadpack/uploadpack_test.go new file mode 100644 index 0000000..a06ba24 --- /dev/null +++ b/go/internal/command/uploadpack/uploadpack_test.go @@ -0,0 +1,31 @@ +package uploadpack + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/config" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver" + "gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper/requesthandlers" +) + +func TestForbiddenAccess(t *testing.T) { + requests := requesthandlers.BuildDisallowedByApiHandlers(t) + url, cleanup := testserver.StartHttpServer(t, requests) + defer cleanup() + + output := &bytes.Buffer{} + + cmd := &Command{ + Config: &config.Config{GitlabUrl: url}, + Args: &commandargs.CommandArgs{GitlabKeyId: "disallowed", SshArgs: []string{"git-upload-pack", "group/repo"}}, + ReadWriter: &readwriter.ReadWriter{ErrOut: output, Out: output}, + } + + err := cmd.Execute() + require.Equal(t, "Disallowed by API call", err.Error()) +} diff --git a/go/internal/gitlabnet/testserver/gitalyserver.go b/go/internal/gitlabnet/testserver/gitalyserver.go index 141a518..a31dfe2 100644 --- a/go/internal/gitlabnet/testserver/gitalyserver.go +++ b/go/internal/gitlabnet/testserver/gitalyserver.go @@ -18,7 +18,6 @@ type testGitalyServer struct{} func (s *testGitalyServer) SSHReceivePack(stream pb.SSHService_SSHReceivePackServer) error { req, err := stream.Recv() - if err != nil { return err } @@ -30,6 +29,14 @@ func (s *testGitalyServer) SSHReceivePack(stream pb.SSHService_SSHReceivePackSer } func (s *testGitalyServer) SSHUploadPack(stream pb.SSHService_SSHUploadPackServer) error { + req, err := stream.Recv() + if err != nil { + return err + } + + response := []byte("UploadPack: " + req.Repository.GlRepository) + stream.Send(&pb.SSHUploadPackResponse{Stdout: response}) + return nil } diff --git a/go/internal/testhelper/requesthandlers/requesthandlers.go b/go/internal/testhelper/requesthandlers/requesthandlers.go index d7e077b..a7bc427 100644 --- a/go/internal/testhelper/requesthandlers/requesthandlers.go +++ b/go/internal/testhelper/requesthandlers/requesthandlers.go @@ -10,6 +10,24 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver" ) +func BuildDisallowedByApiHandlers(t *testing.T) []testserver.TestRequestHandler { + requests := []testserver.TestRequestHandler{ + { + Path: "/api/v4/internal/allowed", + Handler: func(w http.ResponseWriter, r *http.Request) { + body := map[string]interface{}{ + "status": false, + "message": "Disallowed by API call", + } + w.WriteHeader(http.StatusForbidden) + require.NoError(t, json.NewEncoder(w).Encode(body)) + }, + }, + } + + return requests +} + func BuildAllowedWithGitalyHandlers(t *testing.T, gitalyAddress string) []testserver.TestRequestHandler { requests := []testserver.TestRequestHandler{ { |