Merge branch 'master' into backend-error
This commit is contained in:
commit
250485f2bd
|
@ -4,7 +4,10 @@ import (
|
|||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/iegomez/mosquitto-go-auth/hashing"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -25,6 +28,7 @@ type AclRecord struct {
|
|||
|
||||
//FileBE holds paths to files, list of file users and general (no user or pattern) acl records.
|
||||
type Files struct {
|
||||
sync.Mutex
|
||||
PasswordPath string
|
||||
AclPath string
|
||||
CheckAcls bool
|
||||
|
@ -32,14 +36,15 @@ type Files struct {
|
|||
AclRecords []AclRecord
|
||||
filesOnly bool
|
||||
hasher hashing.HashComparer
|
||||
signals chan os.Signal
|
||||
}
|
||||
|
||||
//NewFiles initializes a files backend.
|
||||
func NewFiles(authOpts map[string]string, logLevel log.Level, hasher hashing.HashComparer) (Files, error) {
|
||||
func NewFiles(authOpts map[string]string, logLevel log.Level, hasher hashing.HashComparer) (*Files, error) {
|
||||
|
||||
log.SetLevel(logLevel)
|
||||
|
||||
var files = Files{
|
||||
var files = &Files{
|
||||
PasswordPath: "",
|
||||
AclPath: "",
|
||||
CheckAcls: false,
|
||||
|
@ -47,6 +52,7 @@ func NewFiles(authOpts map[string]string, logLevel log.Level, hasher hashing.Has
|
|||
AclRecords: make([]AclRecord, 0),
|
||||
filesOnly: true,
|
||||
hasher: hasher,
|
||||
signals: make(chan os.Signal, 1),
|
||||
}
|
||||
|
||||
if len(strings.Split(strings.Replace(authOpts["backends"], " ", "", -1), ",")) > 1 {
|
||||
|
@ -56,7 +62,7 @@ func NewFiles(authOpts map[string]string, logLevel log.Level, hasher hashing.Has
|
|||
if passwordPath, ok := authOpts["password_path"]; ok {
|
||||
files.PasswordPath = passwordPath
|
||||
} else {
|
||||
return files, errors.New("Files backend error: no password path given")
|
||||
return nil, errors.New("Files backend error: no password path given")
|
||||
}
|
||||
|
||||
if aclPath, ok := authOpts["acl_path"]; ok {
|
||||
|
@ -67,30 +73,58 @@ func NewFiles(authOpts map[string]string, logLevel log.Level, hasher hashing.Has
|
|||
log.Info("Acls won't be checked")
|
||||
}
|
||||
|
||||
//Now initialize FileUsers by reading from password and acl files.
|
||||
uCount, err := files.readPasswords()
|
||||
err := files.loadFiles()
|
||||
if err != nil {
|
||||
return files, errors.Errorf("read passwords: %s", err)
|
||||
} else {
|
||||
log.Debugf("got %d users from passwords file", uCount)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//Only read acls if path was given.
|
||||
if files.CheckAcls {
|
||||
aclCount, err := files.readAcls()
|
||||
if err != nil {
|
||||
return files, errors.Errorf("read acls: %s", err)
|
||||
} else {
|
||||
log.Infof("got %d lines from acl file", aclCount)
|
||||
}
|
||||
}
|
||||
go files.watchSignals()
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (o *Files) watchSignals() {
|
||||
signal.Notify(o.signals, syscall.SIGHUP)
|
||||
|
||||
for {
|
||||
select {
|
||||
case sig := <-o.signals:
|
||||
if sig == syscall.SIGHUP {
|
||||
log.Debugln("Got SIGHUP, reloading files.")
|
||||
o.loadFiles()
|
||||
}
|
||||
default:
|
||||
// NO-OP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Files) loadFiles() error {
|
||||
o.Lock()
|
||||
defer o.Unlock()
|
||||
|
||||
count, err := o.readPasswords()
|
||||
if err != nil {
|
||||
return errors.Errorf("read passwords: %s", err)
|
||||
}
|
||||
|
||||
log.Debugf("got %d users from passwords file", count)
|
||||
|
||||
//Only read acls if path was given.
|
||||
if o.CheckAcls {
|
||||
count, err := o.readAcls()
|
||||
if err != nil {
|
||||
return errors.Errorf("read acls: %s", err)
|
||||
}
|
||||
|
||||
log.Debugf("got %d lines from acl file", count)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//ReadPasswords read file and populates FileUsers. Return amount of users seen and possile error.
|
||||
func (o Files) readPasswords() (int, error) {
|
||||
func (o *Files) readPasswords() (int, error) {
|
||||
|
||||
usersCount := 0
|
||||
|
||||
|
@ -285,7 +319,7 @@ func checkCommentOrEmpty(line string) bool {
|
|||
}
|
||||
|
||||
//GetUser checks that user exists and password is correct.
|
||||
func (o Files) GetUser(username, password, clientid string) (bool, error) {
|
||||
func (o *Files) GetUser(username, password, clientid string) (bool, error) {
|
||||
|
||||
fileUser, ok := o.Users[username]
|
||||
if !ok {
|
||||
|
@ -303,12 +337,12 @@ func (o Files) GetUser(username, password, clientid string) (bool, error) {
|
|||
}
|
||||
|
||||
//GetSuperuser returns false for files backend.
|
||||
func (o Files) GetSuperuser(username string) (bool, error) {
|
||||
func (o *Files) GetSuperuser(username string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
//CheckAcl checks that the topic may be read/written by the given user/clientid.
|
||||
func (o Files) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
|
||||
func (o *Files) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
|
||||
//If there are no acls and Files is the only backend, all access is allowed.
|
||||
//If there are other backends, then we can't blindly grant access.
|
||||
if !o.CheckAcls {
|
||||
|
@ -339,11 +373,11 @@ func (o Files) CheckAcl(username, topic, clientid string, acc int32) (bool, erro
|
|||
}
|
||||
|
||||
//GetName returns the backend's name
|
||||
func (o Files) GetName() string {
|
||||
func (o *Files) GetName() string {
|
||||
return "Files"
|
||||
}
|
||||
|
||||
//Halt does nothing for files as there's no cleanup needed.
|
||||
func (o Files) Halt() {
|
||||
func (o *Files) Halt() {
|
||||
//Do nothing
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package backends
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/iegomez/mosquitto-go-auth/hashing"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -14,8 +18,10 @@ func TestFiles(t *testing.T) {
|
|||
authOpts := make(map[string]string)
|
||||
|
||||
Convey("Given empty opts NewFiles should fail", t, func() {
|
||||
_, err := NewFiles(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, "files"))
|
||||
files, err := NewFiles(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, "files"))
|
||||
So(err, ShouldBeError)
|
||||
|
||||
files.Halt()
|
||||
})
|
||||
|
||||
pwPath, _ := filepath.Abs("../test-files/passwords")
|
||||
|
@ -173,4 +179,66 @@ func TestFiles(t *testing.T) {
|
|||
//Halt files
|
||||
files.Halt()
|
||||
})
|
||||
|
||||
Convey("On SIGHUP files should be reloaded", t, func() {
|
||||
pwFile, err := os.Create("../test-files/test-passwords")
|
||||
So(err, ShouldBeNil)
|
||||
aclFile, err := os.Create("../test-files/test-acls")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
defer os.Remove(pwFile.Name())
|
||||
defer os.Remove(aclFile.Name())
|
||||
|
||||
hasher := hashing.NewHasher(authOpts, "files")
|
||||
|
||||
user1 := "test1"
|
||||
user2 := "test2"
|
||||
|
||||
pw1, err := hasher.Hash(user1)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
pw2, err := hasher.Hash(user2)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
pwFile.WriteString(fmt.Sprintf("\n%s:%s\n", user1, pw1))
|
||||
|
||||
aclFile.WriteString("\nuser test1")
|
||||
aclFile.WriteString("\ntopic read test/#")
|
||||
|
||||
pwFile.Sync()
|
||||
aclFile.Sync()
|
||||
|
||||
authOpts["password_path"] = pwFile.Name()
|
||||
authOpts["acl_path"] = aclFile.Name()
|
||||
|
||||
files, err := NewFiles(authOpts, log.DebugLevel, hasher)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
user, ok := files.Users[user1]
|
||||
So(ok, ShouldBeTrue)
|
||||
|
||||
record := user.AclRecords[0]
|
||||
So(record.Acc, ShouldEqual, MOSQ_ACL_READ)
|
||||
So(record.Topic, ShouldEqual, "test/#")
|
||||
|
||||
_, ok = files.Users[user2]
|
||||
So(ok, ShouldBeFalse)
|
||||
|
||||
// Now add second user and reload.
|
||||
pwFile.WriteString(fmt.Sprintf("\n%s:%s\n", user2, pw2))
|
||||
|
||||
aclFile.WriteString("\nuser test2")
|
||||
aclFile.WriteString("\ntopic write test/#")
|
||||
|
||||
files.signals <- syscall.SIGHUP
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
user, ok = files.Users[user2]
|
||||
So(ok, ShouldBeTrue)
|
||||
|
||||
record = user.AclRecords[0]
|
||||
So(record.Acc, ShouldEqual, MOSQ_ACL_WRITE)
|
||||
So(record.Topic, ShouldEqual, "test/#")
|
||||
})
|
||||
}
|
||||
|
|
|
@ -301,7 +301,7 @@ func AuthPluginInit(keys []string, values []string, authOptsNum int) {
|
|||
log.Fatalf("Backend register error: couldn't initialize %s backend with error %s.", bename, err)
|
||||
} else {
|
||||
log.Infof("Backend registered: %s", beIface.GetName())
|
||||
cmBackends[filesBackend] = beIface.(bes.Files)
|
||||
cmBackends[filesBackend] = beIface.(*bes.Files)
|
||||
}
|
||||
case redisBackend:
|
||||
beIface, err = bes.NewRedis(authOpts, authPlugin.logLevel, hasher)
|
||||
|
|
Loading…
Reference in New Issue