mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-28 21:51:22 +00:00
Removed keys/server as it is now in cosmos-sdk
This commit is contained in:
parent
6f6bbf718e
commit
69a7b389b8
@ -1,13 +0,0 @@
|
|||||||
# Proxy Server
|
|
||||||
|
|
||||||
This package provides all the functionality for a local http server, providing access to key management functionality (creating, listing, updating, and deleting keys). This is a nice building block for larger apps, and the HTTP handlers here can be embedded in a larger server that does nice things like signing transactions and posting them to a tendermint chain (which requires domain-knowledge of the transactions types and out of scope of this generic app).
|
|
||||||
|
|
||||||
## Key Management
|
|
||||||
|
|
||||||
We expose a number of methods for safely managing your keychain. If you are embedding this in a larger server, you will typically want to mount all these paths under `/keys`.
|
|
||||||
|
|
||||||
* `POST /` - provide a name and passphrase and create a brand new key
|
|
||||||
* `GET /` - get a list of all available key names, along with their public key and address
|
|
||||||
* `GET /{name}` - get public key and address for this named key
|
|
||||||
* `PUT /{name}` - update the passphrase for the given key. requires you to correctly provide the current passphrase, as well as a new one.
|
|
||||||
* `DELETE /{name}` - permanently delete this private key. requires you to correctly provide the current passphrase
|
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
package server provides http handlers to construct a server server
|
|
||||||
for key management, transaction signing, and query validation.
|
|
||||||
|
|
||||||
Please read the README and godoc to see how to
|
|
||||||
configure the server for your application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/tendermint/go-crypto/keys/server/types"
|
|
||||||
data "github.com/tendermint/go-wire/data"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func readRequest(r *http.Request, o interface{}) error {
|
|
||||||
defer r.Body.Close()
|
|
||||||
data, err := ioutil.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Read Request")
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(data, o)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Parse")
|
|
||||||
}
|
|
||||||
return validate(o)
|
|
||||||
}
|
|
||||||
|
|
||||||
// most errors are bad input, so 406... do better....
|
|
||||||
func writeError(w http.ResponseWriter, err error) {
|
|
||||||
// fmt.Printf("Error: %+v\n", err)
|
|
||||||
res := types.ErrorResponse{
|
|
||||||
Code: 406,
|
|
||||||
Error: err.Error(),
|
|
||||||
}
|
|
||||||
writeCode(w, &res, 406)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeCode(w http.ResponseWriter, o interface{}, code int) {
|
|
||||||
// two space indent to make it easier to read
|
|
||||||
data, err := data.ToJSON(o)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
} else {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(code)
|
|
||||||
w.Write(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeSuccess(w http.ResponseWriter, o interface{}) {
|
|
||||||
writeCode(w, o, 200)
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
"github.com/tendermint/go-crypto/keys/server/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Keys struct {
|
|
||||||
manager keys.Manager
|
|
||||||
algo string
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(manager keys.Manager, algo string) Keys {
|
|
||||||
return Keys{
|
|
||||||
manager: manager,
|
|
||||||
algo: algo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) GenerateKey(w http.ResponseWriter, r *http.Request) {
|
|
||||||
req := types.CreateKeyRequest{
|
|
||||||
Algo: k.algo, // default key type from cli
|
|
||||||
}
|
|
||||||
err := readRequest(r, &req)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
key, seed, err := k.manager.Create(req.Name, req.Passphrase, req.Algo)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res := types.CreateKeyResponse{key, seed}
|
|
||||||
writeSuccess(w, &res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) GetKey(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
name := vars["name"]
|
|
||||||
key, err := k.manager.Get(name)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeSuccess(w, &key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) ListKeys(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
keys, err := k.manager.List()
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeSuccess(w, keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) UpdateKey(w http.ResponseWriter, r *http.Request) {
|
|
||||||
req := types.UpdateKeyRequest{}
|
|
||||||
err := readRequest(r, &req)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
name := vars["name"]
|
|
||||||
if name != req.Name {
|
|
||||||
writeError(w, errors.New("path and json key names don't match"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = k.manager.Update(req.Name, req.OldPass, req.NewPass)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := k.manager.Get(req.Name)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeSuccess(w, &key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) DeleteKey(w http.ResponseWriter, r *http.Request) {
|
|
||||||
req := types.DeleteKeyRequest{}
|
|
||||||
err := readRequest(r, &req)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
name := vars["name"]
|
|
||||||
if name != req.Name {
|
|
||||||
writeError(w, errors.New("path and json key names don't match"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = k.manager.Delete(req.Name, req.Passphrase)
|
|
||||||
if err != nil {
|
|
||||||
writeError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// not really an error, but something generic
|
|
||||||
resp := types.ErrorResponse{
|
|
||||||
Success: true,
|
|
||||||
}
|
|
||||||
writeSuccess(w, &resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keys) Register(r *mux.Router) {
|
|
||||||
r.HandleFunc("/", k.GenerateKey).Methods("POST")
|
|
||||||
r.HandleFunc("/", k.ListKeys).Methods("GET")
|
|
||||||
r.HandleFunc("/{name}", k.GetKey).Methods("GET")
|
|
||||||
r.HandleFunc("/{name}", k.UpdateKey).Methods("POST", "PUT")
|
|
||||||
r.HandleFunc("/{name}", k.DeleteKey).Methods("DELETE")
|
|
||||||
}
|
|
@ -1,193 +0,0 @@
|
|||||||
package server_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
"github.com/tendermint/go-crypto/keys/cryptostore"
|
|
||||||
"github.com/tendermint/go-crypto/keys/server"
|
|
||||||
"github.com/tendermint/go-crypto/keys/server/types"
|
|
||||||
"github.com/tendermint/go-crypto/keys/storage/memstorage"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestKeyServer(t *testing.T) {
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
r := setupServer()
|
|
||||||
|
|
||||||
// let's abstract this out a bit....
|
|
||||||
keys, code, err := listKeys(r)
|
|
||||||
require.Nil(err)
|
|
||||||
require.Equal(http.StatusOK, code)
|
|
||||||
assert.Equal(0, len(keys))
|
|
||||||
|
|
||||||
algo := "ed25519"
|
|
||||||
n1, n2 := "personal", "business"
|
|
||||||
p0, p1, p2 := "1234", "over10chars...", "really-secure!@#$"
|
|
||||||
|
|
||||||
// this fails for validation
|
|
||||||
_, code, err = createKey(r, n1, p0, algo)
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.NotEqual(http.StatusOK, code)
|
|
||||||
|
|
||||||
// new password better
|
|
||||||
key, code, err := createKey(r, n1, p1, algo)
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.Equal(http.StatusOK, code)
|
|
||||||
require.Equal(n1, key.Key.Name)
|
|
||||||
require.NotEmpty(n1, key.Seed)
|
|
||||||
|
|
||||||
// the other one works
|
|
||||||
key2, code, err := createKey(r, n2, p2, algo)
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.Equal(http.StatusOK, code)
|
|
||||||
require.Equal(key2.Key.Name, n2)
|
|
||||||
require.NotEmpty(n2, key.Seed)
|
|
||||||
|
|
||||||
// let's abstract this out a bit....
|
|
||||||
keys, code, err = listKeys(r)
|
|
||||||
require.Nil(err)
|
|
||||||
require.Equal(http.StatusOK, code)
|
|
||||||
if assert.Equal(2, len(keys)) {
|
|
||||||
// in alphabetical order
|
|
||||||
assert.Equal(keys[0].Name, n2)
|
|
||||||
assert.Equal(keys[1].Name, n1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get works
|
|
||||||
k, code, err := getKey(r, n1)
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.Equal(http.StatusOK, code)
|
|
||||||
assert.Equal(n1, k.Name)
|
|
||||||
assert.NotNil(k.Address)
|
|
||||||
assert.Equal(key.Key.Address, k.Address)
|
|
||||||
|
|
||||||
// delete with proper key
|
|
||||||
_, code, err = deleteKey(r, n1, p1)
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.Equal(http.StatusOK, code)
|
|
||||||
|
|
||||||
// after delete, get and list different
|
|
||||||
_, code, err = getKey(r, n1)
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.NotEqual(http.StatusOK, code)
|
|
||||||
keys, code, err = listKeys(r)
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.Equal(http.StatusOK, code)
|
|
||||||
if assert.Equal(1, len(keys)) {
|
|
||||||
assert.Equal(keys[0].Name, n2)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupServer() http.Handler {
|
|
||||||
// make the storage with reasonable defaults
|
|
||||||
cstore := cryptostore.New(
|
|
||||||
cryptostore.SecretBox,
|
|
||||||
memstorage.New(),
|
|
||||||
keys.MustLoadCodec("english"),
|
|
||||||
)
|
|
||||||
|
|
||||||
// build your http server
|
|
||||||
ks := server.New(cstore, "ed25519")
|
|
||||||
r := mux.NewRouter()
|
|
||||||
sk := r.PathPrefix("/keys").Subrouter()
|
|
||||||
ks.Register(sk)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// return data, status code, and error
|
|
||||||
func listKeys(h http.Handler) (keys.Infos, int, error) {
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest("GET", "/keys/", nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ServeHTTP(rr, req)
|
|
||||||
if http.StatusOK != rr.Code {
|
|
||||||
return nil, rr.Code, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
data := keys.Infos{}
|
|
||||||
err = json.Unmarshal(rr.Body.Bytes(), &data)
|
|
||||||
return data, rr.Code, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func getKey(h http.Handler, name string) (*keys.Info, int, error) {
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest("GET", "/keys/"+name, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ServeHTTP(rr, req)
|
|
||||||
if http.StatusOK != rr.Code {
|
|
||||||
return nil, rr.Code, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
data := keys.Info{}
|
|
||||||
err = json.Unmarshal(rr.Body.Bytes(), &data)
|
|
||||||
return &data, rr.Code, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func createKey(h http.Handler, name, passphrase, algo string) (*types.CreateKeyResponse, int, error) {
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
post := types.CreateKeyRequest{
|
|
||||||
Name: name,
|
|
||||||
Passphrase: passphrase,
|
|
||||||
Algo: algo,
|
|
||||||
}
|
|
||||||
var b bytes.Buffer
|
|
||||||
err := json.NewEncoder(&b).Encode(&post)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/keys/", &b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ServeHTTP(rr, req)
|
|
||||||
if http.StatusOK != rr.Code {
|
|
||||||
return nil, rr.Code, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
data := new(types.CreateKeyResponse)
|
|
||||||
err = json.Unmarshal(rr.Body.Bytes(), data)
|
|
||||||
return data, rr.Code, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteKey(h http.Handler, name, passphrase string) (*types.ErrorResponse, int, error) {
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
post := types.DeleteKeyRequest{
|
|
||||||
Name: name,
|
|
||||||
Passphrase: passphrase,
|
|
||||||
}
|
|
||||||
var b bytes.Buffer
|
|
||||||
err := json.NewEncoder(&b).Encode(&post)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("DELETE", "/keys/"+name, &b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ServeHTTP(rr, req)
|
|
||||||
if http.StatusOK != rr.Code {
|
|
||||||
return nil, rr.Code, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
data := types.ErrorResponse{}
|
|
||||||
err = json.Unmarshal(rr.Body.Bytes(), &data)
|
|
||||||
return &data, rr.Code, err
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/tendermint/go-crypto/keys"
|
|
||||||
|
|
||||||
// CreateKeyRequest is sent to create a new key
|
|
||||||
type CreateKeyRequest struct {
|
|
||||||
Name string `json:"name" validate:"required,min=4,printascii"`
|
|
||||||
Passphrase string `json:"passphrase" validate:"required,min=10"`
|
|
||||||
Algo string `json:"algo"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteKeyRequest to destroy a key permanently (careful!)
|
|
||||||
type DeleteKeyRequest struct {
|
|
||||||
Name string `json:"name" validate:"required,min=4,printascii"`
|
|
||||||
Passphrase string `json:"passphrase" validate:"required,min=10"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateKeyRequest is sent to update the passphrase for an existing key
|
|
||||||
type UpdateKeyRequest struct {
|
|
||||||
Name string `json:"name" validate:"required,min=4,printascii"`
|
|
||||||
OldPass string `json:"passphrase" validate:"required,min=10"`
|
|
||||||
NewPass string `json:"new_passphrase" validate:"required,min=10"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorResponse is returned for 4xx and 5xx errors
|
|
||||||
type ErrorResponse struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Error string `json:"error"` // error message if Success is false
|
|
||||||
Code int `json:"code"` // error code if Success is false
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateKeyResponse struct {
|
|
||||||
Key keys.Info `json:"key"`
|
|
||||||
Seed string `json:"seed_phrase"`
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gopkg.in/go-playground/validator.v9"
|
|
||||||
)
|
|
||||||
|
|
||||||
var v = validator.New()
|
|
||||||
|
|
||||||
func validate(req interface{}) error {
|
|
||||||
return errors.Wrap(v.Struct(req), "Validate")
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user