From 00304dd094d8f9fd5ae46a27dd7718c6d53f3149 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 10:40:10 +0100 Subject: [PATCH] Prompt for password on sendtx --- client/input.go | 87 ++++++++++++++++++++++++ client/keys/add.go | 4 +- client/keys/delete.go | 3 +- client/keys/update.go | 5 +- client/keys/utils.go | 73 -------------------- examples/basecoin/cmd/basecli/account.go | 3 +- examples/basecoin/cmd/basecli/sendtx.go | 6 +- examples/basecoin/types/account.go | 2 +- 8 files changed, 101 insertions(+), 82 deletions(-) create mode 100644 client/input.go diff --git a/client/input.go b/client/input.go new file mode 100644 index 0000000000..b370b0d88f --- /dev/null +++ b/client/input.go @@ -0,0 +1,87 @@ +package client + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/bgentry/speakeasy" + "github.com/pkg/errors" + isatty "github.com/tendermint/vendor-bak/github.com/mattn/go-isatty" +) + +// MinPassLength is the minimum acceptable password length +const MinPassLength = 8 + +// if we read from non-tty, we just need to init the buffer reader once, +// in case we try to read multiple passwords (eg. update) +var buf *bufio.Reader + +// GetPassword will prompt for a password one-time (to sign a tx) +// It enforces the password length +func GetPassword(prompt string) (pass string, err error) { + if inputIsTty() { + pass, err = speakeasy.Ask(prompt) + } else { + pass, err = stdinPassword() + } + if err != nil { + return "", err + } + if len(pass) < MinPassLength { + return "", errors.Errorf("Password must be at least %d characters", MinPassLength) + } + return pass, nil +} + +// GetSeed will request a seed phrase from stdin and trims off +// leading/trailing spaces +func GetSeed(prompt string) (seed string, err error) { + if inputIsTty() { + fmt.Println(prompt) + } + seed, err = stdinPassword() + seed = strings.TrimSpace(seed) + return +} + +// GetCheckPassword will prompt for a password twice to verify they +// match (for creating a new password). +// It enforces the password length. Only parses password once if +// input is piped in. +func GetCheckPassword(prompt, prompt2 string) (string, error) { + // simple read on no-tty + if !inputIsTty() { + return GetPassword(prompt) + } + + // TODO: own function??? + pass, err := GetPassword(prompt) + if err != nil { + return "", err + } + pass2, err := GetPassword(prompt2) + if err != nil { + return "", err + } + if pass != pass2 { + return "", errors.New("Passphrases don't match") + } + return pass, nil +} + +func inputIsTty() bool { + return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) +} + +func stdinPassword() (string, error) { + if buf == nil { + buf = bufio.NewReader(os.Stdin) + } + pass, err := buf.ReadString('\n') + if err != nil { + return "", err + } + return strings.TrimSpace(pass), nil +} diff --git a/client/keys/add.go b/client/keys/add.go index 88d998323f..07b1deba1c 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -69,14 +69,14 @@ func runAddCmd(cmd *cobra.Command, args []string) error { if err != nil { return err } - pass, err = getCheckPassword("Enter a passphrase for your key:", "Repeat the passphrase:") + pass, err = client.GetCheckPassword("Enter a passphrase for your key:", "Repeat the passphrase:") if err != nil { return err } } if viper.GetBool(flagRecover) { - seed, err := getPassword("Enter your recovery seed phrase:") + seed, err := client.GetSeed("Enter your recovery seed phrase:") if err != nil { return err } diff --git a/client/keys/delete.go b/client/keys/delete.go index ce5a03dd42..58050087a5 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -17,6 +17,7 @@ package keys import ( "fmt" + "github.com/cosmos/cosmos-sdk/client" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -37,7 +38,7 @@ func runDeleteCmd(cmd *cobra.Command, args []string) error { } name := args[0] - oldpass, err := getPassword("DANGER - enter password to permanently delete key:") + oldpass, err := client.GetPassword("DANGER - enter password to permanently delete key:") if err != nil { return err } diff --git a/client/keys/update.go b/client/keys/update.go index 59010a6d3e..a7197dd968 100644 --- a/client/keys/update.go +++ b/client/keys/update.go @@ -17,6 +17,7 @@ package keys import ( "fmt" + "github.com/cosmos/cosmos-sdk/client" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -37,11 +38,11 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error { } name := args[0] - oldpass, err := getPassword("Enter the current passphrase:") + oldpass, err := client.GetPassword("Enter the current passphrase:") if err != nil { return err } - newpass, err := getCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:") + newpass, err := client.GetCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:") if err != nil { return err } diff --git a/client/keys/utils.go b/client/keys/utils.go index 346aa17bcf..5c156cb748 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -1,14 +1,8 @@ package keys import ( - "bufio" "fmt" - "os" - "strings" - "github.com/bgentry/speakeasy" - isatty "github.com/mattn/go-isatty" - "github.com/pkg/errors" "github.com/spf13/viper" keys "github.com/tendermint/go-crypto/keys" @@ -17,9 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" ) -// MinPassLength is the minimum acceptable password length -const MinPassLength = 8 - var ( // keybase is used to make GetKeyBase a singleton keybase keys.Keybase @@ -38,70 +29,6 @@ func GetKeyBase() (keys.Keybase, error) { return keybase, nil } -// if we read from non-tty, we just need to init the buffer reader once, -// in case we try to read multiple passwords (eg. update) -var buf *bufio.Reader - -func inputIsTty() bool { - return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) -} - -func stdinPassword() (string, error) { - if buf == nil { - buf = bufio.NewReader(os.Stdin) - } - pass, err := buf.ReadString('\n') - if err != nil { - return "", err - } - return strings.TrimSpace(pass), nil -} - -func getPassword(prompt string) (pass string, err error) { - if inputIsTty() { - pass, err = speakeasy.Ask(prompt) - } else { - pass, err = stdinPassword() - } - if err != nil { - return "", err - } - if len(pass) < MinPassLength { - return "", errors.Errorf("Password must be at least %d characters", MinPassLength) - } - return pass, nil -} - -func getSeed(prompt string) (seed string, err error) { - if inputIsTty() { - fmt.Println(prompt) - } - seed, err = stdinPassword() - seed = strings.TrimSpace(seed) - return -} - -func getCheckPassword(prompt, prompt2 string) (string, error) { - // simple read on no-tty - if !inputIsTty() { - return getPassword(prompt) - } - - // TODO: own function??? - pass, err := getPassword(prompt) - if err != nil { - return "", err - } - pass2, err := getPassword(prompt2) - if err != nil { - return "", err - } - if pass != pass2 { - return "", errors.New("Passphrases don't match") - } - return pass, nil -} - func printInfo(info keys.Info) { switch viper.Get(cli.OutputFlag) { case "text": diff --git a/examples/basecoin/cmd/basecli/account.go b/examples/basecoin/cmd/basecli/account.go index 26c8329044..118e37b24f 100644 --- a/examples/basecoin/cmd/basecli/account.go +++ b/examples/basecoin/cmd/basecli/account.go @@ -70,8 +70,7 @@ func getAccount(cmd *cobra.Command, args []string) error { return err } - // TODO: better - // fmt.Printf("Account: %#v\n", acct) + // print out whole account or just coins? output, err := json.MarshalIndent(acct, "", " ") // output, err := json.MarshalIndent(acct.BaseAccount.Coins, "", " ") if err != nil { diff --git a/examples/basecoin/cmd/basecli/sendtx.go b/examples/basecoin/cmd/basecli/sendtx.go index b81f898d5e..8eff14b6a1 100644 --- a/examples/basecoin/cmd/basecli/sendtx.go +++ b/examples/basecoin/cmd/basecli/sendtx.go @@ -89,7 +89,11 @@ func buildTx() ([]byte, error) { // sign and build bz := msg.GetSignBytes() - passphrase := "1234567890" // XXX: adults only + prompt := fmt.Sprintf("Password to sign with '%s':", name) + passphrase, err := client.GetPassword(prompt) + if err != nil { + return nil, err + } sig, pubkey, err := keybase.Sign(name, passphrase, bz) if err != nil { return nil, err diff --git a/examples/basecoin/types/account.go b/examples/basecoin/types/account.go index 3177f99122..c9b1d8a239 100644 --- a/examples/basecoin/types/account.go +++ b/examples/basecoin/types/account.go @@ -15,7 +15,7 @@ var _ sdk.Account = (*AppAccount)(nil) // auth.AccountStore uses the flexible go-wire library. type AppAccount struct { auth.BaseAccount - Name string + Name string `json:"name"` } // nolint