From d0e05e8be26436877ade3240ab19e633988b0c72 Mon Sep 17 00:00:00 2001 From: Norwin Date: Tue, 8 Dec 2020 09:21:05 +0800 Subject: [PATCH] move git auth prompts to interact module (#276) move password prompt to interact module closes #231 allow up to 3 ssh key password attempts rename param Co-authored-by: Norwin Roosen Reviewed-on: https://gitea.com/gitea/tea/pulls/276 Reviewed-by: 6543 <6543@obermui.de> Reviewed-by: Lunny Xiao Co-Authored-By: Norwin Co-Committed-By: Norwin --- modules/git/auth.go | 35 +++++++++++++++++------------------ modules/interact/prompts.go | 16 ++++++++++++++++ modules/task/pull_checkout.go | 4 ++-- modules/task/pull_clean.go | 5 +++-- 4 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 modules/interact/prompts.go diff --git a/modules/git/auth.go b/modules/git/auth.go index 1dbc6a5..7d9dd63 100644 --- a/modules/git/auth.go +++ b/modules/git/auth.go @@ -15,13 +15,14 @@ import ( gogit_http "github.com/go-git/go-git/v5/plumbing/transport/http" gogit_ssh "github.com/go-git/go-git/v5/plumbing/transport/ssh" "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/terminal" ) +type pwCallback = func(ctx string) (string, error) + // GetAuthForURL returns the appropriate AuthMethod to be used in Push() / Pull() // operations depending on the protocol, and prompts the user for credentials if // necessary. -func GetAuthForURL(remoteURL *url.URL, authToken, keyFile string) (auth git_transport.AuthMethod, err error) { +func GetAuthForURL(remoteURL *url.URL, authToken, keyFile string, passwordCallback pwCallback) (auth git_transport.AuthMethod, err error) { switch remoteURL.Scheme { case "http", "https": // gitea supports push/pull via app token as username. @@ -32,7 +33,7 @@ func GetAuthForURL(remoteURL *url.URL, authToken, keyFile string) (auth git_tran user := remoteURL.User.Username() auth, err = gogit_ssh.DefaultAuthBuilder(user) if err != nil { - signer, err := readSSHPrivKey(keyFile) + signer, err := readSSHPrivKey(keyFile, passwordCallback) if err != nil { return nil, err } @@ -46,7 +47,7 @@ func GetAuthForURL(remoteURL *url.URL, authToken, keyFile string) (auth git_tran return auth, nil } -func readSSHPrivKey(keyFile string) (sig ssh.Signer, err error) { +func readSSHPrivKey(keyFile string, passwordCallback pwCallback) (sig ssh.Signer, err error) { if keyFile != "" { keyFile, err = utils.AbsPathWithExpansion(keyFile) } else { @@ -60,21 +61,19 @@ func readSSHPrivKey(keyFile string) (sig ssh.Signer, err error) { return nil, err } sig, err = ssh.ParsePrivateKey(sshKey) - if err != nil { - pass, err := promptPass(keyFile) - if err != nil { - return nil, err - } - sig, err = ssh.ParsePrivateKeyWithPassphrase(sshKey, []byte(pass)) - if err != nil { - return nil, err + if _, ok := err.(*ssh.PassphraseMissingError); ok { + // allow for up to 3 password attempts + for i := 0; i < 3; i++ { + var pass string + pass, err = passwordCallback(keyFile) + if err != nil { + return nil, err + } + sig, err = ssh.ParsePrivateKeyWithPassphrase(sshKey, []byte(pass)) + if err == nil { + break + } } } return sig, err } - -func promptPass(domain string) (string, error) { - fmt.Printf("%s password: ", domain) - pass, err := terminal.ReadPassword(0) - return string(pass), err -} diff --git a/modules/interact/prompts.go b/modules/interact/prompts.go new file mode 100644 index 0000000..bd94207 --- /dev/null +++ b/modules/interact/prompts.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package interact + +import ( + "github.com/AlecAivazis/survey/v2" +) + +// PromptPassword asks for a password and blocks until input was made. +func PromptPassword(name string) (pass string, err error) { + promptPW := &survey.Password{Message: name + " password:"} + err = survey.AskOne(promptPW, &pass, survey.WithValidator(survey.Required)) + return +} diff --git a/modules/task/pull_checkout.go b/modules/task/pull_checkout.go index eba917b..ba7e1f9 100644 --- a/modules/task/pull_checkout.go +++ b/modules/task/pull_checkout.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/sdk/gitea" "code.gitea.io/tea/modules/config" local_git "code.gitea.io/tea/modules/git" - + "code.gitea.io/tea/modules/interact" "github.com/go-git/go-git/v5" ) @@ -60,7 +60,7 @@ func PullCheckout(login *config.Login, repoOwner, repoName string, index int64) if err != nil { return err } - auth, err := local_git.GetAuthForURL(url, login.Token, login.SSHKey) + auth, err := local_git.GetAuthForURL(url, login.Token, login.SSHKey, interact.PromptPassword) if err != nil { return err } diff --git a/modules/task/pull_clean.go b/modules/task/pull_clean.go index 8fa654c..f944efb 100644 --- a/modules/task/pull_clean.go +++ b/modules/task/pull_clean.go @@ -7,10 +7,11 @@ package task import ( "fmt" - "code.gitea.io/sdk/gitea" "code.gitea.io/tea/modules/config" local_git "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/interact" + "code.gitea.io/sdk/gitea" git_config "github.com/go-git/go-git/v5/config" ) @@ -78,7 +79,7 @@ call me again with the --ignore-sha flag`, pr.Head.Ref) if err != nil { return err } - auth, err := local_git.GetAuthForURL(url, login.Token, login.SSHKey) + auth, err := local_git.GetAuthForURL(url, login.Token, login.SSHKey, interact.PromptPassword) if err != nil { return err }