Commit 4e143e4f authored by Petar Radovic's avatar Petar Radovic Committed by GitHub

Improve password creation UX (#617)

* check keys and create key prompt
parent c4efb95c
......@@ -11,10 +11,15 @@ import (
"io/ioutil"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/keystore"
filekeystore "github.com/ethersphere/bee/pkg/keystore/file"
memkeystore "github.com/ethersphere/bee/pkg/keystore/mem"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/node"
"github.com/sirupsen/logrus"
......@@ -70,6 +75,14 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
debugAPIAddr = ""
}
var keystore keystore.Service
if c.config.GetString(optionNameDataDir) == "" {
keystore = memkeystore.New()
logger.Warning("data directory not provided, keys are not persisted")
} else {
keystore = filekeystore.New(filepath.Join(c.config.GetString(optionNameDataDir), "keys"))
}
var password string
if p := c.config.GetString(optionNamePassword); p != "" {
password = p
......@@ -80,14 +93,40 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
}
password = string(bytes.Trim(b, "\n"))
} else {
p, err := terminalPromptPassword(cmd, c.passwordReader, "Password")
exists, err := keystore.Exists("swarm")
if err != nil {
return err
}
password = p
if exists {
password, err = terminalPromptPassword(cmd, c.passwordReader, "Password")
if err != nil {
return err
}
} else {
password, err = terminalPromptCreatePassword(cmd, c.passwordReader)
if err != nil {
return err
}
}
}
swarmPrivateKey, created, err := keystore.Key("swarm", password)
if err != nil {
return fmt.Errorf("swarm key: %w", err)
}
address, err := crypto.NewOverlayAddress(swarmPrivateKey.PublicKey, c.config.GetUint64(optionNameNetworkID))
if err != nil {
return err
}
if created {
logger.Infof("new swarm network address created: %s", address)
} else {
logger.Infof("using existing swarm network address: %s", address)
}
b, err := node.NewBee(c.config.GetString(optionNameP2PAddr), logger, node.Options{
b, err := node.NewBee(c.config.GetString(optionNameP2PAddr), address, keystore, swarmPrivateKey, c.config.GetUint64(optionNameNetworkID), logger, node.Options{
DataDir: c.config.GetString(optionNameDataDir),
DBCapacity: c.config.GetUint64(optionNameDBCapacity),
Password: password,
......@@ -97,7 +136,6 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
NATAddr: c.config.GetString(optionNameNATAddr),
EnableWS: c.config.GetBool(optionNameP2PWSEnable),
EnableQUIC: c.config.GetBool(optionNameP2PQUICEnable),
NetworkID: c.config.GetUint64(optionNameNetworkID),
WelcomeMessage: c.config.GetString(optionWelcomeMessage),
Bootnodes: c.config.GetStringSlice(optionNameBootnodes),
CORSAllowedOrigins: c.config.GetStringSlice(optionCORSAllowedOrigins),
......
......@@ -5,6 +5,7 @@
package cmd
import (
"errors"
"os"
"github.com/spf13/cobra"
......@@ -34,3 +35,22 @@ func terminalPromptPassword(cmd *cobra.Command, r passwordReader, title string)
}
return password, nil
}
func terminalPromptCreatePassword(cmd *cobra.Command, r passwordReader) (password string, err error) {
cmd.Println("Bee node is booting up for the first time. Please provide a new password.")
p1, err := terminalPromptPassword(cmd, r, "Password")
if err != nil {
return "", err
}
p2, err := terminalPromptPassword(cmd, r, "Confirm password")
if err != nil {
return "", err
}
if p1 != p2 {
return "", errors.New("passwords are not the same")
}
return p1, nil
}
......@@ -22,6 +22,20 @@ func New(dir string) *Service {
return &Service{dir: dir}
}
func (s *Service) Exists(name string) (bool, error) {
filename := s.keyFilename(name)
data, err := ioutil.ReadFile(filename)
if err != nil && !os.IsNotExist(err) {
return false, fmt.Errorf("read private key: %w", err)
}
if len(data) == 0 {
return false, nil
}
return true, nil
}
func (s *Service) Key(name, password string) (pk *ecdsa.PrivateKey, created bool, err error) {
filename := s.keyFilename(name)
......
......@@ -13,4 +13,5 @@ var ErrInvalidPassword = errors.New("invalid password")
type Service interface {
Key(name, password string) (k *ecdsa.PrivateKey, created bool, err error)
Exists(name string) (bool, error)
}
......@@ -26,6 +26,14 @@ func New() *Service {
}
}
func (s *Service) Exists(name string) (bool, error) {
s.mu.Lock()
defer s.mu.Unlock()
_, ok := s.m[name]
return ok, nil
}
func (s *Service) Key(name, password string) (pk *ecdsa.PrivateKey, created bool, err error) {
s.mu.Lock()
defer s.mu.Unlock()
......
......@@ -13,6 +13,14 @@ import (
)
func Service(t *testing.T, s keystore.Service) {
exists, err := s.Exists("swarm")
if err != nil {
t.Fatal(err)
}
if exists {
t.Fatal("should not exist")
}
// create a new swarm key
k1, created, err := s.Key("swarm", "pass123456")
if err != nil {
......@@ -22,6 +30,15 @@ func Service(t *testing.T, s keystore.Service) {
t.Fatal("key is not created")
}
exists, err = s.Exists("swarm")
if err != nil {
t.Fatal(err)
}
if !exists {
t.Fatal("should exist")
}
// get swarm key
k2, created, err := s.Key("swarm", "pass123456")
if err != nil {
......
......@@ -6,6 +6,7 @@ package node
import (
"context"
"crypto/ecdsa"
"fmt"
"io"
"log"
......@@ -23,8 +24,6 @@ import (
"github.com/ethersphere/bee/pkg/hive"
"github.com/ethersphere/bee/pkg/kademlia"
"github.com/ethersphere/bee/pkg/keystore"
filekeystore "github.com/ethersphere/bee/pkg/keystore/file"
memkeystore "github.com/ethersphere/bee/pkg/keystore/mem"
"github.com/ethersphere/bee/pkg/localstore"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/metrics"
......@@ -77,7 +76,6 @@ type Options struct {
NATAddr string
EnableWS bool
EnableQUIC bool
NetworkID uint64
WelcomeMessage string
Bootnodes []string
CORSAllowedOrigins []string
......@@ -90,7 +88,7 @@ type Options struct {
PaymentTolerance uint64
}
func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
func NewBee(addr string, swarmAddress swarm.Address, keystore keystore.Service, swarmPrivateKey *ecdsa.PrivateKey, networkID uint64, logger logging.Logger, o Options) (*Bee, error) {
tracer, tracerCloser, err := tracing.NewTracer(&tracing.Options{
Enabled: o.TracingEnabled,
Endpoint: o.TracingEndpoint,
......@@ -108,30 +106,8 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
tracerCloser: tracerCloser,
}
var keyStore keystore.Service
if o.DataDir == "" {
keyStore = memkeystore.New()
logger.Warning("data directory not provided, keys are not persisted")
} else {
keyStore = filekeystore.New(filepath.Join(o.DataDir, "keys"))
}
swarmPrivateKey, created, err := keyStore.Key("swarm", o.Password)
if err != nil {
return nil, fmt.Errorf("swarm key: %w", err)
}
address, err := crypto.NewOverlayAddress(swarmPrivateKey.PublicKey, o.NetworkID)
if err != nil {
return nil, err
}
if created {
logger.Infof("new swarm network address created: %s", address)
} else {
logger.Infof("using existing swarm network address: %s", address)
}
// Construct P2P service.
libp2pPrivateKey, created, err := keyStore.Key("libp2p", o.Password)
libp2pPrivateKey, created, err := keystore.Key("libp2p", o.Password)
if err != nil {
return nil, fmt.Errorf("libp2p key: %w", err)
}
......@@ -155,7 +131,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
addressbook := addressbook.New(stateStore)
signer := crypto.NewDefaultSigner(swarmPrivateKey)
p2ps, err := libp2p.New(p2pCtx, signer, o.NetworkID, address, addr, addressbook, logger, tracer, libp2p.Options{
p2ps, err := libp2p.New(p2pCtx, signer, networkID, swarmAddress, addr, addressbook, logger, tracer, libp2p.Options{
PrivateKey: libp2pPrivateKey,
NATAddr: o.NATAddr,
EnableWS: o.EnableWS,
......@@ -188,7 +164,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
return nil, fmt.Errorf("pingpong service: %w", err)
}
hive := hive.New(p2ps, addressbook, o.NetworkID, logger)
hive := hive.New(p2ps, addressbook, networkID, logger)
if err = p2ps.AddProtocol(hive.Protocol()); err != nil {
return nil, fmt.Errorf("hive service: %w", err)
}
......@@ -205,7 +181,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
bootnodes = append(bootnodes, addr)
}
kad := kademlia.New(address, addressbook, hive, p2ps, logger, kademlia.Options{Bootnodes: bootnodes})
kad := kademlia.New(swarmAddress, addressbook, hive, p2ps, logger, kademlia.Options{Bootnodes: bootnodes})
b.topologyCloser = kad
hive.SetAddPeersHandler(kad.AddPeers)
p2ps.AddNotifier(kad)
......@@ -226,7 +202,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
lo := &localstore.Options{
Capacity: o.DBCapacity,
}
storer, err := localstore.New(path, address.Bytes(), lo, logger)
storer, err := localstore.New(path, swarmAddress.Bytes(), lo, logger)
if err != nil {
return nil, fmt.Errorf("localstore: %w", err)
}
......@@ -256,7 +232,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
chunkvalidator := swarm.NewChunkValidator(soc.NewValidator(), content.NewValidator())
retrieve := retrieval.New(p2ps, kad, logger, acc, accounting.NewFixedPricer(address, 10), chunkvalidator)
retrieve := retrieval.New(p2ps, kad, logger, acc, accounting.NewFixedPricer(swarmAddress, 10), chunkvalidator)
tagg := tags.NewTags()
if err = p2ps.AddProtocol(retrieve.Protocol()); err != nil {
......@@ -276,7 +252,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
}
retrieve.SetStorer(ns)
pushSyncProtocol := pushsync.New(p2ps, storer, kad, tagg, psss.TryUnwrap, logger, acc, accounting.NewFixedPricer(address, 10))
pushSyncProtocol := pushsync.New(p2ps, storer, kad, tagg, psss.TryUnwrap, logger, acc, accounting.NewFixedPricer(swarmAddress, 10))
// set the pushSyncer in the PSS
psss.WithPushSyncer(pushSyncProtocol)
......@@ -335,7 +311,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
if o.DebugAPIAddr != "" {
// Debug API server
debugAPIService := debugapi.New(address, p2ps, pingPong, kad, storer, logger, tracer, tagg, acc)
debugAPIService := debugapi.New(swarmAddress, p2ps, pingPong, kad, storer, logger, tracer, tagg, acc)
// register metrics from components
debugAPIService.MustRegisterMetrics(p2ps.Metrics()...)
debugAPIService.MustRegisterMetrics(pingPong.Metrics()...)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment