Skip to content
Snippets Groups Projects
Commit 3d2544f9 authored by Josh Brooks's avatar Josh Brooks
Browse files

Manually pull UD changes from release

parent b19e510f
No related branches found
No related tags found
4 merge requests!510Release,!207WIP: Client Restructure,!203Symmetric broadcast,!199Xx 3866/user discovery
......@@ -9,6 +9,7 @@ package bindings
import (
"fmt"
jww "github.com/spf13/jwalterweatherman"
"time"
"github.com/pkg/errors"
......@@ -46,6 +47,63 @@ func NewUserDiscovery(client *Client) (*UserDiscovery, error) {
}
}
// NewUserDiscoveryFromBackup returns a new user discovery object. It
// wil set up the manager with the backup data. Pass into it the backed up
// facts, one email and phone number each. This will add the registered facts
// to the backed Store. Any one of these fields may be empty,
// however both fields being empty will cause an error. Any other fact that is not
// an email or phone number will return an error. You may only add a fact for the
// accepted types once each. If you attempt to back up a fact type that has already
// been backed up, an error will be returned. Anytime an error is returned, it means
// the backup was not successful.
// NOTE: Do not use this as a direct store operation. This feature is intended to add facts
// to a backend store that have ALREADY BEEN REGISTERED on the account.
// THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
// Only call this once. It must be called after StartNetworkFollower
// is called and will fail if the network has never been contacted.
// This function technically has a memory leak because it causes both sides of
// the bindings to think the other is in charge of the client object.
// In general this is not an issue because the client object should exist
// for the life of the program.
// This must be called while start network follower is running.
func NewUserDiscoveryFromBackup(client *Client,
email, phone string) (*UserDiscovery, error) {
single, err := client.getSingle()
if err != nil {
return nil, errors.WithMessage(err, "Failed to create User Discovery Manager")
}
var emailFact, phoneFact fact.Fact
// Parse email as a fact, if it exists
if len(email) > 2 {
emailFact, err = fact.UnstringifyFact(email)
if err != nil {
return nil, errors.WithMessagef(err,
"Failed to parse malformed email fact: %s", email)
}
} else {
jww.WARN.Printf("Loading manager without a registered email")
}
// Parse phone number as a fact, if it exists
if len(phone) > 2 {
phoneFact, err = fact.UnstringifyFact(phone)
if err != nil {
return nil, errors.WithMessagef(err, "Failed to parse "+
"stringified phone fact %q", phone)
}
} else {
jww.WARN.Printf("Loading manager without a registered phone number")
}
m, err := ud.NewManagerFromBackup(&client.api, single, emailFact, phoneFact)
if err != nil {
return nil, errors.WithMessage(err, "Failed to create User Discovery Manager")
} else {
return &UserDiscovery{ud: m}, nil
}
}
// Register registers a user with user discovery. Will return an error if the
// network signatures are malformed or if the username is taken. Usernames
// cannot be changed after registration at this time. Will fail if the user is
......@@ -103,37 +161,6 @@ func (ud *UserDiscovery) RemoveUser(fStr string) error {
return ud.ud.RemoveUser(f)
}
//BackUpMissingFacts adds a registered fact to the Store object and saves
// it to storage. It can take in both an email or a phone number, passed into
// the function in that order. Any one of these fields may be empty,
// however both fields being empty will cause an error. Any other fact that is not
// an email or phone number will return an error. You may only add a fact for the
// accepted types once each. If you attempt to back up a fact type that has already
// been backed up, an error will be returned. Anytime an error is returned, it means
// the backup was not successful.
// NOTE: Do not use this as a direct store operation. This feature is intended to add facts
// to a backend store that have ALREADY BEEN REGISTERED on the account.
// THIS IS NOT FOR ADDING NEWLY REGISTERED FACTS. That is handled on the backend.
func (ud *UserDiscovery) BackUpMissingFacts(email, phone string) error {
var emailFact, phoneFact fact.Fact
var err error
if len(email) > 2 {
emailFact, err = fact.UnstringifyFact(email)
if err != nil {
return errors.WithMessagef(err, "Failed to parse malformed email fact: %s", email)
}
}
if len(phone) > 2 {
phoneFact, err = fact.UnstringifyFact(phone)
if err != nil {
return errors.WithMessagef(err, "Failed to parse malformed phone fact: %s", phone)
}
}
return ud.ud.BackUpMissingFacts(emailFact, phoneFact)
}
// SearchCallback returns the result of a search
type SearchCallback interface {
Callback(contacts *ContactList, error string)
......
......@@ -22,6 +22,7 @@ import (
"gitlab.com/xx_network/crypto/csprng"
"gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id"
"math"
"time"
)
......@@ -158,6 +159,71 @@ func NewManager(services cmix.Client, e2e e2e.Handler, events event.Manager,
return m, nil
}
// NewManagerFromBackup builds a new user discover manager from a backup.
// It will construct a manager that is already registered and restore
// already registered facts into store.
func NewManagerFromBackup(client *api.Client, single *single.Manager,
email, phone fact.Fact) (*Manager, error) {
jww.INFO.Println("ud.NewManagerFromBackup()")
if client.NetworkFollowerStatus() != api.Running {
return nil, errors.New(
"cannot start UD Manager when network follower is not running.")
}
registered := uint32(0)
m := &Manager{
client: client,
comms: client.GetComms(),
rng: client.GetRng(),
sw: client.GetSwitchboard(),
storage: client.GetStorage(),
net: client.GetNetworkInterface(),
single: single,
registered: &registered,
}
err := m.client.GetStorage().GetUd().
BackUpMissingFacts(email, phone)
if err != nil {
return nil, errors.WithMessage(err, "Failed to restore UD store "+
"from backup")
}
// check that user discovery is available in the NDF
def := m.net.GetInstance().GetPartialNdf().Get()
if def.UDB.Cert == "" {
return nil, errors.New("NDF does not have User Discovery information, " +
"is there network access?: Cert not present.")
}
// Create the user discovery host object
hp := connect.GetDefaultHostParams()
// Client will not send KeepAlive packets
hp.KaClientOpts.Time = time.Duration(math.MaxInt64)
hp.MaxRetries = 3
hp.SendTimeout = 3 * time.Second
hp.AuthEnabled = false
m.myID = m.storage.User().GetCryptographicIdentity().GetReceptionID()
// Get the commonly used data from storage
m.privKey = m.storage.GetUser().ReceptionRSA
// Set as registered. Since it's from a backup,
// the client is already registered
if err = m.setRegistered(); err != nil {
return nil, errors.WithMessage(err, "failed to set client as "+
"registered with user discovery.")
}
// Store the pointer to the group locally for easy access
m.grp = m.storage.E2e().GetGroup()
return m, nil
}
func LoadManager(services cmix.Client, e2e e2e.Handler, events event.Manager,
comms Comms, userStore Userinfo, rng *fastRNG.StreamGenerator,
privKey *rsa.PrivateKey, kv *versioned.KV) (*Manager, error) {
......@@ -234,18 +300,6 @@ func (m *Manager) UnsetAlternativeUserDiscovery() error {
return nil
}
// BackUpMissingFacts adds a registered fact to the Store object.
// It can take in both an email and a phone number. One or the other may be nil,
// however both is considered an error. It checks for the proper fact type for
// the associated fact. Any other fact.FactType is not accepted and returns an
// error and nothing is backed up. If you attempt to back up a fact type that h
// as already been backed up, an error will be returned and nothing will be
// backed up. Otherwise, it adds the fact and returns whether the Store saved
// successfully.
func (m *Manager) BackUpMissingFacts(email, phone fact.Fact) error {
return m.store.BackUpMissingFacts(email, phone)
}
// GetFacts returns a list of fact.Fact objects that exist within the
// Store's registeredFacts map.
func (m *Manager) GetFacts() []fact.Fact {
......
......@@ -21,6 +21,7 @@ const (
"%s (%s) is non-empty but not an email. Cancelling backup operation"
backupMissingAllZeroesFactErr = "Cannot backup missing facts: Both email and phone facts are empty!"
factNotInStoreErr = "Fact %v does not exist in store"
statefulStoreErr = "cannot overwrite ud store with existing data"
)
// Store is the storage object for the higher level ud.Manager object.
......@@ -48,6 +49,26 @@ func NewStore(kv *versioned.KV) (*Store, error) {
return s, s.save()
}
// RestoreFromBackUp initializes the confirmedFacts map
// with the backed up fact data. This will error if
// the store is already stateful.
func (s *Store) RestoreFromBackUp(backupData fact.FactList) error {
s.mux.Lock()
defer s.mux.Unlock()
if len(s.confirmedFacts) != 0 || len(s.unconfirmedFacts) != 0 {
return errors.New(statefulStoreErr)
}
for _, f := range backupData {
if !isFactZero(f) {
s.confirmedFacts[f] = struct{}{}
}
}
return s.save()
}
// StoreUnconfirmedFact stores a fact that has been added to UD but has not been
// confirmed by the user. It is keyed on the confirmation ID given by UD.
func (s *Store) StoreUnconfirmedFact(confirmationId string, f fact.Fact) error {
......@@ -94,10 +115,6 @@ func (s *Store) BackUpMissingFacts(email, phone fact.Fact) error {
s.mux.Lock()
defer s.mux.Unlock()
if isFactZero(email) && isFactZero(phone) {
return errors.New(backupMissingAllZeroesFactErr)
}
modifiedEmail, modifiedPhone := false, false
// Handle email if it is not zero (empty string)
......
......@@ -27,6 +27,66 @@ func TestNewStore(t *testing.T) {
}
// Unit test
func TestStore_RestoreFromBackUp(t *testing.T) {
kv := versioned.NewKV(make(ekv.Memstore))
s, err := NewStore(kv)
if err != nil {
t.Errorf("NewStore() produced an error: %v", err)
}
expected := fact.Fact{
Fact: "josh",
T: fact.Username,
}
fl := fact.FactList{expected}
err = s.RestoreFromBackUp(fl)
if err != nil {
t.Fatalf("RestoreFromBackup err: %v", err)
}
_, exists := s.confirmedFacts[expected]
if !exists {
t.Fatalf("Fact %s does not exist in map", expected)
}
}
// Error case.
func TestStore_RestoreFromBackUp_StatefulStore(t *testing.T) {
kv := versioned.NewKV(make(ekv.Memstore))
s, err := NewStore(kv)
if err != nil {
t.Errorf("NewStore() produced an error: %v", err)
}
confirmId := "confirm"
expected := fact.Fact{
Fact: "josh",
T: fact.Username,
}
err = s.StoreUnconfirmedFact(confirmId, expected)
if err != nil {
t.Fatalf("StoreUnconfirmedFact error: %v", err)
}
// Expected error: should error when restoring on
// a stateful store.
fl := fact.FactList{expected}
err = s.RestoreFromBackUp(fl)
if err == nil {
t.Fatalf("RestoreFromBackup err: %v", err)
}
}
func TestStore_ConfirmFact(t *testing.T) {
kv := versioned.NewKV(make(ekv.Memstore))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment