Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-shell
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-shell
Commits
033c81d5
Commit
033c81d5
authored
May 31, 2019
by
Igor
Committed by
Nick Thomas
May 31, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Go implementation for git-receive-pack
parent
12ca54c2
Changes
37
Hide whitespace changes
Inline
Side-by-side
Showing
37 changed files
with
1688 additions
and
102 deletions
+1688
-102
go/internal/command/command.go
go/internal/command/command.go
+3
-0
go/internal/command/command_test.go
go/internal/command/command_test.go
+19
-4
go/internal/command/commandargs/command_args.go
go/internal/command/commandargs/command_args.go
+32
-11
go/internal/command/commandargs/command_args_test.go
go/internal/command/commandargs/command_args_test.go
+55
-14
go/internal/command/discover/discover_test.go
go/internal/command/discover/discover_test.go
+6
-9
go/internal/command/receivepack/customaction.go
go/internal/command/receivepack/customaction.go
+99
-0
go/internal/command/receivepack/customaction_test.go
go/internal/command/receivepack/customaction_test.go
+105
-0
go/internal/command/receivepack/gitalycall.go
go/internal/command/receivepack/gitalycall.go
+47
-0
go/internal/command/receivepack/gitalycall_test.go
go/internal/command/receivepack/gitalycall_test.go
+40
-0
go/internal/command/receivepack/receivepack.go
go/internal/command/receivepack/receivepack.go
+45
-0
go/internal/command/receivepack/receivepack_test.go
go/internal/command/receivepack/receivepack_test.go
+46
-0
go/internal/command/shared/accessverifier/accessverifier.go
go/internal/command/shared/accessverifier/accessverifier.go
+45
-0
go/internal/command/shared/accessverifier/accessverifier_test.go
...rnal/command/shared/accessverifier/accessverifier_test.go
+82
-0
go/internal/command/twofactorrecover/twofactorrecover_test.go
...nternal/command/twofactorrecover/twofactorrecover_test.go
+1
-2
go/internal/gitlabnet/accessverifier/client.go
go/internal/gitlabnet/accessverifier/client.go
+119
-0
go/internal/gitlabnet/accessverifier/client_test.go
go/internal/gitlabnet/accessverifier/client_test.go
+208
-0
go/internal/gitlabnet/client.go
go/internal/gitlabnet/client.go
+4
-6
go/internal/gitlabnet/client_test.go
go/internal/gitlabnet/client_test.go
+3
-3
go/internal/gitlabnet/discover/client_test.go
go/internal/gitlabnet/discover/client_test.go
+1
-2
go/internal/gitlabnet/httpclient_test.go
go/internal/gitlabnet/httpclient_test.go
+1
-2
go/internal/gitlabnet/httpsclient_test.go
go/internal/gitlabnet/httpsclient_test.go
+1
-2
go/internal/gitlabnet/testserver/gitalyserver.go
go/internal/gitlabnet/testserver/gitalyserver.go
+63
-0
go/internal/gitlabnet/testserver/testserver.go
go/internal/gitlabnet/testserver/testserver.go
+13
-16
go/internal/gitlabnet/twofactorrecover/client_test.go
go/internal/gitlabnet/twofactorrecover/client_test.go
+1
-2
go/internal/handler/exec.go
go/internal/handler/exec.go
+80
-29
go/internal/testhelper/requesthandlers/requesthandlers.go
go/internal/testhelper/requesthandlers/requesthandlers.go
+40
-0
go/internal/testhelper/testdata/testroot/responses/allowed.json
...ernal/testhelper/testdata/testroot/responses/allowed.json
+22
-0
go/internal/testhelper/testdata/testroot/responses/allowed_with_payload.json
...per/testdata/testroot/responses/allowed_with_payload.json
+31
-0
go/vendor/github.com/mattn/go-shellwords/LICENSE
go/vendor/github.com/mattn/go-shellwords/LICENSE
+21
-0
go/vendor/github.com/mattn/go-shellwords/README.md
go/vendor/github.com/mattn/go-shellwords/README.md
+47
-0
go/vendor/github.com/mattn/go-shellwords/go.mod
go/vendor/github.com/mattn/go-shellwords/go.mod
+1
-0
go/vendor/github.com/mattn/go-shellwords/shellwords.go
go/vendor/github.com/mattn/go-shellwords/shellwords.go
+195
-0
go/vendor/github.com/mattn/go-shellwords/util_go15.go
go/vendor/github.com/mattn/go-shellwords/util_go15.go
+24
-0
go/vendor/github.com/mattn/go-shellwords/util_posix.go
go/vendor/github.com/mattn/go-shellwords/util_posix.go
+22
-0
go/vendor/github.com/mattn/go-shellwords/util_windows.go
go/vendor/github.com/mattn/go-shellwords/util_windows.go
+22
-0
go/vendor/vendor.json
go/vendor/vendor.json
+10
-0
spec/gitlab_shell_custom_git_receive_pack_spec.rb
spec/gitlab_shell_custom_git_receive_pack_spec.rb
+134
-0
No files found.
go/internal/command/command.go
View file @
033c81d5
...
...
@@ -5,6 +5,7 @@ import (
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/discover"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/fallback"
"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/config"
)
...
...
@@ -33,6 +34,8 @@ func buildCommand(args *commandargs.CommandArgs, config *config.Config, readWrit
return
&
discover
.
Command
{
Config
:
config
,
Args
:
args
,
ReadWriter
:
readWriter
}
case
commandargs
.
TwoFactorRecover
:
return
&
twofactorrecover
.
Command
{
Config
:
config
,
Args
:
args
,
ReadWriter
:
readWriter
}
case
commandargs
.
ReceivePack
:
return
&
receivepack
.
Command
{
Config
:
config
,
Args
:
args
,
ReadWriter
:
readWriter
}
}
return
nil
...
...
go/internal/command/command_test.go
View file @
033c81d5
...
...
@@ -3,9 +3,11 @@ package command
import
(
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/discover"
"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/config"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper"
...
...
@@ -58,6 +60,19 @@ func TestNew(t *testing.T) {
},
expectedType
:
&
twofactorrecover
.
Command
{},
},
{
desc
:
"it returns a ReceivePack command if the feature is enabled"
,
arguments
:
[]
string
{},
config
:
&
config
.
Config
{
GitlabUrl
:
"http+unix://gitlab.socket"
,
Migration
:
config
.
MigrationConfig
{
Enabled
:
true
,
Features
:
[]
string
{
"git-receive-pack"
}},
},
environment
:
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
"git-receive-pack"
,
},
expectedType
:
&
receivepack
.
Command
{},
},
}
for
_
,
tc
:=
range
testCases
{
...
...
@@ -67,8 +82,8 @@ func TestNew(t *testing.T) {
command
,
err
:=
New
(
tc
.
arguments
,
tc
.
config
,
nil
)
assert
.
NoError
(
t
,
err
)
assert
.
IsType
(
t
,
tc
.
expectedType
,
command
)
require
.
NoError
(
t
,
err
)
require
.
IsType
(
t
,
tc
.
expectedType
,
command
)
})
}
}
...
...
@@ -80,6 +95,6 @@ func TestFailingNew(t *testing.T) {
_
,
err
:=
New
([]
string
{},
&
config
.
Config
{},
nil
)
assert
.
Error
(
t
,
err
,
"Only ssh allowed"
)
require
.
Error
(
t
,
err
,
"Only ssh allowed"
)
})
}
go/internal/command/commandargs/command_args.go
View file @
033c81d5
...
...
@@ -4,6 +4,8 @@ import (
"errors"
"os"
"regexp"
"github.com/mattn/go-shellwords"
)
type
CommandType
string
...
...
@@ -11,6 +13,7 @@ type CommandType string
const
(
Discover
CommandType
=
"discover"
TwoFactorRecover
CommandType
=
"2fa_recovery_codes"
ReceivePack
CommandType
=
"git-receive-pack"
)
var
(
...
...
@@ -21,7 +24,7 @@ var (
type
CommandArgs
struct
{
GitlabUsername
string
GitlabKeyId
string
Ssh
Command
string
Ssh
Args
[]
string
CommandType
CommandType
}
...
...
@@ -30,12 +33,15 @@ func Parse(arguments []string) (*CommandArgs, error) {
return
nil
,
errors
.
New
(
"Only ssh allowed"
)
}
info
:=
&
CommandArgs
{}
args
:=
&
CommandArgs
{}
args
.
parseWho
(
arguments
)
info
.
parseWho
(
arguments
)
info
.
parseCommand
(
os
.
Getenv
(
"SSH_ORIGINAL_COMMAND"
))
if
err
:=
args
.
parseCommand
(
os
.
Getenv
(
"SSH_ORIGINAL_COMMAND"
));
err
!=
nil
{
return
nil
,
errors
.
New
(
"Invalid ssh command"
)
}
args
.
defineCommandType
()
return
info
,
nil
return
args
,
nil
}
func
(
c
*
CommandArgs
)
parseWho
(
arguments
[]
string
)
{
...
...
@@ -74,14 +80,29 @@ func tryParseUsername(argument string) string {
return
""
}
func
(
c
*
CommandArgs
)
parseCommand
(
commandString
string
)
{
c
.
SshCommand
=
commandString
func
(
c
*
CommandArgs
)
parseCommand
(
commandString
string
)
error
{
args
,
err
:=
shellwords
.
Parse
(
commandString
)
if
err
!=
nil
{
return
err
}
if
commandString
==
""
{
c
.
CommandType
=
Discover
// Handle Git for Windows 2.14 using "git upload-pack" instead of git-upload-pack
if
len
(
args
)
>
1
&&
args
[
0
]
==
"git"
{
command
:=
args
[
0
]
+
"-"
+
args
[
1
]
commandArgs
:=
args
[
2
:
]
args
=
append
([]
string
{
command
},
commandArgs
...
)
}
if
CommandType
(
commandString
)
==
TwoFactorRecover
{
c
.
CommandType
=
TwoFactorRecover
c
.
SshArgs
=
args
return
nil
}
func
(
c
*
CommandArgs
)
defineCommandType
()
{
if
len
(
c
.
SshArgs
)
==
0
{
c
.
CommandType
=
Discover
}
else
{
c
.
CommandType
=
CommandType
(
c
.
SshArgs
[
0
])
}
}
go/internal/command/commandargs/command_args_test.go
View file @
033c81d5
...
...
@@ -3,7 +3,8 @@ package commandargs
import
(
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper"
)
...
...
@@ -22,23 +23,16 @@ func TestParseSuccess(t *testing.T) {
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
""
,
},
expectedArgs
:
&
CommandArgs
{
CommandType
:
Discover
},
expectedArgs
:
&
CommandArgs
{
SshArgs
:
[]
string
{},
CommandType
:
Discover
},
},
{
desc
:
"It passes on the original ssh command from the environment"
,
environment
:
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
"hello world"
,
},
expectedArgs
:
&
CommandArgs
{
SshCommand
:
"hello world"
},
},
{
desc
:
"It finds the key id in any passed arguments"
,
environment
:
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
""
,
},
arguments
:
[]
string
{
"hello"
,
"key-123"
},
expectedArgs
:
&
CommandArgs
{
CommandType
:
Discover
,
GitlabKeyId
:
"123"
},
expectedArgs
:
&
CommandArgs
{
SshArgs
:
[]
string
{},
CommandType
:
Discover
,
GitlabKeyId
:
"123"
},
},
{
desc
:
"It finds the username in any passed arguments"
,
environment
:
map
[
string
]
string
{
...
...
@@ -46,7 +40,42 @@ func TestParseSuccess(t *testing.T) {
"SSH_ORIGINAL_COMMAND"
:
""
,
},
arguments
:
[]
string
{
"hello"
,
"username-jane-doe"
},
expectedArgs
:
&
CommandArgs
{
CommandType
:
Discover
,
GitlabUsername
:
"jane-doe"
},
expectedArgs
:
&
CommandArgs
{
SshArgs
:
[]
string
{},
CommandType
:
Discover
,
GitlabUsername
:
"jane-doe"
},
},
{
desc
:
"It parses 2fa_recovery_codes command"
,
environment
:
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
"2fa_recovery_codes"
,
},
expectedArgs
:
&
CommandArgs
{
SshArgs
:
[]
string
{
"2fa_recovery_codes"
},
CommandType
:
TwoFactorRecover
},
},
{
desc
:
"It parses git-receive-pack command"
,
environment
:
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
"git-receive-pack group/repo"
,
},
expectedArgs
:
&
CommandArgs
{
SshArgs
:
[]
string
{
"git-receive-pack"
,
"group/repo"
},
CommandType
:
ReceivePack
},
},
{
desc
:
"It parses git-receive-pack command and a project with single quotes"
,
environment
:
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
"git receive-pack 'group/repo'"
,
},
expectedArgs
:
&
CommandArgs
{
SshArgs
:
[]
string
{
"git-receive-pack"
,
"group/repo"
},
CommandType
:
ReceivePack
},
},
{
desc
:
`It parses "git receive-pack" command`
,
environment
:
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
`git receive-pack "group/repo"`
,
},
expectedArgs
:
&
CommandArgs
{
SshArgs
:
[]
string
{
"git-receive-pack"
,
"group/repo"
},
CommandType
:
ReceivePack
},
},
{
desc
:
`It parses a command followed by control characters`
,
environment
:
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
`git-receive-pack group/repo; any command`
,
},
expectedArgs
:
&
CommandArgs
{
SshArgs
:
[]
string
{
"git-receive-pack"
,
"group/repo"
},
CommandType
:
ReceivePack
},
},
}
...
...
@@ -57,8 +86,8 @@ func TestParseSuccess(t *testing.T) {
result
,
err
:=
Parse
(
tc
.
arguments
)
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tc
.
expectedArgs
,
result
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
tc
.
expectedArgs
,
result
)
})
}
}
...
...
@@ -67,7 +96,19 @@ func TestParseFailure(t *testing.T) {
t
.
Run
(
"It fails if SSH connection is not set"
,
func
(
t
*
testing
.
T
)
{
_
,
err
:=
Parse
([]
string
{})
assert
.
Error
(
t
,
err
,
"Only ssh allowed"
)
require
.
Error
(
t
,
err
,
"Only ssh allowed"
)
})
t
.
Run
(
"It fails if SSH command is invalid"
,
func
(
t
*
testing
.
T
)
{
environment
:=
map
[
string
]
string
{
"SSH_CONNECTION"
:
"1"
,
"SSH_ORIGINAL_COMMAND"
:
`git receive-pack "`
,
}
restoreEnv
:=
testhelper
.
TempEnv
(
environment
)
defer
restoreEnv
()
_
,
err
:=
Parse
([]
string
{})
require
.
Error
(
t
,
err
,
"Invalid ssh command"
)
})
}
go/internal/command/discover/discover_test.go
View file @
033c81d5
...
...
@@ -7,7 +7,6 @@ import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/command/commandargs"
...
...
@@ -45,8 +44,7 @@ var (
)
func
TestExecute
(
t
*
testing
.
T
)
{
cleanup
,
url
,
err
:=
testserver
.
StartSocketHttpServer
(
requests
)
require
.
NoError
(
t
,
err
)
url
,
cleanup
:=
testserver
.
StartSocketHttpServer
(
t
,
requests
)
defer
cleanup
()
testCases
:=
[]
struct
{
...
...
@@ -87,15 +85,14 @@ func TestExecute(t *testing.T) {
err
:=
cmd
.
Execute
()
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tc
.
expectedOutput
,
buffer
.
String
())
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
tc
.
expectedOutput
,
buffer
.
String
())
})
}
}
func
TestFailingExecute
(
t
*
testing
.
T
)
{
cleanup
,
url
,
err
:=
testserver
.
StartSocketHttpServer
(
requests
)
require
.
NoError
(
t
,
err
)
url
,
cleanup
:=
testserver
.
StartSocketHttpServer
(
t
,
requests
)
defer
cleanup
()
testCases
:=
[]
struct
{
...
...
@@ -131,8 +128,8 @@ func TestFailingExecute(t *testing.T) {
err
:=
cmd
.
Execute
()
assert
.
Empty
(
t
,
buffer
.
String
())
assert
.
EqualError
(
t
,
err
,
tc
.
expectedError
)
require
.
Empty
(
t
,
buffer
.
String
())
require
.
EqualError
(
t
,
err
,
tc
.
expectedError
)
})
}
}
go/internal/command/receivepack/customaction.go
0 → 100644
View file @
033c81d5
package
receivepack
import
(
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/accessverifier"
)
type
Request
struct
{
SecretToken
[]
byte
`json:"secret_token"`
Data
accessverifier
.
CustomPayloadData
`json:"data"`
Output
[]
byte
`json:"output"`
}
type
Response
struct
{
Result
[]
byte
`json:"result"`
Message
string
`json:"message"`
}
func
(
c
*
Command
)
processCustomAction
(
response
*
accessverifier
.
Response
)
error
{
data
:=
response
.
Payload
.
Data
apiEndpoints
:=
data
.
ApiEndpoints
if
len
(
apiEndpoints
)
==
0
{
return
errors
.
New
(
"Custom action error: Empty API endpoints"
)
}
c
.
displayInfoMessage
(
data
.
InfoMessage
)
return
c
.
processApiEndpoints
(
response
)
}
func
(
c
*
Command
)
displayInfoMessage
(
infoMessage
string
)
{
messages
:=
strings
.
Split
(
infoMessage
,
"
\n
"
)
for
_
,
msg
:=
range
messages
{
fmt
.
Fprintf
(
c
.
ReadWriter
.
ErrOut
,
"> GitLab: %v
\n
"
,
msg
)
}
}
func
(
c
*
Command
)
processApiEndpoints
(
response
*
accessverifier
.
Response
)
error
{
client
,
err
:=
gitlabnet
.
GetClient
(
c
.
Config
)
if
err
!=
nil
{
return
err
}
data
:=
response
.
Payload
.
Data
request
:=
&
Request
{
Data
:
data
}
request
.
Data
.
UserId
=
response
.
Who
for
_
,
endpoint
:=
range
data
.
ApiEndpoints
{
response
,
err
:=
c
.
performRequest
(
client
,
endpoint
,
request
)
if
err
!=
nil
{
return
err
}
if
err
=
c
.
displayResult
(
response
.
Result
);
err
!=
nil
{
return
err
}
// In the context of the git push sequence of events, it's necessary to read
// stdin in order to capture output to pass onto subsequent commands
output
,
err
:=
ioutil
.
ReadAll
(
c
.
ReadWriter
.
In
)
if
err
!=
nil
{
return
err
}
request
.
Output
=
output
}
return
nil
}
func
(
c
*
Command
)
performRequest
(
client
*
gitlabnet
.
GitlabClient
,
endpoint
string
,
request
*
Request
)
(
*
Response
,
error
)
{
response
,
err
:=
client
.
DoRequest
(
http
.
MethodPost
,
endpoint
,
request
)
if
err
!=
nil
{
return
nil
,
err
}
defer
response
.
Body
.
Close
()
cr
:=
&
Response
{}
if
err
:=
gitlabnet
.
ParseJSON
(
response
,
cr
);
err
!=
nil
{
return
nil
,
err
}
return
cr
,
nil
}
func
(
c
*
Command
)
displayResult
(
result
[]
byte
)
error
{
_
,
err
:=
io
.
Copy
(
c
.
ReadWriter
.
Out
,
bytes
.
NewReader
(
result
))
return
err
}
go/internal/command/receivepack/customaction_test.go
0 → 100644
View file @
033c81d5
package
receivepack
import
(
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"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/accessverifier"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver"
)
func
TestCustomReceivePack
(
t
*
testing
.
T
)
{
repo
:=
"group/repo"
keyId
:=
"1"
requests
:=
[]
testserver
.
TestRequestHandler
{
{
Path
:
"/api/v4/internal/allowed"
,
Handler
:
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
b
,
err
:=
ioutil
.
ReadAll
(
r
.
Body
)
require
.
NoError
(
t
,
err
)
var
request
*
accessverifier
.
Request
require
.
NoError
(
t
,
json
.
Unmarshal
(
b
,
&
request
))
require
.
Equal
(
t
,
"1"
,
request
.
KeyId
)
body
:=
map
[
string
]
interface
{}{
"status"
:
true
,
"gl_id"
:
"1"
,
"payload"
:
map
[
string
]
interface
{}{
"action"
:
"geo_proxy_to_primary"
,
"data"
:
map
[
string
]
interface
{}{
"api_endpoints"
:
[]
string
{
"/geo/proxy_git_push_ssh/info_refs"
,
"/geo/proxy_git_push_ssh/push"
},
"gl_username"
:
"custom"
,
"primary_repo"
:
"https://repo/path"
,
"info_message"
:
"info_message
\n
one more message"
,
},
},
}
w
.
WriteHeader
(
http
.
StatusMultipleChoices
)
require
.
NoError
(
t
,
json
.
NewEncoder
(
w
)
.
Encode
(
body
))
},
},
{
Path
:
"/geo/proxy_git_push_ssh/info_refs"
,
Handler
:
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
b
,
err
:=
ioutil
.
ReadAll
(
r
.
Body
)
require
.
NoError
(
t
,
err
)
var
request
*
Request
require
.
NoError
(
t
,
json
.
Unmarshal
(
b
,
&
request
))
require
.
Equal
(
t
,
request
.
Data
.
UserId
,
"key-"
+
keyId
)
require
.
Empty
(
t
,
request
.
Output
)
err
=
json
.
NewEncoder
(
w
)
.
Encode
(
Response
{
Result
:
[]
byte
(
"custom"
)})
require
.
NoError
(
t
,
err
)
},
},
{
Path
:
"/geo/proxy_git_push_ssh/push"
,
Handler
:
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
b
,
err
:=
ioutil
.
ReadAll
(
r
.
Body
)
require
.
NoError
(
t
,
err
)
var
request
*
Request
require
.
NoError
(
t
,
json
.
Unmarshal
(
b
,
&
request
))
require
.
Equal
(
t
,
request
.
Data
.
UserId
,
"key-"
+
keyId
)
require
.
Equal
(
t
,
"input"
,
string
(
request
.
Output
))
err
=
json
.
NewEncoder
(
w
)
.
Encode
(
Response
{
Result
:
[]
byte
(
"output"
)})
require
.
NoError
(
t
,
err
)
},
},
}
url
,
cleanup
:=
testserver
.
StartSocketHttpServer
(
t
,
requests
)
defer
cleanup
()
outBuf
:=
&
bytes
.
Buffer
{}
errBuf
:=
&
bytes
.
Buffer
{}
input
:=
bytes
.
NewBufferString
(
"input"
)
cmd
:=
&
Command
{
Config
:
&
config
.
Config
{
GitlabUrl
:
url
},
Args
:
&
commandargs
.
CommandArgs
{
GitlabKeyId
:
keyId
,
CommandType
:
commandargs
.
ReceivePack
,
SshArgs
:
[]
string
{
"git-receive-pack"
,
repo
}},
ReadWriter
:
&
readwriter
.
ReadWriter
{
ErrOut
:
errBuf
,
Out
:
outBuf
,
In
:
input
},
}
require
.
NoError
(
t
,
cmd
.
Execute
())
// expect printing of info message, "custom" string from the first request
// and "output" string from the second request
require
.
Equal
(
t
,
"> GitLab: info_message
\n
> GitLab: one more message
\n
"
,
errBuf
.
String
())
require
.
Equal
(
t
,
"customoutput"
,
outBuf
.
String
())
}
go/internal/command/receivepack/gitalycall.go
0 → 100644
View file @
033c81d5
package
receivepack
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/command/shared/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
.
ReceivePack
),
Address
:
response
.
Gitaly
.
Address
,
Token
:
response
.
Gitaly
.
Token
,
}
repo
:=
response
.
Gitaly
.
Repo
request
:=
&
pb
.
SSHReceivePackRequest
{
Repository
:
&
pb
.
Repository
{
StorageName
:
repo
.
StorageName
,
RelativePath
:
repo
.
RelativePath
,
GitObjectDirectory
:
repo
.
GitObjectDirectory
,
GitAlternateObjectDirectories
:
repo
.
GitAlternateObjectDirectories
,
GlRepository
:
repo
.
RepoName
,
GlProjectPath
:
repo
.
ProjectPath
,
},
GlId
:
response
.
UserId
,
GlRepository
:
response
.
Repo
,
GlUsername
:
response
.
Username
,
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
.
ReceivePack
(
ctx
,
conn
,
rw
.
In
,
rw
.
Out
,
rw
.
ErrOut
,
request
)
})
}
go/internal/command/receivepack/gitalycall_test.go
0 → 100644
View file @
033c81d5
package
receivepack
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
TestReceivePack
(
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
.
ReceivePack
,
SshArgs
:
[]
string
{
"git-receive-pack"
,
repo
}},
ReadWriter
:
&
readwriter
.
ReadWriter
{
ErrOut
:
output
,
Out
:
output
,
In
:
input
},
}
err
:=
cmd
.
Execute
()
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
"ReceivePack: "
+
userId
+
" "
+
repo
,
output
.
String
())
}
go/internal/command/receivepack/receivepack.go
0 → 100644
View file @
033c81d5
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/config"
)
var
(
disallowedCommandError
=
errors
.
New
(
"> GitLab: Disallowed command"
)
)
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
disallowedCommandError
}
repo
:=
args
[
1
]
response
,
err
:=
c
.
verifyAccess
(
repo
)
if
err
!=
nil
{
return
err
}
if
response
.
IsCustomAction
()
{
return
c
.
processCustomAction
(
response
)
}
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
)
}
go/internal/command/receivepack/receivepack_test.go
0 → 100644
View file @
033c81d5
package
receivepack
import
(
"bytes"
"encoding/json"
"net/http"
"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"
)
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
))
},
},
}
url
,
cleanup
:=
testserver
.
StartHttpServer
(
t
,
requests
)
defer
cleanup
()
output
:=
&
bytes
.
Buffer
{}
input
:=
bytes
.
NewBufferString
(
"input"
)
cmd
:=
&
Command
{
Config
:
&
config
.
Config
{
GitlabUrl
:
url
},
Args
:
&
commandargs
.
CommandArgs
{
GitlabKeyId
:
"disallowed"
,
SshArgs
:
[]
string
{
"git-receive-pack"
,
"group/repo"
}},
ReadWriter
:
&
readwriter
.
ReadWriter
{
ErrOut
:
output
,
Out
:
output
,
In
:
input
},
}
err
:=
cmd
.
Execute
()
require
.
Equal
(
t
,
"Disallowed by API call"
,
err
.
Error
())
}
go/internal/command/shared/accessverifier/accessverifier.go
0 → 100644
View file @
033c81d5
package
accessverifier
import
(
"errors"
"fmt"
"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/accessverifier"
)
type
Response
=
accessverifier
.
Response
type
Command
struct
{
Config
*
config
.
Config
Args
*
commandargs
.
CommandArgs
ReadWriter
*
readwriter
.
ReadWriter
}
func
(
c
*
Command
)
Verify
(
action
commandargs
.
CommandType
,
repo
string
)
(
*
Response
,
error
)
{
client
,
err
:=
accessverifier
.
NewClient
(
c
.
Config
)
if
err
!=
nil
{
return
nil
,
err
}
response
,
err
:=
client
.
Verify
(
c
.
Args
,
action
,
repo
)
if
err
!=
nil
{
return
nil
,
err
}
c
.
displayConsoleMessages
(
response
.
ConsoleMessages
)
if
!
response
.
Success
{
return
nil
,
errors
.
New
(
response
.
Message
)
}
return
response
,
nil
}
func
(
c
*
Command
)
displayConsoleMessages
(
messages
[]
string
)
{
for
_
,
msg
:=
range
messages
{
fmt
.
Fprintf
(
c
.
ReadWriter
.
ErrOut
,
"> GitLab: %v
\n
"
,
msg
)
}
}
go/internal/command/shared/accessverifier/accessverifier_test.go
0 → 100644
View file @
033c81d5
package
accessverifier
import
(
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"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/accessverifier"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver"
)
var
(
repo
=
"group/repo"
action
=
commandargs
.
ReceivePack
)
func
setup
(
t
*
testing
.
T
)
(
*
Command
,
*
bytes
.
Buffer
,
*
bytes
.
Buffer
,
func
())
{
requests
:=
[]
testserver
.
TestRequestHandler
{
{
Path
:
"/api/v4/internal/allowed"
,
Handler
:
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
b
,
err
:=
ioutil
.
ReadAll
(
r
.
Body
)
require
.
NoError
(
t
,
err
)
var
requestBody
*
accessverifier
.
Request
err
=
json
.
Unmarshal
(
b
,
&
requestBody
)
require
.
NoError
(
t
,
err
)
if
requestBody
.
KeyId
==
"1"
{
body
:=
map
[
string
]
interface
{}{
"gl_console_messages"
:
[]
string
{
"console"
,
"message"
},
}
require
.
NoError
(
t
,
json
.
NewEncoder
(
w
)
.
Encode
(
body
))
}
else
{
body
:=
map
[
string
]
interface
{}{
"status"
:
false
,
"message"
:
"missing user"
,
}
require
.
NoError
(
t
,
json
.
NewEncoder
(
w
)
.
Encode
(
body
))
}
},
},
}
url
,
cleanup
:=
testserver
.
StartSocketHttpServer
(
t
,
requests
)
errBuf
:=
&
bytes
.
Buffer
{}
outBuf
:=
&
bytes
.
Buffer
{}
readWriter
:=
&
readwriter
.
ReadWriter
{
Out
:
outBuf
,
ErrOut
:
errBuf
}
cmd
:=
&
Command
{
Config
:
&
config
.
Config
{
GitlabUrl
:
url
},
ReadWriter
:
readWriter
}
return
cmd
,
errBuf
,
outBuf
,
cleanup
}
func
TestMissingUser
(
t
*
testing
.
T
)
{
cmd
,
_
,
_
,
cleanup
:=
setup
(
t
)
defer
cleanup
()
cmd
.
Args
=
&
commandargs
.
CommandArgs
{
GitlabKeyId
:
"2"
}
_
,
err
:=
cmd
.
Verify
(
action
,
repo
)
require
.
Equal
(
t
,
"missing user"
,
err
.
Error
())
}
func
TestConsoleMessages
(
t
*
testing
.
T
)
{
cmd
,
errBuf
,
outBuf
,
cleanup
:=
setup
(
t
)
defer
cleanup
()
cmd
.
Args
=
&
commandargs
.
CommandArgs
{
GitlabKeyId
:
"1"
}
cmd
.
Verify
(
action
,
repo
)
require
.
Equal
(
t
,
"> GitLab: console
\n
> GitLab: message
\n
"
,
errBuf
.
String
())
require
.
Empty
(
t
,
outBuf
.
String
())
}
go/internal/command/twofactorrecover/twofactorrecover_test.go
View file @
033c81d5
...
...
@@ -64,8 +64,7 @@ const (
func
TestExecute
(
t
*
testing
.
T
)
{
setup
(
t
)
cleanup
,
url
,
err
:=
testserver
.
StartSocketHttpServer
(
requests
)
require
.
NoError
(
t
,
err
)
url
,
cleanup
:=
testserver
.
StartSocketHttpServer
(
t
,
requests
)
defer
cleanup
()
testCases
:=
[]
struct
{
...
...
go/internal/gitlabnet/accessverifier/client.go
0 → 100644
View file @
033c81d5
package
accessverifier
import
(
"fmt"
"net/http"
"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/gitlabnet"
)
const
(
protocol
=
"ssh"
anyChanges
=
"_any"
)
type
Client
struct
{
client
*
gitlabnet
.
GitlabClient
}
type
Request
struct
{
Action
commandargs
.
CommandType
`json:"action"`
Repo
string
`json:"project"`
Changes
string
`json:"changes"`
Protocol
string
`json:"protocol"`
KeyId
string
`json:"key_id,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
{
Repo
GitalyRepo
`json:"repository"`
Address
string
`json:"address"`
Token
string
`json:"token"`
}
type
CustomPayloadData
struct
{
ApiEndpoints
[]
string
`json:"api_endpoints"`
Username
string
`json:"gl_username"`
PrimaryRepo
string
`json:"primary_repo"`
InfoMessage
string
`json:"info_message"`
UserId
string
`json:"gl_id,omitempty"`
}
type
CustomPayload
struct
{
Action
string
`json:"action"`
Data
CustomPayloadData
`json:"data"`
}
type
Response
struct
{
Success
bool
`json:"status"`
Message
string
`json:"message"`
Repo
string
`json:"gl_repository"`
UserId
string
`json:"gl_id"`
Username
string
`json:"gl_username"`
GitConfigOptions
[]
string
`json:"git_config_options"`
Gitaly
Gitaly
`json:"gitaly"`
GitProtocol
string
`json:"git_protocol"`
Payload
CustomPayload
`json:"payload"`
ConsoleMessages
[]
string
`json:"gl_console_messages"`
Who
string
StatusCode
int
}
func
NewClient
(
config
*
config
.
Config
)
(
*
Client
,
error
)
{
client
,
err
:=
gitlabnet
.
GetClient
(
config
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"Error creating http client: %v"
,
err
)
}
return
&
Client
{
client
:
client
},
nil
}
func
(
c
*
Client
)
Verify
(
args
*
commandargs
.
CommandArgs
,
action
commandargs
.
CommandType
,
repo
string
)
(
*
Response
,
error
)
{
request
:=
&
Request
{
Action
:
action
,
Repo
:
repo
,
Protocol
:
protocol
,
Changes
:
anyChanges
}
if
args
.
GitlabUsername
!=
""
{
request
.
Username
=
args
.
GitlabUsername
}
else
{
request
.
KeyId
=
args
.
GitlabKeyId
}
response
,
err
:=
c
.
client
.
Post
(
"/allowed"
,
request
)
if
err
!=
nil
{
return
nil
,
err
}
defer
response
.
Body
.
Close
()
return
parse
(
response
,
args
)
}
func
parse
(
hr
*
http
.
Response
,
args
*
commandargs
.
CommandArgs
)
(
*
Response
,
error
)
{
response
:=
&
Response
{}
if
err
:=
gitlabnet
.
ParseJSON
(
hr
,
response
);
err
!=
nil
{
return
nil
,
err
}
if
args
.
GitlabKeyId
!=
""
{
response
.
Who
=
"key-"
+
args
.
GitlabKeyId
}
else
{
response
.
Who
=
response
.
UserId
}
response
.
StatusCode
=
hr
.
StatusCode
return
response
,
nil
}
func
(
r
*
Response
)
IsCustomAction
()
bool
{
return
r
.
StatusCode
==
http
.
StatusMultipleChoices
}
go/internal/gitlabnet/accessverifier/client_test.go
0 → 100644
View file @
033c81d5
package
accessverifier
import
(
"encoding/json"
"io/ioutil"
"net/http"
"path"
"testing"
"github.com/stretchr/testify/require"
"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/gitlabnet"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper"
)
var
(
repo
=
"group/private"
action
=
commandargs
.
ReceivePack
)
func
buildExpectedResponse
(
who
string
)
*
Response
{
response
:=
&
Response
{
Success
:
true
,
UserId
:
"user-1"
,
Repo
:
"project-26"
,
Username
:
"root"
,
GitConfigOptions
:
[]
string
{
"option"
},
Gitaly
:
Gitaly
{
Repo
:
GitalyRepo
{
StorageName
:
"default"
,
RelativePath
:
"@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git"
,
GitObjectDirectory
:
"path/to/git_object_directory"
,
GitAlternateObjectDirectories
:
[]
string
{
"path/to/git_alternate_object_directory"
},
RepoName
:
"project-26"
,
ProjectPath
:
repo
,
},
Address
:
"unix:gitaly.socket"
,
Token
:
"token"
,
},
GitProtocol
:
"protocol"
,
Payload
:
CustomPayload
{},
ConsoleMessages
:
[]
string
{
"console"
,
"message"
},
Who
:
who
,
StatusCode
:
200
,
}
return
response
}
func
TestSuccessfulResponses
(
t
*
testing
.
T
)
{
client
,
cleanup
:=
setup
(
t
)
defer
cleanup
()
testCases
:=
[]
struct
{
desc
string
args
*
commandargs
.
CommandArgs
who
string
}{
{
desc
:
"Provide key id within the request"
,
args
:
&
commandargs
.
CommandArgs
{
GitlabKeyId
:
"1"
},
who
:
"key-1"
,
},
{
desc
:
"Provide username within the request"
,
args
:
&
commandargs
.
CommandArgs
{
GitlabUsername
:
"first"
},
who
:
"user-1"
,
},
}
for
_
,
tc
:=
range
testCases
{
t
.
Run
(
tc
.
desc
,
func
(
t
*
testing
.
T
)
{
result
,
err
:=
client
.
Verify
(
tc
.
args
,
action
,
repo
)
require
.
NoError
(
t
,
err
)
response
:=
buildExpectedResponse
(
tc
.
who
)
require
.
Equal
(
t
,
response
,
result
)
})
}
}
func
TestGetCustomAction
(
t
*
testing
.
T
)
{
client
,
cleanup
:=
setup
(
t
)
defer
cleanup
()
args
:=
&
commandargs
.
CommandArgs
{
GitlabUsername
:
"custom"
}
result
,
err
:=
client
.
Verify
(
args
,
action
,
repo
)
require
.
NoError
(
t
,
err
)
response
:=
buildExpectedResponse
(
"user-1"
)
response
.
Payload
=
CustomPayload
{
Action
:
"geo_proxy_to_primary"
,
Data
:
CustomPayloadData
{
ApiEndpoints
:
[]
string
{
"geo/proxy_git_push_ssh/info_refs"
,
"geo/proxy_git_push_ssh/push"
},
Username
:
"custom"
,
PrimaryRepo
:
"https://repo/path"
,
InfoMessage
:
"message"
,
},
}
response
.
StatusCode
=
300
require
.
True
(
t
,
response
.
IsCustomAction
())
require
.
Equal
(
t
,
response
,
result
)
}
func
TestErrorResponses
(
t
*
testing
.
T
)
{
client
,
cleanup
:=
setup
(
t
)
defer
cleanup
()
testCases
:=
[]
struct
{
desc
string
fakeId
string
expectedError
string
}{
{
desc
:
"A response with an error message"
,
fakeId
:
"2"
,
expectedError
:
"Not allowed!"
,
},
{
desc
:
"A response with bad JSON"
,
fakeId
:
"3"
,
expectedError
:
"Parsing failed"
,
},
{
desc
:
"An error response without message"
,
fakeId
:
"4"
,
expectedError
:
"Internal API error (403)"
,
},
}
for
_
,
tc
:=
range
testCases
{
t
.
Run
(
tc
.
desc
,
func
(
t
*
testing
.
T
)
{
args
:=
&
commandargs
.
CommandArgs
{
GitlabKeyId
:
tc
.
fakeId
}
resp
,
err
:=
client
.
Verify
(
args
,
action
,
repo
)
require
.
EqualError
(
t
,
err
,
tc
.
expectedError
)
require
.
Nil
(
t
,
resp
)
})
}
}
func
setup
(
t
*
testing
.
T
)
(
*
Client
,
func
())
{
testDirCleanup
,
err
:=
testhelper
.
PrepareTestRootDir
()
require
.
NoError
(
t
,
err
)
defer
testDirCleanup
()
body
,
err
:=
ioutil
.
ReadFile
(
path
.
Join
(
testhelper
.
TestRoot
,
"responses/allowed.json"
))
require
.
NoError
(
t
,
err
)
allowedWithPayloadPath
:=
path
.
Join
(
testhelper
.
TestRoot
,
"responses/allowed_with_payload.json"
)
bodyWithPayload
,
err
:=
ioutil
.
ReadFile
(
allowedWithPayloadPath
)
require
.
NoError
(
t
,
err
)
requests
:=
[]
testserver
.
TestRequestHandler
{
{
Path
:
"/api/v4/internal/allowed"
,
Handler
:
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
b
,
err
:=
ioutil
.
ReadAll
(
r
.
Body
)
require
.
NoError
(
t
,
err
)
var
requestBody
*
Request
require
.
NoError
(
t
,
json
.
Unmarshal
(
b
,
&
requestBody
))
switch
requestBody
.
Username
{
case
"first"
:
_
,
err
=
w
.
Write
(
body
)
require
.
NoError
(
t
,
err
)
case
"second"
:
errBody
:=
map
[
string
]
interface
{}{
"status"
:
false
,
"message"
:
"missing user"
,
}
require
.
NoError
(
t
,
json
.
NewEncoder
(
w
)
.
Encode
(
errBody
))
case
"custom"
:
w
.
WriteHeader
(
http
.
StatusMultipleChoices
)
_
,
err
=
w
.
Write
(
bodyWithPayload
)
require
.
NoError
(
t
,
err
)
}
switch
requestBody
.
KeyId
{
case
"1"
:
_
,
err
=
w
.
Write
(
body
)
require
.
NoError
(
t
,
err
)
case
"2"
:
w
.
WriteHeader
(
http
.
StatusForbidden
)
errBody
:=
&
gitlabnet
.
ErrorResponse
{
Message
:
"Not allowed!"
,
}
require
.
NoError
(
t
,
json
.
NewEncoder
(
w
)
.
Encode
(
errBody
))
case
"3"
:
w
.
Write
([]
byte
(
"{
\"
message
\"
:
\"
broken json!
\"
"
))
case
"4"
:
w
.
WriteHeader
(
http
.
StatusForbidden
)
}
},
},
}
url
,
cleanup
:=
testserver
.
StartSocketHttpServer
(
t
,
requests
)
client
,
err
:=
NewClient
(
&
config
.
Config
{
GitlabUrl
:
url
})
require
.
NoError
(
t
,
err
)
return
client
,
cleanup
}
go/internal/gitlabnet/client.go
View file @
033c81d5
...
...
@@ -53,8 +53,6 @@ func normalizePath(path string) string {
}
func
newRequest
(
method
,
host
,
path
string
,
data
interface
{})
(
*
http
.
Request
,
error
)
{
path
=
normalizePath
(
path
)
var
jsonReader
io
.
Reader
if
data
!=
nil
{
jsonData
,
err
:=
json
.
Marshal
(
data
)
...
...
@@ -74,7 +72,7 @@ func newRequest(method, host, path string, data interface{}) (*http.Request, err
}
func
parseError
(
resp
*
http
.
Response
)
error
{
if
resp
.
StatusCode
>=
200
&&
resp
.
StatusCode
<=
2
99
{
if
resp
.
StatusCode
>=
200
&&
resp
.
StatusCode
<=
3
99
{
return
nil
}
defer
resp
.
Body
.
Close
()
...
...
@@ -89,14 +87,14 @@ func parseError(resp *http.Response) error {
}
func
(
c
*
GitlabClient
)
Get
(
path
string
)
(
*
http
.
Response
,
error
)
{
return
c
.
doRequest
(
"GET"
,
path
,
nil
)
return
c
.
DoRequest
(
http
.
MethodGet
,
normalizePath
(
path
)
,
nil
)
}
func
(
c
*
GitlabClient
)
Post
(
path
string
,
data
interface
{})
(
*
http
.
Response
,
error
)
{
return
c
.
doRequest
(
"POST"
,
path
,
data
)
return
c
.
DoRequest
(
http
.
MethodPost
,
normalizePath
(
path
)
,
data
)
}
func
(
c
*
GitlabClient
)
d
oRequest
(
method
,
path
string
,
data
interface
{})
(
*
http
.
Response
,
error
)
{
func
(
c
*
GitlabClient
)
D
oRequest
(
method
,
path
string
,
data
interface
{})
(
*
http
.
Response
,
error
)
{
request
,
err
:=
newRequest
(
method
,
c
.
host
,
path
,
data
)
if
err
!=
nil
{
return
nil
,
err
...
...
go/internal/gitlabnet/client_test.go
View file @
033c81d5
...
...
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"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"
...
...
@@ -71,7 +72,7 @@ func TestClients(t *testing.T) {
testCases
:=
[]
struct
{
desc
string
config
*
config
.
Config
server
func
(
[]
testserver
.
TestRequestHandler
)
(
func
(),
string
,
error
)
server
func
(
*
testing
.
T
,
[]
testserver
.
TestRequestHandler
)
(
string
,
func
()
)
}{
{
desc
:
"Socket client"
,
...
...
@@ -94,9 +95,8 @@ func TestClients(t *testing.T) {
for
_
,
tc
:=
range
testCases
{
t
.
Run
(
tc
.
desc
,
func
(
t
*
testing
.
T
)
{
cleanup
,
url
,
err
:=
tc
.
server
(
requests
)
url
,
cleanup
:=
tc
.
server
(
t
,
requests
)
defer
cleanup
()
require
.
NoError
(
t
,
err
)
tc
.
config
.
GitlabUrl
=
url
tc
.
config
.
Secret
=
"sssh, it's a secret"
...
...
go/internal/gitlabnet/discover/client_test.go
View file @
033c81d5
...
...
@@ -128,8 +128,7 @@ func TestErrorResponses(t *testing.T) {
}
func
setup
(
t
*
testing
.
T
)
(
*
Client
,
func
())
{
cleanup
,
url
,
err
:=
testserver
.
StartSocketHttpServer
(
requests
)
require
.
NoError
(
t
,
err
)
url
,
cleanup
:=
testserver
.
StartSocketHttpServer
(
t
,
requests
)
client
,
err
:=
NewClient
(
&
config
.
Config
{
GitlabUrl
:
url
})
require
.
NoError
(
t
,
err
)
...
...
go/internal/gitlabnet/httpclient_test.go
View file @
033c81d5
...
...
@@ -86,8 +86,7 @@ func TestEmptyBasicAuthSettings(t *testing.T) {
}
func
setup
(
t
*
testing
.
T
,
config
*
config
.
Config
,
requests
[]
testserver
.
TestRequestHandler
)
(
*
GitlabClient
,
func
())
{
cleanup
,
url
,
err
:=
testserver
.
StartHttpServer
(
requests
)
require
.
NoError
(
t
,
err
)
url
,
cleanup
:=
testserver
.
StartHttpServer
(
t
,
requests
)
config
.
GitlabUrl
=
url
client
,
err
:=
GetClient
(
config
)
...
...
go/internal/gitlabnet/httpsclient_test.go
View file @
033c81d5
...
...
@@ -115,8 +115,7 @@ func setupWithRequests(t *testing.T, config *config.Config) (*GitlabClient, func
},
}
cleanup
,
url
,
err
:=
testserver
.
StartHttpsServer
(
requests
)
require
.
NoError
(
t
,
err
)
url
,
cleanup
:=
testserver
.
StartHttpsServer
(
t
,
requests
)
config
.
GitlabUrl
=
url
client
,
err
:=
GetClient
(
config
)
...
...
go/internal/gitlabnet/testserver/gitalyserver.go
0 → 100644
View file @
033c81d5
package
testserver
import
(
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
pb
"gitlab.com/gitlab-org/gitaly-proto/go/gitalypb"
)
type
testGitalyServer
struct
{}
func
(
s
*
testGitalyServer
)
SSHReceivePack
(
stream
pb
.
SSHService_SSHReceivePackServer
)
error
{
req
,
err
:=
stream
.
Recv
()
if
err
!=
nil
{
return
err
}
response
:=
[]
byte
(
"ReceivePack: "
+
req
.
GlId
+
" "
+
req
.
Repository
.
GlRepository
)
stream
.
Send
(
&
pb
.
SSHReceivePackResponse
{
Stdout
:
response
})
return
nil
}
func
(
s
*
testGitalyServer
)
SSHUploadPack
(
stream
pb
.
SSHService_SSHUploadPackServer
)
error
{
return
nil
}
func
(
s
*
testGitalyServer
)
SSHUploadArchive
(
stream
pb
.
SSHService_SSHUploadArchiveServer
)
error
{
return
nil
}
func
StartGitalyServer
(
t
*
testing
.
T
)
(
string
,
func
())
{
tempDir
,
_
:=
ioutil
.
TempDir
(
""
,
"gitlab-shell-test-api"
)
gitalySocketPath
:=
path
.
Join
(
tempDir
,
"gitaly.sock"
)
err
:=
os
.
MkdirAll
(
filepath
.
Dir
(
gitalySocketPath
),
0700
)
require
.
NoError
(
t
,
err
)
server
:=
grpc
.
NewServer
()
listener
,
err
:=
net
.
Listen
(
"unix"
,
gitalySocketPath
)
require
.
NoError
(
t
,
err
)
pb
.
RegisterSSHServiceServer
(
server
,
&
testGitalyServer
{})
go
server
.
Serve
(
listener
)
gitalySocketUrl
:=
"unix:"
+
gitalySocketPath
cleanup
:=
func
()
{
server
.
Stop
()
os
.
RemoveAll
(
tempDir
)
}
return
gitalySocketUrl
,
cleanup
}
go/internal/gitlabnet/testserver/testserver.go
View file @
033c81d5
...
...
@@ -10,6 +10,9 @@ import (
"os"
"path"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/testhelper"
)
...
...
@@ -24,15 +27,12 @@ type TestRequestHandler struct {
Handler
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
}
func
StartSocketHttpServer
(
handlers
[]
TestRequestHandler
)
(
func
(),
string
,
error
)
{
if
err
:=
os
.
MkdirAll
(
filepath
.
Dir
(
testSocket
),
0700
);
err
!=
nil
{
return
nil
,
""
,
err
}
func
StartSocketHttpServer
(
t
*
testing
.
T
,
handlers
[]
TestRequestHandler
)
(
string
,
func
())
{
err
:=
os
.
MkdirAll
(
filepath
.
Dir
(
testSocket
),
0700
)
require
.
NoError
(
t
,
err
)
socketListener
,
err
:=
net
.
Listen
(
"unix"
,
testSocket
)
if
err
!=
nil
{
return
nil
,
""
,
err
}
require
.
NoError
(
t
,
err
)
server
:=
http
.
Server
{
Handler
:
buildHandler
(
handlers
),
...
...
@@ -44,30 +44,27 @@ func StartSocketHttpServer(handlers []TestRequestHandler) (func(), string, error
url
:=
"http+unix://"
+
testSocket
return
cleanupSocket
,
url
,
nil
return
url
,
cleanupSocket
}
func
StartHttpServer
(
handlers
[]
TestRequestHandler
)
(
func
(),
string
,
error
)
{
func
StartHttpServer
(
t
*
testing
.
T
,
handlers
[]
TestRequestHandler
)
(
string
,
func
()
)
{
server
:=
httptest
.
NewServer
(
buildHandler
(
handlers
))
return
server
.
Close
,
server
.
URL
,
nil
return
server
.
URL
,
server
.
Close
}
func
StartHttpsServer
(
handlers
[]
TestRequestHandler
)
(
func
(),
string
,
error
)
{
func
StartHttpsServer
(
t
*
testing
.
T
,
handlers
[]
TestRequestHandler
)
(
string
,
func
()
)
{
crt
:=
path
.
Join
(
testhelper
.
TestRoot
,
"certs/valid/server.crt"
)
key
:=
path
.
Join
(
testhelper
.
TestRoot
,
"certs/valid/server.key"
)
server
:=
httptest
.
NewUnstartedServer
(
buildHandler
(
handlers
))
cer
,
err
:=
tls
.
LoadX509KeyPair
(
crt
,
key
)
if
err
!=
nil
{
return
nil
,
""
,
err
}
require
.
NoError
(
t
,
err
)
server
.
TLS
=
&
tls
.
Config
{
Certificates
:
[]
tls
.
Certificate
{
cer
}}
server
.
StartTLS
()
return
server
.
Close
,
server
.
URL
,
nil
return
server
.
URL
,
server
.
Close
}
func
cleanupSocket
()
{
...
...
go/internal/gitlabnet/twofactorrecover/client_test.go
View file @
033c81d5
...
...
@@ -149,8 +149,7 @@ func TestErrorResponses(t *testing.T) {
func
setup
(
t
*
testing
.
T
)
(
*
Client
,
func
())
{
initialize
(
t
)
cleanup
,
url
,
err
:=
testserver
.
StartSocketHttpServer
(
requests
)
require
.
NoError
(
t
,
err
)
url
,
cleanup
:=
testserver
.
StartSocketHttpServer
(
t
,
requests
)
client
,
err
:=
NewClient
(
&
config
.
Config
{
GitlabUrl
:
url
})
require
.
NoError
(
t
,
err
)
...
...
go/internal/handler/exec.go
View file @
033c81d5
...
...
@@ -14,11 +14,29 @@ import (
"google.golang.org/grpc"
)
// GitalyHandlerFunc implementations are responsible for deserializing
// GitalyHandlerFunc
WithJSON
implementations are responsible for deserializing
// the request JSON into a GRPC request message, making an appropriate Gitaly
// call with the request, using the provided client, and returning the exit code
// or error from the Gitaly call.
type
GitalyHandlerFunc
func
(
ctx
context
.
Context
,
client
*
grpc
.
ClientConn
,
requestJSON
string
)
(
int32
,
error
)
type
GitalyHandlerFuncWithJSON
func
(
ctx
context
.
Context
,
client
*
grpc
.
ClientConn
,
requestJSON
string
)
(
int32
,
error
)
// GitalyHandlerFunc implementations are responsible for making
// an appropriate Gitaly call using the provided client and context
// and returning an error from the Gitaly call.
type
GitalyHandlerFunc
func
(
ctx
context
.
Context
,
client
*
grpc
.
ClientConn
)
(
int32
,
error
)
type
GitalyConn
struct
{
ctx
context
.
Context
conn
*
grpc
.
ClientConn
close
func
()
}
type
GitalyCommand
struct
{
Config
*
config
.
Config
ServiceName
string
Address
string
Token
string
}
// RunGitalyCommand provides a bootstrap for Gitaly commands executed
// through GitLab-Shell. It ensures that logging, tracing and other
...
...
@@ -26,7 +44,7 @@ type GitalyHandlerFunc func(ctx context.Context, client *grpc.ClientConn, reques
// RunGitalyCommand will handle errors internally and call
// `os.Exit()` on completion. This method will never return to
// the caller.
func
RunGitalyCommand
(
handler
GitalyHandlerFunc
)
{
func
RunGitalyCommand
(
handler
GitalyHandlerFunc
WithJSON
)
{
exitCode
,
err
:=
internalRunGitalyCommand
(
os
.
Args
,
handler
)
if
err
!=
nil
{
...
...
@@ -36,10 +54,25 @@ func RunGitalyCommand(handler GitalyHandlerFunc) {
os
.
Exit
(
exitCode
)
}
// internalRunGitalyCommand is like RunGitalyCommand, except that since it doesn't
// call os.Exit, we can rely on its deferred handlers executing correctly
func
internalRunGitalyCommand
(
args
[]
string
,
handler
GitalyHandlerFunc
)
(
int
,
error
)
{
// RunGitalyCommand provides a bootstrap for Gitaly commands executed
// through GitLab-Shell. It ensures that logging, tracing and other
// common concerns are configured before executing the `handler`.
func
(
gc
*
GitalyCommand
)
RunGitalyCommand
(
handler
GitalyHandlerFunc
)
error
{
gitalyConn
,
err
:=
getConn
(
gc
)
if
err
!=
nil
{
return
err
}
_
,
err
=
handler
(
gitalyConn
.
ctx
,
gitalyConn
.
conn
)
gitalyConn
.
close
()
return
err
}
// internalRunGitalyCommand runs Gitaly's command by particular Gitaly address and token
func
internalRunGitalyCommand
(
args
[]
string
,
handler
GitalyHandlerFuncWithJSON
)
(
int
,
error
)
{
if
len
(
args
)
!=
3
{
return
1
,
fmt
.
Errorf
(
"expected 2 arguments, got %v"
,
args
)
}
...
...
@@ -53,13 +86,44 @@ func internalRunGitalyCommand(args []string, handler GitalyHandlerFunc) (int, er
return
1
,
err
}
gc
:=
&
GitalyCommand
{
Config
:
cfg
,
ServiceName
:
args
[
0
],
Address
:
args
[
1
],
Token
:
os
.
Getenv
(
"GITALY_TOKEN"
),
}
requestJSON
:=
string
(
args
[
2
])
gitalyConn
,
err
:=
getConn
(
gc
)
if
err
!=
nil
{
return
1
,
err
}
exitCode
,
err
:=
handler
(
gitalyConn
.
ctx
,
gitalyConn
.
conn
,
requestJSON
)
gitalyConn
.
close
()
return
int
(
exitCode
),
err
}
func
getConn
(
gc
*
GitalyCommand
)
(
*
GitalyConn
,
error
)
{
if
gc
.
Address
==
""
{
return
nil
,
fmt
.
Errorf
(
"no gitaly_address given"
)
}
connOpts
:=
client
.
DefaultDialOpts
if
gc
.
Token
!=
""
{
connOpts
=
append
(
client
.
DefaultDialOpts
,
grpc
.
WithPerRPCCredentials
(
gitalyauth
.
RPCCredentialsV2
(
gc
.
Token
)))
}
// Use a working directory that won't get removed or unmounted.
if
err
:=
os
.
Chdir
(
"/"
);
err
!=
nil
{
return
1
,
err
return
nil
,
err
}
// Configure distributed tracing
serviceName
:=
fmt
.
Sprintf
(
"gitlab-shell-%v"
,
args
[
0
]
)
serviceName
:=
fmt
.
Sprintf
(
"gitlab-shell-%v"
,
gc
.
ServiceName
)
closer
:=
tracing
.
Initialize
(
tracing
.
WithServiceName
(
serviceName
),
...
...
@@ -71,34 +135,21 @@ func internalRunGitalyCommand(args []string, handler GitalyHandlerFunc) (int, er
// Processes are spawned as children of the SSH daemon, which tightly
// controls environment variables; doing this means we don't have to
// enable PermitUserEnvironment
tracing
.
WithConnectionString
(
cf
g
.
GitlabTracing
),
tracing
.
WithConnectionString
(
gc
.
Confi
g
.
GitlabTracing
),
)
defer
closer
.
Close
()
ctx
,
finished
:=
tracing
.
ExtractFromEnv
(
context
.
Background
())
defer
finished
()
gitalyAddress
:=
args
[
1
]
if
gitalyAddress
==
""
{
return
1
,
fmt
.
Errorf
(
"no gitaly_address given"
)
}
conn
,
err
:=
client
.
Dial
(
gitalyAddress
,
dialOpts
())
conn
,
err
:=
client
.
Dial
(
gc
.
Address
,
connOpts
)
if
err
!=
nil
{
return
1
,
err
return
nil
,
err
}
defer
conn
.
Close
()
requestJSON
:=
string
(
args
[
2
])
exitCode
,
err
:=
handler
(
ctx
,
conn
,
requestJSON
)
return
int
(
exitCode
),
err
}
func
dialOpts
()
[]
grpc
.
DialOption
{
connOpts
:=
client
.
DefaultDialOpts
if
token
:=
os
.
Getenv
(
"GITALY_TOKEN"
);
token
!=
""
{
connOpts
=
append
(
client
.
DefaultDialOpts
,
grpc
.
WithPerRPCCredentials
(
gitalyauth
.
RPCCredentialsV2
(
token
)))
finish
:=
func
()
{
finished
()
closer
.
Close
()
conn
.
Close
()
}
return
connOpts
return
&
GitalyConn
{
ctx
:
ctx
,
conn
:
conn
,
close
:
finish
},
nil
}
go/internal/testhelper/requesthandlers/requesthandlers.go
0 → 100644
View file @
033c81d5
package
requesthandlers
import
(
"encoding/json"
"net/http"
"testing"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/gitlabnet/testserver"
)
func
BuildAllowedWithGitalyHandlers
(
t
*
testing
.
T
,
gitalyAddress
string
)
[]
testserver
.
TestRequestHandler
{
requests
:=
[]
testserver
.
TestRequestHandler
{
{
Path
:
"/api/v4/internal/allowed"
,
Handler
:
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
body
:=
map
[
string
]
interface
{}{
"status"
:
true
,
"gl_id"
:
"1"
,
"gitaly"
:
map
[
string
]
interface
{}{
"repository"
:
map
[
string
]
interface
{}{
"storage_name"
:
"storage_name"
,
"relative_path"
:
"relative_path"
,
"git_object_directory"
:
"path/to/git_object_directory"
,
"git_alternate_object_directories"
:
[]
string
{
"path/to/git_alternate_object_directory"
},
"gl_repository"
:
"group/repo"
,
"gl_project_path"
:
"group/project-path"
,
},
"address"
:
gitalyAddress
,
"token"
:
"token"
,
},
}
require
.
NoError
(
t
,
json
.
NewEncoder
(
w
)
.
Encode
(
body
))
},
},
}
return
requests
}
go/internal/testhelper/testdata/testroot/responses/allowed.json
0 → 100644
View file @
033c81d5
{
"status"
:
true
,
"gl_repository"
:
"project-26"
,
"gl_project_path"
:
"group/private"
,
"gl_id"
:
"user-1"
,
"gl_username"
:
"root"
,
"git_config_options"
:
[
"option"
],
"gitaly"
:
{
"repository"
:
{
"storage_name"
:
"default"
,
"relative_path"
:
"@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git"
,
"git_object_directory"
:
"path/to/git_object_directory"
,
"git_alternate_object_directories"
:
[
"path/to/git_alternate_object_directory"
],
"gl_repository"
:
"project-26"
,
"gl_project_path"
:
"group/private"
},
"address"
:
"unix:gitaly.socket"
,
"token"
:
"token"
},
"git_protocol"
:
"protocol"
,
"gl_console_messages"
:
[
"console"
,
"message"
]
}
go/internal/testhelper/testdata/testroot/responses/allowed_with_payload.json
0 → 100644
View file @
033c81d5
{
"status"
:
true
,
"gl_repository"
:
"project-26"
,
"gl_project_path"
:
"group/private"
,
"gl_id"
:
"user-1"
,
"gl_username"
:
"root"
,
"git_config_options"
:
[
"option"
],
"gitaly"
:
{
"repository"
:
{
"storage_name"
:
"default"
,
"relative_path"
:
"@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git"
,
"git_object_directory"
:
"path/to/git_object_directory"
,
"git_alternate_object_directories"
:
[
"path/to/git_alternate_object_directory"
],
"gl_repository"
:
"project-26"
,
"gl_project_path"
:
"group/private"
},
"address"
:
"unix:gitaly.socket"
,
"token"
:
"token"
},
"payload"
:
{
"action"
:
"geo_proxy_to_primary"
,
"data"
:
{
"api_endpoints"
:
[
"geo/proxy_git_push_ssh/info_refs"
,
"geo/proxy_git_push_ssh/push"
],
"gl_username"
:
"custom"
,
"primary_repo"
:
"https://repo/path"
,
"info_message"
:
"message"
}
},
"git_protocol"
:
"protocol"
,
"gl_console_messages"
:
[
"console"
,
"message"
]
}
go/vendor/github.com/mattn/go-shellwords/LICENSE
0 → 100644
View file @
033c81d5
The MIT License (MIT)
Copyright (c) 2017 Yasuhiro Matsumoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
go/vendor/github.com/mattn/go-shellwords/README.md
0 → 100644
View file @
033c81d5
# go-shellwords
[
![Coverage Status
](
https://coveralls.io/repos/mattn/go-shellwords/badge.png?branch=master
)
](https://coveralls.io/r/mattn/go-shellwords?branch=master)
[
![Build Status
](
https://travis-ci.org/mattn/go-shellwords.svg?branch=master
)
](https://travis-ci.org/mattn/go-shellwords)
Parse line as shell words.
## Usage
```
go
args
,
err
:=
shellwords
.
Parse
(
"./foo --bar=baz"
)
// args should be ["./foo", "--bar=baz"]
```
```
go
os
.
Setenv
(
"FOO"
,
"bar"
)
p
:=
shellwords
.
NewParser
()
p
.
ParseEnv
=
true
args
,
err
:=
p
.
Parse
(
"./foo $FOO"
)
// args should be ["./foo", "bar"]
```
```
go
p
:=
shellwords
.
NewParser
()
p
.
ParseBacktick
=
true
args
,
err
:=
p
.
Parse
(
"./foo `echo $SHELL`"
)
// args should be ["./foo", "/bin/bash"]
```
```
go
shellwords
.
ParseBacktick
=
true
p
:=
shellwords
.
NewParser
()
args
,
err
:=
p
.
Parse
(
"./foo `echo $SHELL`"
)
// args should be ["./foo", "/bin/bash"]
```
# Thanks
This is based on cpan module
[
Parse::CommandLine
](
https://metacpan.org/pod/Parse::CommandLine
)
.
# License
under the MIT License: http://mattn.mit-license.org/2017
# Author
Yasuhiro Matsumoto (a.k.a mattn)
go/vendor/github.com/mattn/go-shellwords/go.mod
0 → 100644
View file @
033c81d5
module github.com/mattn/go-shellwords
go/vendor/github.com/mattn/go-shellwords/shellwords.go
0 → 100644
View file @
033c81d5
package
shellwords
import
(
"errors"
"os"
"regexp"
"strings"
)
var
(
ParseEnv
bool
=
false
ParseBacktick
bool
=
false
)
var
envRe
=
regexp
.
MustCompile
(
`\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)`
)
func
isSpace
(
r
rune
)
bool
{
switch
r
{
case
' '
,
'\t'
,
'\r'
,
'\n'
:
return
true
}
return
false
}
func
replaceEnv
(
getenv
func
(
string
)
string
,
s
string
)
string
{
if
getenv
==
nil
{
getenv
=
os
.
Getenv
}
return
envRe
.
ReplaceAllStringFunc
(
s
,
func
(
s
string
)
string
{
s
=
s
[
1
:
]
if
s
[
0
]
==
'{'
{
s
=
s
[
1
:
len
(
s
)
-
1
]
}
return
getenv
(
s
)
})
}
type
Parser
struct
{
ParseEnv
bool
ParseBacktick
bool
Position
int
// If ParseEnv is true, use this for getenv.
// If nil, use os.Getenv.
Getenv
func
(
string
)
string
}
func
NewParser
()
*
Parser
{
return
&
Parser
{
ParseEnv
:
ParseEnv
,
ParseBacktick
:
ParseBacktick
,
Position
:
0
,
}
}
func
(
p
*
Parser
)
Parse
(
line
string
)
([]
string
,
error
)
{
args
:=
[]
string
{}
buf
:=
""
var
escaped
,
doubleQuoted
,
singleQuoted
,
backQuote
,
dollarQuote
bool
backtick
:=
""
pos
:=
-
1
got
:=
false
loop
:
for
i
,
r
:=
range
line
{
if
escaped
{
buf
+=
string
(
r
)
escaped
=
false
continue
}
if
r
==
'\\'
{
if
singleQuoted
{
buf
+=
string
(
r
)
}
else
{
escaped
=
true
}
continue
}
if
isSpace
(
r
)
{
if
singleQuoted
||
doubleQuoted
||
backQuote
||
dollarQuote
{
buf
+=
string
(
r
)
backtick
+=
string
(
r
)
}
else
if
got
{
if
p
.
ParseEnv
{
buf
=
replaceEnv
(
p
.
Getenv
,
buf
)
}
args
=
append
(
args
,
buf
)
buf
=
""
got
=
false
}
continue
}
switch
r
{
case
'`'
:
if
!
singleQuoted
&&
!
doubleQuoted
&&
!
dollarQuote
{
if
p
.
ParseBacktick
{
if
backQuote
{
out
,
err
:=
shellRun
(
backtick
)
if
err
!=
nil
{
return
nil
,
err
}
buf
=
buf
[
:
len
(
buf
)
-
len
(
backtick
)]
+
out
}
backtick
=
""
backQuote
=
!
backQuote
continue
}
backtick
=
""
backQuote
=
!
backQuote
}
case
')'
:
if
!
singleQuoted
&&
!
doubleQuoted
&&
!
backQuote
{
if
p
.
ParseBacktick
{
if
dollarQuote
{
out
,
err
:=
shellRun
(
backtick
)
if
err
!=
nil
{
return
nil
,
err
}
if
r
==
')'
{
buf
=
buf
[
:
len
(
buf
)
-
len
(
backtick
)
-
2
]
+
out
}
else
{
buf
=
buf
[
:
len
(
buf
)
-
len
(
backtick
)
-
1
]
+
out
}
}
backtick
=
""
dollarQuote
=
!
dollarQuote
continue
}
backtick
=
""
dollarQuote
=
!
dollarQuote
}
case
'('
:
if
!
singleQuoted
&&
!
doubleQuoted
&&
!
backQuote
{
if
!
dollarQuote
&&
strings
.
HasSuffix
(
buf
,
"$"
)
{
dollarQuote
=
true
buf
+=
"("
continue
}
else
{
return
nil
,
errors
.
New
(
"invalid command line string"
)
}
}
case
'"'
:
if
!
singleQuoted
&&
!
dollarQuote
{
doubleQuoted
=
!
doubleQuoted
continue
}
case
'\'
'
:
if
!
doubleQuoted
&&
!
dollarQuote
{
singleQuoted
=
!
singleQuoted
continue
}
case
';'
,
'&'
,
'|'
,
'<'
,
'>'
:
if
!
(
escaped
||
singleQuoted
||
doubleQuoted
||
backQuote
)
{
if
r
==
'>'
&&
len
(
buf
)
>
0
{
if
c
:=
buf
[
0
];
'0'
<=
c
&&
c
<=
'9'
{
i
-=
1
got
=
false
}
}
pos
=
i
break
loop
}
}
got
=
true
buf
+=
string
(
r
)
if
backQuote
||
dollarQuote
{
backtick
+=
string
(
r
)
}
}
if
got
{
if
p
.
ParseEnv
{
buf
=
replaceEnv
(
p
.
Getenv
,
buf
)
}
args
=
append
(
args
,
buf
)
}
if
escaped
||
singleQuoted
||
doubleQuoted
||
backQuote
||
dollarQuote
{
return
nil
,
errors
.
New
(
"invalid command line string"
)
}
p
.
Position
=
pos
return
args
,
nil
}
func
Parse
(
line
string
)
([]
string
,
error
)
{
return
NewParser
()
.
Parse
(
line
)
}
go/vendor/github.com/mattn/go-shellwords/util_go15.go
0 → 100644
View file @
033c81d5
// +build !go1.6
package
shellwords
import
(
"os"
"os/exec"
"runtime"
"strings"
)
func
shellRun
(
line
string
)
(
string
,
error
)
{
var
b
[]
byte
var
err
error
if
runtime
.
GOOS
==
"windows"
{
b
,
err
=
exec
.
Command
(
os
.
Getenv
(
"COMSPEC"
),
"/c"
,
line
)
.
Output
()
}
else
{
b
,
err
=
exec
.
Command
(
os
.
Getenv
(
"SHELL"
),
"-c"
,
line
)
.
Output
()
}
if
err
!=
nil
{
return
""
,
err
}
return
strings
.
TrimSpace
(
string
(
b
)),
nil
}
go/vendor/github.com/mattn/go-shellwords/util_posix.go
0 → 100644
View file @
033c81d5
// +build !windows,go1.6
package
shellwords
import
(
"errors"
"os"
"os/exec"
"strings"
)
func
shellRun
(
line
string
)
(
string
,
error
)
{
shell
:=
os
.
Getenv
(
"SHELL"
)
b
,
err
:=
exec
.
Command
(
shell
,
"-c"
,
line
)
.
Output
()
if
err
!=
nil
{
if
eerr
,
ok
:=
err
.
(
*
exec
.
ExitError
);
ok
{
b
=
eerr
.
Stderr
}
return
""
,
errors
.
New
(
err
.
Error
()
+
":"
+
string
(
b
))
}
return
strings
.
TrimSpace
(
string
(
b
)),
nil
}
go/vendor/github.com/mattn/go-shellwords/util_windows.go
0 → 100644
View file @
033c81d5
// +build windows,go1.6
package
shellwords
import
(
"errors"
"os"
"os/exec"
"strings"
)
func
shellRun
(
line
string
)
(
string
,
error
)
{
shell
:=
os
.
Getenv
(
"COMSPEC"
)
b
,
err
:=
exec
.
Command
(
shell
,
"/c"
,
line
)
.
Output
()
if
err
!=
nil
{
if
eerr
,
ok
:=
err
.
(
*
exec
.
ExitError
);
ok
{
b
=
eerr
.
Stderr
}
return
""
,
errors
.
New
(
err
.
Error
()
+
":"
+
string
(
b
))
}
return
strings
.
TrimSpace
(
string
(
b
)),
nil
}
go/vendor/vendor.json
View file @
033c81d5
...
...
@@ -83,6 +83,12 @@
"path"
:
"github.com/lightstep/lightstep-tracer-go/lightsteppb"
,
"revision"
:
"c0184d44cb322c9d9abcaebb2139dc754b3c0145"
},
{
"checksumSHA1"
:
"AZtrc8Chiynrp48VQzRDH5vBR3M="
,
"path"
:
"github.com/mattn/go-shellwords"
,
"revision"
:
"2444a32a19f450fabaa0bb3e96a703f15d9a97d2"
,
"revisionTime"
:
"2019-04-25T16:15:01Z"
},
{
"checksumSHA1"
:
"a/DHmc9bdsYlZZcwp6i3xhvV7Pk="
,
"path"
:
"github.com/opentracing/opentracing-go"
,
...
...
@@ -639,6 +645,10 @@
"revision"
:
"cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b"
,
"revisionTime"
:
"2017-04-07T17:21:22Z"
},
{
"path"
:
"https://github.com/mattn/go-shellwords"
,
"revision"
:
""
},
{
"path"
:
"https://github.com/otiai10/copy"
,
"revision"
:
""
...
...
spec/gitlab_shell_custom_git_receive_pack_spec.rb
0 → 100644
View file @
033c81d5
require_relative
'spec_helper'
require
'open3'
require
'json'
require
'base64'
describe
'Custom bin/gitlab-shell git-receive-pack'
do
include_context
'gitlab shell'
def
mock_server
(
server
)
server
.
mount_proc
(
'/geo/proxy_git_push_ssh/info_refs'
)
do
|
req
,
res
|
res
.
content_type
=
'application/json'
res
.
status
=
200
res
.
body
=
{
"result"
=>
"
#{
Base64
.
encode64
(
'custom'
)
}
"
}.
to_json
end
server
.
mount_proc
(
'/geo/proxy_git_push_ssh/push'
)
do
|
req
,
res
|
res
.
content_type
=
'application/json'
res
.
status
=
200
output
=
JSON
.
parse
(
req
.
body
)[
'output'
]
res
.
body
=
{
"result"
=>
output
}.
to_json
end
server
.
mount_proc
(
'/api/v4/internal/allowed'
)
do
|
req
,
res
|
res
.
content_type
=
'application/json'
key_id
=
req
.
query
[
'key_id'
]
||
req
.
query
[
'username'
]
unless
key_id
body
=
JSON
.
parse
(
req
.
body
)
key_id
=
body
[
'key_id'
]
||
body
[
'username'
].
to_s
end
case
key_id
when
'100'
,
'someone'
then
res
.
status
=
300
body
=
{
"gl_id"
=>
"user-100"
,
"status"
=>
true
,
"payload"
=>
{
"action"
=>
"geo_proxy_to_primary"
,
"data"
=>
{
"api_endpoints"
=>
[
"/geo/proxy_git_push_ssh/info_refs"
,
"/geo/proxy_git_push_ssh/push"
],
"gl_username"
=>
"custom"
,
"primary_repo"
=>
"https://repo/path"
,
"info_message"
=>
"info_message
\n
another_message"
,
},
},
"gl_console_messages"
=>
[
"console"
,
"message"
]
}
res
.
body
=
body
.
to_json
else
res
.
status
=
403
end
end
end
shared_examples
'dialog for performing a custom action'
do
context
'when API calls perform successfully'
do
def
verify_successful_call!
(
cmd
)
Open3
.
popen3
(
env
,
cmd
)
do
|
stdin
,
stdout
,
stderr
|
expect
(
stderr
.
gets
).
to
eq
(
"> GitLab: console
\n
"
)
expect
(
stderr
.
gets
).
to
eq
(
"> GitLab: message
\n
"
)
expect
(
stderr
.
gets
).
to
eq
(
"> GitLab: info_message
\n
"
)
expect
(
stderr
.
gets
).
to
eq
(
"> GitLab: another_message
\n
"
)
expect
(
stdout
.
gets
(
6
)).
to
eq
(
"custom"
)
stdin
.
puts
(
"input"
)
stdin
.
close
expect
(
stdout
.
flush
.
read
).
to
eq
(
"input
\n
"
)
end
end
context
'when key is provided'
do
let
(
:cmd
)
{
"
#{
gitlab_shell_path
}
key-100"
}
it
'custom action is performed'
do
verify_successful_call!
(
cmd
)
end
end
context
'when username is provided'
do
let
(
:cmd
)
{
"
#{
gitlab_shell_path
}
username-someone"
}
it
'custom action is performed'
do
verify_successful_call!
(
cmd
)
end
end
end
context
'when API error occurs'
do
let
(
:cmd
)
{
"
#{
gitlab_shell_path
}
key-101"
}
it
'custom action is not performed'
do
Open3
.
popen2e
(
env
,
cmd
)
do
|
stdin
,
stdout
|
expect
(
stdout
.
gets
).
to
eq
(
inaccessible_error
)
end
end
end
end
let
(
:env
)
{
{
'SSH_CONNECTION'
=>
'fake'
,
'SSH_ORIGINAL_COMMAND'
=>
'git-receive-pack group/repo'
}
}
describe
'without go features'
do
before
(
:context
)
do
write_config
(
"gitlab_url"
=>
"http+unix://
#{
CGI
.
escape
(
tmp_socket_path
)
}
"
,
)
end
it_behaves_like
'dialog for performing a custom action'
do
let
(
:inaccessible_error
)
{
"> GitLab: API is not accessible
\n
"
}
end
end
describe
'with go features'
,
:go
do
before
(
:context
)
do
write_config
(
"gitlab_url"
=>
"http+unix://
#{
CGI
.
escape
(
tmp_socket_path
)
}
"
,
"migration"
=>
{
"enabled"
=>
true
,
"features"
=>
[
"git-receive-pack"
]
}
)
end
it_behaves_like
'dialog for performing a custom action'
do
let
(
:inaccessible_error
)
{
"Internal API error (403)
\n
"
}
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment