Commit 2890d21c authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Add group permissions.

parent cb1782b6
......@@ -89,6 +89,7 @@ type clientMessage struct {
Id string `json:"id,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Permissions userPermission `json:"permissions,omitempty"`
Group string `json:"group,omitempty"`
Value string `json:"value,omitempty"`
Message string `json:"message,omitempty"`
......@@ -149,7 +150,7 @@ func startClient(conn *websocket.Conn) (err error) {
c.writerDone = make(chan struct{})
go clientWriter(conn, c.writeCh, c.writerDone)
g, users, err := addClient(m.Group, c)
g, users, err := addClient(m.Group, c, m.Username, m.Password)
if err != nil {
return
}
......@@ -262,7 +263,7 @@ func addUpConn(c *client, id string) (*upConnection, error) {
clients := c.group.getClients(c)
for _, cc := range clients {
cc.action(addTrackAction{id, local, u, done})
if(done && u.label != "") {
if done && u.label != "" {
cc.action(addLabelAction{id, u.label})
}
}
......@@ -389,7 +390,7 @@ func delDownConn(c *client, id string) {
log.Printf("Deleting unknown connection")
return
}
conn := c.down[id];
conn := c.down[id]
if conn == nil {
log.Printf("Deleting unknown connection")
return
......@@ -667,6 +668,11 @@ func clientLoop(c *client, conn *websocket.Conn) error {
g := c.group
c.write(clientMessage{
Type: "permissions",
Permissions: c.permissions,
})
for _, cc := range g.getClients(c) {
cc.action(pushTracksAction{c})
}
......@@ -721,7 +727,7 @@ func clientLoop(c *client, conn *websocket.Conn) error {
for _, u := range c.up {
var done bool
for i, p := range u.pairs {
done = i >= u.streamCount - 1
done = i >= u.streamCount-1
a.c.action(addTrackAction{
u.id, p.local, u,
done,
......@@ -747,6 +753,9 @@ func clientLoop(c *client, conn *websocket.Conn) error {
func handleClientMessage(c *client, m clientMessage) error {
switch m.Type {
case "offer":
if !c.permissions.Present {
return userError("not authorized")
}
if m.Offer == nil {
return protocolError("null offer")
}
......
......@@ -6,7 +6,10 @@
package main
import (
"encoding/json"
"log"
"os"
"path/filepath"
"sync"
"github.com/pion/webrtc/v2"
......@@ -37,6 +40,7 @@ type client struct {
group *group
id string
username string
permissions userPermission
done chan struct{}
writeCh chan interface{}
writerDone chan struct{}
......@@ -47,7 +51,7 @@ type client struct {
type group struct {
name string
public bool
description *groupDescription
mu sync.Mutex
clients []*client
......@@ -102,8 +106,13 @@ func addGroup(name string) (*group, error) {
g := groups.groups[name]
if g == nil {
desc, err := getDescription(name)
if err != nil {
return nil, err
}
g = &group{
name: name,
description: desc,
}
groups.groups[name] = g
}
......@@ -130,12 +139,18 @@ type userid struct {
username string
}
func addClient(name string, client *client) (*group, []userid, error) {
func addClient(name string, client *client, user, pass string) (*group, []userid, error) {
g, err := addGroup(name)
if err != nil {
return nil, nil, err
}
perms, err := getPermission(g.description, user, pass)
if err != nil {
return nil, nil, err
}
client.permissions = perms
var users []userid
g.mu.Lock()
defer g.mu.Unlock()
......@@ -182,8 +197,8 @@ func (g *group) Range(f func(c *client) bool) {
defer g.mu.Unlock()
for _, c := range g.clients {
ok := f(c)
if(!ok){
break;
if !ok {
break
}
}
}
......@@ -218,6 +233,83 @@ func (c *client) action(m interface{}) error {
}
}
type groupUser struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}
func matchUser(user, pass string, users []groupUser) (bool, bool) {
for _, u := range users {
if (u.Username == "" || u.Username == user) {
return true, (u.Password == "" || u.Password == pass)
}
}
return false, false
}
type groupDescription struct {
Public bool `json:"public,omitempty"`
AllowAnonymous bool `json:"allow-anonymous,omitempty"`
Admin []groupUser `json:"admin,omitempty"`
Presenter []groupUser `json:"presenter,omitempty"`
Other []groupUser `json:"other,omitempty"`
}
func getDescription(name string) (*groupDescription, error) {
r, err := os.Open(filepath.Join(groupsDir, name+".json"))
if err != nil {
if os.IsNotExist(err) {
err = userError("group does not exist")
} else if os.IsPermission(err) {
err = userError("unauthorised")
}
return nil, err
}
defer r.Close()
var desc groupDescription
d := json.NewDecoder(r)
err = d.Decode(&desc)
if err != nil {
return nil, err
}
return &desc, nil
}
type userPermission struct {
Admin bool `json:"admin,omitempty"`
Present bool `json:"present,omitempty"`
}
func getPermission(desc *groupDescription, user, pass string) (userPermission, error) {
var p userPermission
if !desc.AllowAnonymous && user == "" {
return p, userError("anonymous users not allowed in this group")
}
if found, good := matchUser(user, pass, desc.Admin); found {
if good {
p.Admin = true
p.Present = true
return p, nil
}
return p, userError("not authorized")
}
if found, good := matchUser(user, pass, desc.Presenter); found {
if good {
p.Present = true
return p, nil
}
return p, userError("not authorized")
}
if found, good := matchUser(user, pass, desc.Other); found {
if good {
return p, nil
}
return p, userError("not authorized")
}
return p, userError("not authorized")
}
type publicGroup struct {
Name string `json:"name"`
ClientCount int `json:"clientCount"`
......@@ -228,7 +320,7 @@ func getPublicGroups() []publicGroup {
groups.mu.Lock()
defer groups.mu.Unlock()
for _, g := range groups.groups {
if g.public {
if g.description.Public {
gs = append(gs, publicGroup{
Name: g.name,
ClientCount: len(g.clients),
......
......@@ -22,6 +22,7 @@ import (
var httpAddr string
var staticRoot string
var dataDir string
var groupsDir string
var iceFilename string
func main() {
......@@ -30,6 +31,8 @@ func main() {
"web server root `directory`")
flag.StringVar(&dataDir, "data", "./data/",
"data `directory`")
flag.StringVar(&groupsDir, "groups", "./groups/",
"group description `directory`")
flag.Parse()
iceFilename = filepath.Join(staticRoot, "ice-servers.json")
......
......@@ -29,9 +29,9 @@
<div id="optionsdiv">
<label for="presenterbox">Present:</label>
<input id="presenterbox" type="checkbox"/>
<input id="presenterbox" type="checkbox"/ disabled>
<label for="sharebox">Share screen:</label>
<input id="sharebox" type="checkbox"/>
<input id="sharebox" type="checkbox"/ disabled>
</div>
</div>
......
......@@ -279,8 +279,10 @@ function serverConnect() {
socket.onclose = function(e) {
setConnected(false);
document.getElementById('presenterbox').checked = false;
document.getElementById('presenterbox').disabled = true;
setLocalMedia();
document.getElementById('sharebox').checked = false;
document.getElementById('sharebox').disabled = true;
setShareMedia();
reject(new Error('websocket close ' + e.code + ' ' + e.reason));
};
......@@ -305,6 +307,9 @@ function serverConnect() {
case 'label':
gotLabel(m.id, m.value);
break;
case 'permissions':
gotPermissions(m.permissions);
break;
case 'user':
gotUser(m.id, m.username, m.del);
break;
......@@ -483,6 +488,11 @@ function gotUser(id, name, del) {
addUser(id, name);
}
function gotPermissions(permissions) {
document.getElementById('presenterbox').disabled = !permissions.present;
document.getElementById('sharebox').disabled = !permissions.present;
}
const urlRegexp = /https?:\/\/[-a-zA-Z0-9@:%/._\+~#=?]+[-a-zA-Z0-9@:%/_\+~#=]/g;
function formatLine(line) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment