mirror of
https://github.com/jech/galene.git
synced 2024-12-22 07:15:47 +01:00
Replace ClientPermissions with a list of strings.
Now that we support external auth, the permissions list is open-ended. Make it a list for simplicity.
This commit is contained in:
parent
439dbabaa5
commit
a86fb08f6c
11 changed files with 213 additions and 175 deletions
|
@ -385,7 +385,7 @@ allowed to join, then the authorisation server replies with a signed JWT
|
|||
{
|
||||
"sub": username,
|
||||
"aud": "https://galene.example.org/group/groupname",
|
||||
"permissions": ["present": true],
|
||||
"permissions": ["present"],
|
||||
"iat": now,
|
||||
"exp": now + 30s,
|
||||
"iss": authorisation server URL
|
||||
|
|
|
@ -59,21 +59,19 @@ func (client *Client) Username() string {
|
|||
return "RECORDING"
|
||||
}
|
||||
|
||||
func (client *Client) SetPermissions(perms group.ClientPermissions) {
|
||||
func (client *Client) SetPermissions(perms []string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (client *Client) Permissions() group.ClientPermissions {
|
||||
return group.ClientPermissions{
|
||||
System: true,
|
||||
}
|
||||
func (client *Client) Permissions() []string {
|
||||
return []string{"system"}
|
||||
}
|
||||
|
||||
func (client *Client) Data() map[string]interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *Client) PushClient(group, kind, id, username string, permissions group.ClientPermissions, data map[string]interface{}) error {
|
||||
func (client *Client) PushClient(group, kind, id, username string, perms []string, data map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -81,13 +81,6 @@ type ClientPattern struct {
|
|||
Password *Password `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type ClientPermissions struct {
|
||||
Op bool `json:"op,omitempty"`
|
||||
Present bool `json:"present,omitempty"`
|
||||
Record bool `json:"record,omitempty"`
|
||||
System bool `json:"system,omitempty"`
|
||||
}
|
||||
|
||||
type ClientCredentials struct {
|
||||
System bool
|
||||
Username string
|
||||
|
@ -99,12 +92,12 @@ type Client interface {
|
|||
Group() *Group
|
||||
Id() string
|
||||
Username() string
|
||||
Permissions() ClientPermissions
|
||||
SetPermissions(ClientPermissions)
|
||||
Permissions() []string
|
||||
SetPermissions([]string)
|
||||
Data() map[string]interface{}
|
||||
PushConn(g *Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error
|
||||
RequestConns(target Client, g *Group, id string) error
|
||||
Joined(group, kind string) error
|
||||
PushClient(group, kind, id, username string, permissions ClientPermissions, data map[string]interface{}) error
|
||||
PushClient(group, kind, id, username string, perms []string, data map[string]interface{}) error
|
||||
Kick(id, user, message string) error
|
||||
}
|
||||
|
|
135
group/group.go
135
group/group.go
|
@ -544,6 +544,15 @@ func deleteUnlocked(g *Group) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func member(v string, l []string) bool {
|
||||
for _, w := range l {
|
||||
if v == w {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AddClient(group string, c Client, creds ClientCredentials) (*Group, error) {
|
||||
g, err := Add(group, nil)
|
||||
if err != nil {
|
||||
|
@ -555,7 +564,7 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
|
|||
|
||||
clients := g.getClientsUnlocked(nil)
|
||||
|
||||
if !c.Permissions().System {
|
||||
if !member("system", c.Permissions()) {
|
||||
perms, err := g.description.GetPermission(group, creds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -563,7 +572,7 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
|
|||
|
||||
c.SetPermissions(perms)
|
||||
|
||||
if !perms.Op {
|
||||
if !member("op", perms) {
|
||||
if g.locked != nil {
|
||||
m := *g.locked
|
||||
if m == "" {
|
||||
|
@ -574,7 +583,7 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
|
|||
if g.description.Autokick {
|
||||
ops := false
|
||||
for _, c := range clients {
|
||||
if c.Permissions().Op {
|
||||
if member("op", c.Permissions()) {
|
||||
ops = true
|
||||
break
|
||||
}
|
||||
|
@ -588,7 +597,7 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
|
|||
}
|
||||
}
|
||||
|
||||
if !perms.Op && g.description.MaxClients > 0 {
|
||||
if !member("op", perms) && g.description.MaxClients > 0 {
|
||||
if len(g.clients) >= g.description.MaxClients {
|
||||
return nil, UserError("too many users")
|
||||
}
|
||||
|
@ -627,7 +636,7 @@ func autoLockKick(g *Group, clients []Client) {
|
|||
return
|
||||
}
|
||||
for _, c := range clients {
|
||||
if c.Permissions().Op {
|
||||
if member("op", c.Permissions()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -665,7 +674,7 @@ func DelClient(c Client) {
|
|||
c.Joined(g.Name(), "leave")
|
||||
for _, cc := range clients {
|
||||
cc.PushClient(
|
||||
g.Name(), "delete", c.Id(), "", ClientPermissions{}, nil,
|
||||
g.Name(), "delete", c.Id(), "", nil, nil,
|
||||
)
|
||||
}
|
||||
autoLockKick(g, clients)
|
||||
|
@ -1066,82 +1075,76 @@ func GetDescription(name string) (*Description, error) {
|
|||
return &desc, nil
|
||||
}
|
||||
|
||||
func (desc *Description) GetPermission(group string, creds ClientCredentials) (ClientPermissions, error) {
|
||||
var p ClientPermissions
|
||||
func (desc *Description) GetPermission(group string, creds ClientCredentials) ([]string, error) {
|
||||
if !desc.AllowAnonymous && creds.Username == "" {
|
||||
return p, ErrAnonymousNotAuthorised
|
||||
return nil, ErrAnonymousNotAuthorised
|
||||
}
|
||||
|
||||
if found, good := matchClient(group, creds, desc.Op); found {
|
||||
if good {
|
||||
p.Op = true
|
||||
p.Present = true
|
||||
if desc.AllowRecording {
|
||||
p.Record = true
|
||||
if creds.Token == "" {
|
||||
if found, good := matchClient(group, creds, desc.Op); found {
|
||||
if good {
|
||||
var p []string
|
||||
p = []string{"op", "present"}
|
||||
if desc.AllowRecording {
|
||||
p = append(p, "record")
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
return p, nil
|
||||
return nil, ErrNotAuthorised
|
||||
}
|
||||
return p, ErrNotAuthorised
|
||||
}
|
||||
if found, good := matchClient(group, creds, desc.Presenter); found {
|
||||
if good {
|
||||
p.Present = true
|
||||
return p, nil
|
||||
if found, good := matchClient(group, creds, desc.Presenter); found {
|
||||
if good {
|
||||
return []string{"present"}, nil
|
||||
}
|
||||
return nil, ErrNotAuthorised
|
||||
}
|
||||
return p, ErrNotAuthorised
|
||||
}
|
||||
if found, good := matchClient(group, creds, desc.Other); found {
|
||||
if good {
|
||||
return p, nil
|
||||
if found, good := matchClient(group, creds, desc.Other); found {
|
||||
if good {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, ErrNotAuthorised
|
||||
}
|
||||
return p, ErrNotAuthorised
|
||||
return nil, ErrNotAuthorised
|
||||
}
|
||||
|
||||
if creds.Token != "" {
|
||||
aud, perms, err := token.Valid(
|
||||
creds.Username, creds.Token, desc.AuthKeys,
|
||||
)
|
||||
aud, perms, err := token.Valid(
|
||||
creds.Username, creds.Token, desc.AuthKeys,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Token authentication: %v", err)
|
||||
return nil, ErrNotAuthorised
|
||||
}
|
||||
conf, err := GetConfiguration()
|
||||
if err != nil {
|
||||
log.Printf("Read config.json: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
ok := false
|
||||
for _, u := range aud {
|
||||
url, err := url.Parse(u)
|
||||
if err != nil {
|
||||
log.Printf("Token authentication: %v", err)
|
||||
return p, ErrNotAuthorised
|
||||
log.Printf("Token URL: %v", err)
|
||||
continue
|
||||
}
|
||||
conf, err := GetConfiguration()
|
||||
if err != nil {
|
||||
log.Printf("Read config.json: %v", err)
|
||||
return p, err
|
||||
}
|
||||
ok := false
|
||||
for _, u := range aud {
|
||||
url, err := url.Parse(u)
|
||||
if err != nil {
|
||||
log.Printf("Token URL: %v", err)
|
||||
// if canonicalHost is not set, we allow tokens
|
||||
// for any domain name. Hopefully different
|
||||
// servers use distinct keys.
|
||||
if conf.CanonicalHost != "" {
|
||||
if !strings.EqualFold(
|
||||
url.Host, conf.CanonicalHost,
|
||||
) {
|
||||
continue
|
||||
}
|
||||
// if canonicalHost is not set, we allow tokens
|
||||
// for any domain name. Hopefully different
|
||||
// servers use distinct keys.
|
||||
if conf.CanonicalHost != "" {
|
||||
if !strings.EqualFold(
|
||||
url.Host, conf.CanonicalHost,
|
||||
) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if url.Path == path.Join("/group", group)+"/" {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return p, ErrNotAuthorised
|
||||
if url.Path == path.Join("/group", group)+"/" {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
p.Op, _ = perms["op"].(bool)
|
||||
p.Present, _ = perms["present"].(bool)
|
||||
p.Record, _ = perms["record"].(bool)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
return p, ErrNotAuthorised
|
||||
if !ok {
|
||||
return nil, ErrNotAuthorised
|
||||
}
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
|
|
|
@ -132,29 +132,29 @@ var badClients = []ClientCredentials{
|
|||
|
||||
type credPerm struct {
|
||||
c ClientCredentials
|
||||
p ClientPermissions
|
||||
p []string
|
||||
}
|
||||
|
||||
var goodClients = []credPerm{
|
||||
{
|
||||
ClientCredentials{Username: "jch", Password: "topsecret"},
|
||||
ClientPermissions{Op: true, Present: true},
|
||||
[]string{"op", "present"},
|
||||
},
|
||||
{
|
||||
ClientCredentials{Username: "john", Password: "secret"},
|
||||
ClientPermissions{Present: true},
|
||||
[]string{"present"},
|
||||
},
|
||||
{
|
||||
ClientCredentials{Username: "john", Password: "secret2"},
|
||||
ClientPermissions{Present: true},
|
||||
[]string{"present"},
|
||||
},
|
||||
{
|
||||
ClientCredentials{Username: "james", Password: "secret3"},
|
||||
ClientPermissions{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
ClientCredentials{Username: "paul", Password: "secret3"},
|
||||
ClientPermissions{},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ type webClient struct {
|
|||
group *group.Group
|
||||
id string
|
||||
username string
|
||||
permissions group.ClientPermissions
|
||||
permissions []string
|
||||
data map[string]interface{}
|
||||
requested map[string][]string
|
||||
done chan struct{}
|
||||
|
@ -86,7 +86,7 @@ func (c *webClient) Username() string {
|
|||
return c.username
|
||||
}
|
||||
|
||||
func (c *webClient) Permissions() group.ClientPermissions {
|
||||
func (c *webClient) Permissions() []string {
|
||||
return c.permissions
|
||||
}
|
||||
|
||||
|
@ -94,13 +94,13 @@ func (c *webClient) Data() map[string]interface{} {
|
|||
return c.data
|
||||
}
|
||||
|
||||
func (c *webClient) SetPermissions(perms group.ClientPermissions) {
|
||||
func (c *webClient) SetPermissions(perms []string) {
|
||||
c.permissions = perms
|
||||
}
|
||||
|
||||
func (c *webClient) PushClient(group, kind, id, username string, permissions group.ClientPermissions, data map[string]interface{}) error {
|
||||
func (c *webClient) PushClient(group, kind, id, username string, perms []string, data map[string]interface{}) error {
|
||||
return c.action(pushClientAction{
|
||||
group, kind, id, username, permissions, data,
|
||||
group, kind, id, username, perms, data,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ type clientMessage struct {
|
|||
Password string `json:"password,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
Privileged bool `json:"privileged,omitempty"`
|
||||
Permissions *group.ClientPermissions `json:"permissions,omitempty"`
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
Status *group.Status `json:"status,omitempty"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Group string `json:"group,omitempty"`
|
||||
|
@ -900,7 +900,7 @@ type pushClientAction struct {
|
|||
kind string
|
||||
id string
|
||||
username string
|
||||
permissions group.ClientPermissions
|
||||
permissions []string
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
|
@ -919,6 +919,33 @@ type kickAction struct {
|
|||
|
||||
var errEmptyId = group.ProtocolError("empty id")
|
||||
|
||||
func member(v string, l []string) bool {
|
||||
for _, w := range l {
|
||||
if v == w {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func remove(v string, l []string) []string {
|
||||
for i, w := range l {
|
||||
if v == w {
|
||||
l = append(l[:i], l[i+1:]...)
|
||||
return l
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func addnew(v string, l []string) []string {
|
||||
if member(v, l) {
|
||||
return l
|
||||
}
|
||||
l = append(l, v)
|
||||
return l
|
||||
}
|
||||
|
||||
func clientLoop(c *webClient, ws *websocket.Conn) error {
|
||||
read := make(chan interface{}, 1)
|
||||
go clientReader(ws, read, c.done)
|
||||
|
@ -1102,12 +1129,13 @@ func handleAction(c *webClient, a interface{}) error {
|
|||
log.Printf("got client for wrong group")
|
||||
return nil
|
||||
}
|
||||
perms := append([]string(nil), a.permissions...)
|
||||
return c.write(clientMessage{
|
||||
Type: "user",
|
||||
Kind: a.kind,
|
||||
Id: a.id,
|
||||
Username: a.username,
|
||||
Permissions: &a.permissions,
|
||||
Permissions: perms,
|
||||
Data: a.data,
|
||||
})
|
||||
case joinedAction:
|
||||
|
@ -1121,13 +1149,13 @@ func handleAction(c *webClient, a interface{}) error {
|
|||
data = g.Data()
|
||||
}
|
||||
}
|
||||
perms := c.permissions
|
||||
perms := append([]string(nil), c.permissions...)
|
||||
return c.write(clientMessage{
|
||||
Type: "joined",
|
||||
Kind: a.kind,
|
||||
Group: a.group,
|
||||
Username: c.username,
|
||||
Permissions: &perms,
|
||||
Permissions: perms,
|
||||
Status: status,
|
||||
Data: data,
|
||||
RTCConfiguration: ice.ICEConfiguration(),
|
||||
|
@ -1137,18 +1165,18 @@ func handleAction(c *webClient, a interface{}) error {
|
|||
if g == nil {
|
||||
return errors.New("Permissions changed in no group")
|
||||
}
|
||||
perms := c.permissions
|
||||
perms := append([]string(nil), c.permissions...)
|
||||
status := g.Status(true)
|
||||
c.write(clientMessage{
|
||||
Type: "joined",
|
||||
Kind: "change",
|
||||
Group: g.Name(),
|
||||
Username: c.username,
|
||||
Permissions: &perms,
|
||||
Permissions: perms,
|
||||
Status: &status,
|
||||
RTCConfiguration: ice.ICEConfiguration(),
|
||||
})
|
||||
if !c.permissions.Present {
|
||||
if member("present", c.permissions) {
|
||||
up := getUpConns(c)
|
||||
for _, u := range up {
|
||||
err := delUpConn(
|
||||
|
@ -1220,7 +1248,7 @@ func leaveGroup(c *webClient) {
|
|||
}
|
||||
|
||||
group.DelClient(c)
|
||||
c.permissions = group.ClientPermissions{}
|
||||
c.permissions = nil
|
||||
c.data = nil
|
||||
c.requested = make(map[string][]string)
|
||||
c.group = nil
|
||||
|
@ -1260,17 +1288,17 @@ func setPermissions(g *group.Group, id string, perm string) error {
|
|||
|
||||
switch perm {
|
||||
case "op":
|
||||
c.permissions.Op = true
|
||||
c.permissions = addnew("op", c.permissions)
|
||||
if g.Description().AllowRecording {
|
||||
c.permissions.Record = true
|
||||
c.permissions = addnew("record", c.permissions)
|
||||
}
|
||||
case "unop":
|
||||
c.permissions.Op = false
|
||||
c.permissions.Record = false
|
||||
c.permissions = remove("op", c.permissions)
|
||||
c.permissions = remove("record", c.permissions)
|
||||
case "present":
|
||||
c.permissions.Present = true
|
||||
c.permissions = addnew("present", c.permissions)
|
||||
case "unpresent":
|
||||
c.permissions.Present = false
|
||||
c.permissions = remove("present", c.permissions)
|
||||
default:
|
||||
return group.UserError("unknown permission")
|
||||
}
|
||||
|
@ -1356,7 +1384,6 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
Kind: "fail",
|
||||
Group: m.Group,
|
||||
Username: c.username,
|
||||
Permissions: &group.ClientPermissions{},
|
||||
Value: s,
|
||||
})
|
||||
}
|
||||
|
@ -1368,7 +1395,6 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
Kind: "redirect",
|
||||
Group: m.Group,
|
||||
Username: c.username,
|
||||
Permissions: &group.ClientPermissions{},
|
||||
Value: redirect,
|
||||
})
|
||||
}
|
||||
|
@ -1407,7 +1433,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
if m.Id == "" {
|
||||
return errEmptyId
|
||||
}
|
||||
if !c.permissions.Present {
|
||||
if !member("present", c.permissions) {
|
||||
if m.Replace != "" {
|
||||
delUpConn(c, m.Replace, c.id, true)
|
||||
}
|
||||
|
@ -1508,7 +1534,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
Source: m.Source,
|
||||
Dest: m.Dest,
|
||||
Username: m.Username,
|
||||
Privileged: c.permissions.Op,
|
||||
Privileged: member("op", c.permissions),
|
||||
Time: tm,
|
||||
Kind: m.Kind,
|
||||
NoEcho: m.NoEcho,
|
||||
|
@ -1554,7 +1580,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
log.Printf("broadcast(clearchat): %v", err)
|
||||
}
|
||||
case "lock", "unlock":
|
||||
if !c.permissions.Op {
|
||||
if !member("op", c.permissions) {
|
||||
return c.error(group.UserError("not authorised"))
|
||||
}
|
||||
message := ""
|
||||
|
@ -1564,7 +1590,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
}
|
||||
g.SetLocked(m.Kind == "lock", message)
|
||||
case "record":
|
||||
if !c.permissions.Record {
|
||||
if !member("record", c.permissions) {
|
||||
return c.error(group.UserError("not authorised"))
|
||||
}
|
||||
for _, cc := range g.GetClients(c) {
|
||||
|
@ -1585,7 +1611,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
}
|
||||
requestConns(disk, c.group, "")
|
||||
case "unrecord":
|
||||
if !c.permissions.Record {
|
||||
if !member("record", c.permissions) {
|
||||
return c.error(group.UserError("not authorised"))
|
||||
}
|
||||
for _, cc := range g.GetClients(c) {
|
||||
|
@ -1596,7 +1622,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
}
|
||||
}
|
||||
case "subgroups":
|
||||
if !c.permissions.Op {
|
||||
if !member("op", c.permissions) {
|
||||
return c.error(group.UserError("not authorised"))
|
||||
}
|
||||
s := ""
|
||||
|
@ -1616,7 +1642,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
Value: s,
|
||||
})
|
||||
case "setdata":
|
||||
if !c.permissions.Op {
|
||||
if !member("op", c.permissions) {
|
||||
return c.error(group.UserError("not authorised"))
|
||||
}
|
||||
data, ok := m.Value.(map[string]interface{})
|
||||
|
@ -1636,7 +1662,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
}
|
||||
switch m.Kind {
|
||||
case "op", "unop", "present", "unpresent":
|
||||
if !c.permissions.Op {
|
||||
if !member("op", c.permissions) {
|
||||
return c.error(group.UserError("not authorised"))
|
||||
}
|
||||
err := setPermissions(g, m.Dest, m.Kind)
|
||||
|
@ -1644,7 +1670,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||
return c.error(err)
|
||||
}
|
||||
case "kick":
|
||||
if !c.permissions.Op {
|
||||
if !member("op", c.permissions) {
|
||||
return c.error(group.UserError("not authorised"))
|
||||
}
|
||||
message := ""
|
||||
|
@ -1769,7 +1795,7 @@ func clientWriter(conn *websocket.Conn, ch <-chan interface{}, done chan<- struc
|
|||
}
|
||||
|
||||
func (c *webClient) Warn(oponly bool, message string) error {
|
||||
if oponly && !c.permissions.Op {
|
||||
if oponly && !member("op", c.permissions) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -402,6 +402,7 @@ function setVisibility(id, visible) {
|
|||
function setButtonsVisibility() {
|
||||
let connected = serverConnection && serverConnection.socket;
|
||||
let permissions = serverConnection.permissions;
|
||||
let present = permissions.indexOf('present') >= 0;
|
||||
let local = !!findUpMedia('camera');
|
||||
let canWebrtc = !(typeof RTCPeerConnection === 'undefined');
|
||||
let canFile =
|
||||
|
@ -413,19 +414,19 @@ function setButtonsVisibility() {
|
|||
let mobilelayout = isMobileLayout();
|
||||
|
||||
// don't allow multiple presentations
|
||||
setVisibility('presentbutton', canWebrtc && permissions.present && !local);
|
||||
setVisibility('presentbutton', canWebrtc && present && !local);
|
||||
setVisibility('unpresentbutton', local);
|
||||
|
||||
setVisibility('mutebutton', !connected || permissions.present);
|
||||
setVisibility('mutebutton', !connected || present);
|
||||
|
||||
// allow multiple shared documents
|
||||
setVisibility('sharebutton', canWebrtc && permissions.present &&
|
||||
setVisibility('sharebutton', canWebrtc && present &&
|
||||
('getDisplayMedia' in navigator.mediaDevices));
|
||||
|
||||
setVisibility('mediaoptions', permissions.present);
|
||||
setVisibility('sendform', permissions.present);
|
||||
setVisibility('simulcastform', permissions.present);
|
||||
setVisibility('fileform', canFile && permissions.present);
|
||||
setVisibility('mediaoptions', present);
|
||||
setVisibility('sendform', present);
|
||||
setVisibility('simulcastform', present);
|
||||
setVisibility('fileform', canFile && present);
|
||||
|
||||
setVisibility('collapse-video', mediacount && mobilelayout);
|
||||
}
|
||||
|
@ -2012,9 +2013,9 @@ function userMenu(elt) {
|
|||
items.push({label: 'Send file', onClick: () => {
|
||||
sendFile(id);
|
||||
}});
|
||||
if(serverConnection.permissions.op) {
|
||||
if(serverConnection.permissions.indexOf('op') >= 0) {
|
||||
items.push({type: 'seperator'}); // sic
|
||||
if(user.permissions.present)
|
||||
if(user.permissions.indexOf('present') >= 0)
|
||||
items.push({label: 'Forbid presenting', onClick: () => {
|
||||
serverConnection.userAction('unpresent', id);
|
||||
}});
|
||||
|
@ -2139,12 +2140,14 @@ function gotUser(id, kind) {
|
|||
|
||||
function displayUsername() {
|
||||
document.getElementById('userspan').textContent = username;
|
||||
let op = serverConnection.permissions.indexOf('op') >= 0;
|
||||
let present = serverConnection.permissions.indexOf('present') >= 0;
|
||||
let text = '';
|
||||
if(serverConnection.permissions.op && serverConnection.permissions.present)
|
||||
if(op && present)
|
||||
text = '(op, presenter)';
|
||||
else if(serverConnection.permissions.op)
|
||||
else if(op)
|
||||
text = 'operator';
|
||||
else if(serverConnection.permissions.present)
|
||||
else if(present)
|
||||
text = 'presenter';
|
||||
document.getElementById('permspan').textContent = text;
|
||||
}
|
||||
|
@ -2178,7 +2181,7 @@ function setTitle(title) {
|
|||
/**
|
||||
* @this {ServerConnection}
|
||||
* @param {string} group
|
||||
* @param {Object<string,boolean>} perms
|
||||
* @param {Array<string>} perms
|
||||
* @param {Object<string,any>} status
|
||||
* @param {Object<string,any>} data
|
||||
* @param {string} message
|
||||
|
@ -2226,7 +2229,8 @@ async function gotJoined(kind, group, perms, status, data, message) {
|
|||
else
|
||||
this.request(mapRequest(getSettings().request));
|
||||
|
||||
if(serverConnection.permissions.present && !findUpMedia('camera')) {
|
||||
if(serverConnection.permissions.indexOf('present') >= 0 &&
|
||||
!findUpMedia('camera')) {
|
||||
if(present) {
|
||||
if(present === 'mike')
|
||||
updateSettings({video: ''});
|
||||
|
@ -3050,14 +3054,14 @@ let commands = {};
|
|||
|
||||
function operatorPredicate() {
|
||||
if(serverConnection && serverConnection.permissions &&
|
||||
serverConnection.permissions.op)
|
||||
serverConnection.permissions.indexOf('op') >= 0)
|
||||
return null;
|
||||
return 'You are not an operator';
|
||||
}
|
||||
|
||||
function recordingPredicate() {
|
||||
if(serverConnection && serverConnection.permissions &&
|
||||
serverConnection.permissions.record)
|
||||
serverConnection.permissions.indexOf('record') >= 0)
|
||||
return null;
|
||||
return 'You are not allowed to record';
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ function newLocalId() {
|
|||
/**
|
||||
* @typedef {Object} user
|
||||
* @property {string} username
|
||||
* @property {Object<string,boolean>} permissions
|
||||
* @property {Array<string>} permissions
|
||||
* @property {Object<string,any>} data
|
||||
* @property {Object<string,Object<string,boolean>>} down
|
||||
*/
|
||||
|
@ -126,9 +126,9 @@ function ServerConnection() {
|
|||
/**
|
||||
* The permissions granted to this connection.
|
||||
*
|
||||
* @type {Object<string,boolean>}
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
this.permissions = {};
|
||||
this.permissions = [];
|
||||
/**
|
||||
* userdata is a convenient place to attach data to a ServerConnection.
|
||||
* It is not used by the library.
|
||||
|
@ -164,7 +164,7 @@ function ServerConnection() {
|
|||
*
|
||||
* kind is one of 'join', 'fail', 'change' or 'leave'.
|
||||
*
|
||||
* @type{(this: ServerConnection, kind: string, group: string, permissions: Object<string,boolean>, status: Object<string,any>, data: Object<string,any>, message: string) => void}
|
||||
* @type{(this: ServerConnection, kind: string, group: string, permissions: Array<string>, status: Object<string,any>, data: Object<string,any>, message: string) => void}
|
||||
*/
|
||||
this.onjoined = null;
|
||||
/**
|
||||
|
@ -207,7 +207,7 @@ function ServerConnection() {
|
|||
* @property {string} [password]
|
||||
* @property {string} [token]
|
||||
* @property {boolean} [privileged]
|
||||
* @property {Object<string,boolean>} [permissions]
|
||||
* @property {Array<string>} [permissions]
|
||||
* @property {Object<string,any>} [status]
|
||||
* @property {Object<string,any>} [data]
|
||||
* @property {string} [group]
|
||||
|
@ -271,7 +271,7 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
resolve(sc);
|
||||
};
|
||||
this.socket.onclose = function(e) {
|
||||
sc.permissions = {};
|
||||
sc.permissions = [];
|
||||
for(let id in sc.up) {
|
||||
let c = sc.up[id];
|
||||
c.close();
|
||||
|
@ -286,7 +286,7 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
sc.onuser.call(sc, id, 'delete');
|
||||
}
|
||||
if(sc.group && sc.onjoined)
|
||||
sc.onjoined.call(sc, 'leave', sc.group, {}, {}, {}, '');
|
||||
sc.onjoined.call(sc, 'leave', sc.group, [], {}, {}, '');
|
||||
sc.group = null;
|
||||
sc.username = null;
|
||||
if(sc.onclose)
|
||||
|
@ -337,7 +337,7 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
}
|
||||
if(sc.onjoined)
|
||||
sc.onjoined.call(sc, m.kind, m.group,
|
||||
m.permissions || {},
|
||||
m.permissions || [],
|
||||
m.status, m.data,
|
||||
m.value || null);
|
||||
break;
|
||||
|
@ -348,7 +348,7 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
console.warn(`Duplicate user ${m.id} ${m.username}`);
|
||||
sc.users[m.id] = {
|
||||
username: m.username,
|
||||
permissions: m.permissions || {},
|
||||
permissions: m.permissions || [],
|
||||
data: m.data || {},
|
||||
down: {},
|
||||
};
|
||||
|
@ -358,13 +358,13 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
console.warn(`Unknown user ${m.id} ${m.username}`);
|
||||
sc.users[m.id] = {
|
||||
username: m.username,
|
||||
permissions: m.permissions || {},
|
||||
permissions: m.permissions || [],
|
||||
data: m.data || {},
|
||||
down: {},
|
||||
};
|
||||
} else {
|
||||
sc.users[m.id].username = m.username;
|
||||
sc.users[m.id].permissions = m.permissions || {};
|
||||
sc.users[m.id].permissions = m.permissions || [];
|
||||
sc.users[m.id].data = m.data || {};
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -105,7 +105,7 @@ func getKey(header map[string]interface{}, keys []map[string]interface{}) (inter
|
|||
return nil, errors.New("key not found")
|
||||
}
|
||||
|
||||
func Valid(username, token string, keys []map[string]interface{}) ([]string, map[string]interface{}, error) {
|
||||
func Valid(username, token string, keys []map[string]interface{}) ([]string, []string, error) {
|
||||
tok, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||
return getKey(t.Header, keys)
|
||||
})
|
||||
|
@ -118,19 +118,33 @@ func Valid(username, token string, keys []map[string]interface{}) ([]string, map
|
|||
if !ok || sub != username {
|
||||
return nil, nil, ErrUnexpectedSub
|
||||
}
|
||||
aud, ok := claims["aud"]
|
||||
var res []string
|
||||
if ok {
|
||||
switch aud := aud.(type) {
|
||||
|
||||
var aud []string
|
||||
if a, ok := claims["aud"]; ok && a != nil {
|
||||
switch a := a.(type) {
|
||||
case string:
|
||||
res = []string{aud}
|
||||
aud = []string{a}
|
||||
case []string:
|
||||
res = aud
|
||||
aud = a
|
||||
}
|
||||
}
|
||||
perms, ok := claims["permissions"].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, errors.New("invalid 'permissions' field")
|
||||
|
||||
var perms []string
|
||||
if p, ok := claims["permissions"]; ok && p != nil {
|
||||
pp, ok := p.([]interface{})
|
||||
if !ok {
|
||||
return nil, nil,
|
||||
errors.New("invalid 'permissions' field")
|
||||
}
|
||||
perms = make([]string, len(pp))
|
||||
for i, v := range pp {
|
||||
w, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, nil,
|
||||
errors.New("invalid 'permissions' field")
|
||||
}
|
||||
perms[i] = w
|
||||
}
|
||||
}
|
||||
return res, perms, nil
|
||||
return aud, perms, nil
|
||||
}
|
||||
|
|
|
@ -58,13 +58,7 @@ func TestES256(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestValid(t *testing.T) {
|
||||
key := `{
|
||||
"kty":"EC",
|
||||
"alg":"ES256",
|
||||
"crv":"P-256",
|
||||
"x":"CBo2DHISffe8bVr6bNspCiHK3zK9pfMGfWtpHnk9-Lw",
|
||||
"y":"sD5dQ-bJu8AfRGLfA6MigQyUIOQHcYx6HQOdfIbLjHo"
|
||||
}`
|
||||
key := `{"alg":"HS256","k":"H7pCkktUl5KyPCZ7CKw09y1j460tfIv4dRcS1XstUKY","key_ops":["sign","verify"],"kty":"oct"}`
|
||||
var k map[string]interface{}
|
||||
err := json.Unmarshal([]byte(key), &k)
|
||||
if err != nil {
|
||||
|
@ -73,7 +67,7 @@ func TestValid(t *testing.T) {
|
|||
|
||||
keys := []map[string]interface{}{k}
|
||||
|
||||
goodToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJwZXJtaXNzaW9ucyI6eyJwcmVzZW50Ijp0cnVlfSwiaWF0IjoxNjQ1MTk1MzkxLCJleHAiOjIyNzU5MTUzOTEsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MTIzNC8ifQ.PMgfwYwSLSFIfcNJdOEfHEZ41HM2CzbATuS1fTxncbaGyX-xXq7d9V04enXpLOMGnAlsZpOJvd7eJN2mngJMAg"
|
||||
goodToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJwZXJtaXNzaW9ucyI6WyJwcmVzZW50Il0sImlhdCI6MTY0NTMxMDI5NCwiZXhwIjoyOTA2NzUwMjk0LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjEyMzQvIn0.6xXpgBkBMn4PSBpnwYHb-gRn_Q97Yq9DoKkAf2_6iwc"
|
||||
|
||||
aud, perms, err := Valid("john", goodToken, keys)
|
||||
|
||||
|
@ -83,9 +77,7 @@ func TestValid(t *testing.T) {
|
|||
if !reflect.DeepEqual(aud, []string{"https://galene.org:8443/group/auth/"}) {
|
||||
t.Errorf("Unexpected aud: %v", aud)
|
||||
}
|
||||
if !reflect.DeepEqual(
|
||||
perms, map[string]interface{}{"present": true},
|
||||
) {
|
||||
if !reflect.DeepEqual(perms, []string{"present"}) {
|
||||
t.Errorf("Unexpected perms: %v", perms)
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +87,7 @@ func TestValid(t *testing.T) {
|
|||
t.Errorf("Token should have bad username")
|
||||
}
|
||||
|
||||
badToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJwZXJtaXNzaW9ucyI6eyJwcmVzZW50Ijp0cnVlfSwiaWF0IjoxNjQ1MTk2MDE5LCJleHAiOjIyNjAzNjQwMTksImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MTIzNC8ifQ.4TN5zxzuKeNIw0rX0yirEkVYF1d0FHI_Lezmsa27ayi0R4ocSgTZ3q2bmlACXvyuoBqEEbuP4e77BUbGCHmpSg"
|
||||
badToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJwZXJtaXNzaW9ucyI6WyJwcmVzZW50Il0sImlhdCI6MTY0NTMxMDQ2OSwiZXhwIjoyOTA2NzUwNDY5LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjEyMzQvIn0."
|
||||
|
||||
_, _, err = Valid("john", badToken, keys)
|
||||
|
||||
|
@ -104,7 +96,7 @@ func TestValid(t *testing.T) {
|
|||
t.Errorf("Token should fail")
|
||||
}
|
||||
|
||||
expiredToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJwZXJtaXNzaW9ucyI6eyJwcmVzZW50Ijp0cnVlfSwiaWF0IjoxNjQ1MTk1NTY3LCJleHAiOjE2NDUxOTU1OTcsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MTIzNC8ifQ.GXcLeyNVr5cnZjIECENyjMLH1HyNKWKkHMc9onvqA_RVYMyDLeeR_3NKH9Y7eKSXWC8jhatDWtH7Ed3KdsSxAA"
|
||||
expiredToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJwZXJtaXNzaW9ucyI6WyJwcmVzZW50Il0sImlhdCI6MTY0NTMxMDMyMiwiZXhwIjoxNjQ1MzEwMzUyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjEyMzQvIn0.jyqRhoV6iK54SvlP33Fy630aDo-sLNmKKi1kcfqs378"
|
||||
|
||||
_, _, err = Valid("john", expiredToken, keys)
|
||||
|
||||
|
@ -112,8 +104,7 @@ func TestValid(t *testing.T) {
|
|||
t.Errorf("Token should be expired")
|
||||
}
|
||||
|
||||
noneToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJwZXJtaXNzaW9ucyI6eyJwcmVzZW50Ijp0cnVlfSwiaWF0IjoxNjQ1MTk1NzgyLCJleHAiOjIyNjAzNjM3ODIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MTIzNC8ifQ."
|
||||
|
||||
noneToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJwZXJtaXNzaW9ucyI6WyJwcmVzZW50Il0sImlhdCI6MTY0NTMxMDQwMSwiZXhwIjoxNjQ1MzEwNDMxLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjEyMzQvIn0."
|
||||
_, _, err = Valid("john", noneToken, keys)
|
||||
if err == nil {
|
||||
t.Errorf("Unsigned token should fail")
|
||||
|
|
|
@ -592,7 +592,16 @@ func checkGroupPermissions(w http.ResponseWriter, r *http.Request, groupname str
|
|||
Password: pass,
|
||||
},
|
||||
)
|
||||
if err != nil || !p.Record {
|
||||
record := false
|
||||
if err == nil {
|
||||
for _, v := range p {
|
||||
if v == "record" {
|
||||
record = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil || !record {
|
||||
if err == group.ErrNotAuthorised {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue