Browse Source

Adding new authorization level to peers; porting Blocked status to authorization; removing trusted; securing engine/peerapp message processing around authoriztion

pull/310/head
Dan Ballard 2 weeks ago
parent
commit
e91e892eef
15 changed files with 151 additions and 172 deletions
  1. +6
    -4
      app/app.go
  2. +4
    -4
      app/appService.go
  3. +3
    -10
      app/cli/main.go
  4. +6
    -5
      event/common.go
  5. +1
    -2
      go.mod
  6. +2
    -0
      go.sum
  7. +26
    -40
      model/profile.go
  8. +8
    -12
      model/profile_test.go
  9. +10
    -33
      peer/cwtch_peer.go
  10. +3
    -1
      protocol/ControlChannel.pb.go
  11. +44
    -37
      protocol/connections/engine.go
  12. +7
    -3
      protocol/connections/peerapp.go
  13. +1
    -1
      protocol/connections/peerserverconnection_test.go
  14. +16
    -12
      storage/v1/profile_store.go
  15. +14
    -8
      testing/cwtch_peer_server_integration_test.go

+ 6
- 4
app/app.go View File

@@ -121,10 +121,10 @@ func (app *application) CreateTaggedPeer(name string, password string, tag strin
p := peer.FromProfile(pc)
p.Init(app.eventBuses[profile.Onion])

blockedPeers := profile.BlockedPeers()
peerAuthorizations := profile.ContactsAuthorizations()
// TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], peerAuthorizations)

app.peers[profile.Onion] = p
app.engines[profile.Onion] = engine
@@ -213,9 +213,9 @@ func (app *application) LoadProfiles(password string) {
peer := peer.FromProfile(profile)
peer.Init(app.eventBuses[profile.Onion])

blockedPeers := profile.BlockedPeers()
peerAuthorizations := profile.ContactsAuthorizations()
identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, app.eventBuses[profile.Onion], peerAuthorizations)
app.appmutex.Lock()
app.peers[profile.Onion] = peer
app.storage[profile.Onion] = profileStore
@@ -246,10 +246,12 @@ func (ac *applicationCore) GetEventBus(onion string) event.Manager {
func (app *application) getACNStatusHandler() func(int, string) {
return func(progress int, status string) {
progStr := strconv.Itoa(progress)
app.peerLock.Lock()
app.appBus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
for _, bus := range app.eventBuses {
bus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
}
app.peerLock.Unlock()
}
}



+ 4
- 4
app/appService.go View File

@@ -107,10 +107,10 @@ func (as *applicationService) createPeer(name, password, tag string) {

profileStore := storage.CreateProfileWriterStore(as.eventBuses[profile.Onion], path.Join(as.directory, "profiles", profile.LocalID), password, profile)

blockedPeers := profile.BlockedPeers()
peerAuthorizations := profile.ContactsAuthorizations()
// TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, as.acn, as.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, as.acn, as.eventBuses[profile.Onion], peerAuthorizations)

as.storage[profile.Onion] = profileStore
as.engines[profile.Onion] = engine
@@ -126,9 +126,9 @@ func (as *applicationService) loadProfiles(password string) {
as.applicationCore.LoadProfiles(password, false, func(profile *model.Profile, profileStore storage.ProfileStore) {
as.eventBuses[profile.Onion] = event.IPCEventManagerFrom(as.bridge, profile.Onion, as.eventBuses[profile.Onion])

blockedPeers := profile.BlockedPeers()
peerAuthorizations := profile.ContactsAuthorizations()
identity := primitives.InitializeIdentity(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, as.acn, as.eventBuses[profile.Onion], profile.GetContacts(), blockedPeers)
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, as.acn, as.eventBuses[profile.Onion], peerAuthorizations)
as.asmutex.Lock()
as.storage[profile.Onion] = profileStore
as.engines[profile.Onion] = engine


+ 3
- 10
app/cli/main.go View File

@@ -48,7 +48,6 @@ var suggestionsSelectedProfile = []prompt.Suggest{
/*{Text: "/list-servers", Description: "retrieve a list of servers and their connection status"},
{Text: "/list-peers", Description: "retrieve a list of peers and their connection status"},*/
{Text: "/export-group", Description: "export a group invite: prints as a string"},
{Text: "/trust", Description: "trust a peer"},
{Text: "/block", Description: "block a peer - you will no longer see messages or connect to this peer"},
}

@@ -411,7 +410,7 @@ func main() {
// think over.
for _, name := range p.GetContacts() {
profile := p.GetContact(name)
if profile.Trusted && !profile.Blocked {
if profile.Authorization == model.AuthApproved {
p.PeerWithOnion(profile.Onion)
}
}
@@ -453,22 +452,16 @@ func main() {
contacts := peer.GetContacts()
for _, onion := range contacts {
c := peer.GetContact(onion)
fmt.Printf("Name: %v Onion: %v Trusted: %v\n", c.Name, c.Onion, c.Trusted)
fmt.Printf("Name: %v Onion: %v Authorization: %v\n", c.Name, c.Onion, c.Authorization)
}
case "/list-groups":
for _, gid := range peer.GetGroups() {
g := peer.GetGroup(gid)
fmt.Printf("Group Id: %v Owner: %v Accepted:%v\n", gid, g.Owner, g.Accepted)
}
case "/trust":
if len(commands) == 2 {
peer.TrustPeer(commands[1])
} else {
fmt.Printf("Error trusting peer, usage: %s\n", usages[commands[0]])
}
case "/block":
if len(commands) == 2 {
peer.BlockPeer(commands[1])
peer.SetContactAuthorization(commands[1], model.AuthBlocked)
} else {
fmt.Printf("Error blocking peer, usage: %s\n", usages[commands[0]])
}


+ 6
- 5
event/common.go View File

@@ -19,11 +19,9 @@ const (
// RemotePeer: [eg "chpr7qm6op5vfcg2pi4vllco3h6aa7exexc4rqwnlupqhoogx2zgd6qd"
RetryPeerRequest = Type("RetryPeerRequest")

// Blocking Events both Block and Unblock have the same structure
// attributes:
// RemotePeer: [eg "chpr7qm6op5vfcg2pi4vllco3h6aa7exexc4rqwnlupqhoogx2zgd6qd"
BlockPeer = Type("BlockPeer")
UnblockPeer = Type("UnblockPeer")
// RemotePeer
// Authorization(model.peer.Auth_...)
SetPeerAuthorization = Type("UpdatePeerAuthorization")

// Turn on/off blocking of unknown peers (if peers aren't in the contact list then they will be autoblocked
BlockUnknownPeers = Type("BlockUnknownPeers")
@@ -92,6 +90,7 @@ const (
// a peer contact has been added
// attributes:
// RemotePeer [eg ""]
// Authorization
PeerCreated = Type("PeerCreated")

// Password, NewPassword
@@ -244,6 +243,8 @@ const (
Status = Field("Status")
EventID = Field("EventID")
EventContext = Field("EventContext")

Authorization = Field("Authorization")
)

// Defining Common errors


+ 1
- 2
go.mod View File

@@ -4,7 +4,7 @@ go 1.14

require (
cwtch.im/tapir v0.1.18
git.openprivacy.ca/openprivacy/connectivity v1.1.1
git.openprivacy.ca/openprivacy/connectivity v1.1.2
git.openprivacy.ca/openprivacy/libricochet-go v1.0.13
git.openprivacy.ca/openprivacy/log v1.0.0
github.com/c-bata/go-prompt v0.2.3
@@ -19,7 +19,6 @@ require (
golang.org/x/crypto v0.0.0-20200420104511-884d27f42877
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d // indirect
golang.org/x/tools v0.0.0-20200420001825-978e26b7c37c // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b // indirect
)

+ 2
- 0
go.sum View File

@@ -3,6 +3,8 @@ cwtch.im/tapir v0.1.18/go.mod h1:/IrAI6CBHfgzsfgRT8WHVb1P9fCCz7+45hfsdkKn8Zg=
git.openprivacy.ca/openprivacy/connectivity v1.1.0/go.mod h1:4P8mirZZslKbo2zBrXXVjgEdqGwHo/6qoFBwFQW6d6E=
git.openprivacy.ca/openprivacy/connectivity v1.1.1 h1:hKxBOmxP7Jdu3K1BJ93mRtKNiWUoP6YHt/o2snE2Z0w=
git.openprivacy.ca/openprivacy/connectivity v1.1.1/go.mod h1:4P8mirZZslKbo2zBrXXVjgEdqGwHo/6qoFBwFQW6d6E=
git.openprivacy.ca/openprivacy/connectivity v1.1.2 h1:Bk8ul3+4/awpQGvskfLpp7/K3Lj8OAxBwlmQqeZy3Ok=
git.openprivacy.ca/openprivacy/connectivity v1.1.2/go.mod h1:4P8mirZZslKbo2zBrXXVjgEdqGwHo/6qoFBwFQW6d6E=
git.openprivacy.ca/openprivacy/libricochet-go v1.0.13 h1:Z86uL9K47onznY1wP1P/wWfWMbbyvk6xnCp94R180os=
git.openprivacy.ca/openprivacy/libricochet-go v1.0.13/go.mod h1:ZUuX1SOrgV4K18IEcp0hQJNPKszRr2oGb3UeK2iYe5U=
git.openprivacy.ca/openprivacy/log v1.0.0 h1:Rvqm1weUdR4AOnJ79b1upHCc9vC/QF1rhSD2Um7sr1Y=


+ 26
- 40
model/profile.go View File

@@ -18,12 +18,24 @@ import (
"time"
)

// Authorization is a type determining client assigned authorization to a peer
type Authorization string

const (
// AuthUnknown is an inital state for a new unseen peer
AuthUnknown Authorization = "unknown"
// AuthApproved means the client has approved the peer, it can send messages to us, perform GetVals, etc
AuthApproved Authorization = "approved"
// AuthBlocked means the client has blocked the peer, it's messages and connections should be rejected
AuthBlocked Authorization = "blocked"
)

// PublicProfile is a local copy of a CwtchIdentity
type PublicProfile struct {
Name string
Ed25519PublicKey ed25519.PublicKey
Trusted bool
Blocked bool
Authorization Authorization
DeprecatedBlocked bool `json:"Blocked"`
Onion string
Attributes map[string]string
Timeline Timeline `json:"-"`
@@ -239,64 +251,38 @@ func (p *Profile) GetContacts() []string {
return keys
}

// BlockPeer blocks a contact
func (p *Profile) BlockPeer(onion string) (err error) {
// SetContactAuthorization sets the authoirization level of a peer
func (p *Profile) SetContactAuthorization(onion string, auth Authorization) (err error) {
p.lock.Lock()
defer p.lock.Unlock()
contact, ok := p.Contacts[onion]
if ok {
contact.Blocked = true
contact.Authorization = auth
} else {
err = errors.New("peer does not exist")
}
return
}

// UnblockPeer unblocks a contact
func (p *Profile) UnblockPeer(onion string) (err error) {
// GetContactAuthorization returns the contact's authorization level
func (p *Profile) GetContactAuthorization(onion string) Authorization {
p.lock.Lock()
defer p.lock.Unlock()
contact, ok := p.Contacts[onion]
if ok {
contact.Blocked = false
} else {
err = errors.New("peer does not exist")
return contact.Authorization
}
return
return AuthUnknown
}

// BlockedPeers calculates a list of Peers who have been Blocked.
func (p *Profile) BlockedPeers() []string {
blockedPeers := []string{}
// ContactsAuthorizations calculates a list of Peers who are at the supplied auth levels
func (p *Profile) ContactsAuthorizations(authorizationFilter ...Authorization) map[string]Authorization {
authorizations := map[string]Authorization{}
for _, contact := range p.GetContacts() {
c, _ := p.GetContact(contact)
if c.Blocked {
blockedPeers = append(blockedPeers, c.Onion)
}
authorizations[c.Onion] = c.Authorization
}
return blockedPeers
}

// TrustPeer sets a contact to trusted
func (p *Profile) TrustPeer(onion string) (err error) {
p.lock.Lock()
defer p.lock.Unlock()
contact, ok := p.Contacts[onion]
if ok {
contact.Trusted = true
} else {
err = errors.New("peer does not exist")
}
return
}

// IsBlocked returns true if the contact has been blocked, false otherwise
func (p *Profile) IsBlocked(onion string) bool {
contact, ok := p.GetContact(onion)
if ok {
return contact.Blocked
}
return false
return authorizations
}

// GetContact returns a contact if the profile has it


+ 8
- 12
model/profile_test.go View File

@@ -31,13 +31,9 @@ func TestTrustPeer(t *testing.T) {
alice := GenerateNewProfile("Alice")
sarah.AddContact(alice.Onion, &alice.PublicProfile)
alice.AddContact(sarah.Onion, &sarah.PublicProfile)
alice.TrustPeer(sarah.Onion)
if alice.IsBlocked(sarah.Onion) {
t.Errorf("peer should not be blocked")
}

if alice.TrustPeer("") == nil {
t.Errorf("trusting a non existent peer should error")
alice.SetContactAuthorization(sarah.Onion, AuthApproved)
if alice.GetContactAuthorization(sarah.Onion) != AuthApproved {
t.Errorf("peer should be approved")
}
}

@@ -46,13 +42,13 @@ func TestBlockPeer(t *testing.T) {
alice := GenerateNewProfile("Alice")
sarah.AddContact(alice.Onion, &alice.PublicProfile)
alice.AddContact(sarah.Onion, &sarah.PublicProfile)
alice.BlockPeer(sarah.Onion)
if !alice.IsBlocked(sarah.Onion) {
t.Errorf("peer should not be blocked")
alice.SetContactAuthorization(sarah.Onion, AuthBlocked)
if alice.GetContactAuthorization(sarah.Onion) != AuthBlocked {
t.Errorf("peer should be blocked")
}

if alice.BlockPeer("") == nil {
t.Errorf("blocking a non existent peer should error")
if alice.SetContactAuthorization("", AuthUnknown) == nil {
t.Errorf("Seting Auth level of a non existent peer should error")
}
}



+ 10
- 33
peer/cwtch_peer.go View File

@@ -41,9 +41,7 @@ type CwtchPeer interface {
SendMessageToPeer(string, string) string
SendGetValToPeer(string, string, string)

TrustPeer(string) error
BlockPeer(string) error
UnblockPeer(string) error
SetContactAuthorization(string, model.Authorization) error
ProcessInvite(string, string) (string, error)
AcceptInvite(string) error
RejectInvite(string)
@@ -68,7 +66,7 @@ type CwtchPeer interface {
GetGroup(string) *model.Group
GetGroupState(string) (connections.ConnectionState, bool)
GetGroups() []string
AddContact(nick, onion string, trusted bool)
AddContact(nick, onion string, authorization model.Authorization)
GetContacts() []string
GetContact(string) *model.PublicProfile

@@ -191,17 +189,16 @@ func (cp *cwtchPeer) GetGroup(groupID string) *model.Group {
return cp.Profile.GetGroup(groupID)
}

func (cp *cwtchPeer) AddContact(nick, onion string, trusted bool) {
func (cp *cwtchPeer) AddContact(nick, onion string, authorization model.Authorization) {
decodedPub, _ := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
pp := &model.PublicProfile{Name: nick, Ed25519PublicKey: decodedPub, Trusted: trusted, Blocked: false, Onion: onion, Attributes: map[string]string{"nick": nick}}
cp.mutex.Lock()
pp := &model.PublicProfile{Name: nick, Ed25519PublicKey: decodedPub, Authorization: authorization, Onion: onion, Attributes: map[string]string{"nick": nick}}
cp.Profile.AddContact(onion, pp)
cp.mutex.Unlock()
pd, _ := json.Marshal(pp)
cp.eventBus.Publish(event.NewEvent(event.PeerCreated, map[event.Field]string{
event.Data: string(pd),
event.RemotePeer: onion,
}))
cp.eventBus.Publish(event.NewEventList(event.SetPeerAuthorization, event.RemotePeer, onion, event.Authorization, string(authorization)))
}

// GetContacts returns an unordered list of onions
@@ -257,10 +254,10 @@ func (cp *cwtchPeer) GetGroupState(groupid string) (connections.ConnectionState,
// PeerWithOnion is the entry point for cwtchPeer relationships
func (cp *cwtchPeer) PeerWithOnion(onion string) {
cp.mutex.Lock()
defer cp.mutex.Unlock()
if _, exists := cp.Profile.GetContact(onion); !exists {
cp.AddContact(onion, onion, false)
cp.AddContact(onion, onion, model.AuthApproved)
}
defer cp.mutex.Unlock()
cp.eventBus.Publish(event.NewEvent(event.PeerRequest, map[event.Field]string{event.RemotePeer: onion}))
}

@@ -343,32 +340,12 @@ func (cp *cwtchPeer) SendGetValToPeer(onion string, scope string, path string) {
cp.eventBus.Publish(event)
}

// TrustPeer sets an existing peer relationship to trusted
func (cp *cwtchPeer) TrustPeer(peer string) error {
cp.mutex.Lock()
defer cp.mutex.Unlock()
err := cp.Profile.TrustPeer(peer)
if err == nil {
cp.PeerWithOnion(peer)
}
return err
}

// BlockPeer blocks an existing peer relationship.
func (cp *cwtchPeer) BlockPeer(peer string) error {
cp.mutex.Lock()
err := cp.Profile.BlockPeer(peer)
cp.mutex.Unlock()
cp.eventBus.Publish(event.NewEvent(event.BlockPeer, map[event.Field]string{event.RemotePeer: peer}))
return err
}

// UnblockPeer blocks an existing peer relationship.
func (cp *cwtchPeer) UnblockPeer(peer string) error {
func (cp *cwtchPeer) SetContactAuthorization(peer string, authorization model.Authorization) error {
cp.mutex.Lock()
err := cp.Profile.UnblockPeer(peer)
err := cp.Profile.SetContactAuthorization(peer, authorization)
cp.mutex.Unlock()
cp.eventBus.Publish(event.NewEvent(event.UnblockPeer, map[event.Field]string{event.RemotePeer: peer}))
cp.eventBus.Publish(event.NewEvent(event.SetPeerAuthorization, map[event.Field]string{event.RemotePeer: peer, event.Authorization: string(authorization)}))
return err
}



+ 3
- 1
protocol/ControlChannel.pb.go View File

@@ -82,7 +82,9 @@ func (x *ChannelResult_CommonError) UnmarshalJSON(data []byte) error {
*x = ChannelResult_CommonError(value)
return nil
}
func (ChannelResult_CommonError) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} }
func (ChannelResult_CommonError) EnumDescriptor() ([]byte, []int) {
return fileDescriptor0, []int{2, 0}
}

type Packet struct {
// Must contain exactly one field


+ 44
- 37
protocol/connections/engine.go View File

@@ -2,6 +2,7 @@ package connections

import (
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/protocol"
"cwtch.im/tapir"
"cwtch.im/tapir/networks/tor"
@@ -28,8 +29,8 @@ type engine struct {
// Engine State
started bool

// Blocklist
blocked sync.Map
// Authorization list of contacts to authorization status
authorizations sync.Map // string(onion) => model.Authorization

// Block Unknown Contacts
blockUnknownContacts bool
@@ -55,7 +56,7 @@ type Engine interface {
}

// NewProtocolEngine initializes a new engine that runs Cwtch using the given parameters
func NewProtocolEngine(identity primitives.Identity, privateKey ed25519.PrivateKey, acn connectivity.ACN, eventManager event.Manager, knownPeers []string, blockedPeers []string) Engine {
func NewProtocolEngine(identity primitives.Identity, privateKey ed25519.PrivateKey, acn connectivity.ACN, eventManager event.Manager, peerAuthorizations map[string]model.Authorization) Engine {
engine := new(engine)
engine.identity = identity
engine.privateKey = privateKey
@@ -83,17 +84,12 @@ func NewProtocolEngine(identity primitives.Identity, privateKey ed25519.PrivateK
engine.eventManager.Subscribe(event.DeleteContact, engine.queue)
engine.eventManager.Subscribe(event.DeleteGroup, engine.queue)

engine.eventManager.Subscribe(event.BlockPeer, engine.queue)
engine.eventManager.Subscribe(event.UnblockPeer, engine.queue)
engine.eventManager.Subscribe(event.SetPeerAuthorization, engine.queue)
engine.eventManager.Subscribe(event.BlockUnknownPeers, engine.queue)
engine.eventManager.Subscribe(event.AllowUnknownPeers, engine.queue)

for _, peer := range knownPeers {
engine.blocked.Store(peer, false)
}

for _, peer := range blockedPeers {
engine.blocked.Store(peer, true)
for peer, authorization := range peerAuthorizations {
engine.authorizations.Store(peer, authorization)
}
return engine
}
@@ -115,7 +111,6 @@ func (e *engine) eventHandler() {
e.eventManager.Publish(event.Event{EventType: event.ProtocolEngineStatus, EventID: ev.EventID})
case event.PeerRequest:
if torProdider.IsValidHostname(ev.Data[event.RemotePeer]) {
e.blocked.Store(ev.Data[event.RemotePeer], false)
go e.peerWithOnion(ev.Data[event.RemotePeer])
}
case event.RetryPeerRequest:
@@ -131,7 +126,7 @@ func (e *engine) eventHandler() {
case event.DeleteContact:
onion := ev.Data[event.RemotePeer]
// We remove this peer from out blocklist which will prevent them from contacting us if we have "block unknown peers" turned on.
e.blocked.Delete(ev.Data[event.RemotePeer])
e.authorizations.Delete(ev.Data[event.RemotePeer])
e.deleteConnection(onion)
case event.DeleteGroup:
// TODO: There isn't a way here to determine if other Groups are using a server connection...
@@ -151,21 +146,19 @@ func (e *engine) eventHandler() {
e.sendGetValToPeer(ev.EventID, ev.Data[event.RemotePeer], ev.Data[event.Scope], ev.Data[event.Path])
case event.SendRetValMessageToPeer:
e.sendRetValToPeer(ev.EventID, ev.Data[event.RemotePeer], ev.Data[event.Data], ev.Data[event.Exists])
case event.UnblockPeer:
// We simply remove the peer from our blocklist
// The UI has the responsibility to reinitiate contact with the peer.
// (this should happen periodically in any case)
e.blocked.Store(ev.Data[event.RemotePeer], false)
case event.BlockPeer:
e.blocked.Store(ev.Data[event.RemotePeer], true)
connection, err := e.service.GetConnection(ev.Data[event.RemotePeer])
if connection != nil && err == nil {
connection.Close()
case event.SetPeerAuthorization:
auth := model.Authorization(ev.Data[event.Authorization])
e.authorizations.Store(ev.Data[event.RemotePeer], auth)
if auth == model.AuthBlocked {
connection, err := e.service.GetConnection(ev.Data[event.RemotePeer])
if connection != nil && err == nil {
connection.Close()
}
// Explicitly send a disconnected event (if we don't do this here then the UI can wait for a while before
// an ongoing Open() connection fails and so the user will see a blocked peer as still connecting (because
// there isn't an active connection and we are stuck waiting for tor to time out)
e.peerDisconnected(ev.Data[event.RemotePeer])
}
// Explicitly send a disconnected event (if we don't do this here then the UI can wait for a while before
// an ongoing Open() connection fails and so the user will see a blocked peer as still connecting (because
// there isn't an active connection and we are stuck waiting for tor to time out)
e.peerDisconnected(ev.Data[event.RemotePeer])
case event.AllowUnknownPeers:
e.blockUnknownContacts = false
case event.BlockUnknownPeers:
@@ -178,16 +171,27 @@ func (e *engine) eventHandler() {
}
}

func (e *engine) isBlocked(onion string) bool {
authorization, known := e.authorizations.Load(onion)
if !known {
// if we block unknown peers we will block this contact
return e.blockUnknownContacts
}
return authorization.(model.Authorization) == model.AuthBlocked
}

func (e *engine) isApproved(onion string) bool {
authorization, known := e.authorizations.Load(onion)
if !known {
return false
}
return authorization.(model.Authorization) == model.AuthApproved
}

func (e *engine) createPeerTemplate() *PeerApp {
peerAppTemplate := new(PeerApp)
peerAppTemplate.IsBlocked = func(onion string) bool {
blocked, known := e.blocked.Load(onion)
if !known {
// if we block unknown peers we will block this contact
return e.blockUnknownContacts
}
return blocked.(bool)
}
peerAppTemplate.IsBlocked = e.isBlocked
peerAppTemplate.IsApproved = e.isApproved
peerAppTemplate.MessageHandler = e.handlePeerMessage
peerAppTemplate.OnAcknowledgement = e.ignoreOnShutdown2(e.peerAck)
peerAppTemplate.OnAuth = e.ignoreOnShutdown(e.peerAuthed)
@@ -217,8 +221,7 @@ func (e *engine) Shutdown() {
// peerWithOnion is the entry point for cwtchPeer relationships
// needs to be run in a goroutine as will block on Open.
func (e *engine) peerWithOnion(onion string) {
blocked, known := e.blocked.Load(onion)
if known && !(blocked.(bool)) {
if !e.isBlocked(onion) {
e.ignoreOnShutdown(e.peerConnecting)(onion)
connected, err := e.service.Connect(onion, e.createPeerTemplate())

@@ -258,6 +261,10 @@ func (e *engine) ignoreOnShutdown2(f func(string, string)) func(string, string)
}

func (e *engine) peerAuthed(onion string) {
_, known := e.authorizations.Load(onion)
if !known {
e.authorizations.Store(onion, model.AuthUnknown)
}
e.eventManager.Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
event.RemotePeer: string(onion),
event.ConnectionState: ConnectionStateName[AUTHENTICATED],


+ 7
- 3
protocol/connections/peerapp.go View File

@@ -18,6 +18,7 @@ type PeerApp struct {
MessageHandler func(string, string, string, []byte)
RetValHandler func(string, []byte, []byte)
IsBlocked func(string) bool
IsApproved func(string) bool
OnAcknowledgement func(string, string)
OnAuth func(string)
OnClose func(string)
@@ -47,6 +48,7 @@ func (pa *PeerApp) NewInstance() tapir.Application {
newApp := new(PeerApp)
newApp.MessageHandler = pa.MessageHandler
newApp.IsBlocked = pa.IsBlocked
newApp.IsApproved = pa.IsApproved
newApp.OnAcknowledgement = pa.OnAcknowledgement
newApp.OnAuth = pa.OnAuth
newApp.OnClose = pa.OnClose
@@ -99,10 +101,12 @@ func (pa *PeerApp) listen() {
pa.getValRequests.Delete(peerMessage.ID)
}
default:
pa.MessageHandler(pa.connection.Hostname(), peerMessage.ID, peerMessage.Context, peerMessage.Data)
if pa.IsApproved(pa.connection.Hostname()) {
pa.MessageHandler(pa.connection.Hostname(), peerMessage.ID, peerMessage.Context, peerMessage.Data)

// Acknowledge the message
pa.SendMessage(PeerMessage{peerMessage.ID, event.ContextAck, []byte{}})
// Acknowledge the message
pa.SendMessage(PeerMessage{peerMessage.ID, event.ContextAck, []byte{}})
}
}
} else {
log.Errorf("Error unmarshalling PeerMessage package: %x %v", message, err)


+ 1
- 1
protocol/connections/peerserverconnection_test.go View File

@@ -78,7 +78,7 @@ func TestPeerServerConnection(t *testing.T) {
onionAddr := identity.Hostname()

manager := event.NewEventManager()
engine := NewProtocolEngine(identity, priv, connectivity.NewLocalACN(), manager, nil, nil)
engine := NewProtocolEngine(identity, priv, connectivity.NewLocalACN(), manager, nil)

psc := NewPeerServerConnection(engine, "127.0.0.1:5451|"+onionAddr)
numcalls := 0


+ 16
- 12
storage/v1/profile_store.go View File

@@ -66,8 +66,7 @@ func (ps *ProfileStoreV1) initProfileWriterStore() {
ps.queue = event.NewQueue()
go ps.eventHandler()

ps.eventManager.Subscribe(event.BlockPeer, ps.queue)
ps.eventManager.Subscribe(event.UnblockPeer, ps.queue)
ps.eventManager.Subscribe(event.SetPeerAuthorization, ps.queue)
ps.eventManager.Subscribe(event.PeerCreated, ps.queue)
ps.eventManager.Subscribe(event.GroupCreated, ps.queue)
ps.eventManager.Subscribe(event.SetProfileName, ps.queue)
@@ -249,6 +248,18 @@ func (ps *ProfileStoreV1) load() error {
if err == nil {
ps.profile = cp

// TODO 2020.06.09: v1 update, Remove on v2
// if we already have the contact it can be assumed "approved" unless blocked
for _, contact := range cp.Contacts {
if contact.Authorization == "" {
if contact.DeprecatedBlocked {
contact.Authorization = model.AuthBlocked
} else {
contact.Authorization = model.AuthApproved
}
}
}

for gid, group := range cp.Groups {
ss := NewStreamStore(ps.directory, group.LocalID, ps.key)

@@ -272,16 +283,9 @@ func (ps *ProfileStoreV1) eventHandler() {
log.Debugf("eventHandler event %v %v\n", ev.EventType, ev.EventID)

switch ev.EventType {
case event.BlockPeer:
contact, exists := ps.profile.GetContact(ev.Data[event.RemotePeer])
if exists {
contact.Blocked = true
ps.save()
}
case event.UnblockPeer:
contact, exists := ps.profile.GetContact(ev.Data[event.RemotePeer])
if exists {
contact.Blocked = false
case event.SetPeerAuthorization:
err := ps.profile.SetContactAuthorization(ev.Data[event.RemotePeer], model.Authorization(ev.Data[event.Authorization]))
if err == nil {
ps.save()
}
case event.PeerCreated:


+ 14
- 8
testing/cwtch_peer_server_integration_test.go View File

@@ -188,9 +188,11 @@ func TestCwtchPeerIntegration(t *testing.T) {
app.LaunchPeers()
appClient.LaunchPeers()

fmt.Println("Waiting for Alice, Bob, and Carol to connect with onion network...")
time.Sleep(time.Second * 60)
waitTime := time.Duration(60) * time.Second
t.Logf("** Waiting for Alice, Bob, and Carol to connect with onion network... (%v)\n", waitTime)
time.Sleep(waitTime)
numGoRoutinesPostPeerStart := runtime.NumGoroutine()
fmt.Println("** Wait Done!")

// ***** Peering, server joining, group creation / invite *****

@@ -198,11 +200,9 @@ func TestCwtchPeerIntegration(t *testing.T) {
alice.JoinServer(serverAddr)

fmt.Println("Alice peering with Bob...")
alice.AddContact("Bob", bob.GetOnion(), false) // Add contact so we can track connection state
alice.PeerWithOnion(bob.GetOnion())

fmt.Println("Alice peering with Carol...")
alice.AddContact("Carol", carol.GetOnion(), false)
alice.PeerWithOnion(carol.GetOnion())

fmt.Println("Creating group on ", serverAddr, "...")
@@ -218,9 +218,15 @@ func TestCwtchPeerIntegration(t *testing.T) {

fmt.Println("Waiting for alice and Bob to peer...")
waitForPeerPeerConnection(t, alice, bob)
bob.AddContact("alice?", alice.GetOnion(), true)
// Need to add contact else SetContactAuth fails on peer peer doesnt exist
// Normal flow would be Bob app monitors for the new connection (a new connection state change to Auth
// and the adds the user to peer, and then approves or blocks it
bob.AddContact("alice?", alice.GetOnion(), model.AuthApproved)
bob.SetContactAuthorization(alice.GetOnion(), model.AuthApproved)

waitForPeerPeerConnection(t, alice, carol)
carol.AddContact("alice?", alice.GetOnion(), true)
carol.AddContact("alice?", alice.GetOnion(), model.AuthApproved)
carol.SetContactAuthorization(alice.GetOnion(), model.AuthApproved)

fmt.Println("Alice and Bob getVal public.name...")

@@ -234,13 +240,13 @@ func TestCwtchPeerIntegration(t *testing.T) {

aliceName, exists := bob.GetContactAttribute(alice.GetOnion(), attr.GetPeerScope("name"))
if !exists || aliceName != "Alice" {
t.Fatalf("alice GetKeyVal error on alice peer.name %v\n", exists)
t.Fatalf("Bob: alice GetKeyVal error on alice peer.name %v\n", exists)
}
fmt.Printf("Bob has alice's name as '%v'\n", aliceName)

bobName, exists := alice.GetContactAttribute(bob.GetOnion(), attr.GetPeerScope("name"))
if !exists || bobName != "Bob" {
t.Fatalf("bob GetKeyVal error, alice peer.name\n")
t.Fatalf("Alice: bob GetKeyVal error on bob peer.name\n")
}
fmt.Printf("Alice has bob's name as '%v'\n", bobName)



Loading…
Cancel
Save