215 lines
5.9 KiB
Go
215 lines
5.9 KiB
Go
package services
|
|
|
|
import (
|
|
"git.saqut.com/saqut/mwse/internal/protocol"
|
|
"git.saqut.com/saqut/mwse/internal/ws"
|
|
)
|
|
|
|
// ---- relationship helpers ------------------------------------------------
|
|
//
|
|
// "Secure" reachability comes in two forms: a mutual pairing, or co-membership of
|
|
// a room. These helpers centralise the checks the original Client.isSecure /
|
|
// getSucureClients spread across files (and fix their bugs).
|
|
|
|
// isPaired reports a *mutual* pairing between a and b.
|
|
func isPaired(a, b *ws.Client) bool {
|
|
return a.HasPair(b.ID) && b.HasPair(a.ID)
|
|
}
|
|
|
|
// shareRoom reports whether a and b are members of at least one common room.
|
|
func shareRoom(hub *ws.Hub, a, b *ws.Client) bool {
|
|
for _, rid := range a.Rooms() {
|
|
if r, ok := hub.Room(rid); ok && r.Has(b.ID) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isSecure reports whether a may address the client with id peerID.
|
|
func isSecure(hub *ws.Hub, a *ws.Client, peerID string) bool {
|
|
peer, ok := hub.Client(peerID)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return isPaired(a, peer) || shareRoom(hub, a, peer)
|
|
}
|
|
|
|
// secureClients returns the connected peers c may exchange info with: those c has
|
|
// paired toward, and those sharing a room with c.
|
|
func secureClients(hub *ws.Hub, c *ws.Client) (pairs, roompairs map[string]*ws.Client) {
|
|
pairs = make(map[string]*ws.Client)
|
|
for _, id := range c.Pairs() {
|
|
if pc, ok := hub.Client(id); ok {
|
|
pairs[id] = pc
|
|
}
|
|
}
|
|
roompairs = make(map[string]*ws.Client)
|
|
for _, rid := range c.Rooms() {
|
|
r, ok := hub.Room(rid)
|
|
if !ok {
|
|
continue
|
|
}
|
|
for _, m := range r.Members() {
|
|
if m.ID == c.ID {
|
|
continue
|
|
}
|
|
roompairs[m.ID] = m
|
|
}
|
|
}
|
|
return pairs, roompairs
|
|
}
|
|
|
|
func registerAuth(hub *ws.Hub) {
|
|
// On disconnect: tell secure peers we are gone, then drop pairing edges both
|
|
// ways so no stale references remain.
|
|
hub.OnDisconnect(func(c *ws.Client) {
|
|
pairs, roompairs := secureClients(hub, c)
|
|
|
|
notified := make(map[string]bool)
|
|
notify := func(set map[string]*ws.Client) {
|
|
for id, peer := range set {
|
|
if notified[id] {
|
|
continue
|
|
}
|
|
notified[id] = true
|
|
peer.Signal("peer/disconnect", map[string]any{"id": c.ID})
|
|
}
|
|
}
|
|
notify(pairs)
|
|
notify(roompairs)
|
|
|
|
for id, peer := range pairs {
|
|
peer.RemovePair(c.ID)
|
|
c.RemovePair(id)
|
|
}
|
|
})
|
|
|
|
hub.Register("auth/pair-system", func(c *ws.Client, m protocol.Message) any {
|
|
switch m.Str("value") {
|
|
case "everybody":
|
|
c.SetRequiredPair(true)
|
|
return success()
|
|
case "disable":
|
|
c.SetRequiredPair(false)
|
|
return success()
|
|
}
|
|
return fail("INVALID_VALUE")
|
|
})
|
|
|
|
hub.Register("my/socketid", func(c *ws.Client, m protocol.Message) any {
|
|
return c.ID
|
|
})
|
|
|
|
hub.Register("auth/public", func(c *ws.Client, m protocol.Message) any {
|
|
c.SetRequiredPair(false)
|
|
return map[string]any{"value": "success", "mode": "public"}
|
|
})
|
|
|
|
hub.Register("auth/private", func(c *ws.Client, m protocol.Message) any {
|
|
c.SetRequiredPair(true)
|
|
return map[string]any{"value": "success", "mode": "private"}
|
|
})
|
|
|
|
// request/pair: ask `to` to pair with us. We record our side of the edge and
|
|
// notify the target; they complete it with accept/pair.
|
|
hub.Register("request/pair", func(c *ws.Client, m protocol.Message) any {
|
|
to := m.Str("to")
|
|
target, ok := hub.Client(to)
|
|
if !ok {
|
|
return fail("CLIENT_NOT_FOUND")
|
|
}
|
|
if isPaired(c, target) {
|
|
return map[string]any{"status": "success", "message": "ALREADY-PAIRED"}
|
|
}
|
|
if c.HasPair(to) {
|
|
return fail("ALREADY-REQUESTED")
|
|
}
|
|
c.AddPair(to)
|
|
target.Signal("request/pair", map[string]any{"from": c.ID, "info": c.Info()})
|
|
return map[string]any{"status": "success", "message": "REQUESTED"}
|
|
})
|
|
|
|
// accept/pair: complete a pairing the peer `to` requested from us.
|
|
hub.Register("accept/pair", func(c *ws.Client, m protocol.Message) any {
|
|
to := m.Str("to")
|
|
requester, ok := hub.Client(to)
|
|
if !ok {
|
|
return fail("CLIENT_NOT_FOUND")
|
|
}
|
|
if isPaired(c, requester) {
|
|
return map[string]any{"status": "success", "message": "ALREADY-PAIRED"}
|
|
}
|
|
if !requester.HasPair(c.ID) {
|
|
return fail("NOT_REQUESTED_PAIR")
|
|
}
|
|
c.AddPair(to)
|
|
requester.Signal("accepted/pair", map[string]any{"from": c.ID, "info": c.Info()})
|
|
return success()
|
|
})
|
|
|
|
// reject/pair and end/pair both tear the edge down in both directions and
|
|
// notify the other side.
|
|
teardown := func(c *ws.Client, m protocol.Message) any {
|
|
to := m.Str("to")
|
|
other, ok := hub.Client(to)
|
|
if !ok {
|
|
return fail("CLIENT_NOT_FOUND")
|
|
}
|
|
c.RemovePair(to)
|
|
other.RemovePair(c.ID)
|
|
other.Signal("end/pair", map[string]any{"from": c.ID})
|
|
return success()
|
|
}
|
|
hub.Register("reject/pair", teardown)
|
|
hub.Register("end/pair", teardown)
|
|
|
|
hub.Register("pair/list", func(c *ws.Client, m protocol.Message) any {
|
|
var list []string
|
|
for _, id := range c.Pairs() {
|
|
if other, ok := hub.Client(id); ok && other.HasPair(c.ID) {
|
|
list = append(list, id)
|
|
}
|
|
}
|
|
return map[string]any{"type": "pair/list", "value": list}
|
|
})
|
|
|
|
hub.Register("is/reachable", func(c *ws.Client, m protocol.Message) any {
|
|
to := m.Str("to")
|
|
other, ok := hub.Client(to)
|
|
if !ok {
|
|
return false
|
|
}
|
|
if other.RequiredPair() && !other.HasPair(c.ID) {
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
// auth/info: set our metadata and push the change to every secure peer.
|
|
hub.Register("auth/info", func(c *ws.Client, m protocol.Message) any {
|
|
name := m.Str("name")
|
|
value := m.Get("value")
|
|
c.SetInfo(name, value)
|
|
|
|
pairs, roompairs := secureClients(hub, c)
|
|
payload := map[string]any{"from": c.ID, "name": name, "value": value}
|
|
for _, peer := range pairs {
|
|
peer.Signal("pair/info", payload)
|
|
}
|
|
for _, peer := range roompairs {
|
|
peer.Signal("pair/info", payload)
|
|
}
|
|
return success()
|
|
})
|
|
|
|
hub.Register("peer/info", func(c *ws.Client, m protocol.Message) any {
|
|
peerID := m.Str("peer")
|
|
if !isSecure(hub, c, peerID) {
|
|
return map[string]any{"status": "fail", "message": "unaccessible user"}
|
|
}
|
|
peer, _ := hub.Client(peerID)
|
|
return map[string]any{"status": "success", "info": peer.Info()}
|
|
})
|
|
}
|