Merge PR #3674: Remove password/keybase from REST Client
This commit is contained in:
committed by
Jack Zampolin
parent
6967de1073
commit
e39debd359
+1
-194
@@ -2,10 +2,7 @@ package keys
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
@@ -13,15 +10,13 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/go-bip39"
|
||||
bip39 "github.com/cosmos/go-bip39"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/multisig"
|
||||
@@ -304,191 +299,3 @@ func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// REST
|
||||
|
||||
// function to just create a new seed to display in the UI before actually persisting it in the keybase
|
||||
func generateMnemonic(algo keys.SigningAlgo) string {
|
||||
kb := keys.NewInMemory()
|
||||
pass := app.DefaultKeyPass
|
||||
name := "inmemorykey"
|
||||
_, seed, _ := kb.CreateMnemonic(name, keys.English, pass, algo)
|
||||
return seed
|
||||
}
|
||||
|
||||
// CheckAndWriteErrorResponse will check for errors and return
|
||||
// a given error message when corresponding
|
||||
//TODO: Move to utils/rest or similar
|
||||
func CheckAndWriteErrorResponse(w http.ResponseWriter, httpErr int, err error) bool {
|
||||
if err != nil {
|
||||
w.WriteHeader(httpErr)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// add new key REST handler
|
||||
func AddNewKeyRequestHandler(indent bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var kb keys.Keybase
|
||||
var m AddNewKey
|
||||
|
||||
kb, err := NewKeyBaseFromHomeFlag()
|
||||
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &m)
|
||||
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check parameters
|
||||
if m.Name == "" {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingName())
|
||||
return
|
||||
}
|
||||
if m.Password == "" {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingPassword())
|
||||
return
|
||||
}
|
||||
|
||||
mnemonic := m.Mnemonic
|
||||
// if mnemonic is empty, generate one
|
||||
if mnemonic == "" {
|
||||
mnemonic = generateMnemonic(keys.Secp256k1)
|
||||
}
|
||||
if !bip39.IsMnemonicValid(mnemonic) {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidMnemonic())
|
||||
}
|
||||
|
||||
if m.Account < 0 || m.Account > maxValidAccountValue {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidAccountNumber())
|
||||
return
|
||||
}
|
||||
|
||||
if m.Index < 0 || m.Index > maxValidIndexalue {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidIndexNumber())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = kb.Get(m.Name)
|
||||
if err == nil {
|
||||
CheckAndWriteErrorResponse(w, http.StatusConflict, errKeyNameConflict(m.Name))
|
||||
return
|
||||
}
|
||||
|
||||
// create account
|
||||
account := uint32(m.Account)
|
||||
index := uint32(m.Index)
|
||||
info, err := kb.CreateAccount(m.Name, mnemonic, keys.DefaultBIP39Passphrase, m.Password, account, index)
|
||||
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
|
||||
return
|
||||
}
|
||||
|
||||
keyOutput, err := Bech32KeyOutput(info)
|
||||
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
|
||||
return
|
||||
}
|
||||
|
||||
keyOutput.Mnemonic = mnemonic
|
||||
|
||||
rest.PostProcessResponse(w, cdc, keyOutput, indent)
|
||||
}
|
||||
}
|
||||
|
||||
// Seed REST request handler
|
||||
func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
algoType := vars["type"]
|
||||
|
||||
// algo type defaults to secp256k1
|
||||
if algoType == "" {
|
||||
algoType = "secp256k1"
|
||||
}
|
||||
|
||||
algo := keys.SigningAlgo(algoType)
|
||||
seed := generateMnemonic(algo)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(seed))
|
||||
}
|
||||
|
||||
// RecoverRequestHandler performs key recover request
|
||||
func RecoverRequestHandler(indent bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
var m RecoverKey
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = cdc.UnmarshalJSON(body, &m)
|
||||
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
|
||||
return
|
||||
}
|
||||
|
||||
kb, err := NewKeyBaseFromHomeFlag()
|
||||
CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err)
|
||||
|
||||
if name == "" {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingName())
|
||||
return
|
||||
}
|
||||
if m.Password == "" {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingPassword())
|
||||
return
|
||||
}
|
||||
|
||||
mnemonic := m.Mnemonic
|
||||
if !bip39.IsMnemonicValid(mnemonic) {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidMnemonic())
|
||||
}
|
||||
|
||||
if m.Mnemonic == "" {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingMnemonic())
|
||||
return
|
||||
}
|
||||
|
||||
if m.Account < 0 || m.Account > maxValidAccountValue {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidAccountNumber())
|
||||
return
|
||||
}
|
||||
|
||||
if m.Index < 0 || m.Index > maxValidIndexalue {
|
||||
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidIndexNumber())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = kb.Get(name)
|
||||
if err == nil {
|
||||
CheckAndWriteErrorResponse(w, http.StatusConflict, errKeyNameConflict(name))
|
||||
return
|
||||
}
|
||||
|
||||
account := uint32(m.Account)
|
||||
index := uint32(m.Index)
|
||||
|
||||
info, err := kb.CreateAccount(name, mnemonic, keys.DefaultBIP39Passphrase, m.Password, account, index)
|
||||
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
|
||||
return
|
||||
}
|
||||
|
||||
keyOutput, err := Bech32KeyOutput(info)
|
||||
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
|
||||
return
|
||||
}
|
||||
|
||||
rest.PostProcessResponse(w, cdc, keyOutput, indent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,9 @@ package keys
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
|
||||
@@ -61,39 +57,3 @@ func Test_runAddCmdBasic(t *testing.T) {
|
||||
err = runAddCmd(cmd, []string{"keyname2"})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
type MockResponseWriter struct {
|
||||
dataHeaderStatus int
|
||||
dataBody []byte
|
||||
}
|
||||
|
||||
func (MockResponseWriter) Header() http.Header {
|
||||
panic("Unexpected call!")
|
||||
}
|
||||
|
||||
func (w *MockResponseWriter) Write(data []byte) (int, error) {
|
||||
w.dataBody = append(w.dataBody, data...)
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (w *MockResponseWriter) WriteHeader(statusCode int) {
|
||||
w.dataHeaderStatus = statusCode
|
||||
}
|
||||
|
||||
func TestCheckAndWriteErrorResponse(t *testing.T) {
|
||||
mockRW := MockResponseWriter{}
|
||||
|
||||
mockRW.WriteHeader(100)
|
||||
assert.Equal(t, 100, mockRW.dataHeaderStatus)
|
||||
|
||||
detected := CheckAndWriteErrorResponse(&mockRW, http.StatusBadRequest, errors.New("some ERROR"))
|
||||
require.True(t, detected)
|
||||
require.Equal(t, http.StatusBadRequest, mockRW.dataHeaderStatus)
|
||||
require.Equal(t, "some ERROR", string(mockRW.dataBody[:]))
|
||||
|
||||
mockRW = MockResponseWriter{}
|
||||
detected = CheckAndWriteErrorResponse(&mockRW, http.StatusBadRequest, nil)
|
||||
require.False(t, detected)
|
||||
require.Equal(t, 0, mockRW.dataHeaderStatus)
|
||||
require.Equal(t, "", string(mockRW.dataBody[:]))
|
||||
}
|
||||
|
||||
@@ -2,19 +2,14 @@ package keys
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -101,51 +96,3 @@ func confirmDeletion(buf *bufio.Reader) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
// REST
|
||||
|
||||
// delete key request REST body
|
||||
type DeleteKeyBody struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// delete key REST handler
|
||||
func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
var kb keys.Keybase
|
||||
var m DeleteKeyBody
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&m)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
kb, err = NewKeyBaseFromHomeFlag()
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
err = kb.Delete(name, m.Password, false)
|
||||
if keyerror.IsErrKeyNotFound(err) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
} else if keyerror.IsErrWrongPassword(err) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
} else if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -29,36 +26,3 @@ func runListCmd(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// REST
|
||||
|
||||
// query key list REST handler
|
||||
func QueryKeysRequestHandler(indent bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
kb, err := NewKeyBaseFromHomeFlag()
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
infos, err := kb.List()
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
// an empty list will be JSONized as null, but we want to keep the empty list
|
||||
if len(infos) == 0 {
|
||||
rest.PostProcessResponse(w, cdc, []string{}, indent)
|
||||
return
|
||||
}
|
||||
keysOutput, err := Bech32KeysOutput(infos)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
rest.PostProcessResponse(w, cdc, keysOutput, indent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
@@ -30,14 +29,3 @@ func Commands() *cobra.Command {
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// resgister REST routes
|
||||
func RegisterRoutes(r *mux.Router, indent bool) {
|
||||
r.HandleFunc("/keys", QueryKeysRequestHandler(indent)).Methods("GET")
|
||||
r.HandleFunc("/keys", AddNewKeyRequestHandler(indent)).Methods("POST")
|
||||
r.HandleFunc("/keys/seed", SeedRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/keys/{name}/recover", RecoverRequestHandler(indent)).Methods("POST")
|
||||
r.HandleFunc("/keys/{name}", GetKeyRequestHandler(indent)).Methods("GET")
|
||||
r.HandleFunc("/keys/{name}", UpdateKeyRequestHandler).Methods("PUT")
|
||||
r.HandleFunc("/keys/{name}", DeleteKeyRequestHandler).Methods("DELETE")
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func TestCommands(t *testing.T) {
|
||||
@@ -15,8 +13,3 @@ func TestCommands(t *testing.T) {
|
||||
// Commands are registered
|
||||
assert.Equal(t, 7, len(rootCommands.Commands()))
|
||||
}
|
||||
|
||||
func TestRegisterRoutes(t *testing.T) {
|
||||
fakeRouter := mux.Router{}
|
||||
RegisterRoutes(&fakeRouter, false)
|
||||
}
|
||||
|
||||
@@ -2,22 +2,18 @@ package keys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/tendermint/crypto/multisig"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
@@ -149,46 +145,3 @@ func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
|
||||
|
||||
return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix)
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// REST
|
||||
|
||||
// get key REST handler
|
||||
func GetKeyRequestHandler(indent bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
bechPrefix := r.URL.Query().Get(FlagBechPrefix)
|
||||
|
||||
if bechPrefix == "" {
|
||||
bechPrefix = sdk.PrefixAccount
|
||||
}
|
||||
|
||||
bechKeyOut, err := getBechKeyOut(bechPrefix)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
info, err := GetKeyInfo(name)
|
||||
if keyerror.IsErrKeyNotFound(err) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
} else if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
keyOutput, err := bechKeyOut(info)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
rest.PostProcessResponse(w, cdc, keyOutput, indent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
|
||||
)
|
||||
|
||||
func updateKeyCommand() *cobra.Command {
|
||||
@@ -52,55 +45,3 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error {
|
||||
fmt.Println("Password successfully updated!")
|
||||
return nil
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// REST
|
||||
|
||||
// update key request REST body
|
||||
type UpdateKeyBody struct {
|
||||
NewPassword string `json:"new_password"`
|
||||
OldPassword string `json:"old_password"`
|
||||
}
|
||||
|
||||
// update key REST handler
|
||||
func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
var kb keys.Keybase
|
||||
var m UpdateKeyBody
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&m)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
kb, err = NewKeyBaseFromHomeFlag()
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
getNewpass := func() (string, error) { return m.NewPassword, nil }
|
||||
|
||||
err = kb.Update(name, m.OldPassword, getNewpass)
|
||||
if keyerror.IsErrKeyNotFound(err) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
} else if keyerror.IsErrWrongPassword(err) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
} else if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user