forked from cwtch.im/cwtch
Adding new authorization level to peers; porting Blocked status to authorization; removing trusted; securing engine/peerapp message processing around authoriztion
This commit is contained in:
parent
e05d1addf6
commit
e91e892eef
10
app/app.go
10
app/app.go
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]])
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
3
go.mod
3
go.mod
|
@ -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
go.sum
2
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
if _, exists := cp.Profile.GetContact(onion); !exists {
|
||||
cp.AddContact(onion, onion, false)
|
||||
}
|
||||
defer cp.mutex.Unlock()
|
||||
if _, exists := cp.Profile.GetContact(onion); !exists {
|
||||
cp.AddContact(onion, onion, model.AuthApproved)
|
||||
}
|
||||
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 {
|
||||
func (cp *cwtchPeer) SetContactAuthorization(peer string, authorization model.Authorization) error {
|
||||
cp.mutex.Lock()
|
||||
err := cp.Profile.BlockPeer(peer)
|
||||
err := cp.Profile.SetContactAuthorization(peer, authorization)
|
||||
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 {
|
||||
cp.mutex.Lock()
|
||||
err := cp.Profile.UnblockPeer(peer)
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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…
Reference in New Issue