mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-06 16:17:11 +00:00
Moved keys cmd to top level
This commit is contained in:
+117
@@ -0,0 +1,117 @@
|
||||
# Keys CLI
|
||||
|
||||
This is as much an example how to expose cobra/viper, as for a cli itself
|
||||
(I think this code is overkill for what go-keys needs). But please look at
|
||||
the commands, and give feedback and changes.
|
||||
|
||||
`RootCmd` calls some initialization functions (`cobra.OnInitialize` and `RootCmd.PersistentPreRunE`) which serve to connect environmental variables and cobra flags, as well as load the config file. It also validates the flags registered on root and creates the cryptomanager, which will be used by all subcommands.
|
||||
|
||||
## Help info
|
||||
|
||||
```
|
||||
# keys help
|
||||
|
||||
Keys allows you to manage your local keystore for tendermint.
|
||||
|
||||
These keys may be in any format supported by go-crypto and can be
|
||||
used by light-clients, full nodes, or any other application that
|
||||
needs to sign with a private key.
|
||||
|
||||
Usage:
|
||||
keys [command]
|
||||
|
||||
Available Commands:
|
||||
get Get details of one key
|
||||
list List all keys
|
||||
new Create a new public/private key pair
|
||||
serve Run the key manager as an http server
|
||||
update Change the password for a private key
|
||||
|
||||
Flags:
|
||||
--keydir string Directory to store private keys (subdir of root) (default "keys")
|
||||
-o, --output string Output format (text|json) (default "text")
|
||||
-r, --root string root directory for config and data (default "/Users/ethan/.tlc")
|
||||
|
||||
Use "keys [command] --help" for more information about a command.
|
||||
```
|
||||
|
||||
## Getting the config file
|
||||
|
||||
The first step is to load in root, by checking the following in order:
|
||||
|
||||
* -r, --root command line flag
|
||||
* TM_ROOT environmental variable
|
||||
* default ($HOME/.tlc evaluated at runtime)
|
||||
|
||||
Once the `rootDir` is established, the script looks for a config file named `keys.{json,toml,yaml,hcl}` in that directory and parses it. These values will provide defaults for flags of the same name.
|
||||
|
||||
There is an example config file for testing out locally, which writes keys to `./.mykeys`. You can
|
||||
|
||||
## Getting/Setting variables
|
||||
|
||||
When we want to get the value of a user-defined variable (eg. `output`), we can call `viper.GetString("output")`, which will do the following checks, until it finds a match:
|
||||
|
||||
* Is `--output` command line flag present?
|
||||
* Is `TM_OUTPUT` environmental variable set?
|
||||
* Was a config file found and does it have an `output` variable?
|
||||
* Is there a default set on the command line flag?
|
||||
|
||||
If no variable is set and there was no default, we get back "".
|
||||
|
||||
This setup allows us to have powerful command line flags, but use env variables or config files (local or 12-factor style) to avoid passing these arguments every time.
|
||||
|
||||
## Nesting structures
|
||||
|
||||
Sometimes we don't just need key-value pairs, but actually a multi-level config file, like
|
||||
|
||||
```
|
||||
[mail]
|
||||
from = "no-reply@example.com"
|
||||
server = "mail.example.com"
|
||||
port = 567
|
||||
password = "XXXXXX"
|
||||
```
|
||||
|
||||
This CLI is too simple to warant such a structure, but I think eg. tendermint could benefit from such an approach. Here are some pointers:
|
||||
|
||||
* [Accessing nested keys from config files](https://github.com/spf13/viper#accessing-nested-keys)
|
||||
* [Overriding nested values with envvars](https://www.netlify.com/blog/2016/09/06/creating-a-microservice-boilerplate-in-go/#nested-config-values) - the mentioned outstanding PR is already merged into master!
|
||||
* Overriding nested values with cli flags? (use `--log_config.level=info` ??)
|
||||
|
||||
I'd love to see an example of this fully worked out in a more complex CLI.
|
||||
|
||||
## Have your cake and eat it too
|
||||
|
||||
It's easy to render data different ways. Some better for viewing, some better for importing to other programs. You can just add some global (persistent) flags to control the output formatting, and everyone gets what they want.
|
||||
|
||||
```
|
||||
# keys list -e hex
|
||||
All keys:
|
||||
betty d0789984492b1674e276b590d56b7ae077f81adc
|
||||
john b77f4720b220d1411a649b6c7f1151eb6b1c226a
|
||||
|
||||
# keys list -e btc
|
||||
All keys:
|
||||
betty 3uTF4r29CbtnzsNHZoPSYsE4BDwH
|
||||
john 3ZGp2Md35iw4XVtRvZDUaAEkCUZP
|
||||
|
||||
# keys list -e b64 -o json
|
||||
[
|
||||
{
|
||||
"name": "betty",
|
||||
"address": "0HiZhEkrFnTidrWQ1Wt64Hf4Gtw=",
|
||||
"pubkey": {
|
||||
"type": "secp256k1",
|
||||
"data": "F83WvhT0KwttSoqQqd_0_r2ztUUaQix5EXdO8AZyREoV31Og780NW59HsqTAb2O4hZ-w-j0Z-4b2IjfdqqfhVQ=="
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "john",
|
||||
"address": "t39HILIg0UEaZJtsfxFR62scImo=",
|
||||
"pubkey": {
|
||||
"type": "ed25519",
|
||||
"data": "t1LFmbg_8UTwj-n1wkqmnTp6NfaOivokEhlYySlGYCY="
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/go-data/base58"
|
||||
data "github.com/tendermint/go-wire/data"
|
||||
)
|
||||
|
||||
/*******
|
||||
|
||||
TODO
|
||||
|
||||
This file should move into go-common or the like as a basis for all cli tools.
|
||||
It is here for experimentation of re-use between go-keys and light-client.
|
||||
|
||||
*********/
|
||||
|
||||
const (
|
||||
RootFlag = "root"
|
||||
OutputFlag = "output"
|
||||
EncodingFlag = "encoding"
|
||||
)
|
||||
|
||||
func PrepareMainCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() {
|
||||
cobra.OnInitialize(func() { initEnv(envPrefix) })
|
||||
cmd.PersistentFlags().StringP(RootFlag, "r", defautRoot, "root directory for config and data")
|
||||
cmd.PersistentFlags().StringP(EncodingFlag, "e", "hex", "Binary encoding (hex|b64|btc)")
|
||||
cmd.PersistentFlags().StringP(OutputFlag, "o", "text", "Output format (text|json)")
|
||||
cmd.PersistentPreRunE = multiE(bindFlags, setEncoding, validateOutput, cmd.PersistentPreRunE)
|
||||
return func() { execute(cmd) }
|
||||
}
|
||||
|
||||
// initEnv sets to use ENV variables if set.
|
||||
func initEnv(prefix string) {
|
||||
// env variables with TM prefix (eg. TM_ROOT)
|
||||
viper.SetEnvPrefix(prefix)
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
|
||||
// execute adds all child commands to the root command sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func execute(cmd *cobra.Command) {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
type wrapE func(cmd *cobra.Command, args []string) error
|
||||
|
||||
func multiE(fs ...wrapE) wrapE {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
for _, f := range fs {
|
||||
if f != nil {
|
||||
if err := f(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func bindFlags(cmd *cobra.Command, args []string) error {
|
||||
// cmd.Flags() includes flags from this command and all persistent flags from the parent
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rootDir is command line flag, env variable, or default $HOME/.tlc
|
||||
rootDir := viper.GetString("root")
|
||||
viper.SetConfigName("config") // name of config file (without extension)
|
||||
viper.AddConfigPath(rootDir) // search root directory
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
// stderr, so if we redirect output to json file, this doesn't appear
|
||||
// fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
} else if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||
// we ignore not found error, only parse error
|
||||
// stderr, so if we redirect output to json file, this doesn't appear
|
||||
fmt.Fprintf(os.Stderr, "%#v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setEncoding reads the encoding flag
|
||||
func setEncoding(cmd *cobra.Command, args []string) error {
|
||||
// validate and set encoding
|
||||
enc := viper.GetString("encoding")
|
||||
switch enc {
|
||||
case "hex":
|
||||
data.Encoder = data.HexEncoder
|
||||
case "b64":
|
||||
data.Encoder = data.B64Encoder
|
||||
case "btc":
|
||||
data.Encoder = base58.BTCEncoder
|
||||
default:
|
||||
return errors.Errorf("Unsupported encoding: %s", enc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateOutput(cmd *cobra.Command, args []string) error {
|
||||
// validate output format
|
||||
output := viper.GetString(OutputFlag)
|
||||
switch output {
|
||||
case "text", "json":
|
||||
default:
|
||||
return errors.Errorf("Unsupported output format: %s", output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// 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/spf13/cobra"
|
||||
)
|
||||
|
||||
// getCmd represents the get command
|
||||
var getCmd = &cobra.Command{
|
||||
Use: "get <name>",
|
||||
Short: "Get details of one key",
|
||||
Long: `Return public details of one local key.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
fmt.Println("You must provide a name for the key")
|
||||
return
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
info, err := GetKeyManager().Get(name)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
printInfo(info)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(getCmd)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tendermint/go-crypto/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc"))
|
||||
cmd.RootCmd.Execute()
|
||||
// exec()
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// 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/spf13/cobra"
|
||||
)
|
||||
|
||||
// listCmd represents the list command
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all keys",
|
||||
Long: `Return a list of all public keys stored by this key manager
|
||||
along with their associated name and address.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
infos, err := GetKeyManager().List()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
printInfos(infos)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(listCmd)
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// 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/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// newCmd represents the new command
|
||||
var newCmd = &cobra.Command{
|
||||
Use: "new <name>",
|
||||
Short: "Create a new public/private key pair",
|
||||
Long: `Add a public/private key pair to the key store.
|
||||
The password muts be entered in the terminal and not
|
||||
passed as a command line argument for security.`,
|
||||
Run: newPassword,
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(newCmd)
|
||||
newCmd.Flags().StringP("type", "t", "ed25519", "Type of key (ed25519|secp256k1)")
|
||||
}
|
||||
|
||||
func newPassword(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
fmt.Println("You must provide a name for the key")
|
||||
return
|
||||
}
|
||||
name := args[0]
|
||||
algo := viper.GetString("type")
|
||||
|
||||
pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
info, err := GetKeyManager().Create(name, pass, algo)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
printInfo(info)
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// 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 (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
"github.com/tendermint/go-crypto/keys/cryptostore"
|
||||
"github.com/tendermint/go-crypto/keys/storage/filestorage"
|
||||
)
|
||||
|
||||
const KeySubdir = "keys"
|
||||
|
||||
var (
|
||||
manager keys.Manager
|
||||
)
|
||||
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "keys",
|
||||
Short: "Key manager for tendermint clients",
|
||||
Long: `Keys allows you to manage your local keystore for tendermint.
|
||||
|
||||
These keys may be in any format supported by go-crypto and can be
|
||||
used by light-clients, full nodes, or any other application that
|
||||
needs to sign with a private key.`,
|
||||
}
|
||||
|
||||
// GetKeyManager initializes a key manager based on the configuration
|
||||
func GetKeyManager() keys.Manager {
|
||||
if manager == nil {
|
||||
// store the keys directory
|
||||
rootDir := viper.GetString("root")
|
||||
keyDir := filepath.Join(rootDir, KeySubdir)
|
||||
// and construct the key manager
|
||||
manager = cryptostore.New(
|
||||
cryptostore.SecretBox,
|
||||
filestorage.New(keyDir),
|
||||
)
|
||||
}
|
||||
return manager
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
// 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"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/go-crypto/keys/server"
|
||||
)
|
||||
|
||||
// serveCmd represents the serve command
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Run the key manager as an http server",
|
||||
Long: `Launch an http server with a rest api to manage the
|
||||
private keys much more in depth than the cli can perform.
|
||||
In particular, this will allow you to sign transactions with
|
||||
the private keys in the store.`,
|
||||
RunE: serveHTTP,
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(serveCmd)
|
||||
serveCmd.Flags().IntP("port", "p", 8118, "TCP Port for listen for http server")
|
||||
serveCmd.Flags().StringP("socket", "s", "", "UNIX socket for more secure http server")
|
||||
serveCmd.Flags().StringP("type", "t", "ed25519", "Default key type (ed25519|secp256k1)")
|
||||
}
|
||||
|
||||
func serveHTTP(cmd *cobra.Command, args []string) error {
|
||||
var l net.Listener
|
||||
var err error
|
||||
socket := viper.GetString("socket")
|
||||
if socket != "" {
|
||||
l, err = createSocket(socket)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot create socket")
|
||||
}
|
||||
} else {
|
||||
port := viper.GetInt("port")
|
||||
l, err = net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
return errors.Errorf("Cannot listen on port %d", port)
|
||||
}
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
ks := server.New(GetKeyManager(), viper.GetString("type"))
|
||||
ks.Register(router)
|
||||
|
||||
// only set cors for tcp listener
|
||||
var h http.Handler
|
||||
if socket == "" {
|
||||
allowedHeaders := handlers.AllowedHeaders([]string{"Content-Type"})
|
||||
h = handlers.CORS(allowedHeaders)(router)
|
||||
} else {
|
||||
h = router
|
||||
}
|
||||
|
||||
err = http.Serve(l, h)
|
||||
fmt.Printf("Server Killed: %+v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// createSocket deletes existing socket if there, creates a new one,
|
||||
// starts a server on the socket, and sets permissions to 0600
|
||||
func createSocket(socket string) (net.Listener, error) {
|
||||
err := os.Remove(socket)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
// only fail if it does exist and cannot be deleted
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l, err := net.Listen("unix", socket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mode := os.FileMode(0700) | os.ModeSocket
|
||||
err = os.Chmod(socket, mode)
|
||||
if err != nil {
|
||||
l.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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/spf13/cobra"
|
||||
)
|
||||
|
||||
// updateCmd represents the update command
|
||||
var updateCmd = &cobra.Command{
|
||||
Use: "update <name>",
|
||||
Short: "Change the password for a private key",
|
||||
Long: `Change the password for a private key.`,
|
||||
Run: updatePassword,
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(updateCmd)
|
||||
}
|
||||
|
||||
func updatePassword(cmd *cobra.Command, args []string) {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
fmt.Println("You must provide a name for the key")
|
||||
return
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
oldpass, err := getPassword("Enter the current passphrase:")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
newpass, err := getCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = GetKeyManager().Update(name, oldpass, newpass)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
} else {
|
||||
fmt.Println("Password successfully updated!")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
data "github.com/tendermint/go-wire/data"
|
||||
)
|
||||
|
||||
const PassLength = 10
|
||||
|
||||
func getPassword(prompt string) (string, error) {
|
||||
pass, err := speakeasy.Ask(prompt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(pass) < PassLength {
|
||||
return "", errors.Errorf("Password must be at least %d characters", PassLength)
|
||||
}
|
||||
return pass, nil
|
||||
}
|
||||
|
||||
func getCheckPassword(prompt, prompt2 string) (string, error) {
|
||||
// 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(OutputFlag) {
|
||||
case "text":
|
||||
addr, err := data.ToText(info.Address)
|
||||
if err != nil {
|
||||
panic(err) // really shouldn't happen...
|
||||
}
|
||||
sep := "\t\t"
|
||||
if len(info.Name) > 7 {
|
||||
sep = "\t"
|
||||
}
|
||||
fmt.Printf("%s%s%s\n", info.Name, sep, addr)
|
||||
case "json":
|
||||
json, err := data.ToJSON(info)
|
||||
if err != nil {
|
||||
panic(err) // really shouldn't happen...
|
||||
}
|
||||
fmt.Println(string(json))
|
||||
}
|
||||
}
|
||||
|
||||
func printInfos(infos keys.Infos) {
|
||||
switch viper.Get(OutputFlag) {
|
||||
case "text":
|
||||
fmt.Println("All keys:")
|
||||
for _, i := range infos {
|
||||
printInfo(i)
|
||||
}
|
||||
case "json":
|
||||
json, err := data.ToJSON(infos)
|
||||
if err != nil {
|
||||
panic(err) // really shouldn't happen...
|
||||
}
|
||||
fmt.Println(string(json))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user