Skip to content

Commit

Permalink
Merge branch 'master' into backend-error
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreF committed Feb 13, 2021
2 parents 4be4a08 + 46f79fc commit 250485f
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 23 deletions.
76 changes: 55 additions & 21 deletions backends/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
"bufio"
"fmt"
"os"
"os/signal"
"strings"
"sync"
"syscall"

"github.com/iegomez/mosquitto-go-auth/hashing"
"github.com/pkg/errors"
Expand All @@ -25,28 +28,31 @@ 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
Users map[string]*FileUser //Users keeps a registry of username/FileUser pairs, holding a user's password and Acl records.
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,
Users: make(map[string]*FileUser),
AclRecords: make([]AclRecord, 0),
filesOnly: true,
hasher: hasher,
signals: make(chan os.Signal, 1),
}

if len(strings.Split(strings.Replace(authOpts["backends"], " ", "", -1), ",")) > 1 {
Expand All @@ -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 {
Expand All @@ -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
}

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 files.CheckAcls {
aclCount, err := files.readAcls()
if o.CheckAcls {
count, err := o.readAcls()
if err != nil {
return files, errors.Errorf("read acls: %s", err)
} else {
log.Infof("got %d lines from acl file", aclCount)
return errors.Errorf("read acls: %s", err)
}
}

return files, nil
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

Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
70 changes: 69 additions & 1 deletion backends/files_test.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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")
Expand Down Expand Up @@ -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/#")
})
}
2 changes: 1 addition & 1 deletion go-auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 250485f

Please sign in to comment.