mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-23 03:17:43 +08:00
Merge pull request #3892 from MichaelMure/keystore_key_rm
Implement ipfs key {rm, rename}
This commit is contained in:
commit
0780a4fdb4
@ -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()
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user