Commit cde5b73c authored by Igor's avatar Igor Committed by Nick Thomas

Go implementation for git-upload-pack

parent beb58555
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" "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/receivepack"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/twofactorrecover" "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/config"
) )
...@@ -38,6 +39,8 @@ func buildCommand(args *commandargs.CommandArgs, config *config.Config, readWrit ...@@ -38,6 +39,8 @@ func buildCommand(args *commandargs.CommandArgs, config *config.Config, readWrit
return &twofactorrecover.Command{Config: config, Args: args, ReadWriter: readWriter} return &twofactorrecover.Command{Config: config, Args: args, ReadWriter: readWriter}
case commandargs.ReceivePack: case commandargs.ReceivePack:
return &receivepack.Command{Config: config, Args: args, ReadWriter: readWriter} return &receivepack.Command{Config: config, Args: args, ReadWriter: readWriter}
case commandargs.UploadPack:
return &uploadpack.Command{Config: config, Args: args, ReadWriter: readWriter}
} }
return nil return nil
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/fallback" "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/receivepack"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/twofactorrecover" "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/config"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper" "gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper"
) )
...@@ -68,6 +69,18 @@ func TestNew(t *testing.T) { ...@@ -68,6 +69,18 @@ func TestNew(t *testing.T) {
}, },
expectedType: &receivepack.Command{}, 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", desc: "it returns a Fallback command if the feature is unimplemented",
config: &config.Config{ config: &config.Config{
......
...@@ -14,6 +14,7 @@ const ( ...@@ -14,6 +14,7 @@ const (
Discover CommandType = "discover" Discover CommandType = "discover"
TwoFactorRecover CommandType = "2fa_recovery_codes" TwoFactorRecover CommandType = "2fa_recovery_codes"
ReceivePack CommandType = "git-receive-pack" ReceivePack CommandType = "git-receive-pack"
UploadPack CommandType = "git-upload-pack"
) )
var ( var (
......
...@@ -76,6 +76,13 @@ func TestParseSuccess(t *testing.T) { ...@@ -76,6 +76,13 @@ func TestParseSuccess(t *testing.T) {
"SSH_ORIGINAL_COMMAND": `git-receive-pack group/repo; any command`, "SSH_ORIGINAL_COMMAND": `git-receive-pack group/repo; any command`,
}, },
expectedArgs: &CommandArgs{SshArgs: []string{"git-receive-pack", "group/repo"}, CommandType: ReceivePack}, 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},
}, },
} }
......
...@@ -20,16 +20,8 @@ func (c *Command) performGitalyCall(response *accessverifier.Response) error { ...@@ -20,16 +20,8 @@ func (c *Command) performGitalyCall(response *accessverifier.Response) error {
Token: response.Gitaly.Token, Token: response.Gitaly.Token,
} }
repo := response.Gitaly.Repo
request := &pb.SSHReceivePackRequest{ request := &pb.SSHReceivePackRequest{
Repository: &pb.Repository{ Repository: &response.Gitaly.Repo,
StorageName: repo.StorageName,
RelativePath: repo.RelativePath,
GitObjectDirectory: repo.GitObjectDirectory,
GitAlternateObjectDirectories: repo.GitAlternateObjectDirectories,
GlRepository: repo.RepoName,
GlProjectPath: repo.ProjectPath,
},
GlId: response.UserId, GlId: response.UserId,
GlRepository: response.Repo, GlRepository: response.Repo,
GlUsername: response.Username, GlUsername: response.Username,
......
package receivepack package receivepack
import ( import (
"errors"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" "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/readwriter"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/shared/accessverifier" "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" "gitlab.com/gitlab-org/gitlab-shell/go/internal/config"
) )
var (
disallowedCommandError = errors.New("> GitLab: Disallowed command")
)
type Command struct { type Command struct {
Config *config.Config Config *config.Config
Args *commandargs.CommandArgs Args *commandargs.CommandArgs
...@@ -22,7 +17,7 @@ type Command struct { ...@@ -22,7 +17,7 @@ type Command struct {
func (c *Command) Execute() error { func (c *Command) Execute() error {
args := c.Args.SshArgs args := c.Args.SshArgs
if len(args) != 2 { if len(args) != 2 {
return disallowedCommandError return disallowedcommand.Error
} }
repo := args[1] repo := args[1]
......
...@@ -2,8 +2,6 @@ package receivepack ...@@ -2,8 +2,6 @@ package receivepack
import ( import (
"bytes" "bytes"
"encoding/json"
"net/http"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -12,23 +10,11 @@ import ( ...@@ -12,23 +10,11 @@ import (
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/readwriter" "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/config"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver" "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) { func TestForbiddenAccess(t *testing.T) {
requests := []testserver.TestRequestHandler{ requests := requesthandlers.BuildDisallowedByApiHandlers(t)
{
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))
},
},
}
url, cleanup := testserver.StartHttpServer(t, requests) url, cleanup := testserver.StartHttpServer(t, requests)
defer cleanup() defer cleanup()
......
package disallowedcommand
import "errors"
var (
Error = errors.New("> GitLab: Disallowed command")
)
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,
}
request := &pb.SSHUploadPackRequest{
Repository: &response.Gitaly.Repo,
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)
})
}
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())
}
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)
}
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())
}
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
pb "gitlab.com/gitlab-org/gitaly-proto/go/gitalypb"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/config" "gitlab.com/gitlab-org/gitlab-shell/go/internal/config"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet" "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet"
...@@ -27,19 +28,10 @@ type Request struct { ...@@ -27,19 +28,10 @@ type Request struct {
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
} }
type GitalyRepo struct {
StorageName string `json:"storage_name"`
RelativePath string `json:"relative_path"`
GitObjectDirectory string `json:"git_object_directory"`
GitAlternateObjectDirectories []string `json:"git_alternate_object_directories"`
RepoName string `json:"gl_repository"`
ProjectPath string `json:"gl_project_path"`
}
type Gitaly struct { type Gitaly struct {
Repo GitalyRepo `json:"repository"` Repo pb.Repository `json:"repository"`
Address string `json:"address"` Address string `json:"address"`
Token string `json:"token"` Token string `json:"token"`
} }
type CustomPayloadData struct { type CustomPayloadData struct {
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
pb "gitlab.com/gitlab-org/gitaly-proto/go/gitalypb"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/config" "gitlab.com/gitlab-org/gitlab-shell/go/internal/config"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet" "gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet"
...@@ -29,13 +30,13 @@ func buildExpectedResponse(who string) *Response { ...@@ -29,13 +30,13 @@ func buildExpectedResponse(who string) *Response {
Username: "root", Username: "root",
GitConfigOptions: []string{"option"}, GitConfigOptions: []string{"option"},
Gitaly: Gitaly{ Gitaly: Gitaly{
Repo: GitalyRepo{ Repo: pb.Repository{
StorageName: "default", StorageName: "default",
RelativePath: "@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git", RelativePath: "@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git",
GitObjectDirectory: "path/to/git_object_directory", GitObjectDirectory: "path/to/git_object_directory",
GitAlternateObjectDirectories: []string{"path/to/git_alternate_object_directory"}, GitAlternateObjectDirectories: []string{"path/to/git_alternate_object_directory"},
RepoName: "project-26", GlRepository: "project-26",
ProjectPath: repo, GlProjectPath: repo,
}, },
Address: "unix:gitaly.socket", Address: "unix:gitaly.socket",
Token: "token", Token: "token",
......
...@@ -18,7 +18,6 @@ type testGitalyServer struct{} ...@@ -18,7 +18,6 @@ type testGitalyServer struct{}
func (s *testGitalyServer) SSHReceivePack(stream pb.SSHService_SSHReceivePackServer) error { func (s *testGitalyServer) SSHReceivePack(stream pb.SSHService_SSHReceivePackServer) error {
req, err := stream.Recv() req, err := stream.Recv()
if err != nil { if err != nil {
return err return err
} }
...@@ -30,6 +29,14 @@ func (s *testGitalyServer) SSHReceivePack(stream pb.SSHService_SSHReceivePackSer ...@@ -30,6 +29,14 @@ func (s *testGitalyServer) SSHReceivePack(stream pb.SSHService_SSHReceivePackSer
} }
func (s *testGitalyServer) SSHUploadPack(stream pb.SSHService_SSHUploadPackServer) error { 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 return nil
} }
......
...@@ -10,6 +10,24 @@ import ( ...@@ -10,6 +10,24 @@ import (
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver" "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 { func BuildAllowedWithGitalyHandlers(t *testing.T, gitalyAddress string) []testserver.TestRequestHandler {
requests := []testserver.TestRequestHandler{ requests := []testserver.TestRequestHandler{
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment