diff --git a/README.PROTOCOL b/README.PROTOCOL index 40da45c..64d1853 100644 --- a/README.PROTOCOL +++ b/README.PROTOCOL @@ -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 diff --git a/diskwriter/diskwriter.go b/diskwriter/diskwriter.go index c585f11..713c80b 100644 --- a/diskwriter/diskwriter.go +++ b/diskwriter/diskwriter.go @@ -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 } diff --git a/group/client.go b/group/client.go index 78330f5..ab5e19c 100644 --- a/group/client.go +++ b/group/client.go @@ -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 } diff --git a/group/group.go b/group/group.go index 4c3c8fa..f46eb73 100644 --- a/group/group.go +++ b/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 { diff --git a/group/group_test.go b/group/group_test.go index 4bba819..f0354a9 100644 --- a/group/group_test.go +++ b/group/group_test.go @@ -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, }, } diff --git a/rtpconn/webclient.go b/rtpconn/webclient.go index 7b70014..1916a21 100644 --- a/rtpconn/webclient.go +++ b/rtpconn/webclient.go @@ -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 } diff --git a/static/galene.js b/static/galene.js index 2a25b32..0ae5e80 100644 --- a/static/galene.js +++ b/static/galene.js @@ -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} perms + * @param {Array} perms * @param {Object} status * @param {Object} 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'; } diff --git a/static/protocol.js b/static/protocol.js index 5ab9459..7c07f91 100644 --- a/static/protocol.js +++ b/static/protocol.js @@ -63,7 +63,7 @@ function newLocalId() { /** * @typedef {Object} user * @property {string} username - * @property {Object} permissions + * @property {Array} permissions * @property {Object} data * @property {Object>} down */ @@ -126,9 +126,9 @@ function ServerConnection() { /** * The permissions granted to this connection. * - * @type {Object} + * @type {Array} */ - 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, status: Object, data: Object, message: string) => void} + * @type{(this: ServerConnection, kind: string, group: string, permissions: Array, status: Object, data: Object, message: string) => void} */ this.onjoined = null; /** @@ -207,7 +207,7 @@ function ServerConnection() { * @property {string} [password] * @property {string} [token] * @property {boolean} [privileged] - * @property {Object} [permissions] + * @property {Array} [permissions] * @property {Object} [status] * @property {Object} [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; diff --git a/token/token.go b/token/token.go index 5d7455a..c3cd331 100644 --- a/token/token.go +++ b/token/token.go @@ -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 } diff --git a/token/token_test.go b/token/token_test.go index 360f822..358f2a7 100644 --- a/token/token_test.go +++ b/token/token_test.go @@ -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") diff --git a/webserver/webserver.go b/webserver/webserver.go index f3afa3c..a3fce22 100644 --- a/webserver/webserver.go +++ b/webserver/webserver.go @@ -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) }