Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove password/keybase from REST Client #3674

Merged
merged 12 commits into from
Feb 19, 2019
5 changes: 5 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

### Gaia REST API

* [\#3641] Remove the ability to use a Keybase from the REST API client:
* `password` and `generate_only` have been removed from the `base_req` object
* All txs that used to sign or use the Keybase now only generate the tx
* `keys` routes completely removed

### Gaia CLI

### Gaia
Expand Down
195 changes: 1 addition & 194 deletions client/keys/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,21 @@ package keys

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"sort"

"github.com/cosmos/cosmos-sdk/client"
"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"
Expand Down Expand Up @@ -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)
}
}
40 changes: 0 additions & 40 deletions client/keys/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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[:]))
}
53 changes: 0 additions & 53 deletions client/keys/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
}
Loading