1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-07-20 22:22:50 +08:00

Merge pull request from ipfs/refactor/coreapi/key

key cmd: Refactor to use coreapi
This commit is contained in:
Whyrusleeping
2018-08-07 16:03:35 -07:00
committed by GitHub
5 changed files with 97 additions and 234 deletions
core

@ -1,20 +1,15 @@
package commands package commands
import ( import (
"bytes"
"crypto/rand"
"fmt" "fmt"
"io" "io"
"sort"
"strings"
"text/tabwriter" "text/tabwriter"
cmds "github.com/ipfs/go-ipfs/commands" "github.com/ipfs/go-ipfs/core/commands/e"
e "github.com/ipfs/go-ipfs/core/commands/e" "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
"gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
"gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit" "gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
peer "gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer"
ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto"
) )
var KeyCmd = &cmds.Command{ var KeyCmd = &cmds.Command{
@ -70,99 +65,54 @@ var keyGenCmd = &cmds.Command{
Arguments: []cmdkit.Argument{ Arguments: []cmdkit.Argument{
cmdkit.StringArg("name", true, false, "name of key to create"), cmdkit.StringArg("name", true, false, "name of key to create"),
}, },
Run: func(req cmds.Request, res cmds.Response) { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
n, err := req.InvocContext().GetNode() api, err := GetApi(env)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
typ, f, err := req.Option("type").String()
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
typ, f := req.Options["type"].(string)
if !f { if !f {
res.SetError(fmt.Errorf("please specify a key type with --type"), cmdkit.ErrNormal) res.SetError(fmt.Errorf("please specify a key type with --type"), cmdkit.ErrNormal)
return return
} }
size, sizefound, err := req.Option("size").Int() name := req.Arguments[0]
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
name := req.Arguments()[0]
if name == "self" { if name == "self" {
res.SetError(fmt.Errorf("cannot create key with name 'self'"), cmdkit.ErrNormal) res.SetError(fmt.Errorf("cannot create key with name 'self'"), cmdkit.ErrNormal)
return return
} }
var sk ci.PrivKey opts := []options.KeyGenerateOption{options.Key.Type(typ)}
var pk ci.PubKey
switch typ { size, sizefound := req.Options["size"].(int)
case "rsa": if sizefound {
if !sizefound { opts = append(opts, options.Key.Size(size))
res.SetError(fmt.Errorf("please specify a key size with --size"), cmdkit.ErrNormal)
return
} }
priv, pub, err := ci.GenerateKeyPairWithReader(ci.RSA, size, rand.Reader) key, err := api.Key().Generate(req.Context, name, opts...)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
sk = priv cmds.EmitOnce(res, &KeyOutput{
pk = pub
case "ed25519":
priv, pub, err := ci.GenerateEd25519Key(rand.Reader)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
sk = priv
pk = pub
default:
res.SetError(fmt.Errorf("unrecognized key type: %s", typ), cmdkit.ErrNormal)
return
}
err = n.Repo.Keystore().Put(name, sk)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
pid, err := peer.IDFromPublicKey(pk)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
res.SetOutput(&KeyOutput{
Name: name, Name: name,
Id: pid.Pretty(), Id: key.ID().Pretty(),
}) })
}, },
Marshalers: cmds.MarshalerMap{ Encoders: cmds.EncoderMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) { cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
v, err := unwrapOutput(res.Output())
if err != nil {
return nil, err
}
k, ok := v.(*KeyOutput) k, ok := v.(*KeyOutput)
if !ok { if !ok {
return nil, e.TypeErr(k, v) return e.TypeErr(k, v)
} }
return strings.NewReader(k.Id + "\n"), nil _, err := w.Write([]byte(k.Id + "\n"))
}, return err
}),
}, },
Type: KeyOutput{}, Type: KeyOutput{},
} }
@ -174,47 +124,29 @@ var keyListCmd = &cmds.Command{
Options: []cmdkit.Option{ Options: []cmdkit.Option{
cmdkit.BoolOption("l", "Show extra information about keys."), cmdkit.BoolOption("l", "Show extra information about keys."),
}, },
Run: func(req cmds.Request, res cmds.Response) { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
n, err := req.InvocContext().GetNode() api, err := GetApi(env)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
keys, err := n.Repo.Keystore().List() keys, err := api.Key().List(req.Context)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
sort.Strings(keys) list := make([]KeyOutput, 0, len(keys))
list := make([]KeyOutput, 0, len(keys)+1)
list = append(list, KeyOutput{Name: "self", Id: n.Identity.Pretty()})
for _, key := range keys { for _, key := range keys {
privKey, err := n.Repo.Keystore().Get(key) list = append(list, KeyOutput{Name: key.Name(), Id: key.ID().Pretty()})
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
} }
pubKey := privKey.GetPublic() cmds.EmitOnce(res, &KeyOutputList{list})
pid, err := peer.IDFromPublicKey(pubKey)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
list = append(list, KeyOutput{Name: key, Id: pid.Pretty()})
}
res.SetOutput(&KeyOutputList{list})
}, },
Marshalers: cmds.MarshalerMap{ Encoders: cmds.EncoderMap{
cmds.Text: keyOutputListMarshaler, cmds.Text: keyOutputListMarshaler(),
}, },
Type: KeyOutputList{}, Type: KeyOutputList{},
} }
@ -230,100 +162,44 @@ var keyRenameCmd = &cmds.Command{
Options: []cmdkit.Option{ Options: []cmdkit.Option{
cmdkit.BoolOption("force", "f", "Allow to overwrite an existing key."), cmdkit.BoolOption("force", "f", "Allow to overwrite an existing key."),
}, },
Run: func(req cmds.Request, res cmds.Response) { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
n, err := req.InvocContext().GetNode() api, err := GetApi(env)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
ks := n.Repo.Keystore() name := req.Arguments[0]
newName := req.Arguments[1]
force, _ := req.Options["force"].(bool)
name := req.Arguments()[0] key, overwritten, err := api.Key().Rename(req.Context, name, newName, options.Key.Force(force))
newName := req.Arguments()[1]
if name == "self" {
res.SetError(fmt.Errorf("cannot rename key with name 'self'"), cmdkit.ErrNormal)
return
}
if newName == "self" {
res.SetError(fmt.Errorf("cannot overwrite key with name 'self'"), cmdkit.ErrNormal)
return
}
oldKey, err := ks.Get(name)
if err != nil {
res.SetError(fmt.Errorf("no key named %s was found", name), cmdkit.ErrNormal)
return
}
pubKey := oldKey.GetPublic()
pid, err := peer.IDFromPublicKey(pubKey)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
overwrite := false cmds.EmitOnce(res, &KeyRenameOutput{
force, _, _ := res.Request().Option("f").Bool()
if force {
exist, err := ks.Has(newName)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
if exist {
overwrite = true
err := ks.Delete(newName)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
}
}
err = ks.Put(newName, oldKey)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
err = ks.Delete(name)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
res.SetOutput(&KeyRenameOutput{
Was: name, Was: name,
Now: newName, Now: newName,
Id: pid.Pretty(), Id: key.ID().Pretty(),
Overwrite: overwrite, Overwrite: overwritten,
}) })
}, },
Marshalers: cmds.MarshalerMap{ Encoders: cmds.EncoderMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) { cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
v, err := unwrapOutput(res.Output())
if err != nil {
return nil, err
}
k, ok := v.(*KeyRenameOutput) k, ok := v.(*KeyRenameOutput)
if !ok { if !ok {
return nil, fmt.Errorf("expected a KeyRenameOutput as command result") return fmt.Errorf("expected a KeyRenameOutput as command result")
} }
buf := new(bytes.Buffer)
if k.Overwrite { if k.Overwrite {
fmt.Fprintf(buf, "Key %s renamed to %s with overwriting\n", k.Id, k.Now) fmt.Fprintf(w, "Key %s renamed to %s with overwriting\n", k.Id, k.Now)
} else { } else {
fmt.Fprintf(buf, "Key %s renamed to %s\n", k.Id, k.Now) fmt.Fprintf(w, "Key %s renamed to %s\n", k.Id, k.Now)
} }
return buf, nil return nil
}, }),
}, },
Type: KeyRenameOutput{}, Type: KeyRenameOutput{},
} }
@ -338,76 +214,52 @@ var keyRmCmd = &cmds.Command{
Options: []cmdkit.Option{ Options: []cmdkit.Option{
cmdkit.BoolOption("l", "Show extra information about keys."), cmdkit.BoolOption("l", "Show extra information about keys."),
}, },
Run: func(req cmds.Request, res cmds.Response) { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
n, err := req.InvocContext().GetNode() api, err := GetApi(env)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
names := req.Arguments() names := req.Arguments
list := make([]KeyOutput, 0, len(names)) list := make([]KeyOutput, 0, len(names))
for _, name := range names { for _, name := range names {
if name == "self" { key, err := api.Key().Remove(req.Context, name)
res.SetError(fmt.Errorf("cannot remove key with name 'self'"), cmdkit.ErrNormal)
return
}
removed, err := n.Repo.Keystore().Get(name)
if err != nil {
res.SetError(fmt.Errorf("no key named %s was found", name), cmdkit.ErrNormal)
return
}
pubKey := removed.GetPublic()
pid, err := peer.IDFromPublicKey(pubKey)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
list = append(list, KeyOutput{Name: name, Id: pid.Pretty()}) list = append(list, KeyOutput{Name: name, Id: key.ID().Pretty()})
} }
for _, name := range names { cmds.EmitOnce(res, &KeyOutputList{list})
err = n.Repo.Keystore().Delete(name)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
}
res.SetOutput(&KeyOutputList{list})
}, },
Marshalers: cmds.MarshalerMap{ Encoders: cmds.EncoderMap{
cmds.Text: keyOutputListMarshaler, cmds.Text: keyOutputListMarshaler(),
}, },
Type: KeyOutputList{}, Type: KeyOutputList{},
} }
func keyOutputListMarshaler(res cmds.Response) (io.Reader, error) { func keyOutputListMarshaler() cmds.EncoderFunc {
withId, _, _ := res.Request().Option("l").Bool() return cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
withID, _ := req.Options["l"].(bool)
v, err := unwrapOutput(res.Output())
if err != nil {
return nil, err
}
list, ok := v.(*KeyOutputList) list, ok := v.(*KeyOutputList)
if !ok { if !ok {
return nil, e.TypeErr(list, v) return e.TypeErr(list, v)
} }
buf := new(bytes.Buffer) tw := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)
w := tabwriter.NewWriter(buf, 1, 2, 1, ' ', 0)
for _, s := range list.Keys { for _, s := range list.Keys {
if withId { if withID {
fmt.Fprintf(w, "%s\t%s\t\n", s.Id, s.Name) fmt.Fprintf(tw, "%s\t%s\t\n", s.Id, s.Name)
} else { } else {
fmt.Fprintf(w, "%s\n", s.Name) fmt.Fprintf(tw, "%s\n", s.Name)
} }
} }
w.Flush() tw.Flush()
return buf, nil return nil
})
} }

@ -121,7 +121,7 @@ var rootSubcommands = map[string]*cmds.Command{
"diag": lgc.NewCommand(DiagCmd), "diag": lgc.NewCommand(DiagCmd),
"dns": lgc.NewCommand(DNSCmd), "dns": lgc.NewCommand(DNSCmd),
"id": lgc.NewCommand(IDCmd), "id": lgc.NewCommand(IDCmd),
"key": lgc.NewCommand(KeyCmd), "key": KeyCmd,
"log": lgc.NewCommand(LogCmd), "log": lgc.NewCommand(LogCmd),
"ls": lgc.NewCommand(LsCmd), "ls": lgc.NewCommand(LsCmd),
"mount": lgc.NewCommand(MountCmd), "mount": lgc.NewCommand(MountCmd),

@ -4,14 +4,20 @@ import (
"context" "context"
options "github.com/ipfs/go-ipfs/core/coreapi/interface/options" options "github.com/ipfs/go-ipfs/core/coreapi/interface/options"
"gx/ipfs/QmdVrMn1LhB4ybb8hMVaMLXnA8XRSewMnK6YqXKXoTcRvN/go-libp2p-peer"
) )
// Key specifies the interface to Keys in KeyAPI Keystore // Key specifies the interface to Keys in KeyAPI Keystore
type Key interface { type Key interface {
// Key returns key name // Key returns key name
Name() string Name() string
// Path returns key path // Path returns key path
Path() Path Path() Path
// ID returns key PeerID
ID() peer.ID
} }
// KeyAPI specifies the interface to Keystore // KeyAPI specifies the interface to Keystore
@ -28,5 +34,5 @@ type KeyAPI interface {
List(ctx context.Context) ([]Key, error) List(ctx context.Context) ([]Key, error)
// Remove removes keys from keystore. Returns ipns path of the removed key // Remove removes keys from keystore. Returns ipns path of the removed key
Remove(ctx context.Context, name string) (Path, error) Remove(ctx context.Context, name string) (Key, error)
} }

@ -18,7 +18,7 @@ type KeyAPI CoreAPI
type key struct { type key struct {
name string name string
peerId string peerID peer.ID
} }
// Name returns the key name // Name returns the key name
@ -28,7 +28,7 @@ func (k *key) Name() string {
// Path returns the path of the key. // Path returns the path of the key.
func (k *key) Path() coreiface.Path { func (k *key) Path() coreiface.Path {
path, err := coreiface.ParsePath(ipfspath.Join([]string{"/ipns", k.peerId})) path, err := coreiface.ParsePath(ipfspath.Join([]string{"/ipns", k.peerID.Pretty()}))
if err != nil { if err != nil {
panic("error parsing path: " + err.Error()) panic("error parsing path: " + err.Error())
} }
@ -36,6 +36,11 @@ func (k *key) Path() coreiface.Path {
return path return path
} }
// ID returns key PeerID
func (k *key) ID() peer.ID {
return k.peerID
}
// Generate generates new key, stores it in the keystore under the specified // Generate generates new key, stores it in the keystore under the specified
// name and returns a base58 encoded multihash of its public key. // name and returns a base58 encoded multihash of its public key.
func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.KeyGenerateOption) (coreiface.Key, error) { func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.KeyGenerateOption) (coreiface.Key, error) {
@ -45,7 +50,7 @@ func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.Key
} }
if name == "self" { if name == "self" {
return nil, fmt.Errorf("cannot overwrite key with name 'self'") return nil, fmt.Errorf("cannot create key with name 'self'")
} }
_, err = api.node.Repo.Keystore().Get(name) _, err = api.node.Repo.Keystore().Get(name)
@ -91,7 +96,7 @@ func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.Key
return nil, err return nil, err
} }
return &key{name, pid.Pretty()}, nil return &key{name, pid}, nil
} }
// List returns a list keys stored in keystore. // List returns a list keys stored in keystore.
@ -104,7 +109,7 @@ func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) {
sort.Strings(keys) sort.Strings(keys)
out := make([]coreiface.Key, len(keys)+1) out := make([]coreiface.Key, len(keys)+1)
out[0] = &key{"self", api.node.Identity.Pretty()} out[0] = &key{"self", api.node.Identity}
for n, k := range keys { for n, k := range keys {
privKey, err := api.node.Repo.Keystore().Get(k) privKey, err := api.node.Repo.Keystore().Get(k)
@ -119,7 +124,7 @@ func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) {
return nil, err return nil, err
} }
out[n+1] = &key{k, pid.Pretty()} out[n+1] = &key{k, pid}
} }
return out, nil return out, nil
} }
@ -175,11 +180,11 @@ func (api *KeyAPI) Rename(ctx context.Context, oldName string, newName string, o
return nil, false, err return nil, false, err
} }
return &key{newName, pid.Pretty()}, overwrite, ks.Delete(oldName) return &key{newName, pid}, overwrite, ks.Delete(oldName)
} }
// Remove removes keys from keystore. Returns ipns path of the removed key. // Remove removes keys from keystore. Returns ipns path of the removed key.
func (api *KeyAPI) Remove(ctx context.Context, name string) (coreiface.Path, error) { func (api *KeyAPI) Remove(ctx context.Context, name string) (coreiface.Key, error) {
ks := api.node.Repo.Keystore() ks := api.node.Repo.Keystore()
if name == "self" { if name == "self" {
@ -203,5 +208,5 @@ func (api *KeyAPI) Remove(ctx context.Context, name string) (coreiface.Path, err
return nil, err return nil, err
} }
return (&key{"", pid.Pretty()}).Path(), nil return &key{"", pid}, nil
} }

@ -174,8 +174,8 @@ func TestGenerateExisting(t *testing.T) {
if err == nil { if err == nil {
t.Error("expected error to not be nil") t.Error("expected error to not be nil")
} else { } else {
if err.Error() != "cannot overwrite key with name 'self'" { if err.Error() != "cannot create key with name 'self'" {
t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) t.Fatalf("expected error 'cannot create key with name 'self'', got '%s'", err.Error())
} }
} }
} }
@ -396,8 +396,8 @@ func TestRemove(t *testing.T) {
return return
} }
if k.Path().String() != p.String() { if k.Path().String() != p.Path().String() {
t.Errorf("k and p should have equal paths, '%s'!='%s'", k.Path().String(), p.String()) t.Errorf("k and p should have equal paths, '%s'!='%s'", k.Path().String(), p.Path().String())
} }
l, err = api.Key().List(ctx) l, err = api.Key().List(ctx)