Skip to content
Snippets Groups Projects
Commit 2a2185d7 authored by Jonah Husson's avatar Jonah Husson
Browse files

Merge branch 'release' into hotfix/singleUseListenBindings

parents 38dd025a 9ee3aee2
Branches
Tags
2 merge requests!510Release,!296Fix listen to return an interface for stopping
///////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
///////////////////////////////////////////////////////////////////////////////
package bindings
import (
"gitlab.com/elixxir/client/backup"
)
// Backup is a bindings-level struct encapsulating the backup.Backup
// client object.
type Backup struct {
b *backup.Backup
}
// UpdateBackupFunc contains a function callback that returns new backups.
type UpdateBackupFunc interface {
UpdateBackup(encryptedBackup []byte)
}
// InitializeBackup creates a bindings-layer Backup object.
//
// Params
// - e2eID - ID of the E2e object in the e2e tracker.
// - udID - ID of the UserDiscovery object in the ud tracker.
// - password - password used in LoadCmix.
// - cb - the callback to be called when a backup is triggered.
func InitializeBackup(e2eID, udID int, password string,
cb UpdateBackupFunc) (*Backup, error) {
// Retrieve the user from the tracker
user, err := e2eTrackerSingleton.get(e2eID)
if err != nil {
return nil, err
}
// Retrieve the UD manager
ud, err := udTrackerSingleton.get(udID)
if err != nil {
return nil, err
}
// Initialize backup
b, err := backup.InitializeBackup(password, cb.UpdateBackup,
user.api.GetBackupContainer(), user.api.GetE2E(),
user.api.GetStorage(), ud.api,
user.api.GetStorage().GetKV(), user.api.GetRng())
if err != nil {
return nil, err
}
return &Backup{b: b}, nil
}
// ResumeBackup resumes the backup processes with a new callback.
// Call this function only when resuming a backup that has already been
// initialized or to replace the callback.
// To start the backup for the first time or to use a new password, use
// InitializeBackup.
//
// Params
// - e2eID - ID of the E2e object in the e2e tracker.
// - udID - ID of the UserDiscovery object in the ud tracker.
// - cb - the callback to be called when a backup is triggered.
// This will replace any callback that has been passed into InitializeBackup.
func ResumeBackup(e2eID, udID int, cb UpdateBackupFunc) (
*Backup, error) {
// Retrieve the user from the tracker
user, err := e2eTrackerSingleton.get(e2eID)
if err != nil {
return nil, err
}
// Retrieve the UD manager
ud, err := udTrackerSingleton.get(udID)
if err != nil {
return nil, err
}
// Resume backup
b, err := backup.ResumeBackup(cb.UpdateBackup, user.api.GetBackupContainer(),
user.api.GetE2E(), user.api.GetStorage(), ud.api,
user.api.GetStorage().GetKV(), user.api.GetRng())
if err != nil {
return nil, err
}
return &Backup{b}, nil
}
// StopBackup stops the backup processes and deletes the user's password from
// storage. To enable backups again, call InitializeBackup.
func (b *Backup) StopBackup() error {
return b.b.StopBackup()
}
// IsBackupRunning returns true if the backup has been initialized and is
// running. Returns false if it has been stopped.
func (b *Backup) IsBackupRunning() bool {
return b.b.IsBackupRunning()
}
// AddJson stores the argument within the Backup structure.
//
// Params
// - json - JSON string
func (b *Backup) AddJson(json string) {
b.b.AddJson(json)
}
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
////////////////////////////////////////////////////////////////////////////////
package bindings
import (
"encoding/json"
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/ud"
"gitlab.com/elixxir/client/xxdk"
"gitlab.com/elixxir/primitives/fact"
"sync"
)
////////////////////////////////////////////////////////////////////////////////
// Singleton Tracker //
////////////////////////////////////////////////////////////////////////////////
// udTrackerSingleton is used to track UserDiscovery objects so that they can be
// referenced by ID back over the bindings.
var udTrackerSingleton = &udTracker{
tracked: make(map[int]*UserDiscovery),
count: 0,
}
// udTracker is a singleton used to keep track of extant UserDiscovery objects,
// preventing race conditions created by passing it over the bindings.
type udTracker struct {
tracked map[int]*UserDiscovery
count int
mux sync.RWMutex
}
// make create a UserDiscovery from an ud.Manager, assigns it a unique ID, and
// adds it to the udTracker.
func (ut *udTracker) make(u *ud.Manager) *UserDiscovery {
ut.mux.Lock()
defer ut.mux.Unlock()
id := ut.count
ut.count++
ut.tracked[id] = &UserDiscovery{
api: u,
id: id,
}
return ut.tracked[id]
}
// get an UserDiscovery from the udTracker given its ID.
func (ut *udTracker) get(id int) (*UserDiscovery, error) {
ut.mux.RLock()
defer ut.mux.RUnlock()
c, exist := ut.tracked[id]
if !exist {
return nil, errors.Errorf(
"Cannot get UserDiscovery for ID %d, does not exist", id)
}
return c, nil
}
// delete removes a UserDiscovery from the udTracker.
func (ut *udTracker) delete(id int) {
ut.mux.Lock()
defer ut.mux.Unlock()
delete(ut.tracked, id)
}
////////////////////////////////////////////////////////////////////////////////
// Structs and Interfaces //
////////////////////////////////////////////////////////////////////////////////
// UserDiscovery is a bindings-layer struct that wraps an ud.Manager interface.
type UserDiscovery struct {
api *ud.Manager
id int
}
// GetID returns the udTracker ID for the UserDiscovery object.
func (ud *UserDiscovery) GetID() int {
return ud.id
}
// UdNetworkStatus contains the UdNetworkStatus, which is a bindings-level
// interface for ud.udNetworkStatus.
type UdNetworkStatus interface {
// UdNetworkStatus returns:
// - int - a xxdk.Status int
UdNetworkStatus() int
}
////////////////////////////////////////////////////////////////////////////////
// Main functions //
////////////////////////////////////////////////////////////////////////////////
// LoadOrNewUserDiscovery creates a bindings-level user discovery manager.
//
// Parameters:
// - e2eID - e2e object ID in the tracker
// - follower - network follower func wrapped in UdNetworkStatus
func LoadOrNewUserDiscovery(e2eID int, follower UdNetworkStatus,
username string, registrationValidationSignature []byte) (
*UserDiscovery, error) {
// Get user from singleton
user, err := e2eTrackerSingleton.get(e2eID)
if err != nil {
return nil, err
}
UdNetworkStatusFn := func() xxdk.Status {
return xxdk.Status(follower.UdNetworkStatus())
}
u, err := ud.LoadOrNewManager(user.api, user.api.GetComms(),
UdNetworkStatusFn, username, registrationValidationSignature)
if err != nil {
return nil, err
}
return udTrackerSingleton.make(u), nil
}
// NewUdManagerFromBackup 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.
//
// Parameters:
// - e2eID - e2e object ID in the tracker
// - follower - network follower func wrapped in UdNetworkStatus
// - emailFactJson - a JSON marshalled email fact.Fact
// - phoneFactJson - a JSON marshalled phone fact.Fact
func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus, emailFactJson,
phoneFactJson []byte) (*UserDiscovery, error) {
// Get user from singleton
user, err := e2eTrackerSingleton.get(e2eID)
if err != nil {
return nil, err
}
var email, phone fact.Fact
err = json.Unmarshal(emailFactJson, &email)
if err != nil {
return nil, err
}
err = json.Unmarshal(phoneFactJson, &phone)
if err != nil {
return nil, err
}
UdNetworkStatusFn := func() xxdk.Status {
return xxdk.Status(follower.UdNetworkStatus())
}
u, err := ud.NewManagerFromBackup(
user.api, user.api.GetComms(), UdNetworkStatusFn, email, phone)
if err != nil {
return nil, err
}
return udTrackerSingleton.make(u), nil
}
// GetFacts returns a JSON marshalled list of fact.Fact objects that exist
// within the Store's registeredFacts map.
func (ud *UserDiscovery) GetFacts() []byte {
jsonData, err := json.Marshal(ud.api.GetFacts())
if err != nil {
jww.FATAL.Panicf("Failed to JSON marshal fact list: %+v", err)
}
return jsonData
}
// GetContact returns the marshalled bytes of the contact.Contact for UD as
// retrieved from the NDF.
func (ud *UserDiscovery) GetContact() ([]byte, error) {
c, err := ud.api.GetContact()
if err != nil {
return nil, err
}
return json.Marshal(c)
}
// ConfirmFact confirms a fact first registered via AddFact. The confirmation ID
// comes from AddFact while the code will come over the associated
// communications system.
func (ud *UserDiscovery) ConfirmFact(confirmationID, code string) error {
return ud.api.ConfirmFact(confirmationID, code)
}
// SendRegisterFact adds a fact for the user to user discovery. Will only
// succeed if the user is already registered and the system does not have the
// fact currently registered for any user.
//
// This does not complete the fact registration process, it returns a
// confirmation ID instead. Over the communications system the fact is
// associated with, a code will be sent. This confirmation ID needs to be called
// along with the code to finalize the fact.
//
// Parameters:
// - factJson - a JSON marshalled fact.Fact
func (ud *UserDiscovery) SendRegisterFact(factJson []byte) (string, error) {
var f fact.Fact
err := json.Unmarshal(factJson, &f)
if err != nil {
return "", err
}
return ud.api.SendRegisterFact(f)
}
// PermanentDeleteAccount removes the username associated with this user from
// the UD service. This will only take a username type fact, and the fact must
// be associated with this user.
//
// Parameters:
// - factJson - a JSON marshalled fact.Fact
func (ud *UserDiscovery) PermanentDeleteAccount(factJson []byte) error {
var f fact.Fact
err := json.Unmarshal(factJson, &f)
if err != nil {
return err
}
return ud.api.PermanentDeleteAccount(f)
}
// RemoveFact removes a previously confirmed fact. This will fail if the fact
// passed in is not UD service does not associate this fact with this user.
//
// Parameters:
// - factJson - a JSON marshalled fact.Fact
func (ud *UserDiscovery) RemoveFact(factJson []byte) error {
var f fact.Fact
err := json.Unmarshal(factJson, &f)
if err != nil {
return err
}
return ud.api.RemoveFact(f)
}
// SetAlternativeUserDiscovery sets the alternativeUd object within manager.
// Once set, any user discovery operation will go through the alternative
// user discovery service.
//
// To undo this operation, use UnsetAlternativeUserDiscovery.
func (ud *UserDiscovery) SetAlternativeUserDiscovery(
altCert, altAddress, contactFile []byte) error {
return ud.api.SetAlternativeUserDiscovery(altCert, altAddress, contactFile)
}
// UnsetAlternativeUserDiscovery clears out the information from the Manager
// object.
func (ud *UserDiscovery) UnsetAlternativeUserDiscovery() error {
return ud.api.UnsetAlternativeUserDiscovery()
}
......@@ -10,7 +10,6 @@ package cmd
import (
"fmt"
"strings"
"time"
"gitlab.com/elixxir/client/single"
......@@ -62,21 +61,12 @@ var udCmd = &cobra.Command{
jww.TRACE.Printf("[UD] Connected!")
// Make user discovery manager
rng := user.GetRng()
userToRegister := viper.GetString(udRegisterFlag)
jww.TRACE.Printf("[UD] Registering identity %v...", userToRegister)
userDiscoveryMgr, err := ud.NewManager(user, user.GetComms(),
userDiscoveryMgr, err := ud.LoadOrNewManager(user, user.GetComms(),
user.NetworkFollowerStatus, userToRegister, nil)
if err != nil {
if strings.Contains(err.Error(), ud.IsRegisteredErr) {
userDiscoveryMgr, err = ud.LoadManager(user, user.GetComms())
if err != nil {
jww.FATAL.Panicf("Failed to load UD manager: %+v", err)
}
} else {
jww.FATAL.Panicf("Failed to create new UD manager: %+v", err)
}
jww.FATAL.Panicf("Failed to load or create new UD manager: %+v", err)
}
jww.INFO.Printf("[UD] Registered user %v", userToRegister)
......@@ -145,13 +135,11 @@ var udCmd = &cobra.Command{
printContact(newContact)
}
stream := rng.GetStream()
_, _, err = ud.Lookup(user,
udContact, cb, lookupID, single.GetDefaultRequestParams())
if err != nil {
jww.WARN.Printf("Failed UD lookup: %+v", err)
}
stream.Close()
time.Sleep(31 * time.Second)
}
......
......@@ -14,10 +14,11 @@ import (
// SendRegisterFact adds a fact for the user to user discovery. Will only
// succeed if the user is already registered and the system does not have the
// fact currently registered for any user.
//
// This does not complete the fact registration process, it returns a
// confirmation id instead. Over the communications system the fact is
// associated with, a code will be sent. This confirmation ID needs to be
// called along with the code to finalize the fact.
// confirmation ID instead. Over the communications system the fact is
// associated with, a code will be sent. This confirmation ID needs to be called
// along with the code to finalize the fact.
func (m *Manager) SendRegisterFact(f fact.Fact) (string, error) {
jww.INFO.Printf("ud.SendRegisterFact(%s)", f.Stringify())
m.factMux.Lock()
......
......@@ -19,6 +19,7 @@ type alternateUd struct {
// SetAlternativeUserDiscovery sets the alternativeUd object within manager.
// Once set, any user discovery operation will go through the alternative
// user discovery service.
//
// To undo this operation, use UnsetAlternativeUserDiscovery.
func (m *Manager) SetAlternativeUserDiscovery(altCert, altAddress,
contactFile []byte) error {
......
......@@ -18,12 +18,6 @@ import (
"gitlab.com/xx_network/primitives/id"
)
const (
IsRegisteredErr = "NewManager is already registered. " +
"NewManager is meant for the first instantiation. Use LoadManager " +
"for all other calls"
)
// Manager is the control structure for the contacting the user discovery service.
type Manager struct {
......@@ -48,19 +42,22 @@ type Manager struct {
// alternativeUd is an alternate User discovery service to circumvent
// production. This is for testing with a separately deployed UD service.
alternativeUd *alternateUd
// registrationValidationSignature for the ReceptionID
// Optional, depending on UD configuration
registrationValidationSignature []byte
}
// NewManager builds a new user discovery manager.
// It requires that an updated
// NDF is available and will error if one is not.
// registrationValidationSignature may be set to nil
func NewManager(user udE2e, comms Comms, follower udNetworkStatus,
username string, registrationValidationSignature []byte) (*Manager, error) {
jww.INFO.Println("ud.NewManager()")
// LoadOrNewManager loads an existing Manager from storage or creates a
// new one if there is no extant storage information.
//
// Params
// - user is an interface that adheres to the xxdk.E2e object.
// - comms is an interface that adheres to client.Comms object.
// - follower is a method off of xxdk.Cmix which returns the network follower's status.
// - username is the name of the user as it is registered with UD. This will be what the end user
// provides if through the bindings.
// - networkValidationSig is a signature provided by the network (i.e. the client registrar). This may
// be nil, however UD may return an error in some cases (e.g. in a production level environment).
func LoadOrNewManager(user udE2e, comms Comms, follower udNetworkStatus,
username string, networkValidationSig []byte) (*Manager, error) {
jww.INFO.Println("ud.LoadOrNewManager()")
if follower() != xxdk.Running {
return nil, errors.New(
......@@ -71,11 +68,16 @@ func NewManager(user udE2e, comms Comms, follower udNetworkStatus,
m := &Manager{
user: user,
comms: comms,
registrationValidationSignature: registrationValidationSignature,
}
if m.isRegistered() {
return nil, errors.Errorf(IsRegisteredErr)
// Load manager if already registered
var err error
m.store, err = store.NewOrLoadStore(m.getKv())
if err != nil {
return nil, errors.Errorf("Failed to initialize store: %v", err)
}
return m, nil
}
// Initialize store
......@@ -95,7 +97,7 @@ func NewManager(user udE2e, comms Comms, follower udNetworkStatus,
// Register with user discovery
stream := m.getRng().GetStream()
defer stream.Close()
err = m.register(username, stream, m.comms, udHost)
err = m.register(username, networkValidationSig, stream, m.comms, udHost)
if err != nil {
return nil, errors.Errorf("Failed to register: %v", err)
}
......@@ -159,7 +161,7 @@ func NewManagerFromBackup(user udE2e, comms Comms, follower udNetworkStatus,
return m, nil
}
// InitStoreFromBackup initializes the UD storage from the backup subsystem
// InitStoreFromBackup initializes the UD storage from the backup subsystem.
func InitStoreFromBackup(kv *versioned.KV,
username, email, phone fact.Fact) error {
// Initialize our store
......@@ -185,32 +187,9 @@ func InitStoreFromBackup(kv *versioned.KV,
return nil
}
// LoadManager loads the state of the Manager
// from disk. This is meant to be called after any the first
// instantiation of the manager by NewUserDiscovery.
func LoadManager(user udE2e, comms Comms) (*Manager, error) {
m := &Manager{
user: user,
comms: comms,
}
if !m.isRegistered() {
return nil, errors.Errorf("LoadManager could not detect that " +
"the user has been registered. Has a manager been initiated before?")
}
var err error
m.store, err = store.NewOrLoadStore(m.getKv())
if err != nil {
return nil, errors.Errorf("Failed to initialize store: %v", err)
}
return m, err
}
// GetFacts returns a list of fact.Fact objects that exist within the
// Store's registeredFacts map.
func (m *Manager) GetFacts() []fact.Fact {
func (m *Manager) GetFacts() fact.FactList {
return m.store.GetFacts()
}
......@@ -312,9 +291,9 @@ func (m *Manager) getOrAddUdHost() (*connect.Host, error) {
return host, nil
}
/////////////////////////////////////////////////////////////////////////////////////////
// Internal getters /////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Internal Getters //
////////////////////////////////////////////////////////////////////////////////
// getCmix retrieve a sub-interface of cmix.Client.
// It allows the Manager to retrieve network state.
......
......@@ -14,8 +14,8 @@ import (
// register initiates registration with user discovery given a specified
// username. Provided a comms sub-interface to facilitate testing.
func (m *Manager) register(username string, rng csprng.Source,
comm registerUserComms, udHost *connect.Host) error {
func (m *Manager) register(username string, networkSignature []byte,
rng csprng.Source, comm registerUserComms, udHost *connect.Host) error {
var err error
identity := m.user.GetReceptionIdentity()
......@@ -35,7 +35,7 @@ func (m *Manager) register(username string, rng csprng.Source,
// Construct the user registration message
msg := &pb.UDBUserRegistration{
PermissioningSignature: m.registrationValidationSignature,
PermissioningSignature: networkSignature,
RSAPublicPem: string(rsa.CreatePublicKeyPem(privKey.GetPublic())),
IdentityRegistration: &pb.Identity{
Username: username,
......
......@@ -34,13 +34,15 @@ func TestManager_register(t *testing.T) {
c := &testRegisterComm{}
prng := NewPrng(42)
err = m.register("testUser", prng, c, udHost)
mockSig := []byte("mock")
err = m.register("testUser", mockSig, prng, c, udHost)
if err != nil {
t.Errorf("register() returned an error: %+v", err)
}
// Check if the UDBUserRegistration contents are correct
isCorrect("testUser", c.msg, m, t)
isCorrect("testUser", mockSig, c.msg, m, t)
// Verify the signed identity data
pubKeyPem := m.user.GetReceptionIdentity().RSAPrivatePem
......@@ -66,10 +68,10 @@ func TestManager_register(t *testing.T) {
// isCorrect checks if the UDBUserRegistration has all the expected fields minus
// any signatures.
func isCorrect(username string, msg *pb.UDBUserRegistration, m *Manager, t *testing.T) {
if !bytes.Equal(m.registrationValidationSignature, msg.PermissioningSignature) {
func isCorrect(username string, mockSig []byte, msg *pb.UDBUserRegistration, m *Manager, t *testing.T) {
if !bytes.Equal(mockSig, msg.PermissioningSignature) {
t.Errorf("PermissioningSignature incorrect.\n\texpected: %v\n\treceived: %v",
m.registrationValidationSignature, msg.PermissioningSignature)
mockSig, msg.PermissioningSignature)
}
identity := m.user.GetReceptionIdentity()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment