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 ( ...@@ -11,10 +11,15 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"strings" "strings"
"syscall" "syscall"
"time" "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/logging"
"github.com/ethersphere/bee/pkg/node" "github.com/ethersphere/bee/pkg/node"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
...@@ -70,6 +75,14 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz ...@@ -70,6 +75,14 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
debugAPIAddr = "" 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 var password string
if p := c.config.GetString(optionNamePassword); p != "" { if p := c.config.GetString(optionNamePassword); p != "" {
password = p password = p
...@@ -80,14 +93,40 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz ...@@ -80,14 +93,40 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
} }
password = string(bytes.Trim(b, "\n")) password = string(bytes.Trim(b, "\n"))
} else { } else {
p, err := terminalPromptPassword(cmd, c.passwordReader, "Password") exists, err := keystore.Exists("swarm")
if err != nil { if err != nil {
return err 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), DataDir: c.config.GetString(optionNameDataDir),
DBCapacity: c.config.GetUint64(optionNameDBCapacity), DBCapacity: c.config.GetUint64(optionNameDBCapacity),
Password: password, Password: password,
...@@ -97,7 +136,6 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz ...@@ -97,7 +136,6 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
NATAddr: c.config.GetString(optionNameNATAddr), NATAddr: c.config.GetString(optionNameNATAddr),
EnableWS: c.config.GetBool(optionNameP2PWSEnable), EnableWS: c.config.GetBool(optionNameP2PWSEnable),
EnableQUIC: c.config.GetBool(optionNameP2PQUICEnable), EnableQUIC: c.config.GetBool(optionNameP2PQUICEnable),
NetworkID: c.config.GetUint64(optionNameNetworkID),
WelcomeMessage: c.config.GetString(optionWelcomeMessage), WelcomeMessage: c.config.GetString(optionWelcomeMessage),
Bootnodes: c.config.GetStringSlice(optionNameBootnodes), Bootnodes: c.config.GetStringSlice(optionNameBootnodes),
CORSAllowedOrigins: c.config.GetStringSlice(optionCORSAllowedOrigins), CORSAllowedOrigins: c.config.GetStringSlice(optionCORSAllowedOrigins),
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package cmd package cmd
import ( import (
"errors"
"os" "os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
...@@ -34,3 +35,22 @@ func terminalPromptPassword(cmd *cobra.Command, r passwordReader, title string) ...@@ -34,3 +35,22 @@ func terminalPromptPassword(cmd *cobra.Command, r passwordReader, title string)
} }
return password, nil 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 { ...@@ -22,6 +22,20 @@ func New(dir string) *Service {
return &Service{dir: dir} 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) { func (s *Service) Key(name, password string) (pk *ecdsa.PrivateKey, created bool, err error) {
filename := s.keyFilename(name) filename := s.keyFilename(name)
......
...@@ -13,4 +13,5 @@ var ErrInvalidPassword = errors.New("invalid password") ...@@ -13,4 +13,5 @@ var ErrInvalidPassword = errors.New("invalid password")
type Service interface { type Service interface {
Key(name, password string) (k *ecdsa.PrivateKey, created bool, err error) Key(name, password string) (k *ecdsa.PrivateKey, created bool, err error)
Exists(name string) (bool, error)
} }
...@@ -26,6 +26,14 @@ func New() *Service { ...@@ -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) { func (s *Service) Key(name, password string) (pk *ecdsa.PrivateKey, created bool, err error) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
......
...@@ -13,6 +13,14 @@ import ( ...@@ -13,6 +13,14 @@ import (
) )
func Service(t *testing.T, s keystore.Service) { 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 // create a new swarm key
k1, created, err := s.Key("swarm", "pass123456") k1, created, err := s.Key("swarm", "pass123456")
if err != nil { if err != nil {
...@@ -22,6 +30,15 @@ func Service(t *testing.T, s keystore.Service) { ...@@ -22,6 +30,15 @@ func Service(t *testing.T, s keystore.Service) {
t.Fatal("key is not created") 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 // get swarm key
k2, created, err := s.Key("swarm", "pass123456") k2, created, err := s.Key("swarm", "pass123456")
if err != nil { if err != nil {
......
...@@ -6,6 +6,7 @@ package node ...@@ -6,6 +6,7 @@ package node
import ( import (
"context" "context"
"crypto/ecdsa"
"fmt" "fmt"
"io" "io"
"log" "log"
...@@ -23,8 +24,6 @@ import ( ...@@ -23,8 +24,6 @@ import (
"github.com/ethersphere/bee/pkg/hive" "github.com/ethersphere/bee/pkg/hive"
"github.com/ethersphere/bee/pkg/kademlia" "github.com/ethersphere/bee/pkg/kademlia"
"github.com/ethersphere/bee/pkg/keystore" "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/localstore"
"github.com/ethersphere/bee/pkg/logging" "github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/metrics" "github.com/ethersphere/bee/pkg/metrics"
...@@ -77,7 +76,6 @@ type Options struct { ...@@ -77,7 +76,6 @@ type Options struct {
NATAddr string NATAddr string
EnableWS bool EnableWS bool
EnableQUIC bool EnableQUIC bool
NetworkID uint64
WelcomeMessage string WelcomeMessage string
Bootnodes []string Bootnodes []string
CORSAllowedOrigins []string CORSAllowedOrigins []string
...@@ -90,7 +88,7 @@ type Options struct { ...@@ -90,7 +88,7 @@ type Options struct {
PaymentTolerance uint64 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{ tracer, tracerCloser, err := tracing.NewTracer(&tracing.Options{
Enabled: o.TracingEnabled, Enabled: o.TracingEnabled,
Endpoint: o.TracingEndpoint, Endpoint: o.TracingEndpoint,
...@@ -108,30 +106,8 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) { ...@@ -108,30 +106,8 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
tracerCloser: tracerCloser, 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. // Construct P2P service.
libp2pPrivateKey, created, err := keyStore.Key("libp2p", o.Password) libp2pPrivateKey, created, err := keystore.Key("libp2p", o.Password)
if err != nil { if err != nil {
return nil, fmt.Errorf("libp2p key: %w", err) return nil, fmt.Errorf("libp2p key: %w", err)
} }
...@@ -155,7 +131,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) { ...@@ -155,7 +131,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
addressbook := addressbook.New(stateStore) addressbook := addressbook.New(stateStore)
signer := crypto.NewDefaultSigner(swarmPrivateKey) 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, PrivateKey: libp2pPrivateKey,
NATAddr: o.NATAddr, NATAddr: o.NATAddr,
EnableWS: o.EnableWS, EnableWS: o.EnableWS,
...@@ -188,7 +164,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) { ...@@ -188,7 +164,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
return nil, fmt.Errorf("pingpong service: %w", err) 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 { if err = p2ps.AddProtocol(hive.Protocol()); err != nil {
return nil, fmt.Errorf("hive service: %w", err) return nil, fmt.Errorf("hive service: %w", err)
} }
...@@ -205,7 +181,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) { ...@@ -205,7 +181,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
bootnodes = append(bootnodes, addr) 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 b.topologyCloser = kad
hive.SetAddPeersHandler(kad.AddPeers) hive.SetAddPeersHandler(kad.AddPeers)
p2ps.AddNotifier(kad) p2ps.AddNotifier(kad)
...@@ -226,7 +202,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) { ...@@ -226,7 +202,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
lo := &localstore.Options{ lo := &localstore.Options{
Capacity: o.DBCapacity, Capacity: o.DBCapacity,
} }
storer, err := localstore.New(path, address.Bytes(), lo, logger) storer, err := localstore.New(path, swarmAddress.Bytes(), lo, logger)
if err != nil { if err != nil {
return nil, fmt.Errorf("localstore: %w", err) return nil, fmt.Errorf("localstore: %w", err)
} }
...@@ -256,7 +232,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) { ...@@ -256,7 +232,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
chunkvalidator := swarm.NewChunkValidator(soc.NewValidator(), content.NewValidator()) 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() tagg := tags.NewTags()
if err = p2ps.AddProtocol(retrieve.Protocol()); err != nil { if err = p2ps.AddProtocol(retrieve.Protocol()); err != nil {
...@@ -276,7 +252,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) { ...@@ -276,7 +252,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
} }
retrieve.SetStorer(ns) 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 // set the pushSyncer in the PSS
psss.WithPushSyncer(pushSyncProtocol) psss.WithPushSyncer(pushSyncProtocol)
...@@ -335,7 +311,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) { ...@@ -335,7 +311,7 @@ func NewBee(addr string, logger logging.Logger, o Options) (*Bee, error) {
if o.DebugAPIAddr != "" { if o.DebugAPIAddr != "" {
// Debug API server // 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 // register metrics from components
debugAPIService.MustRegisterMetrics(p2ps.Metrics()...) debugAPIService.MustRegisterMetrics(p2ps.Metrics()...)
debugAPIService.MustRegisterMetrics(pingPong.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