mirror of
https://github.com/jech/galene.git
synced 2024-11-25 01:55:57 +01:00
Implement hashed passwords.
This commit is contained in:
parent
c178c28b60
commit
4c0fd01258
10 changed files with 313 additions and 45 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
||||||
*~
|
*~
|
||||||
data/*.pem
|
data/*.pem
|
||||||
sfu
|
sfu
|
||||||
|
sfu-password-generator/sfu-password-generator
|
||||||
passwd
|
passwd
|
||||||
groups/*.json
|
groups/*.json
|
||||||
static/*.d.ts
|
static/*.d.ts
|
||||||
|
|
36
README
36
README
|
@ -55,10 +55,10 @@ options are described below.
|
||||||
vi groups/public.json
|
vi groups/public.json
|
||||||
|
|
||||||
{
|
{
|
||||||
"public":true,
|
"public": true,
|
||||||
"op":[{"username":"jch","password":"1234"}],
|
"op":i [{"username":"jch","password":"1234"}],
|
||||||
"presenter":[{}],
|
"presenter": [{}],
|
||||||
"max-users":100
|
"max-users": 100
|
||||||
}
|
}
|
||||||
|
|
||||||
## Copy the necessary files to your server:
|
## Copy the necessary files to your server:
|
||||||
|
@ -128,23 +128,31 @@ A user definition is a dictionary with the following fields:
|
||||||
|
|
||||||
- `username`: the username of the user; if omitted, any username is
|
- `username`: the username of the user; if omitted, any username is
|
||||||
allowed;
|
allowed;
|
||||||
- `password`: the password of the user; if omitted, then any password
|
- `password`: if omitted, then no password is required. Otherwise, this
|
||||||
(including the empty paassword) is allowed.
|
can either be a string, specifying a plain text password, or
|
||||||
|
a dictionary generated by the `sfu-password-generator` utility.
|
||||||
|
|
||||||
For example
|
For example,
|
||||||
|
|
||||||
{"username":"jch", "password":"topsecret"}
|
{"username": "jch", "password": "topsecret"}
|
||||||
|
|
||||||
specifies user *jch* with password *topsecret*, while
|
specifies user *jch* with password *topsecret*, while
|
||||||
|
|
||||||
{"password":"topsecret"}
|
{"password": "topsecret"}
|
||||||
|
|
||||||
specifies that any username will do. The empty dictionary
|
specifies that any username will do. An entry with a hashed password
|
||||||
|
looks like this:
|
||||||
{}
|
|
||||||
|
|
||||||
specifies that any username will do and that passwords are not verified.
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"username": "jch",
|
||||||
|
"password": {
|
||||||
|
"type": "pbkdf2",
|
||||||
|
"hash": "sha-256",
|
||||||
|
"key": "f591c35604e6aef572851d9c3543c812566b032b6dc083c81edd15cc24449913",
|
||||||
|
"salt": "92bff2ace56fe38f",
|
||||||
|
"iterations": 4096
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Commands
|
# Commands
|
||||||
|
|
||||||
|
|
|
@ -49,8 +49,12 @@ func (client *Client) Id() string {
|
||||||
return client.id
|
return client.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Credentials() group.ClientCredentials {
|
func (client *Client) Username() string {
|
||||||
return group.ClientCredentials{"RECORDING", ""}
|
return "RECORDING"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) Challenge(group string, cred group.ClientCredentials) bool {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) OverridePermissions(g *group.Group) bool {
|
func (client *Client) OverridePermissions(g *group.Group) bool {
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -9,4 +9,5 @@ require (
|
||||||
github.com/pion/rtcp v1.2.4
|
github.com/pion/rtcp v1.2.4
|
||||||
github.com/pion/rtp v1.6.1
|
github.com/pion/rtp v1.6.1
|
||||||
github.com/pion/webrtc/v3 v3.0.0-beta.7
|
github.com/pion/webrtc/v3 v3.0.0-beta.7
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,12 +1,83 @@
|
||||||
package group
|
package group
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
"sfu/conn"
|
"sfu/conn"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type RawPassword struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Hash string `json:"hash,omitempty"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Salt string `json:"salt,omitempty"`
|
||||||
|
Iterations int `json:"iterations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Password RawPassword
|
||||||
|
|
||||||
|
func (p Password) Match(pw string) (bool, error) {
|
||||||
|
switch p.Type {
|
||||||
|
case "":
|
||||||
|
return p.Key == pw, nil
|
||||||
|
case "pbkdf2":
|
||||||
|
key, err := hex.DecodeString(p.Key)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
salt, err := hex.DecodeString(p.Salt)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
var h func() hash.Hash
|
||||||
|
switch p.Hash {
|
||||||
|
case "sha-256":
|
||||||
|
h = sha256.New
|
||||||
|
default:
|
||||||
|
return false, errors.New("unknown hash type")
|
||||||
|
}
|
||||||
|
theirKey := pbkdf2.Key(
|
||||||
|
[]byte(pw), salt, p.Iterations, len(key), h,
|
||||||
|
)
|
||||||
|
return bytes.Compare(key, theirKey) == 0, nil
|
||||||
|
default:
|
||||||
|
return false, errors.New("unknown password type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Password) UnmarshalJSON(b []byte) error {
|
||||||
|
var k string
|
||||||
|
err := json.Unmarshal(b, &k)
|
||||||
|
if err == nil {
|
||||||
|
*p = Password{
|
||||||
|
Key: k,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var r RawPassword
|
||||||
|
err = json.Unmarshal(b, &r)
|
||||||
|
if err == nil {
|
||||||
|
*p = Password(r)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Password) MarshalJSON() ([]byte, error) {
|
||||||
|
if p.Type == "" && p.Hash == "" && p.Salt == "" && p.Iterations == 0 {
|
||||||
|
return json.Marshal(p.Key)
|
||||||
|
}
|
||||||
|
return json.Marshal(RawPassword(p))
|
||||||
|
}
|
||||||
|
|
||||||
type ClientCredentials struct {
|
type ClientCredentials struct {
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password *Password `json:"password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientPermissions struct {
|
type ClientPermissions struct {
|
||||||
|
@ -15,10 +86,15 @@ type ClientPermissions struct {
|
||||||
Record bool `json:"record,omitempty"`
|
Record bool `json:"record,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Challengeable interface {
|
||||||
|
Username() string
|
||||||
|
Challenge(string, ClientCredentials) bool
|
||||||
|
}
|
||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
Group() *Group
|
Group() *Group
|
||||||
Id() string
|
Id() string
|
||||||
Credentials() ClientCredentials
|
Challengeable
|
||||||
SetPermissions(ClientPermissions)
|
SetPermissions(ClientPermissions)
|
||||||
OverridePermissions(*Group) bool
|
OverridePermissions(*Group) bool
|
||||||
PushConn(id string, conn conn.Up, tracks []conn.UpTrack, label string) error
|
PushConn(id string, conn conn.Up, tracks []conn.UpTrack, label string) error
|
||||||
|
|
87
group/client_test.go
Normal file
87
group/client_test.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package group
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pw1 = Password{}
|
||||||
|
var pw2 = Password{Key: "pass"}
|
||||||
|
var pw3 = Password{
|
||||||
|
Type: "pbkdf2",
|
||||||
|
Hash: "sha-256",
|
||||||
|
Key: "fe499504e8f144693fae828e8e371d50e019d0e4c84994fa03f7f445bd8a570a",
|
||||||
|
Salt: "bcc1717851030776",
|
||||||
|
Iterations: 4096,
|
||||||
|
}
|
||||||
|
var pw4 = Password{
|
||||||
|
Type: "bad",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGood(t *testing.T) {
|
||||||
|
if match, err := pw2.Match("pass"); err != nil || !match {
|
||||||
|
t.Errorf("pw2 doesn't match (%v)", err)
|
||||||
|
}
|
||||||
|
if match, err := pw3.Match("pass"); err != nil || !match {
|
||||||
|
t.Errorf("pw3 doesn't match (%v)", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBad(t *testing.T) {
|
||||||
|
if match, err := pw1.Match("bad"); err != nil || match {
|
||||||
|
t.Errorf("pw1 matches")
|
||||||
|
}
|
||||||
|
if match, err := pw2.Match("bad"); err != nil || match {
|
||||||
|
t.Errorf("pw2 matches")
|
||||||
|
}
|
||||||
|
if match, err := pw3.Match("bad"); err != nil || match {
|
||||||
|
t.Errorf("pw3 matches")
|
||||||
|
}
|
||||||
|
if match, err := pw4.Match("bad"); err == nil || match {
|
||||||
|
t.Errorf("pw4 matches")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSON(t *testing.T) {
|
||||||
|
plain, err := json.Marshal(pw2)
|
||||||
|
if err != nil || string(plain) != `"pass"` {
|
||||||
|
t.Errorf("Expected \"pass\", got %v", string(plain))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pw := range []Password{pw1, pw2, pw3, pw4} {
|
||||||
|
j, err := json.Marshal(pw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal: %v", err)
|
||||||
|
}
|
||||||
|
if testing.Verbose() {
|
||||||
|
log.Printf("%v", string(j))
|
||||||
|
}
|
||||||
|
var pw2 Password
|
||||||
|
err = json.Unmarshal(j, &pw2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
} else if !reflect.DeepEqual(pw, pw2) {
|
||||||
|
t.Errorf("Expected %v, got %v", pw, pw2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPlain(b *testing.B) {
|
||||||
|
for i :=0; i < b.N; i++ {
|
||||||
|
match, err := pw2.Match("bad")
|
||||||
|
if err != nil || match {
|
||||||
|
b.Errorf("pw2 matched")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPBKDF2(b *testing.B) {
|
||||||
|
for i :=0; i < b.N; i++ {
|
||||||
|
match, err := pw3.Match("bad")
|
||||||
|
if err != nil || match {
|
||||||
|
b.Errorf("pw3 matched")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -263,8 +263,8 @@ func delGroupUnlocked(name string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddClient(name string, c Client) (*Group, error) {
|
func AddClient(group string, c Client) (*Group, error) {
|
||||||
g, err := Add(name, nil)
|
g, err := Add(group, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ func AddClient(name string, c Client) (*Group, error) {
|
||||||
defer g.mu.Unlock()
|
defer g.mu.Unlock()
|
||||||
|
|
||||||
if(!c.OverridePermissions(g)) {
|
if(!c.OverridePermissions(g)) {
|
||||||
perms, err := g.description.GetPermission(c.Credentials())
|
perms, err := g.description.GetPermission(group, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -302,10 +302,10 @@ func AddClient(name string, c Client) (*Group, error) {
|
||||||
g.clients[c.Id()] = c
|
g.clients[c.Id()] = c
|
||||||
|
|
||||||
go func(clients []Client) {
|
go func(clients []Client) {
|
||||||
u := c.Credentials().Username
|
u := c.Username()
|
||||||
c.PushClient(c.Id(), u, true)
|
c.PushClient(c.Id(), u, true)
|
||||||
for _, cc := range clients {
|
for _, cc := range clients {
|
||||||
uu := cc.Credentials().Username
|
uu := cc.Username()
|
||||||
c.PushClient(cc.Id(), uu, true)
|
c.PushClient(cc.Id(), uu, true)
|
||||||
cc.PushClient(c.Id(), u, true)
|
cc.PushClient(c.Id(), u, true)
|
||||||
}
|
}
|
||||||
|
@ -330,7 +330,7 @@ func DelClient(c Client) {
|
||||||
|
|
||||||
go func(clients []Client) {
|
go func(clients []Client) {
|
||||||
for _, cc := range clients {
|
for _, cc := range clients {
|
||||||
cc.PushClient(c.Id(), c.Credentials().Username, false)
|
cc.PushClient(c.Id(), c.Username(), false)
|
||||||
}
|
}
|
||||||
}(g.getClientsUnlocked(nil))
|
}(g.getClientsUnlocked(nil))
|
||||||
}
|
}
|
||||||
|
@ -453,15 +453,18 @@ func (g *Group) GetChatHistory() []ChatHistoryEntry {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchUser(user ClientCredentials, users []ClientCredentials) (bool, bool) {
|
func matchClient(group string, c Challengeable, users []ClientCredentials) (bool, bool) {
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
if u.Username == "" {
|
if u.Username == "" {
|
||||||
if u.Password == "" || u.Password == user.Password {
|
if c.Challenge(group, u) {
|
||||||
return true, true
|
return true, true
|
||||||
}
|
}
|
||||||
} else if u.Username == user.Username {
|
} else if u.Username == c.Username() {
|
||||||
return true,
|
if c.Challenge(group, u) {
|
||||||
(u.Password == "" || u.Password == user.Password)
|
return true, true
|
||||||
|
} else {
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, false
|
return false, false
|
||||||
|
@ -568,12 +571,12 @@ func GetDescription(name string) (*description, error) {
|
||||||
return &desc, nil
|
return &desc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (desc *description) GetPermission(creds ClientCredentials) (ClientPermissions, error) {
|
func (desc *description) GetPermission(group string, c Challengeable) (ClientPermissions, error) {
|
||||||
var p ClientPermissions
|
var p ClientPermissions
|
||||||
if !desc.AllowAnonymous && creds.Username == "" {
|
if !desc.AllowAnonymous && c.Username() == "" {
|
||||||
return p, UserError("anonymous users not allowed in this group, please choose a username")
|
return p, UserError("anonymous users not allowed in this group, please choose a username")
|
||||||
}
|
}
|
||||||
if found, good := matchUser(creds, desc.Op); found {
|
if found, good := matchClient(group, c, desc.Op); found {
|
||||||
if good {
|
if good {
|
||||||
p.Op = true
|
p.Op = true
|
||||||
p.Present = true
|
p.Present = true
|
||||||
|
@ -584,14 +587,14 @@ func (desc *description) GetPermission(creds ClientCredentials) (ClientPermissio
|
||||||
}
|
}
|
||||||
return p, UserError("not authorised")
|
return p, UserError("not authorised")
|
||||||
}
|
}
|
||||||
if found, good := matchUser(creds, desc.Presenter); found {
|
if found, good := matchClient(group, c, desc.Presenter); found {
|
||||||
if good {
|
if good {
|
||||||
p.Present = true
|
p.Present = true
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
return p, UserError("not authorised")
|
return p, UserError("not authorised")
|
||||||
}
|
}
|
||||||
if found, good := matchUser(creds, desc.Other); found {
|
if found, good := matchClient(group, c, desc.Other); found {
|
||||||
if good {
|
if good {
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,8 @@ func isWSNormalError(err error) bool {
|
||||||
type webClient struct {
|
type webClient struct {
|
||||||
group *group.Group
|
group *group.Group
|
||||||
id string
|
id string
|
||||||
credentials group.ClientCredentials
|
username string
|
||||||
|
password string
|
||||||
permissions group.ClientPermissions
|
permissions group.ClientPermissions
|
||||||
requested map[string]uint32
|
requested map[string]uint32
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
@ -65,8 +66,20 @@ func (c *webClient) Id() string {
|
||||||
return c.id
|
return c.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *webClient) Credentials() group.ClientCredentials {
|
func (c *webClient) Username() string {
|
||||||
return c.credentials
|
return c.username
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *webClient) Challenge(group string, creds group.ClientCredentials) bool {
|
||||||
|
if creds.Password == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
m, err := creds.Password.Match(c.password)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Password match: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *webClient) SetPermissions(perms group.ClientPermissions) {
|
func (c *webClient) SetPermissions(perms group.ClientPermissions) {
|
||||||
|
@ -452,7 +465,7 @@ func gotOffer(c *webClient, id string, offer webrtc.SessionDescription, renegoti
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if u := c.Credentials().Username; u != "" {
|
if u := c.Username(); u != "" {
|
||||||
up.label = u
|
up.label = u
|
||||||
}
|
}
|
||||||
err = up.pc.SetRemoteDescription(offer)
|
err = up.pc.SetRemoteDescription(offer)
|
||||||
|
@ -646,10 +659,8 @@ func StartClient(conn *websocket.Conn) (err error) {
|
||||||
|
|
||||||
c := &webClient{
|
c := &webClient{
|
||||||
id: m.Id,
|
id: m.Id,
|
||||||
credentials: group.ClientCredentials{
|
username: m.Username,
|
||||||
m.Username,
|
password: m.Password,
|
||||||
m.Password,
|
|
||||||
},
|
|
||||||
actionCh: make(chan interface{}, 10),
|
actionCh: make(chan interface{}, 10),
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
55
sfu-password-generator/sfu-password-generator.go
Normal file
55
sfu-password-generator/sfu-password-generator.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
|
||||||
|
"sfu/group"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var iterations int
|
||||||
|
var length int
|
||||||
|
var saltLen int
|
||||||
|
flag.IntVar(&iterations, "iterations", 4096, "number of iterations")
|
||||||
|
flag.IntVar(&length, "key length", 32, "key length")
|
||||||
|
flag.IntVar(&saltLen, "salt", 8, "salt length")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if len(flag.Args()) == 0 {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
salt := make([]byte, saltLen)
|
||||||
|
|
||||||
|
for _, pw := range flag.Args() {
|
||||||
|
_, err := rand.Read(salt)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Salt: %v", err)
|
||||||
|
}
|
||||||
|
key := pbkdf2.Key(
|
||||||
|
[]byte(pw), salt, iterations, length, sha256.New,
|
||||||
|
)
|
||||||
|
|
||||||
|
p := group.Password{
|
||||||
|
Type: "pbkdf2",
|
||||||
|
Hash: "sha-256",
|
||||||
|
Key: hex.EncodeToString(key),
|
||||||
|
Salt: hex.EncodeToString(salt),
|
||||||
|
Iterations: iterations,
|
||||||
|
}
|
||||||
|
e := json.NewEncoder(os.Stdout)
|
||||||
|
err = e.Encode(p)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Encode: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -526,6 +526,27 @@ func handleGroupAction(w http.ResponseWriter, r *http.Request, group string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type httpClient struct {
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c httpClient) Username() string {
|
||||||
|
return c.username
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c httpClient) Challenge(group string, creds group.ClientCredentials) bool {
|
||||||
|
if creds.Password == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
m, err := creds.Password.Match(c.password)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Password match: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
func checkGroupPermissions(w http.ResponseWriter, r *http.Request, groupname string) bool {
|
func checkGroupPermissions(w http.ResponseWriter, r *http.Request, groupname string) bool {
|
||||||
desc, err := group.GetDescription(groupname)
|
desc, err := group.GetDescription(groupname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -537,7 +558,8 @@ func checkGroupPermissions(w http.ResponseWriter, r *http.Request, groupname str
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := desc.GetPermission(group.ClientCredentials{user, pass})
|
p, err := desc.GetPermission(groupname, httpClient{user, pass})
|
||||||
|
|
||||||
if err != nil || !p.Record {
|
if err != nil || !p.Record {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue