diff options
author | Patrick Bajao <ebajao@gitlab.com> | 2019-07-29 15:56:00 +0800 |
---|---|---|
committer | Patrick Bajao <ebajao@gitlab.com> | 2019-07-31 12:03:37 +0800 |
commit | 3b0176df497263323da2fae793a79b568502e6db (patch) | |
tree | f4ff3e232bc3717c539d73a4f0460d6d12b4a6de /go/internal/command/commandargs/shell.go | |
parent | aab85f3600caf04b491d6ca4fc3f0f004d9e3fc0 (diff) | |
download | gitlab-shell-3b0176df497263323da2fae793a79b568502e6db.tar.gz |
Support different CommandArgs type
`CommandArgs` has been renamed to `Shell`.
An interface has been added that includes `Executable()` and
`Arguments()` method. The `BaseArgs` implement this methods
and should be embeeded in each type.
Diffstat (limited to 'go/internal/command/commandargs/shell.go')
-rw-r--r-- | go/internal/command/commandargs/shell.go | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/go/internal/command/commandargs/shell.go b/go/internal/command/commandargs/shell.go new file mode 100644 index 0000000..04b1040 --- /dev/null +++ b/go/internal/command/commandargs/shell.go @@ -0,0 +1,110 @@ +package commandargs + +import ( + "errors" + "os" + "regexp" + + "github.com/mattn/go-shellwords" +) + +const ( + Discover CommandType = "discover" + TwoFactorRecover CommandType = "2fa_recovery_codes" + LfsAuthenticate CommandType = "git-lfs-authenticate" + ReceivePack CommandType = "git-receive-pack" + UploadPack CommandType = "git-upload-pack" + UploadArchive CommandType = "git-upload-archive" +) + +var ( + whoKeyRegex = regexp.MustCompile(`\bkey-(?P<keyid>\d+)\b`) + whoUsernameRegex = regexp.MustCompile(`\busername-(?P<username>\S+)\b`) +) + +type Shell struct { + *BaseArgs + GitlabUsername string + GitlabKeyId string + SshArgs []string + CommandType CommandType +} + +func (s *Shell) Parse() error { + if sshConnection := os.Getenv("SSH_CONNECTION"); sshConnection == "" { + return errors.New("Only ssh allowed") + } + + s.parseWho() + + if err := s.parseCommand(os.Getenv("SSH_ORIGINAL_COMMAND")); err != nil { + return errors.New("Invalid ssh command") + } + + s.defineCommandType() + + return nil +} + +func (s *Shell) parseWho() { + for _, argument := range s.arguments { + if keyId := tryParseKeyId(argument); keyId != "" { + s.GitlabKeyId = keyId + break + } + + if username := tryParseUsername(argument); username != "" { + s.GitlabUsername = username + break + } + } +} + +func tryParseKeyId(argument string) string { + matchInfo := whoKeyRegex.FindStringSubmatch(argument) + if len(matchInfo) == 2 { + // The first element is the full matched string + // The second element is the named `keyid` + return matchInfo[1] + } + + return "" +} + +func tryParseUsername(argument string) string { + matchInfo := whoUsernameRegex.FindStringSubmatch(argument) + if len(matchInfo) == 2 { + // The first element is the full matched string + // The second element is the named `username` + return matchInfo[1] + } + + return "" +} + +func (s *Shell) parseCommand(commandString string) error { + args, err := shellwords.Parse(commandString) + if err != nil { + return err + } + + // 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...) + } + + s.SshArgs = args + + return nil +} + +func (s *Shell) defineCommandType() { + if len(s.SshArgs) == 0 { + s.CommandType = Discover + } else { + s.CommandType = CommandType(s.SshArgs[0]) + } +} |