Add backends tests and fix a couple of issues in Redis.

This commit is contained in:
Ignacio Gómez 2021-03-06 00:36:18 -03:00
parent ca22c6f9fa
commit 5cc687351e
No known key found for this signature in database
GPG Key ID: 15A77C6BEC604B06
9 changed files with 398 additions and 108 deletions

View File

@ -1,14 +1,11 @@
# Mosquitto Go Auth
Mosquitto Go Auth is an authentication and authorization plugin for the Mosquitto MQTT broker.
The name is terrible, I know, but it's too late to change it. And, you know: naming, cache invalidation, off-by-one errors and whatnot.
# Current state
I don't use Mosquitto or any other MQTT broker and haven't in a very long time, nor do I have a need for them or this plugin.
I do maintain it still and will try to keep doing so. This is the list of status, current work and priorities:
- The plugin is up to date and is compatible with the recent [2.0 Mosquitto version](https://mosquitto.org/blog/2020/12/version-2-0-0-released/).
- Delayed work on disabling superusers is not yet ready.
- Bug reports will be attended as they appear and will take priority over any work in progress.
- Reviewing ongoing PRs is my next priority.
- Feature requests are the lowest priority. Unless they are a super easy win in importance and implementation effort, I'll accept contributions and review
@ -16,7 +13,8 @@ I do maintain it still and will try to keep doing so. This is the list of status
### Intro
This is an authentication and authorization plugin for [mosquitto](https://mosquitto.org/), a well known open source MQTT broker. It's written (almost) entirely in Go: it uses `cgo` to expose mosquitto's auth plugin needed functions, but internally just calls Go to get everything done.
This is an authentication and authorization plugin for [mosquitto](https://mosquitto.org/), a well known open source MQTT broker.
It's written (almost) entirely in Go: it uses `cgo` to expose mosquitto's auth plugin needed functions, but internally just calls Go to get everything done.
It is greatly inspired in [jpmens'](https://github.com/jpmens) [mosquitto-auth-plug](https://github.com/jpmens/mosquitto-auth-plug).
@ -52,6 +50,7 @@ Please open an issue with the `feature` or `enhancement` tag to request new back
- [Log level](#log-level)
- [Prefixes](#prefixes)
- [Backend options](#backend-options)
- [Registering checks](#registering-checks)
- [Files](#files)
- [Passwords file](#passwords-file)
- [ACL file](#acl-file)
@ -341,7 +340,7 @@ auth_opt_pg_hasher_parallelism # degree of parallelism (i.e. number o
#### Logging
You can set the log level with the `log_level` option. Valid values are: debug, info, warn, error, fatal and panic. If not set, default value is `info`.
You can set the log level with the `log_level` option. Valid values are: `debug`, `info`, `warn`, `error`, `fatal` and `panic`. If not set, default value is `info`.
```
auth_opt_log_level debug
@ -356,6 +355,12 @@ auth_opt_log_file /var/log/mosquitto/mosquitto.log
If `log_dest` or `log_file` are invalid, or if there's an error opening the file (e.g. no permissions), logging will default to `stderr`.
**Do not, I repeat, do not set `log_level` to `debug` in production, it may leak sensitive information.**
**Reason? When debugging it's quite useful to log actual passwords, hashes, etc. to check which backend or hasher is failing to do its job.**
**This should be used only when debugging locally, I can't stress enough how log level should never, ever be set to `debug` in production.**
**You've been warned.**
#### Retry
By default, if backend had an error (and no other backend granted access), an error is returned to Mosquitto.
@ -445,7 +450,7 @@ make test
### Registering checks
Backends may register which checks they'll run, enabling the option to e.g. only check user auth through an HTTP backend while delegating ACL to another backend, e.g. Files.
Backends may register which checks they'll run, enabling the option to only check user auth through some backends, for example an HTTP one, while delegating ACL checks to another backend, e.g. Files.
By default, when the option is not present, all checks for that backend will be enabled (unless `superuser` is globally disabled in the case of `superuser` checks).
For `user` and `acl` checks, at least one backend needs to be registered, either explicitly or by default.
@ -456,7 +461,7 @@ auth_opt_files_register user, acl
auth_opt_redis_register superuser
```
Possible values for checks are `user`, `superuser` and `acl`. Any other value will result in an error initializing the plugin.
Possible values for checks are `user`, `superuser` and `acl`. Any other value will result in an error on plugin initialization.
### Files

View File

@ -31,7 +31,7 @@ type Backends struct {
}
const (
//backends
// backends
postgresBackend = "postgres"
jwtBackend = "jwt"
redisBackend = "redis"
@ -44,7 +44,7 @@ const (
grpcBackend = "grpc"
jsBackend = "js"
//checks
// checks
aclCheck = "acl"
userCheck = "user"
superuserCheck = "superuser"
@ -66,7 +66,7 @@ var AllowedBackendsOptsPrefix = map[string]string{
}
// Initialize sets general options, tries to build the backends and register their checkers.
func Initialize(authOpts map[string]string, logLevel log.Level, backends []string) *Backends {
func Initialize(authOpts map[string]string, logLevel log.Level, backends []string) (*Backends, error) {
b := &Backends{
backends: make(map[string]Backend),
@ -82,17 +82,26 @@ func Initialize(authOpts map[string]string, logLevel log.Level, backends []strin
b.disableSuperuser = true
}
err := b.addBackends(authOpts, logLevel, backends)
if err != nil {
return nil, err
}
err = b.setCheckers(authOpts)
if err != nil {
return nil, err
}
b.setPrefixes(authOpts, backends)
return b, nil
}
func (b *Backends) addBackends(authOpts map[string]string, logLevel log.Level, backends []string) error {
for _, bename := range backends {
var beIface Backend
var err error
/*
TODO: this could be nicer if we had a initializer map, e.g.:
var initializers map[string] func(authOpts map[string]string, logLevel log.Level, hasher hashing.Hasher) (Backend, error)
Sadly, not all backends require a hasher and I'm not sure about changing them to accept a dummy one just for the sake of this.
But I'll keep this comment for further thought and to remind me about changing those that don't return a pointer to do so.
*/
hasher := hashing.NewHasher(authOpts, AllowedBackendsOptsPrefix[bename])
switch bename {
case postgresBackend:
@ -183,13 +192,12 @@ func Initialize(authOpts map[string]string, logLevel log.Level, backends []strin
log.Infof("Backend registered: %s", beIface.GetName())
b.backends[pluginBackend] = beIface.(*CustomPlugin)
}
default:
return fmt.Errorf("unkown backend %s", bename)
}
}
b.setCheckers(authOpts)
b.setPrefixes(authOpts, backends)
return b
return nil
}
func (b *Backends) setCheckers(authOpts map[string]string) error {
@ -206,18 +214,24 @@ func (b *Backends) setCheckers(authOpts map[string]string) error {
switch check {
case aclCheck:
b.aclCheckers = append(b.aclCheckers, name)
log.Infof("registered acl checker: %s", name)
case userCheck:
b.userCheckers = append(b.userCheckers, name)
log.Infof("registered user checker: %s", name)
case superuserCheck:
b.superuserCheckers = append(b.superuserCheckers, name)
log.Infof("registered superuser checker: %s", name)
default:
return fmt.Errorf("unsupported check %s found for backend %s, skipping registration", check, name)
return fmt.Errorf("unsupported check %s found for backend %s", check, name)
}
}
} else {
b.aclCheckers = append(b.aclCheckers, name)
log.Infof("registered acl checker: %s", name)
b.userCheckers = append(b.userCheckers, name)
log.Infof("registered user checker: %s", name)
b.superuserCheckers = append(b.superuserCheckers, name)
log.Infof("registered superuser checker: %s", name)
}
}
@ -235,11 +249,13 @@ func (b *Backends) setCheckers(authOpts map[string]string) error {
// setPrefixes sets options for prefixes handling.
func (b *Backends) setPrefixes(authOpts map[string]string, backends []string) {
if checkPrefix, ok := authOpts["check_prefix"]; ok && strings.Replace(checkPrefix, " ", "", -1) == "true" {
//Check that backends match prefixes.
// Check that backends match prefixes.
if prefixesStr, ok := authOpts["prefixes"]; ok {
prefixes := strings.Split(strings.Replace(prefixesStr, " ", "", -1), ",")
if len(prefixes) == len(backends) {
//Set prefixes
// Set prefixes
// (I know some people find this type of comments useless, even harmful,
// but I find them helpful for quick code navigation on a project I don't work on daily, so screw them).
for i, backend := range backends {
b.prefixes[prefixes[i]] = backend
}
@ -296,32 +312,31 @@ func (b *Backends) AuthUnpwdCheck(username, password, clientid string) (bool, er
var authenticated bool
var err error
//If prefixes are enabled, check if username has a valid prefix and use the correct backend if so.
if b.checkPrefix {
validPrefix, bename := b.lookupPrefix(username)
// If prefixes are enabled, check if username has a valid prefix and use the correct backend if so.
if !b.checkPrefix {
return b.checkAuth(username, password, clientid)
}
if !checkRegistered(bename, b.userCheckers) {
return false, fmt.Errorf("backends %s not registered to check users", bename)
}
validPrefix, bename := b.lookupPrefix(username)
if validPrefix {
// If the backend is JWT and the token was prefixed, then strip the token. If the token was passed without a prefix it will be handled in the common case.
if bename == jwtBackend {
prefix := b.getPrefixForBackend(bename)
username = strings.TrimPrefix(username, prefix+"_")
}
var backend = b.backends[bename]
if !checkRegistered(bename, b.userCheckers) {
return false, fmt.Errorf("backend %s not registered to check users", bename)
}
authenticated, err = backend.GetUser(username, password, clientid)
if authenticated && err == nil {
log.Debugf("user %s authenticated with backend %s", username, backend.GetName())
}
} else {
//If there's no valid prefix, check all backends.
authenticated, err = b.checkAuth(username, password, clientid)
}
} else {
authenticated, err = b.checkAuth(username, password, clientid)
if !validPrefix {
return b.checkAuth(username, password, clientid)
}
// If the backend is JWT and the token was prefixed, then strip the token. If the token was passed without a prefix it will be handled in the common case.
if bename == jwtBackend {
prefix := b.getPrefixForBackend(bename)
username = strings.TrimPrefix(username, prefix+"_")
}
var backend = b.backends[bename]
authenticated, err = backend.GetUser(username, password, clientid)
if authenticated && err == nil {
log.Debugf("user %s authenticated with backend %s", username, backend.GetName())
}
return authenticated, err
@ -354,56 +369,56 @@ func (b *Backends) checkAuth(username, password, clientid string) (bool, error)
return authenticated, err
}
// AuthAclCheck checks user/topic/acc authentication.
// AuthAclCheck checks user/topic/acc authorization.
func (b *Backends) AuthAclCheck(clientid, username, topic string, acc int) (bool, error) {
var aclCheck bool
var err error
//If prefixes are enabled, check if username has a valid prefix and use the correct backend if so.
//Else, check all backends.
if b.checkPrefix {
validPrefix, bename := b.lookupPrefix(username)
if validPrefix {
// If the backend is JWT and the token was prefixed, then strip the token. If the token was passed without a prefix then it be handled in the common case.
if bename == jwtBackend {
prefix := b.getPrefixForBackend(bename)
username = strings.TrimPrefix(username, prefix+"_")
}
var backend = b.backends[bename]
// If prefixes are enabled, check if username has a valid prefix and use the correct backend if so.
// Else, check all backends.
if !b.checkPrefix {
return b.checkAcl(username, topic, clientid, acc)
}
// Short circuit checks when superusers are disabled.
if !b.disableSuperuser {
log.Debugf("Superuser check with backend %s", backend.GetName())
if !checkRegistered(bename, b.superuserCheckers) {
return false, fmt.Errorf("backends %s not registered to check superusers", bename)
}
validPrefix, bename := b.lookupPrefix(username)
aclCheck, err = backend.GetSuperuser(username)
if !validPrefix {
return b.checkAcl(username, topic, clientid, acc)
}
if aclCheck && err == nil {
log.Debugf("superuser %s acl authenticated with backend %s", username, backend.GetName())
}
}
//If not superuser, check acl.
if !aclCheck {
if !checkRegistered(bename, b.aclCheckers) {
return false, fmt.Errorf("backends %s not registered to check superusers", bename)
}
// If the backend is JWT and the token was prefixed, then strip the token. If the token was passed without a prefix then let it be handled in the common case.
if bename == jwtBackend {
prefix := b.getPrefixForBackend(bename)
username = strings.TrimPrefix(username, prefix+"_")
}
var backend = b.backends[bename]
log.Debugf("Acl check with backend %s", backend.GetName())
if ok, checkACLErr := backend.CheckAcl(username, topic, clientid, int32(acc)); ok && checkACLErr == nil {
aclCheck = true
log.Debugf("user %s acl authenticated with backend %s", username, backend.GetName())
} else if checkACLErr != nil && err == nil {
err = checkACLErr
}
}
} else {
//If there's no valid prefix, check all backends.
aclCheck, err = b.checkAcl(username, topic, clientid, acc)
// Short circuit checks when superusers are disabled.
if !b.disableSuperuser {
log.Debugf("Superuser check with backend %s", backend.GetName())
if !checkRegistered(bename, b.superuserCheckers) {
return false, fmt.Errorf("backend %s not registered to check superusers", bename)
}
aclCheck, err = backend.GetSuperuser(username)
if aclCheck && err == nil {
log.Debugf("superuser %s acl authenticated with backend %s", username, backend.GetName())
}
}
// If not superuser, check acl.
if !aclCheck {
if !checkRegistered(bename, b.aclCheckers) {
return false, fmt.Errorf("backend %s not registered to check superusers", bename)
}
log.Debugf("Acl check with backend %s", backend.GetName())
if ok, checkACLErr := backend.CheckAcl(username, topic, clientid, int32(acc)); ok && checkACLErr == nil {
aclCheck = true
log.Debugf("user %s acl authenticated with backend %s", username, backend.GetName())
} else if checkACLErr != nil && err == nil {
err = checkACLErr
}
} else {
aclCheck, err = b.checkAcl(username, topic, clientid, acc)
}
log.Debugf("Acl is %t for user %s", aclCheck, username)
@ -411,7 +426,7 @@ func (b *Backends) AuthAclCheck(clientid, username, topic string, acc int) (bool
}
func (b *Backends) checkAcl(username, topic, clientid string, acc int) (bool, error) {
//Check superusers first
// Check superusers first
var err error
aclCheck := false
if !b.disableSuperuser {
@ -454,7 +469,7 @@ func (b *Backends) checkAcl(username, topic, clientid string, acc int) (bool, er
}
func (b *Backends) Halt() {
//Halt every registered backend.
// Halt every registered backend.
for _, v := range b.backends {
v.Halt()
}

182
backends/backends_test.go Normal file
View File

@ -0,0 +1,182 @@
package backends
import (
"context"
"path/filepath"
"testing"
"github.com/iegomez/mosquitto-go-auth/hashing"
log "github.com/sirupsen/logrus"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
)
func TestBackends(t *testing.T) {
/*
No way we're gonna test every possibility given the amount of backends,
let's just make a sanity check for relevant functionality:
- Test there must be at least one user and acl checker.
- Test backend is valid.
- Test non registered checks are skipped.
- Test checking user and acls from different backends works.
- Test initialization actually returns a useful initialized struct.
*/
authOpts := make(map[string]string)
pwPath, _ := filepath.Abs("../test-files/passwords")
aclPath, _ := filepath.Abs("../test-files/acls")
authOpts["password_path"] = pwPath
authOpts["acl_path"] = aclPath
authOpts["redis_host"] = "localhost"
authOpts["redis_port"] = "6379"
authOpts["redis_db"] = "2"
authOpts["redis_password"] = ""
backends := []string{"files", "redis"}
username := "test1"
password := "test1"
passwordHash := "PBKDF2$sha512$100000$2WQHK5rjNN+oOT+TZAsWAw==$TDf4Y6J+9BdnjucFQ0ZUWlTwzncTjOOeE00W4Qm8lfPQyPCZACCjgfdK353jdGFwJjAf6vPAYaba9+z4GWK7Gg=="
clientid := "clientid"
Convey("An unknown backend should result in an error", t, func() {
backends := []string{"unknown"}
authOpts["backends"] = "unkown"
_, err := Initialize(authOpts, log.DebugLevel, backends)
So(err, ShouldNotBeNil)
So(err.Error(), ShouldEqual, "unkown backend unknown")
})
Convey("On initialization, lacking user/acl checkers should result in an error", t, func() {
authOpts["backends"] = "files, redis"
authOpts["files_register"] = "user"
authOpts["redis_register"] = "user"
_, err := Initialize(authOpts, log.DebugLevel, backends)
So(err, ShouldNotBeNil)
So(err.Error(), ShouldEqual, "no backend registered ACL checks")
authOpts["backends"] = "files, redis"
authOpts["files_register"] = "acl"
authOpts["redis_register"] = "acl"
_, err = Initialize(authOpts, log.DebugLevel, backends)
So(err, ShouldNotBeNil)
So(err.Error(), ShouldEqual, "no backend registered user checks")
})
Convey("On initialization, unknown checkers should result in an error", t, func() {
authOpts["backends"] = "files, redis"
authOpts["files_register"] = "user"
authOpts["redis_register"] = "unknown"
_, err := Initialize(authOpts, log.DebugLevel, backends)
So(err, ShouldNotBeNil)
So(err.Error(), ShouldEqual, "unsupported check unknown found for backend redis")
})
Convey("We should be able to auth users with one backend and acls with a different one", t, func() {
authOpts["backends"] = "files, redis"
authOpts["files_register"] = "acl"
authOpts["redis_register"] = "user"
redis, err := NewRedis(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, "redis"))
assert.Nil(t, err)
ctx := context.Background()
// Insert a user to test auth
username = "test1"
redis.conn.Set(ctx, username, passwordHash, 0)
b, err := Initialize(authOpts, log.DebugLevel, backends)
So(err, ShouldBeNil)
// Redis only contains test1, while files has a bunch of more users.
// Since Files only registers acl checks, those users should fail.
tt1, err1 := b.checkAuth(username, password, clientid)
tt2, err2 := b.checkAuth("test2", "test2", clientid)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(err2, ShouldBeNil)
So(tt2, ShouldBeFalse)
/*
Files grants these to user test1:
user test1
topic write test/topic/1
topic read test/topic/2
topic readwrite readwrite/topic
So if we add test/redis topic to Redis, the user should not have permission because acl chekcs are done by Files only.
*/
redis.conn.SAdd(ctx, username+":racls", "test/redis")
aclCheck, err := b.checkAcl(username, "test/redis", clientid, 1)
So(err, ShouldBeNil)
So(aclCheck, ShouldBeFalse)
aclCheck, err = b.checkAcl(username, "test/topic/1", clientid, 2)
So(err, ShouldBeNil)
So(aclCheck, ShouldBeTrue)
})
Convey("When not registering checks, all of them should be available", t, func() {
authOpts["backends"] = "files, redis"
delete(authOpts, "files_register")
delete(authOpts, "redis_register")
redis, err := NewRedis(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, "redis"))
assert.Nil(t, err)
ctx := context.Background()
// Insert a user to test auth
username = "test1"
redis.conn.Set(ctx, username, passwordHash, 0)
b, err := Initialize(authOpts, log.DebugLevel, backends)
So(err, ShouldBeNil)
tt1, err1 := b.checkAuth(username, password, clientid)
tt2, err2 := b.checkAuth("test2", "test2", clientid)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(err2, ShouldBeNil)
So(tt2, ShouldBeTrue)
/*
Files grants these to user test1:
user test1
topic write test/topic/1
topic read test/topic/2
topic readwrite readwrite/topic
Now the user should have permission for the redis topic since all backends do acl checks.
*/
redis.conn.SAdd(ctx, username+":racls", "test/redis")
aclCheck, err := b.checkAcl(username, "test/redis", clientid, 1)
So(err, ShouldBeNil)
So(aclCheck, ShouldBeTrue)
aclCheck, err = b.checkAcl(username, "test/topic/1", clientid, 2)
So(err, ShouldBeNil)
So(aclCheck, ShouldBeTrue)
})
}

View File

@ -0,0 +1,41 @@
package backends
import (
"testing"
log "github.com/sirupsen/logrus"
. "github.com/smartystreets/goconvey/convey"
)
func TestCustomPlugin(t *testing.T) {
// There's not much to test other than it loads and calls the functions as expected.
authOpts := map[string]string{
"plugin_path": "../plugin/plugin.so",
}
username := "user"
password := "password"
clientid := "clientid"
topic := "topic"
acc := int32(1)
Convey("Loading dummy plugin should work", t, func() {
plugin, err := NewCustomPlugin(authOpts, log.DebugLevel)
So(err, ShouldBeNil)
userCheck, err := plugin.GetUser(username, password, clientid)
So(err, ShouldBeNil)
So(userCheck, ShouldBeFalse)
superuserCheck, err := plugin.getSuperuser(username)
So(err, ShouldBeNil)
So(superuserCheck, ShouldBeFalse)
aclCheck, err := plugin.CheckAcl(username, topic, clientid, acc)
So(err, ShouldBeNil)
So(aclCheck, ShouldBeFalse)
name := plugin.GetName()
So(name, ShouldEqual, "Custom plugin")
})
}

View File

@ -131,6 +131,18 @@ func TestFiles(t *testing.T) {
authenticated, err := files.GetUser(user1, user1, clientID)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
authenticated, err = files.GetUser(user2, user2, clientID)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
authenticated, err = files.GetUser(user3, user3, clientID)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
authenticated, err = files.GetUser(elton, elton, clientID)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given a username and an incorrect password, it should not authenticate it", func() {

View File

@ -163,7 +163,9 @@ func (o Redis) GetUser(username, password, _ string) (bool, error) {
func (o Redis) getUser(username, password string) (bool, error) {
pwHash, err := o.conn.Get(o.ctx, username).Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
@ -200,12 +202,15 @@ func (o Redis) GetSuperuser(username string) (bool, error) {
if err != nil {
log.Debugf("redis get superuser error: %s", err)
}
return ok, err
}
func (o Redis) getSuperuser(username string) (bool, error) {
isSuper, err := o.conn.Get(o.ctx, fmt.Sprintf("%s:su", username)).Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
@ -252,34 +257,46 @@ func (o Redis) checkAcl(username, topic, clientid string, acc int32) (bool, erro
//Get all user subscribe acls.
var err error
acls, err = o.conn.SMembers(o.ctx, fmt.Sprintf("%s:sacls", username)).Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
//Get common subscribe acls.
commonAcls, err = o.conn.SMembers(o.ctx, "common:sacls").Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
case MOSQ_ACL_READ:
//Get all user read and readwrite acls.
urAcls, err := o.conn.SMembers(o.ctx, fmt.Sprintf("%s:racls", username)).Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
urwAcls, err := o.conn.SMembers(o.ctx, fmt.Sprintf("%s:rwacls", username)).Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
//Get common read and readwrite acls
rAcls, err := o.conn.SMembers(o.ctx, "common:racls").Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
rwAcls, err := o.conn.SMembers(o.ctx, "common:rwacls").Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
@ -293,21 +310,29 @@ func (o Redis) checkAcl(username, topic, clientid string, acc int32) (bool, erro
case MOSQ_ACL_WRITE:
//Get all user write and readwrite acls.
uwAcls, err := o.conn.SMembers(o.ctx, fmt.Sprintf("%s:wacls", username)).Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
urwAcls, err := o.conn.SMembers(o.ctx, fmt.Sprintf("%s:rwacls", username)).Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
//Get common write and readwrite acls
wAcls, err := o.conn.SMembers(o.ctx, "common:wacls").Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}
rwAcls, err := o.conn.SMembers(o.ctx, "common:rwacls").Result()
if err != nil {
if err == goredis.Nil {
return false, nil
} else if err != nil {
return false, err
}

View File

@ -57,13 +57,18 @@ func testRedis(ctx context.Context, t *testing.T, authOpts map[string]string) {
assert.Nil(t, err)
assert.False(t, authenticated)
redis.conn.Set(ctx, username+":su", "true", 0)
superuser, err := redis.GetSuperuser(username)
authenticated, err = redis.GetUser("wrong-user", userPass, "")
assert.Nil(t, err)
assert.False(t, authenticated)
redis.conn.Set(ctx, "superuser", userPassHash, 0)
redis.conn.Set(ctx, "superuser:su", "true", 0)
superuser, err := redis.GetSuperuser("superuser")
assert.Nil(t, err)
assert.True(t, superuser)
redis.disableSuperuser = true
superuser, err = redis.GetSuperuser(username)
superuser, err = redis.GetSuperuser("superuser")
assert.Nil(t, err)
assert.False(t, superuser)

View File

@ -124,7 +124,12 @@ func AuthPluginInit(keys []string, values []string, authOptsNum int) {
}
}
authPlugin.backends = bes.Initialize(authOpts, authPlugin.logLevel, backends)
var err error
authPlugin.backends, err = bes.Initialize(authOpts, authPlugin.logLevel, backends)
if err != nil {
log.Fatalf("error initializing backends: %s", err)
}
if cache, ok := authOpts["cache"]; ok && strings.Replace(cache, " ", "", -1) == "true" {
log.Info("redisCache activated")

View File

@ -21,7 +21,7 @@ func GetSuperuser(username string) (bool, error) {
return false, nil
}
func CheckAcl(username, topic, clientid string, acc int) (bool, error) {
func CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
log.Debugf("Checking acl with custom plugin.")
return false, nil
}