mosquitto-go-auth/backends/redis_test.go

190 lines
6.0 KiB
Go

package backends
import (
"testing"
log "github.com/sirupsen/logrus"
. "github.com/smartystreets/goconvey/convey"
)
func TestRedis(t *testing.T) {
//Initialize Redis with some test values.
authOpts := make(map[string]string)
authOpts["redis_host"] = "localhost"
authOpts["redis_port"] = "6379"
authOpts["redis_db"] = "2"
authOpts["redis_password"] = ""
Convey("Given valid params NewRedis should return a Redis backend instance", t, func() {
redis, err := NewRedis(authOpts, log.DebugLevel)
So(err, ShouldBeNil)
//Empty db
redis.Conn.FlushDB()
//Insert a user to test auth
username := "test"
userPass := "testpw"
//Hash generated by the pw utility
userPassHash := "PBKDF2$sha512$100000$os24lcPr9cJt2QDVWssblQ==$BK1BQ2wbwU1zNxv3Ml3wLuu5//hPop3/LvaPYjjCwdBvnpwusnukJPpcXQzyyjOlZdieXTx6sXAcX4WnZRZZnw=="
redis.Conn.Set(username, userPassHash, 0)
Convey("Given a username and a correct password, it should correctly authenticate it", func() {
authenticated := redis.GetUser(username, userPass, "")
So(authenticated, ShouldBeTrue)
})
Convey("Given a username and an incorrect password, it should not authenticate it", func() {
authenticated := redis.GetUser(username, "wrong_password", "")
So(authenticated, ShouldBeFalse)
})
redis.Conn.Set(username+":su", "true", 0)
Convey("Given a username that is superuser, super user check should pass", func() {
superuser := redis.GetSuperuser(username)
So(superuser, ShouldBeTrue)
})
//Now create some acls and test topics
strictAcl := "test/topic/1"
singleLevelAcl := "test/topic/+"
hierarchyAcl := "test/#"
userPattern := "test/%u"
clientPattern := "test/%c"
clientID := "test_client"
writeAcl := "write/test"
readWriteAcl := "test/readwrite/1"
commonTopic := "common/test/topic"
redis.Conn.SAdd(username+":racls", strictAcl)
Convey("Given only strict acl in DB, an exact match should work and and inexact one not", func() {
testTopic1 := `test/topic/1`
testTopic2 := `test/topic/2`
tt1 := redis.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2 := redis.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
})
Convey("Given wildcard subscriptions against strict db acl, acl checks should fail", func() {
tt1 := redis.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2 := redis.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeFalse)
})
//Now check against common patterns.
redis.Conn.SAdd("common:racls", userPattern)
Convey("Given a topic that mentions username and subscribes to it, acl check should pass", func() {
tt1 := redis.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeTrue)
})
redis.Conn.SAdd("common:racls", clientPattern)
Convey("Given a topic that mentions clientid, acl check should pass", func() {
tt1 := redis.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeTrue)
})
//Now insert single level topic to check against.
redis.Conn.SAdd(username+":racls", singleLevelAcl)
Convey("Given a topic not strictly present that matches a db single level wildcard, acl check should pass", func() {
tt1 := redis.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeTrue)
})
//Now insert hierarchy wildcard to check against.
redis.Conn.SAdd(username+":racls", hierarchyAcl)
Convey("Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass", func() {
tt1 := redis.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeTrue)
})
//Now test against a publish subscription
Convey("Given a publish attempt for a read only acl, acl check should fail", func() {
tt1 := redis.CheckAcl(username, "test/test", clientID, MOSQ_ACL_WRITE)
So(tt1, ShouldBeFalse)
})
//Add a write only acl and check for subscription.
redis.Conn.SAdd(username+":wacls", writeAcl)
Convey("Given a subscription attempt on a write only acl, acl check should fail", func() {
tt1 := redis.CheckAcl(username, writeAcl, clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeFalse)
})
//Add a readwrite acl and check for subscription.
redis.Conn.SAdd(username+":rwacls", readWriteAcl)
Convey("Given a sub/pub attempt on a readwrite acl, acl check should pass for both", func() {
tt1 := redis.CheckAcl(username, readWriteAcl, clientID, MOSQ_ACL_READ)
tt2 := redis.CheckAcl(username, readWriteAcl, clientID, MOSQ_ACL_WRITE)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
})
//Now common acl to check against.
redis.Conn.SAdd("common:racls", commonTopic)
Convey("Given a topic not present in user's acls but present in common ones, acl check should pass", func() {
tt1 := redis.CheckAcl("unknown", commonTopic, clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeTrue)
})
Convey("Given a topic thay may be read but not subscribed to, checking for subscribe should failbut read shoud succeed", func() {
topic := "some/topic"
redis.Conn.SAdd(username+":racls", topic)
tt1 := redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2 := redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeTrue)
Convey("When adding subscribe permissions, both should be accepted", func() {
redis.Conn.SAdd(username+":sacls", topic)
tt1 := redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2 := redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
})
Convey("When adding it as a common subscribe acl, both should be accepted", func() {
redis.Conn.SAdd("common:sacls", topic)
tt1 := redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2 := redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
})
})
//Empty db
redis.Conn.FlushDB()
redis.Halt()
})
}