Skip to content
Snippets Groups Projects
Commit 7072565b authored by Benjamin Wenger's avatar Benjamin Wenger
Browse files

made a more complex and flexible auth callback registration systems

parent 8d363edb
No related branches found
No related tags found
No related merge requests found
......@@ -4,6 +4,7 @@ import (
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/auth"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/contact"
"gitlab.com/elixxir/client/storage/e2e"
"gitlab.com/xx_network/primitives/id"
......@@ -30,26 +31,12 @@ func (c *Client) RequestAuthenticatedChannel(recipient, me contact.Contact,
c.storage, c.network)
}
// RegisterAuthCallbacks registers both callbacks for authenticated channels.
// This can only be called once
func (c *Client) RegisterAuthCallbacks(request auth.RequestCallback,
confirm auth.ConfirmCallback) error {
jww.INFO.Printf("RegisterAuthCallbacks(...)")
// GetAuthRegistrar gets the object which allows the registration of auth
// callbacks
func (c *Client) GetAuthRegistrar() interfaces.Auth {
jww.INFO.Printf("GetAuthRegistrar(...)")
exicuted := false
c.authOnce.Do(func() {
stop := auth.RegisterCallbacks(request, confirm, c.switchboard,
c.storage, c.network)
c.runner.Add(stop)
exicuted = true
})
if !exicuted {
return errors.New("Cannot register auth callbacks more than " +
"once")
}
return nil
return c.auth
}
// GetAuthenticatedChannelRequest returns the contact received in a request if
......
......@@ -9,6 +9,7 @@ package api
import (
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/auth"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/params"
"gitlab.com/elixxir/client/interfaces/user"
......@@ -25,7 +26,6 @@ import (
"gitlab.com/elixxir/crypto/large"
"gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/ndf"
"sync"
"time"
)
......@@ -46,14 +46,12 @@ type Client struct {
network interfaces.NetworkManager
//object used to register and communicate with permissioning
permissioning *permissioning.Permissioning
//object containing auth interactions
auth *auth.Manager
//contains stopables for all running threads
runner *stoppable.Multi
status *statusTracker
// contains the sync once used to ensure authenticated channel callbacks are
// only registered once
authOnce sync.Once
}
// NewClient creates client storage, generates keys, connects, and registers
......@@ -224,6 +222,9 @@ func loadClient(session *storage.Session, rngStreamGen *fastRNG.StreamGenerator)
return nil, err
}
//initilize the auth tracker
c.auth = auth.NewManager(c.switchboard, c.storage, c.network)
return c, nil
}
......@@ -256,6 +257,8 @@ func loadClient(session *storage.Session, rngStreamGen *fastRNG.StreamGenerator)
// Responds to sent rekeys and executes them
// - KeyExchange Confirm (/keyExchange/confirm.go)
// Responds to confirmations of successful rekey operations
// - Auth Callback (/auth/callback.go)
// Handles both auth confirm and requests
func (c *Client) StartNetworkFollower() error {
jww.INFO.Printf("StartNetworkFollower()")
......@@ -264,6 +267,9 @@ func (c *Client) StartNetworkFollower() error {
return errors.WithMessage(err, "Failed to Start the Network Follower")
}
stopAuth := c.auth.StartProcessies()
c.runner.Add(stopAuth)
stopFollow, err := c.network.Follow()
if err != nil {
return errors.WithMessage(err, "Failed to start following "+
......
......@@ -3,39 +3,30 @@ package auth
import (
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/contact"
"gitlab.com/elixxir/client/interfaces/message"
"gitlab.com/elixxir/client/stoppable"
"gitlab.com/elixxir/client/storage"
"gitlab.com/elixxir/client/storage/auth"
"gitlab.com/elixxir/client/storage/e2e"
"gitlab.com/elixxir/crypto/cyclic"
cAuth "gitlab.com/elixxir/crypto/e2e/auth"
"gitlab.com/elixxir/primitives/format"
"gitlab.com/xx_network/primitives/id"
"strings"
)
type RequestCallback func(requestor contact.Contact, message string)
type ConfirmCallback func(partner contact.Contact)
func RegisterCallbacks(rcb RequestCallback, ccb ConfirmCallback,
sw interfaces.Switchboard, storage *storage.Session,
net interfaces.NetworkManager) stoppable.Stoppable {
rawMessages := make(chan message.Receive, 1000)
sw.RegisterChannel("Auth", &id.ID{}, message.Raw, rawMessages)
func (m *Manager) StartProcessies() stoppable.Stoppable {
stop := stoppable.NewSingle("Auth")
authStore := storage.Auth()
grp := storage.E2e().GetGroup()
authStore := m.storage.Auth()
grp := m.storage.E2e().GetGroup()
go func() {
select {
case <-stop.Quit():
return
case msg := <-rawMessages:
case msg := <-m.rawMessages:
//lookup the message, check if it is an auth request
cmixMsg := format.Unmarshal(msg.Payload)
fp := cmixMsg.GetKeyFP()
......@@ -50,26 +41,24 @@ func RegisterCallbacks(rcb RequestCallback, ccb ConfirmCallback,
}
//denote that the message is not garbled
storage.GetGarbledMessages().Remove(cmixMsg)
m.storage.GetGarbledMessages().Remove(cmixMsg)
switch fpType {
// if it is general, that means a new request has been received
case auth.General:
handleRequest(cmixMsg, myHistoricalPrivKey, grp, storage, rcb,
ccb, net)
m.handleRequest(cmixMsg, myHistoricalPrivKey, grp)
// if it is specific, that means the original request was sent
// by this users and a confirmation has been received
case auth.Specific:
handleConfirm(cmixMsg, sr, ccb, storage, grp, net)
m.handleConfirm(cmixMsg, sr, grp)
}
}
}()
return stop
}
func handleRequest(cmixMsg format.Message, myHistoricalPrivKey *cyclic.Int,
grp *cyclic.Group, storage *storage.Session, rcb RequestCallback,
ccb ConfirmCallback, net interfaces.NetworkManager) {
func (m *Manager) handleRequest(cmixMsg format.Message,
myHistoricalPrivKey *cyclic.Int, grp *cyclic.Group) {
//decode the outer format
baseFmt, partnerPubKey, err := handleBaseFormat(cmixMsg, grp)
if err != nil {
......@@ -116,14 +105,14 @@ func handleRequest(cmixMsg format.Message, myHistoricalPrivKey *cyclic.Int,
// if it does and the keys used are the same as we have, send a
// confirmation in case there are state issues.
// do not store
if _, err := storage.E2e().GetPartner(partnerID); err == nil {
if _, err := m.storage.E2e().GetPartner(partnerID); err == nil {
jww.WARN.Printf("Recieved Auth request for %s, "+
"channel already exists. Ignoring", partnerID)
//exit
return
} else {
//check if the relationship already exists,
rType, sr2, _, err := storage.Auth().GetRequest(partnerID)
rType, sr2, _, err := m.storage.Auth().GetRequest(partnerID)
if err != nil && !strings.Contains(err.Error(), auth.NoRequest) {
// if another error is recieved, print it and exist
jww.WARN.Printf("Recieved new Auth request for %s, "+
......@@ -142,8 +131,8 @@ func handleRequest(cmixMsg format.Message, myHistoricalPrivKey *cyclic.Int,
// then exit, nothing else needed
case auth.Sent:
// do the confirmation
if err := doConfirm(sr2, grp, partnerPubKey, ecrFmt.GetOwnership(),
storage, ccb, net); err != nil {
if err := m.doConfirm(sr2, grp, partnerPubKey,
ecrFmt.GetOwnership()); err != nil {
jww.WARN.Printf("Confirmation failed: %s", err)
}
//exit
......@@ -172,26 +161,29 @@ func handleRequest(cmixMsg format.Message, myHistoricalPrivKey *cyclic.Int,
// fixme: the client will never be notified of the channel creation if a
// crash occurs after the store but before the conclusion of the callback
//create the auth storage
if err = storage.Auth().AddReceived(c); err != nil {
if err = m.storage.Auth().AddReceived(c); err != nil {
jww.WARN.Printf("failed to store contact Auth "+
"Request: %s", err)
return
}
//call the callback
// fixme: if a crash occurs before or during the calls, the notification
// will never be sent.
cbList := m.requestCallbacks.Get(c.ID)
for _, cb := range cbList {
rcb := cb.(RequestCallback)
go rcb(c, msg)
}
return
}
func handleConfirm(cmixMsg format.Message, sr *auth.SentRequest,
ccb ConfirmCallback, storage *storage.Session, grp *cyclic.Group,
net interfaces.NetworkManager) {
func (m *Manager) handleConfirm(cmixMsg format.Message, sr *auth.SentRequest,
grp *cyclic.Group) {
// check if relationship already exists
if m, err := storage.E2e().GetPartner(sr.GetPartner()); m != nil || err == nil {
if mgr, err := m.storage.E2e().GetPartner(sr.GetPartner()); mgr != nil || err == nil {
jww.WARN.Printf("Cannot confirm auth for %s, channel already "+
"exists.", sr.GetPartner())
storage.Auth().Fail(sr.GetPartner())
m.storage.Auth().Fail(sr.GetPartner())
return
}
......@@ -199,7 +191,7 @@ func handleConfirm(cmixMsg format.Message, sr *auth.SentRequest,
baseFmt, partnerPubKey, err := handleBaseFormat(cmixMsg, grp)
if err != nil {
jww.WARN.Printf("Failed to handle auth confirm: %s", err)
storage.Auth().Fail(sr.GetPartner())
m.storage.Auth().Fail(sr.GetPartner())
return
}
......@@ -211,7 +203,7 @@ func handleConfirm(cmixMsg format.Message, sr *auth.SentRequest,
if !success {
jww.WARN.Printf("Recieved auth confirmation failed its mac " +
"check")
storage.Auth().Fail(sr.GetPartner())
m.storage.Auth().Fail(sr.GetPartner())
return
}
......@@ -219,22 +211,20 @@ func handleConfirm(cmixMsg format.Message, sr *auth.SentRequest,
if err != nil {
jww.WARN.Printf("Failed to unmarshal auth confirmation's "+
"encrypted payload: %s", err)
storage.Auth().Fail(sr.GetPartner())
m.storage.Auth().Fail(sr.GetPartner())
return
}
// finalize the confirmation
if err := doConfirm(sr, grp, partnerPubKey, ecrFmt.GetOwnership(),
storage, ccb, net); err != nil {
if err := m.doConfirm(sr, grp, partnerPubKey, ecrFmt.GetOwnership()); err != nil {
jww.WARN.Printf("Confirmation failed: %s", err)
storage.Auth().Fail(sr.GetPartner())
m.storage.Auth().Fail(sr.GetPartner())
return
}
}
func doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
partnerPubKey *cyclic.Int, ownershipProof []byte, storage *storage.Session,
ccb ConfirmCallback, net interfaces.NetworkManager) error {
func (m *Manager) doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
partnerPubKey *cyclic.Int, ownershipProof []byte) error {
// verify the message came from the intended recipient
if !cAuth.VerifyOwnershipProof(sr.GetMyPrivKey(),
sr.GetPartnerHistoricalPubKey(), grp, ownershipProof) {
......@@ -245,7 +235,7 @@ func doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
// fixme: channel can get into a bricked state if the first save occurs and
// the second does not
p := e2e.GetDefaultSessionParams()
if err := storage.E2e().AddPartner(sr.GetPartner(),
if err := m.storage.E2e().AddPartner(sr.GetPartner(),
partnerPubKey, sr.GetMyPrivKey(), p, p); err != nil {
return errors.Errorf("Failed to create channel with partner (%s) "+
"after confirmation: %+v",
......@@ -254,7 +244,7 @@ func doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
// delete the in progress negotiation
// this undoes the request lock
if err := storage.Auth().Delete(sr.GetPartner()); err != nil {
if err := m.storage.Auth().Delete(sr.GetPartner()); err != nil {
return errors.Errorf("UNRECOVERABLE! Failed to delete in "+
"progress negotiation with partner (%s) after confirmation: %+v",
sr.GetPartner(), err)
......@@ -268,11 +258,15 @@ func doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
Facts: make([]contact.Fact, 0),
}
// fixme: if a crash occurs before or during the call, the notification
// fixme: if a crash occurs before or during the calls, the notification
// will never be sent.
cbList := m.confirmCallbacks.Get(c.ID)
for _, cb := range cbList {
ccb := cb.(ConfirmCallback)
go ccb(c)
}
net.CheckGarbledMessages()
m.net.CheckGarbledMessages()
return nil
}
......
package auth
import (
"gitlab.com/xx_network/primitives/id"
"sync"
)
type callbackMap struct {
generalCallback []interface{}
specificCallback map[id.ID]interface{}
overrideCallback []interface{}
mux sync.RWMutex
}
func newCallbackMap() *callbackMap {
return &callbackMap{
generalCallback: make([]interface{}, 0),
specificCallback: make(map[id.ID]interface{}),
overrideCallback: make([]interface{}, 0),
}
}
//adds a general callback. This will be preempted by any specific callback
func (cm *callbackMap) AddGeneral(cb interface{}) {
cm.mux.Lock()
cm.generalCallback = append(cm.generalCallback, cb)
cm.mux.Unlock()
}
//adds an override callback. This will NOT be preempted by any callback
func (cm *callbackMap) AddOverride(cb interface{}) {
cm.mux.Lock()
cm.overrideCallback = append(cm.overrideCallback, cb)
cm.mux.Unlock()
}
// adds a callback for a specific user ID. Only only callback can exist for a
// user ID. False will be returned if a callback already exists and the new
// one was not added
func (cm *callbackMap) AddSpecific(id *id.ID, cb interface{}) bool {
cm.mux.Lock()
defer cm.mux.Unlock()
if _, ok := cm.specificCallback[*id]; ok {
return false
}
cm.specificCallback[*id] = cb
return true
}
// removes a callback for a specific user ID if it exists.
func (cm *callbackMap) RemoveSpecific(id *id.ID) {
cm.mux.Lock()
defer cm.mux.Unlock()
delete(cm.specificCallback, *id)
}
//get all callback which fit with the passed id
func (cm *callbackMap) Get(id *id.ID) []interface{} {
cm.mux.RLock()
defer cm.mux.RUnlock()
cbList := cm.overrideCallback
if specific, ok := cm.specificCallback[*id]; ok {
cbList = append(cbList, specific)
} else {
cbList = append(cbList, cm.generalCallback)
}
return cbList
}
package auth
import (
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/message"
"gitlab.com/elixxir/client/storage"
"gitlab.com/xx_network/primitives/id"
)
type Manager struct {
requestCallbacks *callbackMap
confirmCallbacks *callbackMap
rawMessages chan message.Receive
storage *storage.Session
net interfaces.NetworkManager
}
func NewManager(sw interfaces.Switchboard, storage *storage.Session,
net interfaces.NetworkManager) *Manager {
m := &Manager{
requestCallbacks: newCallbackMap(),
confirmCallbacks: newCallbackMap(),
rawMessages: make(chan message.Receive, 1000),
storage: storage,
net: net,
}
sw.RegisterChannel("Auth", &id.ID{}, message.Raw, m.rawMessages)
return m
}
// Adds a general callback to be used on auth requests. This will be preempted
// by any specific callback
func (m *Manager) AddGeneralRequestCallback(cb RequestCallback) {
m.requestCallbacks.AddGeneral(cb)
}
// Adds a general callback to be used on auth requests. This will not be
// preempted by any specific callback. It is recommended that the specific
// callbacks are used, this is primarily for debugging.
func (m *Manager) AddOverrideRequestCallback(cb RequestCallback) {
m.requestCallbacks.AddOverride(cb)
}
// Adds a specific callback to be used on auth requests. This will preempt a
// general callback, meaning the request will be heard on this callback and not
// the general. Request will still be heard on override callbacks.
func (m *Manager) AddSpecificRequestCallback(id *id.ID, cb RequestCallback) {
m.requestCallbacks.AddSpecific(id, cb)
}
// Removes a specific callback to be used on auth requests.
func (m *Manager) RemoveSpecificRequestCallback(id *id.ID) {
m.requestCallbacks.RemoveSpecific(id)
}
// Adds a general callback to be used on auth confirms. This will be preempted
// by any specific callback
func (m *Manager) AddGeneralConfirmCallback(cb ConfirmCallback) {
m.confirmCallbacks.AddGeneral(cb)
}
// Adds a general callback to be used on auth confirms. This will not be
// preempted by any specific callback. It is recommended that the specific
// callbacks are used, this is primarily for debugging.
func (m *Manager) AddOverrideConfirmCallback(cb ConfirmCallback) {
m.confirmCallbacks.AddOverride(cb)
}
// Adds a specific callback to be used on auth confirms. This will preempt a
// general callback, meaning the request will be heard on this callback and not
// the general. Request will still be heard on override callbacks.
func (m *Manager) AddSpecificConfirmCallback(id *id.ID, cb ConfirmCallback) {
m.confirmCallbacks.AddSpecific(id, cb)
}
// Removes a specific callback to be used on auth confirm.
func (m *Manager) RemoveSpecificConfirmCallback(id *id.ID) {
m.confirmCallbacks.RemoveSpecific(id)
}
package interfaces
import (
"gitlab.com/elixxir/client/auth"
"gitlab.com/xx_network/primitives/id"
)
type Auth interface {
// Adds a general callback to be used on auth requests. This will be preempted
// by any specific callback
AddGeneralRequestCallback(cb auth.RequestCallback)
// Adds a general callback to be used on auth requests. This will not be
// preempted by any specific callback. It is recommended that the specific
// callbacks are used, this is primarily for debugging.
AddOverrideRequestCallback(cb auth.RequestCallback)
// Adds a specific callback to be used on auth requests. This will preempt a
// general callback, meaning the request will be heard on this callback and not
// the general. Request will still be heard on override callbacks.
AddSpecificRequestCallback(id *id.ID, cb auth.RequestCallback)
// Removes a specific callback to be used on auth requests.
RemoveSpecificRequestCallback(id *id.ID)
// Adds a general callback to be used on auth confirms. This will be preempted
// by any specific callback
AddGeneralConfirmCallback(cb auth.ConfirmCallback)
// Adds a general callback to be used on auth confirms. This will not be
// preempted by any specific callback. It is recommended that the specific
// callbacks are used, this is primarily for debugging.
AddOverrideConfirmCallback(cb auth.ConfirmCallback)
// Adds a specific callback to be used on auth confirms. This will preempt a
// general callback, meaning the request will be heard on this callback and not
// the general. Request will still be heard on override callbacks.
AddSpecificConfirmCallback(id *id.ID, cb auth.ConfirmCallback)
// Removes a specific callback to be used on auth confirm.
RemoveSpecificConfirmCallback(id *id.ID)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment