From 25825e5b22cf1b9d76a4b24f55674225bcd4f744 Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Sat, 25 Apr 2020 17:36:35 +0200 Subject: [PATCH] Implement kick, op and friends. --- client.go | 27 ++++++++++++++++++++- group.go | 65 ++++++++++++++++++++++++++++++++++++++++++++------- static/sfu.js | 42 +++++++++++++++++++++++++++------ 3 files changed, 118 insertions(+), 16 deletions(-) diff --git a/client.go b/client.go index 3780c19..c427256 100644 --- a/client.go +++ b/client.go @@ -740,6 +740,13 @@ func clientLoop(c *client, conn *websocket.Conn) error { } } + case sendPermissionsAction: + c.write(clientMessage{ + Type: "permissions", + Permissions: c.permissions, + }) + case kickAction: + return userError("you have been kicked") default: log.Printf("unexpected action %T", a) return errors.New("unexpected action") @@ -754,7 +761,7 @@ func handleClientMessage(c *client, m clientMessage) error { switch m.Type { case "offer": if !c.permissions.Present { - return userError("not authorized") + return userError("not authorised") } if m.Offer == nil { return protocolError("null offer") @@ -786,6 +793,24 @@ func handleClientMessage(c *client, m clientMessage) error { for _, cc := range clients { cc.write(m) } + case "op", "unop", "present", "unpresent": + if !c.permissions.Admin { + c.error(userError("not authorised")) + return nil + } + err := setPermission(c.group, m.Id, m.Type) + if err != nil { + return c.error(err) + } + case "kick": + if !c.permissions.Admin { + c.error(userError("not authorised")) + return nil + } + err := kickClient(c.group, m.Id) + if err != nil { + return c.error(err) + } default: log.Printf("unexpected message: %v", m.Type) return protocolError("unexpected message") diff --git a/group.go b/group.go index ed2acea..9eb8f8e 100644 --- a/group.go +++ b/group.go @@ -10,8 +10,8 @@ import ( "log" "os" "path/filepath" - "sync" "strings" + "sync" "time" "github.com/pion/webrtc/v2" @@ -84,6 +84,10 @@ type pushTracksAction struct { c *client } +type sendPermissionsAction struct{} + +type kickAction struct{} + var groups struct { mu sync.Mutex groups map[string]*group @@ -117,7 +121,7 @@ func addGroup(name string, desc *groupDescription) (*group, error) { g := groups.groups[name] if g == nil { - if(desc == nil) { + if desc == nil { desc, err = getDescription(name) if err != nil { return nil, err @@ -235,6 +239,15 @@ func (g *group) getClients(except *client) []*client { return clients } +func (g *group) getClientUnlocked(id string) *client { + for _, c := range g.clients { + if c.id == id { + return c + } + } + return nil +} + func (g *group) Range(f func(c *client) bool) { g.mu.Lock() defer g.mu.Unlock() @@ -295,7 +308,7 @@ type groupUser struct { func matchUser(user, pass string, users []groupUser) (bool, bool) { for _, u := range users { - if (u.Username == "" || u.Username == user) { + if u.Username == "" || u.Username == user { return true, (u.Password == "" || u.Password == pass) } } @@ -372,22 +385,58 @@ func getPermission(desc *groupDescription, user, pass string) (userPermission, e p.Present = true return p, nil } - return p, userError("not authorized") + return p, userError("not authorised") } if found, good := matchUser(user, pass, desc.Presenter); found { if good { p.Present = true return p, nil } - return p, userError("not authorized") + return p, userError("not authorised") } if found, good := matchUser(user, pass, desc.Other); found { if good { return p, nil } - return p, userError("not authorized") + return p, userError("not authorised") } - return p, userError("not authorized") + return p, userError("not authorised") +} + +func setPermission(g *group, id string, perm string) error { + g.mu.Lock() + defer g.mu.Unlock() + + c := g.getClientUnlocked(id) + if c == nil { + return userError("no such user") + } + + switch perm { + case "op": + c.permissions.Admin = true + case "unop": + c.permissions.Admin = false + case "present": + c.permissions.Present = true + case "unpresent": + c.permissions.Present = false + default: + return userError("unknown permission") + } + return c.action(sendPermissionsAction{}) +} + +func kickClient(g *group, id string) error { + g.mu.Lock() + defer g.mu.Unlock() + + c := g.getClientUnlocked(id) + if c == nil { + return userError("no such user") + } + + return c.action(kickAction{}) } type publicGroup struct { @@ -427,7 +476,7 @@ func readPublicGroups() { if !strings.HasSuffix(fi.Name(), ".json") { continue } - name := fi.Name()[:len(fi.Name()) - 5] + name := fi.Name()[:len(fi.Name())-5] desc, err := getDescription(name) if err != nil { continue diff --git a/static/sfu.js b/static/sfu.js index e4f0f30..3cd8973 100644 --- a/static/sfu.js +++ b/static/sfu.js @@ -15,6 +15,8 @@ let up = {}, down = {}; let iceServers = []; +let permissions = {}; + function toHex(array) { let a = new Uint8Array(array); let s = ''; @@ -515,9 +517,10 @@ function gotUser(id, name, del) { addUser(id, name); } -function gotPermissions(permissions) { - document.getElementById('presenterbox').disabled = !permissions.present; - document.getElementById('sharebox').disabled = !permissions.present; +function gotPermissions(perm) { + permissions = perm; + document.getElementById('presenterbox').disabled = !perm.present; + document.getElementById('sharebox').disabled = !perm.present; } const urlRegexp = /https?:\/\/[-a-zA-Z0-9@:%/._\+~#=?]+[-a-zA-Z0-9@:%/_\+~#=]/g; @@ -634,14 +637,39 @@ function handleInput() { } switch(cmd) { - case '/nick': - setNick(rest); - storeNick(rest); - return; case '/me': message = rest; me = true; break; + case '/op': + case '/unop': + case '/kick': + case '/present': + case '/unpresent': + if(!permissions.admin) { + displayError("You're not an administrator"); + return; + } + let id; + if(id in users) { + id = rest; + } else { + for(let i in users) { + if(users[i] === rest) { + id = i; + break; + } + } + } + if(!id) { + displayError('Unknown user ' + rest); + return; + } + send({ + type: cmd.slice(1), + id: id, + }); + return; default: displayError('Uknown command ' + cmd); return;