Merge pull request #3892 from MichaelMure/keystore_key_rm

Implement ipfs key {rm, rename}
This commit is contained in:
Jeromy Johnson 2017-05-17 21:28:36 -07:00 committed by GitHub
commit 0780a4fdb4
6 changed files with 279 additions and 4 deletions

View File

@ -33,8 +33,10 @@ var KeyCmd = &cmds.Command{
`,
},
Subcommands: map[string]*cmds.Command{
"gen": KeyGenCmd,
"list": KeyListCmd,
"gen": keyGenCmd,
"list": keyListCmd,
"rename": keyRenameCmd,
"rm": keyRmCmd,
},
}
@ -47,7 +49,15 @@ type KeyOutputList struct {
Keys []KeyOutput
}
var KeyGenCmd = &cmds.Command{
// KeyRenameOutput define the output type of keyRenameCmd
type KeyRenameOutput struct {
Was string
Now string
Id string
Overwrite bool
}
var keyGenCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Create a new keypair",
},
@ -150,7 +160,7 @@ var KeyGenCmd = &cmds.Command{
Type: KeyOutput{},
}
var KeyListCmd = &cmds.Command{
var keyListCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "List all local keypairs",
},
@ -202,6 +212,170 @@ var KeyListCmd = &cmds.Command{
Type: KeyOutputList{},
}
var keyRenameCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Rename a keypair",
},
Arguments: []cmds.Argument{
cmds.StringArg("name", true, false, "name of key to rename"),
cmds.StringArg("newName", true, false, "new name of the key"),
},
Options: []cmds.Option{
cmds.BoolOption("force", "f", "Allow to overwrite an existing key."),
},
Run: func(req cmds.Request, res cmds.Response) {
n, err := req.InvocContext().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
ks := n.Repo.Keystore()
name := req.Arguments()[0]
newName := req.Arguments()[1]
if name == "self" {
res.SetError(fmt.Errorf("cannot rename key with name 'self'"), cmds.ErrNormal)
return
}
if newName == "self" {
res.SetError(fmt.Errorf("cannot overwrite key with name 'self'"), cmds.ErrNormal)
return
}
oldKey, err := ks.Get(name)
if err != nil {
res.SetError(fmt.Errorf("no key named %s was found", name), cmds.ErrNormal)
return
}
pubKey := oldKey.GetPublic()
pid, err := peer.IDFromPublicKey(pubKey)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
overwrite := false
force, _, _ := res.Request().Option("f").Bool()
if force {
exist, err := ks.Has(newName)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
if exist {
overwrite = true
err := ks.Delete(newName)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
}
}
err = ks.Put(newName, oldKey)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
err = ks.Delete(name)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
res.SetOutput(&KeyRenameOutput{
Was: name,
Now: newName,
Id: pid.Pretty(),
Overwrite: overwrite,
})
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
k, ok := res.Output().(*KeyRenameOutput)
if !ok {
return nil, fmt.Errorf("expected a KeyRenameOutput as command result")
}
buf := new(bytes.Buffer)
if k.Overwrite {
fmt.Fprintf(buf, "Key %s renamed to %s with overwriting\n", k.Id, k.Now)
} else {
fmt.Fprintf(buf, "Key %s renamed to %s\n", k.Id, k.Now)
}
return buf, nil
},
},
Type: KeyRenameOutput{},
}
var keyRmCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Remove a keypair",
},
Arguments: []cmds.Argument{
cmds.StringArg("name", true, true, "names of keys to remove").EnableStdin(),
},
Options: []cmds.Option{
cmds.BoolOption("l", "Show extra information about keys."),
},
Run: func(req cmds.Request, res cmds.Response) {
n, err := req.InvocContext().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
names := req.Arguments()
list := make([]KeyOutput, 0, len(names))
for _, name := range names {
if name == "self" {
res.SetError(fmt.Errorf("cannot remove key with name 'self'"), cmds.ErrNormal)
return
}
removed, err := n.Repo.Keystore().Get(name)
if err != nil {
res.SetError(fmt.Errorf("no key named %s was found", name), cmds.ErrNormal)
return
}
pubKey := removed.GetPublic()
pid, err := peer.IDFromPublicKey(pubKey)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
list = append(list, KeyOutput{Name: name, Id: pid.Pretty()})
}
for _, name := range names {
err = n.Repo.Keystore().Delete(name)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
}
res.SetOutput(&KeyOutputList{list})
},
Marshalers: cmds.MarshalerMap{
cmds.Text: keyOutputListMarshaler,
},
Type: KeyOutputList{},
}
func keyOutputListMarshaler(res cmds.Response) (io.Reader, error) {
withId, _, _ := res.Request().Option("l").Bool()

View File

@ -11,9 +11,15 @@ import (
)
type Keystore interface {
// Has return whether or not a key exist in the Keystore
Has(string) (bool, error)
// Put store a key in the Keystore
Put(string, ci.PrivKey) error
// Get retrieve a key from the Keystore
Get(string) (ci.PrivKey, error)
// Delete remove a key from the Keystore
Delete(string) error
// List return a list of key identifier
List() ([]string, error)
}
@ -54,6 +60,24 @@ func NewFSKeystore(dir string) (*FSKeystore, error) {
return &FSKeystore{dir}, nil
}
// Has return whether or not a key exist in the Keystore
func (ks *FSKeystore) Has(name string) (bool, error) {
kp := filepath.Join(ks.dir, name)
_, err := os.Stat(kp)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
}
// Put store a key in the Keystore
func (ks *FSKeystore) Put(name string, k ci.PrivKey) error {
if err := validateName(name); err != nil {
return err
@ -87,6 +111,7 @@ func (ks *FSKeystore) Put(name string, k ci.PrivKey) error {
return nil
}
// Get retrieve a key from the Keystore
func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) {
if err := validateName(name); err != nil {
return nil, err
@ -105,6 +130,7 @@ func (ks *FSKeystore) Get(name string) (ci.PrivKey, error) {
return ci.UnmarshalPrivateKey(data)
}
// Delete remove a key from the Keystore
func (ks *FSKeystore) Delete(name string) error {
if err := validateName(name); err != nil {
return err
@ -115,6 +141,7 @@ func (ks *FSKeystore) Delete(name string) error {
return os.Remove(kp)
}
// List return a list of key identifier
func (ks *FSKeystore) List() ([]string, error) {
dir, err := os.Open(ks.dir)
if err != nil {

View File

@ -82,6 +82,22 @@ func TestKeystoreBasics(t *testing.T) {
t.Fatal(err)
}
exist, err := ks.Has("foo")
if !exist {
t.Fatal("should know it has a key named foo")
}
if err != nil {
t.Fatal(err)
}
exist, err = ks.Has("nonexistingkey")
if exist {
t.Fatal("should know it doesn't have a key named nonexistingkey")
}
if err != nil {
t.Fatal(err)
}
if err := ks.Delete("bar"); err != nil {
t.Fatal(err)
}

View File

@ -10,6 +10,13 @@ func NewMemKeystore() *MemKeystore {
return &MemKeystore{make(map[string]ci.PrivKey)}
}
// Has return whether or not a key exist in the Keystore
func (mk *MemKeystore) Has(name string) (bool, error) {
_, ok := mk.keys[name]
return ok, nil
}
// Put store a key in the Keystore
func (mk *MemKeystore) Put(name string, k ci.PrivKey) error {
if err := validateName(name); err != nil {
return err
@ -24,6 +31,7 @@ func (mk *MemKeystore) Put(name string, k ci.PrivKey) error {
return nil
}
// Get retrieve a key from the Keystore
func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) {
if err := validateName(name); err != nil {
return nil, err
@ -37,6 +45,7 @@ func (mk *MemKeystore) Get(name string) (ci.PrivKey, error) {
return k, nil
}
// Delete remove a key from the Keystore
func (mk *MemKeystore) Delete(name string) error {
if err := validateName(name); err != nil {
return err
@ -46,6 +55,7 @@ func (mk *MemKeystore) Delete(name string) error {
return nil
}
// List return a list of key identifier
func (mk *MemKeystore) List() ([]string, error) {
out := make([]string, 0, len(mk.keys))
for k, _ := range mk.keys {

View File

@ -46,6 +46,23 @@ func TestMemKeyStoreBasics(t *testing.T) {
if err == nil {
t.Fatal("should not be able to overwrite key")
}
exist, err := ks.Has("foo")
if !exist {
t.Fatal("should know it has a key named foo")
}
if err != nil {
t.Fatal(err)
}
exist, err = ks.Has("nonexistingkey")
if exist {
t.Fatal("should know it doesn't have a key named nonexistingkey")
}
if err != nil {
t.Fatal(err)
}
if err := ks.Delete("bar"); err != nil {
t.Fatal(err)
}

View File

@ -36,6 +36,37 @@ test_key_cmd() {
PeerID="$(ipfs config Identity.PeerID)"
ipfs key list -l | grep "$PeerID self"
'
test_expect_success "key rm remove a key" '
ipfs key rm foobarsa
echo bazed > list_exp &&
echo self >> list_exp
ipfs key list | sort > list_out &&
test_cmp list_exp list_out
'
test_expect_success "key rm can't remove self" '
test_must_fail ipfs key rm self 2>&1 | tee key_rm_out &&
grep -q "Error: cannot remove key with name" key_rm_out
'
test_expect_success "key rename rename a key" '
ipfs key rename bazed fooed
echo fooed > list_exp &&
echo self >> list_exp
ipfs key list | sort > list_out &&
test_cmp list_exp list_out
'
test_expect_success "key rename can't rename self" '
test_must_fail ipfs key rename self bar 2>&1 | tee key_rename_out &&
grep -q "Error: cannot rename key with name" key_rename_out
'
test_expect_success "key rename can't overwrite self, even with force" '
test_must_fail ipfs key rename -f fooed self 2>&1 | tee key_rename_out &&
grep -q "Error: cannot overwrite key with name" key_rename_out
'
}
test_key_cmd