Add delete and recover commands, and test them
This commit is contained in:
parent
e9537b2da6
commit
1ab9ab9494
|
@ -0,0 +1,49 @@
|
|||
// Copyright © 2017 Ethan Frey
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// deleteCmd represents the delete command
|
||||
var deleteCmd = &cobra.Command{
|
||||
Use: "delete <name>",
|
||||
Short: "DANGER: Delete a private key from your system",
|
||||
RunE: runDeleteCmd,
|
||||
}
|
||||
|
||||
func runDeleteCmd(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide a name for the key")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
oldpass, err := getPassword("DANGER - enter password to permanently delete key:")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = GetKeyManager().Delete(name, oldpass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Password deleted forever (uh oh!)")
|
||||
return nil
|
||||
}
|
41
cmd/new.go
41
cmd/new.go
|
@ -15,14 +15,20 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
flagType = "type"
|
||||
flagType = "type"
|
||||
flagNoBackup = "no-backup"
|
||||
)
|
||||
|
||||
// newCmd represents the new command
|
||||
|
@ -37,6 +43,7 @@ passed as a command line argument for security.`,
|
|||
|
||||
func init() {
|
||||
newCmd.Flags().StringP(flagType, "t", "ed25519", "Type of key (ed25519|secp256k1)")
|
||||
newCmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)")
|
||||
}
|
||||
|
||||
func runNewCmd(cmd *cobra.Command, args []string) error {
|
||||
|
@ -51,9 +58,37 @@ func runNewCmd(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
info, _, err := GetKeyManager().Create(name, pass, algo)
|
||||
info, seed, err := GetKeyManager().Create(name, pass, algo)
|
||||
if err == nil {
|
||||
printInfo(info)
|
||||
printCreate(info, seed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type NewOutput struct {
|
||||
Key keys.Info `json:"key"`
|
||||
Seed string `json:"seed"`
|
||||
}
|
||||
|
||||
func printCreate(info keys.Info, seed string) {
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
printInfo(info)
|
||||
// print seed unless requested not to.
|
||||
if !viper.GetBool(flagNoBackup) {
|
||||
fmt.Println("**Important** write this seed phrase in a safe place.")
|
||||
fmt.Println("It is the only way to recover your account if you ever forget your password.\n")
|
||||
fmt.Println(seed)
|
||||
}
|
||||
case "json":
|
||||
out := NewOutput{Key: info}
|
||||
if !viper.GetBool(flagNoBackup) {
|
||||
out.Seed = seed
|
||||
}
|
||||
json, err := data.ToJSON(out)
|
||||
if err != nil {
|
||||
panic(err) // really shouldn't happen...
|
||||
}
|
||||
fmt.Println(string(json))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright © 2017 Ethan Frey
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// recoverCmd represents the recover command
|
||||
var recoverCmd = &cobra.Command{
|
||||
Use: "recover <name>",
|
||||
Short: "Change the password for a private key",
|
||||
RunE: runRecoverCmd,
|
||||
}
|
||||
|
||||
func runRecoverCmd(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide a name for the key")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
pass, err := getPassword("Enter the new passphrase:")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// not really a password... huh?
|
||||
seed, err := getSeed("Enter your recovery seed phrase")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := GetKeyManager().Recover(name, pass, seed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printInfo(info)
|
||||
return nil
|
||||
}
|
|
@ -41,6 +41,8 @@ func init() {
|
|||
RootCmd.AddCommand(listCmd)
|
||||
RootCmd.AddCommand(newCmd)
|
||||
RootCmd.AddCommand(updateCmd)
|
||||
RootCmd.AddCommand(deleteCmd)
|
||||
RootCmd.AddCommand(recoverCmd)
|
||||
}
|
||||
|
||||
func RegisterServer() {
|
||||
|
|
|
@ -76,6 +76,12 @@ func getPassword(prompt string) (pass string, err error) {
|
|||
return pass, nil
|
||||
}
|
||||
|
||||
func getSeed(prompt string) (seed string, err error) {
|
||||
seed, err = stdinPassword()
|
||||
seed = strings.TrimSpace(seed)
|
||||
return
|
||||
}
|
||||
|
||||
func getCheckPassword(prompt, prompt2 string) (string, error) {
|
||||
// simple read on no-tty
|
||||
if !inputIsTty() {
|
||||
|
|
|
@ -12,8 +12,9 @@ oneTimeSetUp() {
|
|||
newKey(){
|
||||
assertNotNull "keyname required" "$1"
|
||||
KEYPASS=${2:-qwertyuiop}
|
||||
KEY=$(echo $KEYPASS | ${EXE} new $1)
|
||||
assertTrue "created $1" $?
|
||||
KEY=$(echo $KEYPASS | ${EXE} new $1 -o json)
|
||||
if ! assertTrue "created $1" $?; then return 1; fi
|
||||
assertEquals "$1" $(echo $KEY | jq .key.name | tr -d \")
|
||||
return $?
|
||||
}
|
||||
|
||||
|
@ -59,6 +60,52 @@ test02updateKeys() {
|
|||
assertTrue "takes new key after update" "updateKey $USER $PASS2 $PASS3"
|
||||
}
|
||||
|
||||
test03recoverKeys() {
|
||||
USER=sleepy
|
||||
PASS1=S4H.9j.D9S7hso
|
||||
|
||||
USER2=easy
|
||||
PASS2=1234567890
|
||||
|
||||
# make a user and check they exist
|
||||
echo "create..."
|
||||
KEY=$(echo $PASS1 | ${EXE} new $USER -o json)
|
||||
if ! assertTrue "created $USER" $?; then return 1; fi
|
||||
if [ -n "$DEBUG" ]; then echo $KEY; echo; fi
|
||||
|
||||
SEED=$(echo $KEY | jq .seed | tr -d \")
|
||||
ADDR=$(echo $KEY | jq .key.address | tr -d \")
|
||||
PUBKEY=$(echo $KEY | jq .key.pubkey | tr -d \")
|
||||
assertTrue "${EXE} get $USER > /dev/null"
|
||||
|
||||
# let's delete this key
|
||||
echo "delete..."
|
||||
assertFalse "echo foo | ${EXE} delete $USER > /dev/null"
|
||||
assertTrue "echo $PASS1 | ${EXE} delete $USER > /dev/null"
|
||||
assertFalse "${EXE} get $USER > /dev/null"
|
||||
|
||||
# fails on short password
|
||||
echo "recover..."
|
||||
assertFalse "echo foo; echo $SEED | ${EXE} recover $USER2 -o json > /dev/null"
|
||||
# fails on bad seed
|
||||
assertFalse "echo $PASS2; echo \"silly white whale tower bongo\" | ${EXE} recover $USER2 -o json > /dev/null"
|
||||
# now we got it
|
||||
KEY2=$((echo $PASS2; echo $SEED) | ${EXE} recover $USER2 -o json)
|
||||
if ! assertTrue "recovery failed: $KEY2" $?; then return 1; fi
|
||||
if [ -n "$DEBUG" ]; then echo $KEY2; echo; fi
|
||||
|
||||
# make sure it looks the same
|
||||
NAME2=$(echo $KEY2 | jq .name | tr -d \")
|
||||
ADDR2=$(echo $KEY2 | jq .address | tr -d \")
|
||||
PUBKEY2=$(echo $KEY2 | jq .pubkey | tr -d \")
|
||||
assertEquals "wrong username" "$USER2" "$NAME2"
|
||||
assertEquals "address doesn't match" "$ADDR" "$ADDR2"
|
||||
assertEquals "pubkey doesn't match" "$PUBKEY" "$PUBKEY2"
|
||||
|
||||
# and we can find the info
|
||||
assertTrue "${EXE} get $USER2 > /dev/null"
|
||||
}
|
||||
|
||||
# load and run these tests with shunit2!
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #get this files directory
|
||||
. $DIR/shunit2
|
||||
|
|
Loading…
Reference in New Issue