Merge pull request #120 from PierreF/backend-error

Improve handling of backend failure
This commit is contained in:
Pierre Fersing 2021-02-17 16:37:45 +01:00 committed by GitHub
commit c0667a4c6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1056 additions and 505 deletions

View File

@ -14,6 +14,11 @@
#include "go-auth.h"
// Same constant as one in go-auth.go.
#define AuthRejected 0
#define AuthGranted 1
#define AuthError 2
int mosquitto_auth_plugin_version(void) {
#ifdef MOSQ_AUTH_PLUGIN_VERSION
#if MOSQ_AUTH_PLUGIN_VERSION == 5
@ -87,11 +92,23 @@ int mosquitto_auth_unpwd_check(void *userdata, const char *username, const char
GoString go_password = {password, strlen(password)};
GoString go_clientid = {clientid, strlen(clientid)};
if(AuthUnpwdCheck(go_username, go_password, go_clientid)){
return MOSQ_ERR_SUCCESS;
}
GoUint8 ret = AuthUnpwdCheck(go_username, go_password, go_clientid);
return MOSQ_ERR_AUTH;
switch (ret)
{
case AuthGranted:
return MOSQ_ERR_SUCCESS;
break;
case AuthRejected:
return MOSQ_ERR_AUTH;
break;
case AuthError:
return MOSQ_ERR_UNKNOWN;
break;
default:
fprintf(stderr, "unknown plugin error: %d\n", ret);
return MOSQ_ERR_UNKNOWN;
}
}
#if MOSQ_AUTH_PLUGIN_VERSION >= 4
@ -118,11 +135,23 @@ int mosquitto_auth_acl_check(void *userdata, const char *clientid, const char *u
GoString go_topic = {topic, strlen(topic)};
GoInt32 go_access = access;
if(AuthAclCheck(go_clientid, go_username, go_topic, go_access)){
return MOSQ_ERR_SUCCESS;
}
GoUint8 ret = AuthAclCheck(go_clientid, go_username, go_topic, go_access);
return MOSQ_ERR_ACL_DENIED;
switch (ret)
{
case AuthGranted:
return MOSQ_ERR_SUCCESS;
break;
case AuthRejected:
return MOSQ_ERR_ACL_DENIED;
break;
case AuthError:
return MOSQ_ERR_UNKNOWN;
break;
default:
fprintf(stderr, "unknown plugin error: %d\n", ret);
return MOSQ_ERR_UNKNOWN;
}
}
#if MOSQ_AUTH_PLUGIN_VERSION >= 4

View File

@ -317,34 +317,34 @@ func checkCommentOrEmpty(line string) bool {
}
//GetUser checks that user exists and password is correct.
func (o *Files) GetUser(username, password, clientid string) bool {
func (o *Files) GetUser(username, password, clientid string) (bool, error) {
fileUser, ok := o.Users[username]
if !ok {
return false
return false, nil
}
if o.hasher.Compare(password, fileUser.Password) {
return true
return true, nil
}
log.Warnf("wrong password for user %s", username)
return false
return false, nil
}
//GetSuperuser returns false for files backend.
func (o *Files) GetSuperuser(username string) bool {
return false
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 {
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 {
return o.filesOnly
return o.filesOnly, nil
}
fileUser, ok := o.Users[username]
@ -353,7 +353,7 @@ func (o *Files) CheckAcl(username, topic, clientid string, acc int32) bool {
if ok {
for _, aclRecord := range fileUser.AclRecords {
if TopicsMatch(aclRecord.Topic, topic) && (acc == int32(aclRecord.Acc) || int32(aclRecord.Acc) == MOSQ_ACL_READWRITE || (acc == MOSQ_ACL_SUBSCRIBE && topic != "#" && (int32(aclRecord.Acc) == MOSQ_ACL_READ || int32(aclRecord.Acc) == MOSQ_ACL_SUBSCRIBE))) {
return true
return true, nil
}
}
}
@ -362,11 +362,11 @@ func (o *Files) CheckAcl(username, topic, clientid string, acc int32) bool {
aclTopic := strings.Replace(aclRecord.Topic, "%c", clientid, -1)
aclTopic = strings.Replace(aclTopic, "%u", username, -1)
if TopicsMatch(aclTopic, topic) && (acc == int32(aclRecord.Acc) || int32(aclRecord.Acc) == MOSQ_ACL_READWRITE || (acc == MOSQ_ACL_SUBSCRIBE && topic != "#" && (int32(aclRecord.Acc) == MOSQ_ACL_READ || int32(aclRecord.Acc) == MOSQ_ACL_SUBSCRIBE))) {
return true
return true, nil
}
}
return false
return false, nil
}

View File

@ -77,19 +77,34 @@ func TestFiles(t *testing.T) {
})
Convey("Given a username and a correct password, it should correctly authenticate it", func() {
authenticated := files.GetUser(user1, user1, clientID)
authenticated, err := files.GetUser(user1, user1, clientID)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given a username and an incorrect password, it should not authenticate it", func() {
authenticated := files.GetUser(user1, user2, clientID)
authenticated, err := files.GetUser(user1, user2, clientID)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a wrong username, it should not authenticate it and not return error", func() {
authenticated, err := files.GetUser(user4, "whatever_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
//There are no superusers for files
Convey("For any user superuser should return false", func() {
superuser := files.GetSuperuser(user1)
superuser, err := files.GetSuperuser(user1)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
Convey("Including non-present username", func() {
superuser, err := files.GetSuperuser(user4)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
})
testTopic1 := `test/topic/1`
@ -99,11 +114,15 @@ func TestFiles(t *testing.T) {
readWriteTopic := "readwrite/topic"
Convey("User 1 should be able to publish and not subscribe to test topic 1, and only subscribe but not publish to topic 2", func() {
tt1 := files.CheckAcl(user1, testTopic1, clientID, 2)
tt2 := files.CheckAcl(user1, testTopic1, clientID, 1)
tt3 := files.CheckAcl(user1, testTopic2, clientID, 2)
tt4 := files.CheckAcl(user1, testTopic2, clientID, 1)
tt1, err1 := files.CheckAcl(user1, testTopic1, clientID, 2)
tt2, err2 := files.CheckAcl(user1, testTopic1, clientID, 1)
tt3, err3 := files.CheckAcl(user1, testTopic2, clientID, 2)
tt4, err4 := files.CheckAcl(user1, testTopic2, clientID, 1)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(err3, ShouldBeNil)
So(err4, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
So(tt3, ShouldBeFalse)
@ -111,28 +130,37 @@ func TestFiles(t *testing.T) {
})
Convey("User 1 should be able to subscribe or publish to a readwrite topic rule", func() {
tt1 := files.CheckAcl(user1, readWriteTopic, clientID, 2)
tt2 := files.CheckAcl(user1, readWriteTopic, clientID, 1)
tt1, err1 := files.CheckAcl(user1, readWriteTopic, clientID, 2)
tt2, err2 := files.CheckAcl(user1, readWriteTopic, clientID, 1)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
})
Convey("User 2 should be able to read any test/topic/X but not any/other", func() {
tt1 := files.CheckAcl(user2, testTopic1, clientID, 1)
tt2 := files.CheckAcl(user2, testTopic2, clientID, 1)
tt3 := files.CheckAcl(user2, testTopic3, clientID, 1)
tt1, err1 := files.CheckAcl(user2, testTopic1, clientID, 1)
tt2, err2 := files.CheckAcl(user2, testTopic2, clientID, 1)
tt3, err3 := files.CheckAcl(user2, testTopic3, clientID, 1)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(err3, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
So(tt3, ShouldBeFalse)
})
Convey("User 3 should be able to read any test/X but not other/...", func() {
tt1 := files.CheckAcl(user3, testTopic1, clientID, 1)
tt2 := files.CheckAcl(user3, testTopic2, clientID, 1)
tt3 := files.CheckAcl(user3, testTopic3, clientID, 1)
tt4 := files.CheckAcl(user3, testTopic4, clientID, 1)
tt1, err1 := files.CheckAcl(user3, testTopic1, clientID, 1)
tt2, err2 := files.CheckAcl(user3, testTopic2, clientID, 1)
tt3, err3 := files.CheckAcl(user3, testTopic3, clientID, 1)
tt4, err4 := files.CheckAcl(user3, testTopic4, clientID, 1)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(err3, ShouldBeNil)
So(err4, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
So(tt3, ShouldBeTrue)
@ -140,20 +168,23 @@ func TestFiles(t *testing.T) {
})
Convey("User 4 should not be able to read since it's not in the passwords file", func() {
tt1 := files.CheckAcl(user4, testTopic1, clientID, 1)
tt1, err1 := files.CheckAcl(user4, testTopic1, clientID, 1)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
//Now check against patterns.
Convey("Given a topic that mentions username, acl check should pass", func() {
tt1 := files.CheckAcl(user1, "test/test1", clientID, 1)
tt1, err1 := files.CheckAcl(user1, "test/test1", clientID, 1)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
Convey("Given a topic that mentions clientid, acl check should pass", func() {
tt1 := files.CheckAcl(user1, "test/test_client", clientID, 1)
tt1, err1 := files.CheckAcl(user1, "test/test_client", clientID, 1)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})

View File

@ -52,7 +52,7 @@ func NewGRPC(authOpts map[string]string, logLevel log.Level) (GRPC, error) {
}
// GetUser checks that the username exists and the given password hashes to the same password.
func (o GRPC) GetUser(username, password, clientid string) bool {
func (o GRPC) GetUser(username, password, clientid string) (bool, error) {
req := gs.GetUserRequest{
Username: username,
@ -64,18 +64,18 @@ func (o GRPC) GetUser(username, password, clientid string) bool {
if err != nil {
log.Errorf("grpc get user error: %s", err)
return false
return false, err
}
return resp.Ok
return resp.Ok, nil
}
// GetSuperuser checks that the user is a superuser.
func (o GRPC) GetSuperuser(username string) bool {
func (o GRPC) GetSuperuser(username string) (bool, error) {
if o.disableSuperuser {
return false
return false, nil
}
req := gs.GetSuperuserRequest{
@ -86,15 +86,15 @@ func (o GRPC) GetSuperuser(username string) bool {
if err != nil {
log.Errorf("grpc get superuser error: %s", err)
return false
return false, err
}
return resp.Ok
return resp.Ok, nil
}
// CheckAcl checks if the user has access to the given topic.
func (o GRPC) CheckAcl(username, topic, clientid string, acc int32) bool {
func (o GRPC) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
req := gs.CheckAclRequest{
Username: username,
@ -107,10 +107,10 @@ func (o GRPC) CheckAcl(username, topic, clientid string, acc int32) bool {
if err != nil {
log.Errorf("grpc check acl error: %s", err)
return false
return false, err
}
return resp.Ok
return resp.Ok, nil
}

View File

@ -92,19 +92,23 @@ func TestGRPC(t *testing.T) {
Convey("given incorrect credentials user should not be authenticated", func(c C) {
auth := g.GetUser(grpcUsername, "wrong", grpcClientId)
auth, err := g.GetUser(grpcUsername, "wrong", grpcClientId)
So(err, ShouldBeNil)
c.So(auth, ShouldBeFalse)
Convey("given correct credential user should be authenticated", func(c C) {
auth := g.GetUser(grpcUsername, grpcPassword, grpcClientId)
auth, err := g.GetUser(grpcUsername, grpcPassword, grpcClientId)
So(err, ShouldBeNil)
c.So(auth, ShouldBeTrue)
Convey("given a non superuser user the service should respond false", func(c C) {
auth = g.GetSuperuser(grpcUsername)
auth, err = g.GetSuperuser(grpcUsername)
So(err, ShouldBeNil)
So(auth, ShouldBeFalse)
Convey("switching to a superuser should return true", func(c C) {
auth = g.GetSuperuser(grpcSuperuser)
auth, err = g.GetSuperuser(grpcSuperuser)
So(err, ShouldBeNil)
So(auth, ShouldBeTrue)
Convey("but if we disable superuser checks it should return false", func(c C) {
@ -112,16 +116,19 @@ func TestGRPC(t *testing.T) {
g, err = NewGRPC(authOpts, log.DebugLevel)
c.So(err, ShouldBeNil)
auth = g.GetSuperuser(grpcSuperuser)
auth, err = g.GetSuperuser(grpcSuperuser)
So(err, ShouldBeNil)
So(auth, ShouldBeFalse)
})
Convey("authorizing a wrong topic should fail", func(c C) {
auth = g.CheckAcl(grpcUsername, "wrong/topic", grpcClientId, grpcAcc)
auth, err = g.CheckAcl(grpcUsername, "wrong/topic", grpcClientId, grpcAcc)
So(err, ShouldBeNil)
So(auth, ShouldBeFalse)
Convey("switching to a correct one should succedd", func(c C) {
auth = g.CheckAcl(grpcUsername, grpcTopic, grpcClientId, grpcAcc)
auth, err = g.CheckAcl(grpcUsername, grpcTopic, grpcClientId, grpcAcc)
So(err, ShouldBeNil)
So(auth, ShouldBeTrue)
})

View File

@ -126,7 +126,7 @@ func NewHTTP(authOpts map[string]string, logLevel log.Level) (HTTP, error) {
return http, nil
}
func (o HTTP) GetUser(username, password, clientid string) bool {
func (o HTTP) GetUser(username, password, clientid string) (bool, error) {
var dataMap = map[string]interface{}{
"username": username,
@ -144,10 +144,10 @@ func (o HTTP) GetUser(username, password, clientid string) bool {
}
func (o HTTP) GetSuperuser(username string) bool {
func (o HTTP) GetSuperuser(username string) (bool, error) {
if o.SuperuserUri == "" {
return false
return false, nil
}
var dataMap = map[string]interface{}{
@ -162,7 +162,7 @@ func (o HTTP) GetSuperuser(username string) bool {
}
func (o HTTP) CheckAcl(username, topic, clientid string, acc int32) bool {
func (o HTTP) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
dataMap := map[string]interface{}{
"username": username,
@ -182,11 +182,11 @@ func (o HTTP) CheckAcl(username, topic, clientid string, acc int32) bool {
}
func (o HTTP) httpRequest(uri, username string, dataMap map[string]interface{}, urlValues map[string][]string) bool {
func (o HTTP) httpRequest(uri, username string, dataMap map[string]interface{}, urlValues map[string][]string) (bool, error) {
// Don't do the request if the client is nil.
if o.Client == nil {
return false
return false, errors.New("http client not initialized")
}
tlsStr := "http://"
@ -211,7 +211,7 @@ func (o HTTP) httpRequest(uri, username string, dataMap map[string]interface{},
if err != nil {
log.Errorf("marshal error: %s", err)
return false
return false, err
}
contentReader := bytes.NewReader(dataJson)
@ -220,7 +220,7 @@ func (o HTTP) httpRequest(uri, username string, dataMap map[string]interface{},
if err != nil {
log.Errorf("req error: %s", err)
return false
return false, err
}
req.Header.Set("Content-Type", "application/json")
@ -230,21 +230,24 @@ func (o HTTP) httpRequest(uri, username string, dataMap map[string]interface{},
if err != nil {
log.Errorf("POST error: %s", err)
return false
return false, err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Errorf("read error: %s", err)
return false
return false, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Infof("error code: %d", resp.StatusCode)
return false
if resp.StatusCode >= 500 {
err = fmt.Errorf("error code: %d", resp.StatusCode)
}
return false, err
}
if o.ResponseMode == "text" {
@ -252,7 +255,7 @@ func (o HTTP) httpRequest(uri, username string, dataMap map[string]interface{},
//For test response, we expect "ok" or an error message.
if string(body) != "ok" {
log.Infof("api error: %s", string(body))
return false
return false, nil
}
} else if o.ResponseMode == "json" {
@ -263,18 +266,18 @@ func (o HTTP) httpRequest(uri, username string, dataMap map[string]interface{},
if err != nil {
log.Errorf("unmarshal error: %s", err)
return false
return false, err
}
if !response.Ok {
log.Infof("api error: %s", response.Error)
return false
return false, nil
}
}
log.Debugf("http request approved for %s", username)
return true
return true, nil
}

View File

@ -102,26 +102,30 @@ func TestHTTPAllJsonServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(username, password, clientId)
authenticated, err := hb.GetUser(username, password, clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(username, "wrong_password", clientId)
authenticated, err := hb.GetUser(username, "wrong_password", clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(username)
authenticated, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
hb.SuperuserUri = ""
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -129,35 +133,40 @@ func TestHTTPAllJsonServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser("not_admin")
authenticated, err := hb.GetSuperuser("not_admin")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientId that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -236,26 +245,30 @@ func TestHTTPJsonStatusOnlyServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(username, password, clientId)
authenticated, err := hb.GetUser(username, password, clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(username, "wrong_password", clientId)
authenticated, err := hb.GetUser(username, "wrong_password", clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(username)
authenticated, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
hb.SuperuserUri = ""
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -263,35 +276,40 @@ func TestHTTPJsonStatusOnlyServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser("not_admin")
authenticated, err := hb.GetSuperuser("not_admin")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientId that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -374,26 +392,30 @@ func TestHTTPJsonTextResponseServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(username, password, clientId)
authenticated, err := hb.GetUser(username, password, clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(username, "wrong_password", clientId)
authenticated, err := hb.GetUser(username, "wrong_password", clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(username)
authenticated, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
hb.SuperuserUri = ""
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -401,35 +423,40 @@ func TestHTTPJsonTextResponseServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser("not_admin")
authenticated, err := hb.GetSuperuser("not_admin")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientId that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -521,26 +548,30 @@ func TestHTTPFormJsonResponseServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(username, password, clientId)
authenticated, err := hb.GetUser(username, password, clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(username, "wrong_password", clientId)
authenticated, err := hb.GetUser(username, "wrong_password", clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(username)
authenticated, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
hb.SuperuserUri = ""
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -548,35 +579,40 @@ func TestHTTPFormJsonResponseServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser("not_admin")
authenticated, err := hb.GetSuperuser("not_admin")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientId that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -646,26 +682,30 @@ func TestHTTPFormStatusOnlyServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(username, password, clientId)
authenticated, err := hb.GetUser(username, password, clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(username, "wrong_password", clientId)
authenticated, err := hb.GetUser(username, "wrong_password", clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(username)
authenticated, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
hb.SuperuserUri = ""
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -673,35 +713,40 @@ func TestHTTPFormStatusOnlyServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser("not_admin")
authenticated, err := hb.GetSuperuser("not_admin")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientId that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -776,26 +821,30 @@ func TestHTTPFormTextResponseServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(username, password, clientId)
authenticated, err := hb.GetUser(username, password, clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(username, "wrong_password", clientId)
authenticated, err := hb.GetUser(username, "wrong_password", clientId)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(username)
authenticated, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
hb.SuperuserUri = ""
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -803,35 +852,40 @@ func TestHTTPFormTextResponseServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser("not_admin")
authenticated, err := hb.GetSuperuser("not_admin")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(username, topic, clientId, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, "fake/topic", clientId, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientId that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(username, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})

View File

@ -95,7 +95,7 @@ func NewJavascript(authOpts map[string]string, logLevel log.Level) (*Javascript,
return javascript, nil
}
func (o *Javascript) GetUser(username, password, clientid string) bool {
func (o *Javascript) GetUser(username, password, clientid string) (bool, error) {
params := map[string]interface{}{
"username": username,
"password": password,
@ -107,10 +107,10 @@ func (o *Javascript) GetUser(username, password, clientid string) bool {
log.Errorf("js error: %s", err)
}
return granted
return granted, err
}
func (o *Javascript) GetSuperuser(username string) bool {
func (o *Javascript) GetSuperuser(username string) (bool, error) {
params := map[string]interface{}{
"username": username,
}
@ -120,10 +120,10 @@ func (o *Javascript) GetSuperuser(username string) bool {
log.Errorf("js error: %s", err)
}
return granted
return granted, err
}
func (o *Javascript) CheckAcl(username, topic, clientid string, acc int32) bool {
func (o *Javascript) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
params := map[string]interface{}{
"username": username,
"topic": topic,
@ -136,7 +136,7 @@ func (o *Javascript) CheckAcl(username, topic, clientid string, acc int32) bool
log.Errorf("js error: %s", err)
}
return granted
return granted, err
}
//GetName returns the backend's name

View File

@ -40,38 +40,48 @@ func TestJavascript(t *testing.T) {
So(err, ShouldBeNil)
Convey("User checks should work", func() {
userResponse := javascript.GetUser("correct", "good", "some-id")
userResponse, err := javascript.GetUser("correct", "good", "some-id")
So(err, ShouldBeNil)
So(userResponse, ShouldBeTrue)
userResponse = javascript.GetUser("correct", "bad", "some-id")
userResponse, err = javascript.GetUser("correct", "bad", "some-id")
So(err, ShouldBeNil)
So(userResponse, ShouldBeFalse)
userResponse = javascript.GetUser("wrong", "good", "some-id")
userResponse, err = javascript.GetUser("wrong", "good", "some-id")
So(err, ShouldBeNil)
So(userResponse, ShouldBeFalse)
})
Convey("Superuser checks should work", func() {
superuserResponse := javascript.GetSuperuser("admin")
superuserResponse, err := javascript.GetSuperuser("admin")
So(err, ShouldBeNil)
So(superuserResponse, ShouldBeTrue)
superuserResponse = javascript.GetSuperuser("non-admin")
superuserResponse, err = javascript.GetSuperuser("non-admin")
So(err, ShouldBeNil)
So(superuserResponse, ShouldBeFalse)
})
Convey("ACL checks should work", func() {
aclResponse := javascript.CheckAcl("correct", "test/topic", "id", 1)
aclResponse, err := javascript.CheckAcl("correct", "test/topic", "id", 1)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeTrue)
aclResponse = javascript.CheckAcl("incorrect", "test/topic", "id", 1)
aclResponse, err = javascript.CheckAcl("incorrect", "test/topic", "id", 1)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeFalse)
aclResponse = javascript.CheckAcl("correct", "bad/topic", "id", 1)
aclResponse, err = javascript.CheckAcl("correct", "bad/topic", "id", 1)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeFalse)
aclResponse = javascript.CheckAcl("correct", "test/topic", "wrong-id", 1)
aclResponse, err = javascript.CheckAcl("correct", "test/topic", "wrong-id", 1)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeFalse)
aclResponse = javascript.CheckAcl("correct", "test/topic", "id", 2)
aclResponse, err = javascript.CheckAcl("correct", "test/topic", "id", 2)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeFalse)
})
})

View File

@ -21,9 +21,9 @@ type tokenOptions struct {
}
type jwtChecker interface {
GetUser(username string) bool
GetSuperuser(username string) bool
CheckAcl(username, topic, clientid string, acc int32) bool
GetUser(username string) (bool, error)
GetSuperuser(username string) (bool, error)
CheckAcl(username, topic, clientid string, acc int32) (bool, error)
Halt()
}
@ -97,17 +97,17 @@ func NewJWT(authOpts map[string]string, logLevel log.Level, hasher hashing.HashC
}
//GetUser authenticates a given user.
func (o *JWT) GetUser(token, password, clientid string) bool {
func (o *JWT) GetUser(token, password, clientid string) (bool, error) {
return o.checker.GetUser(token)
}
//GetSuperuser checks if the given user is a superuser.
func (o *JWT) GetSuperuser(token string) bool {
func (o *JWT) GetSuperuser(token string) (bool, error) {
return o.checker.GetSuperuser(token)
}
//CheckAcl checks user authorization.
func (o *JWT) CheckAcl(token, topic, clientid string, acc int32) bool {
func (o *JWT) CheckAcl(token, topic, clientid string, acc int32) (bool, error) {
return o.checker.CheckAcl(token, topic, clientid, acc)
}

View File

@ -84,7 +84,7 @@ func NewJsJWTChecker(authOpts map[string]string, options tokenOptions) (jwtCheck
return checker, nil
}
func (o *jsJWTChecker) GetUser(token string) bool {
func (o *jsJWTChecker) GetUser(token string) (bool, error) {
params := map[string]interface{}{
"token": token,
}
@ -94,7 +94,7 @@ func (o *jsJWTChecker) GetUser(token string) bool {
if err != nil {
log.Printf("jwt get user error: %s", err)
return false
return false, err
}
params["username"] = username
@ -105,10 +105,10 @@ func (o *jsJWTChecker) GetUser(token string) bool {
log.Errorf("js error: %s", err)
}
return granted
return granted, err
}
func (o *jsJWTChecker) GetSuperuser(token string) bool {
func (o *jsJWTChecker) GetSuperuser(token string) (bool, error) {
params := map[string]interface{}{
"token": token,
}
@ -118,7 +118,7 @@ func (o *jsJWTChecker) GetSuperuser(token string) bool {
if err != nil {
log.Printf("jwt get user error: %s", err)
return false
return false, err
}
params["username"] = username
@ -129,10 +129,10 @@ func (o *jsJWTChecker) GetSuperuser(token string) bool {
log.Errorf("js error: %s", err)
}
return granted
return granted, err
}
func (o *jsJWTChecker) CheckAcl(token, topic, clientid string, acc int32) bool {
func (o *jsJWTChecker) CheckAcl(token, topic, clientid string, acc int32) (bool, error) {
params := map[string]interface{}{
"token": token,
"topic": topic,
@ -145,7 +145,7 @@ func (o *jsJWTChecker) CheckAcl(token, topic, clientid string, acc int32) bool {
if err != nil {
log.Printf("jwt get user error: %s", err)
return false
return false, err
}
params["username"] = username
@ -156,7 +156,7 @@ func (o *jsJWTChecker) CheckAcl(token, topic, clientid string, acc int32) bool {
log.Errorf("js error: %s", err)
}
return granted
return granted, err
}
func (o *jsJWTChecker) Halt() {

View File

@ -77,23 +77,23 @@ func NewLocalJWTChecker(authOpts map[string]string, logLevel log.Level, hasher h
return checker, nil
}
func (o *localJWTChecker) GetUser(token string) bool {
func (o *localJWTChecker) GetUser(token string) (bool, error) {
username, err := getUsernameForToken(o.options, token, o.options.skipUserExpiration)
if err != nil {
log.Printf("jwt local get user error: %s", err)
return false
return false, err
}
return o.getLocalUser(username)
}
func (o *localJWTChecker) GetSuperuser(token string) bool {
func (o *localJWTChecker) GetSuperuser(token string) (bool, error) {
username, err := getUsernameForToken(o.options, token, o.options.skipUserExpiration)
if err != nil {
log.Printf("jwt local get superuser error: %s", err)
return false
return false, err
}
if o.db == mysqlDB {
@ -103,12 +103,12 @@ func (o *localJWTChecker) GetSuperuser(token string) bool {
return o.postgres.GetSuperuser(username)
}
func (o *localJWTChecker) CheckAcl(token, topic, clientid string, acc int32) bool {
func (o *localJWTChecker) CheckAcl(token, topic, clientid string, acc int32) (bool, error) {
username, err := getUsernameForToken(o.options, token, o.options.skipACLExpiration)
if err != nil {
log.Printf("jwt local check acl error: %s", err)
return false
return false, err
}
if o.db == mysqlDB {
@ -132,9 +132,9 @@ func (o *localJWTChecker) Halt() {
}
}
func (o *localJWTChecker) getLocalUser(username string) bool {
func (o *localJWTChecker) getLocalUser(username string) (bool, error) {
if o.userQuery == "" {
return false
return false, nil
}
var count sql.NullInt64
@ -147,19 +147,19 @@ func (o *localJWTChecker) getLocalUser(username string) bool {
if err != nil {
log.Debugf("local JWT get user error: %s", err)
return false
return false, err
}
if !count.Valid {
log.Debugf("local JWT get user error: user %s not found", username)
return false
return false, nil
}
if count.Int64 > 0 {
return true
return true, nil
}
return false
return false, nil
}
func extractOpts(authOpts map[string]string, db string) map[string]string {

View File

@ -118,7 +118,7 @@ func NewRemoteJWTChecker(authOpts map[string]string, options tokenOptions) (jwtC
return checker, nil
}
func (o *remoteJWTChecker) GetUser(token string) bool {
func (o *remoteJWTChecker) GetUser(token string) (bool, error) {
var dataMap map[string]interface{}
var urlValues url.Values
@ -127,7 +127,7 @@ func (o *remoteJWTChecker) GetUser(token string) bool {
if err != nil {
log.Printf("jwt remote get user error: %s", err)
return false
return false, err
}
dataMap = map[string]interface{}{
@ -142,9 +142,9 @@ func (o *remoteJWTChecker) GetUser(token string) bool {
return o.jwtRequest(o.host, o.userUri, token, dataMap, urlValues)
}
func (o *remoteJWTChecker) GetSuperuser(token string) bool {
func (o *remoteJWTChecker) GetSuperuser(token string) (bool, error) {
if o.superuserUri == "" {
return false
return false, nil
}
var dataMap map[string]interface{}
var urlValues = url.Values{}
@ -154,7 +154,7 @@ func (o *remoteJWTChecker) GetSuperuser(token string) bool {
if err != nil {
log.Printf("jwt remote get superuser error: %s", err)
return false
return false, err
}
dataMap = map[string]interface{}{
@ -169,7 +169,7 @@ func (o *remoteJWTChecker) GetSuperuser(token string) bool {
return o.jwtRequest(o.host, o.superuserUri, token, dataMap, urlValues)
}
func (o *remoteJWTChecker) CheckAcl(token, topic, clientid string, acc int32) bool {
func (o *remoteJWTChecker) CheckAcl(token, topic, clientid string, acc int32) (bool, error) {
dataMap := map[string]interface{}{
"clientid": clientid,
"topic": topic,
@ -186,7 +186,7 @@ func (o *remoteJWTChecker) CheckAcl(token, topic, clientid string, acc int32) bo
if err != nil {
log.Printf("jwt remote check acl error: %s", err)
return false
return false, err
}
dataMap["username"] = username
@ -201,11 +201,11 @@ func (o *remoteJWTChecker) Halt() {
// NO-OP
}
func (o *remoteJWTChecker) jwtRequest(host, uri, token string, dataMap map[string]interface{}, urlValues url.Values) bool {
func (o *remoteJWTChecker) jwtRequest(host, uri, token string, dataMap map[string]interface{}, urlValues url.Values) (bool, error) {
// Don't do the request if the client is nil.
if o.client == nil {
return false
return false, errors.New("jwt http client not initialized")
}
tlsStr := "http://"
@ -229,7 +229,7 @@ func (o *remoteJWTChecker) jwtRequest(host, uri, token string, dataMap map[strin
if err != nil {
log.Errorf("marshal error: %s", err)
return false
return false, err
}
contentReader := bytes.NewReader(dataJSON)
@ -237,7 +237,7 @@ func (o *remoteJWTChecker) jwtRequest(host, uri, token string, dataMap map[strin
if err != nil {
log.Errorf("req error: %s", err)
return false
return false, err
}
req.Header.Set("Content-Type", "application/json")
default:
@ -247,7 +247,7 @@ func (o *remoteJWTChecker) jwtRequest(host, uri, token string, dataMap map[strin
if err != nil {
log.Errorf("req error: %s", err)
return false
return false, err
}
}
@ -257,21 +257,24 @@ func (o *remoteJWTChecker) jwtRequest(host, uri, token string, dataMap map[strin
if err != nil {
log.Errorf("error: %v", err)
return false
return false, err
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Errorf("read error: %s", err)
return false
return false, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Infof("error code: %d", resp.StatusCode)
return false
if resp.StatusCode >= 500 {
err = fmt.Errorf("error code: %d", resp.StatusCode)
}
return false, err
}
if o.responseMode == "text" {
@ -279,7 +282,7 @@ func (o *remoteJWTChecker) jwtRequest(host, uri, token string, dataMap map[strin
//For test response, we expect "ok" or an error message.
if string(body) != "ok" {
log.Infof("api error: %s", string(body))
return false
return false, nil
}
} else if o.responseMode == "json" {
@ -290,16 +293,16 @@ func (o *remoteJWTChecker) jwtRequest(host, uri, token string, dataMap map[strin
if err != nil {
log.Errorf("unmarshal error: %s", err)
return false
return false, err
}
if !response.Ok {
log.Infof("api error: %s", response.Error)
return false
return false, nil
}
}
log.Debugf("jwt request approved for %s", token)
return true
return true, nil
}

View File

@ -115,31 +115,40 @@ func TestJsJWTChecker(t *testing.T) {
checker, err := NewJsJWTChecker(authOpts, tkOptions)
So(err, ShouldBeNil)
userResponse := checker.GetUser("correct")
userResponse, err := checker.GetUser("correct")
So(err, ShouldBeNil)
So(userResponse, ShouldBeTrue)
userResponse = checker.GetUser("bad")
userResponse, err = checker.GetUser("bad")
So(err, ShouldBeNil)
So(userResponse, ShouldBeFalse)
superuserResponse := checker.GetSuperuser("admin")
superuserResponse, err := checker.GetSuperuser("admin")
So(err, ShouldBeNil)
So(superuserResponse, ShouldBeTrue)
superuserResponse = checker.GetSuperuser("non-admin")
superuserResponse, err = checker.GetSuperuser("non-admin")
So(err, ShouldBeNil)
So(superuserResponse, ShouldBeFalse)
aclResponse := checker.CheckAcl("correct", "test/topic", "id", 1)
aclResponse, err := checker.CheckAcl("correct", "test/topic", "id", 1)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeTrue)
aclResponse = checker.CheckAcl("incorrect", "test/topic", "id", 1)
aclResponse, err = checker.CheckAcl("incorrect", "test/topic", "id", 1)
So(err, ShouldBeNil)
So(userResponse, ShouldBeFalse)
aclResponse = checker.CheckAcl("correct", "bad/topic", "id", 1)
aclResponse, err = checker.CheckAcl("correct", "bad/topic", "id", 1)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeFalse)
aclResponse = checker.CheckAcl("correct", "test/topic", "wrong-id", 1)
aclResponse, err = checker.CheckAcl("correct", "test/topic", "wrong-id", 1)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeFalse)
aclResponse = checker.CheckAcl("correct", "test/topic", "id", 2)
aclResponse, err = checker.CheckAcl("correct", "test/topic", "id", 2)
So(err, ShouldBeNil)
So(aclResponse, ShouldBeFalse)
Convey("Tokens may be pre-parsed and passed to the scripts", func() {
@ -157,7 +166,8 @@ func TestJsJWTChecker(t *testing.T) {
token, err := jwtToken.SignedString([]byte(jwtSecret))
So(err, ShouldBeNil)
userResponse := checker.GetUser(token)
userResponse, err := checker.GetUser(token)
So(err, ShouldBeNil)
So(userResponse, ShouldBeTrue)
})
})
@ -222,7 +232,8 @@ func TestLocalPostgresJWT(t *testing.T) {
Convey("Given a correct token, it should correctly authenticate it", func() {
authenticated := jwt.GetUser(token)
authenticated, err := jwt.GetUser(token)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
@ -231,21 +242,24 @@ func TestLocalPostgresJWT(t *testing.T) {
wrongToken, err := wrongJwtToken.SignedString([]byte(jwtSecret))
So(err, ShouldBeNil)
authenticated := jwt.GetUser(wrongToken)
authenticated, err := jwt.GetUser(wrongToken)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a token that is admin, super user should pass", func() {
superuser := jwt.GetSuperuser(token)
superuser, err := jwt.GetSuperuser(token)
So(err, ShouldBeNil)
So(superuser, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
authOpts["jwt_superquery"] = ""
authOpts["jwt_pg_superquery"] = ""
jwt, err := NewLocalJWTChecker(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, ""), tkOptions)
So(err, ShouldBeNil)
superuser := jwt.GetSuperuser(username)
superuser, err := jwt.GetSuperuser(token)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
})
@ -268,9 +282,11 @@ func TestLocalPostgresJWT(t *testing.T) {
testTopic1 := `test/topic/1`
testTopic2 := `test/topic/2`
tt1 := jwt.CheckAcl(token, testTopic1, clientID, MOSQ_ACL_READ)
tt2 := jwt.CheckAcl(token, testTopic2, clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, testTopic1, clientID, MOSQ_ACL_READ)
tt2, err2 := jwt.CheckAcl(token, testTopic2, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
@ -279,16 +295,19 @@ func TestLocalPostgresJWT(t *testing.T) {
Convey("Given read only privileges, a pub check should fail", func() {
testTopic1 := "test/topic/1"
tt1 := jwt.CheckAcl(token, testTopic1, clientID, MOSQ_ACL_WRITE)
tt1, err1 := jwt.CheckAcl(token, testTopic1, clientID, MOSQ_ACL_WRITE)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given wildcard subscriptions against strict db acl, acl checks should fail", func() {
tt1 := jwt.CheckAcl(token, singleLevelACL, clientID, MOSQ_ACL_READ)
tt2 := jwt.CheckAcl(token, hierarchyACL, clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, singleLevelACL, clientID, MOSQ_ACL_READ)
tt2, err2 := jwt.CheckAcl(token, hierarchyACL, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeFalse)
@ -300,7 +319,8 @@ func TestLocalPostgresJWT(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a db single level wildcard, acl check should pass", func() {
tt1 := jwt.CheckAcl(token, "test/topic/whatever", clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, "test/topic/whatever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -310,7 +330,8 @@ func TestLocalPostgresJWT(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass", func() {
tt1 := jwt.CheckAcl(token, "test/what/ever", clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, "test/what/ever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -324,13 +345,16 @@ func TestLocalPostgresJWT(t *testing.T) {
Convey("So checking against them should give false and true for any user", func() {
tt1 := jwt.CheckAcl(token, singleLevelACL, clientID, MOSQ_ACL_READ)
tt2 := jwt.CheckAcl(token, hierarchyACL, clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, singleLevelACL, clientID, MOSQ_ACL_READ)
tt2, err2 := jwt.CheckAcl(token, hierarchyACL, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
superuser := jwt.GetSuperuser(token)
superuser, err := jwt.GetSuperuser(token)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -411,7 +435,8 @@ func TestLocalMysqlJWT(t *testing.T) {
Convey("Given a correct token, it should correctly authenticate it", func() {
authenticated := jwt.GetUser(token)
authenticated, err := jwt.GetUser(token)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
@ -421,20 +446,23 @@ func TestLocalMysqlJWT(t *testing.T) {
wrongToken, err := wrongJwtToken.SignedString([]byte(jwtSecret))
So(err, ShouldBeNil)
authenticated := jwt.GetUser(wrongToken)
authenticated, err := jwt.GetUser(wrongToken)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a token that is admin, super user should pass", func() {
superuser := jwt.GetSuperuser(token)
superuser, err := jwt.GetSuperuser(token)
So(err, ShouldBeNil)
So(superuser, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
authOpts["jwt_superquery"] = ""
authOpts["jwt_mysql_superquery"] = ""
jwt, err := NewLocalJWTChecker(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, ""), tkOptions)
So(err, ShouldBeNil)
superuser := jwt.GetSuperuser(username)
superuser, err := jwt.GetSuperuser(token)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
})
@ -458,9 +486,11 @@ func TestLocalMysqlJWT(t *testing.T) {
testTopic1 := `test/topic/1`
testTopic2 := `test/topic/2`
tt1 := jwt.CheckAcl(token, testTopic1, clientID, MOSQ_ACL_READ)
tt2 := jwt.CheckAcl(token, testTopic2, clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, testTopic1, clientID, MOSQ_ACL_READ)
tt2, err2 := jwt.CheckAcl(token, testTopic2, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
@ -469,16 +499,19 @@ func TestLocalMysqlJWT(t *testing.T) {
Convey("Given read only privileges, a pub check should fail", func() {
testTopic1 := "test/topic/1"
tt1 := jwt.CheckAcl(token, testTopic1, clientID, MOSQ_ACL_WRITE)
tt1, err1 := jwt.CheckAcl(token, testTopic1, clientID, MOSQ_ACL_WRITE)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given wildcard subscriptions against strict db acl, acl checks should fail", func() {
tt1 := jwt.CheckAcl(token, singleLevelACL, clientID, MOSQ_ACL_READ)
tt2 := jwt.CheckAcl(token, hierarchyACL, clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, singleLevelACL, clientID, MOSQ_ACL_READ)
tt2, err2 := jwt.CheckAcl(token, hierarchyACL, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeFalse)
@ -490,7 +523,8 @@ func TestLocalMysqlJWT(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a db single level wildcard, acl check should pass", func() {
tt1 := jwt.CheckAcl(token, "test/topic/whatever", clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, "test/topic/whatever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -500,7 +534,8 @@ func TestLocalMysqlJWT(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass", func() {
tt1 := jwt.CheckAcl(token, "test/what/ever", clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, "test/what/ever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -514,13 +549,16 @@ func TestLocalMysqlJWT(t *testing.T) {
Convey("So checking against them should give false and true for any user", func() {
tt1 := jwt.CheckAcl(token, singleLevelACL, clientID, MOSQ_ACL_READ)
tt2 := jwt.CheckAcl(token, hierarchyACL, clientID, MOSQ_ACL_READ)
tt1, err1 := jwt.CheckAcl(token, singleLevelACL, clientID, MOSQ_ACL_READ)
tt2, err2 := jwt.CheckAcl(token, hierarchyACL, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
superuser := jwt.GetSuperuser(token)
superuser, err := jwt.GetSuperuser(token)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -626,21 +664,24 @@ func TestJWTAllJsonServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(token, "", "")
authenticated, err := hb.GetUser(token, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(wrongToken, "", "")
authenticated, err := hb.GetUser(wrongToken, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(token)
authenticated, err := hb.GetSuperuser(token)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
@ -648,7 +689,8 @@ func TestJWTAllJsonServer(t *testing.T) {
hb, err := NewJWT(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, ""))
So(err, ShouldBeNil)
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -656,35 +698,40 @@ func TestJWTAllJsonServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser(wrongToken)
authenticated, err := hb.GetSuperuser(wrongToken)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientID that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -758,21 +805,24 @@ func TestJWTJsonStatusOnlyServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(token, "", "")
authenticated, err := hb.GetUser(token, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(wrongToken, "", "")
authenticated, err := hb.GetUser(wrongToken, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(token)
authenticated, err := hb.GetSuperuser(token)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
@ -780,7 +830,8 @@ func TestJWTJsonStatusOnlyServer(t *testing.T) {
hb, err := NewJWT(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, ""))
So(err, ShouldBeNil)
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -788,35 +839,40 @@ func TestJWTJsonStatusOnlyServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser(wrongToken)
authenticated, err := hb.GetSuperuser(wrongToken)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientID that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -892,21 +948,24 @@ func TestJWTJsonTextResponseServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(token, "", "")
authenticated, err := hb.GetUser(token, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(wrongToken, "", "")
authenticated, err := hb.GetUser(wrongToken, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(token)
authenticated, err := hb.GetSuperuser(token)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
@ -914,7 +973,8 @@ func TestJWTJsonTextResponseServer(t *testing.T) {
hb, err := NewJWT(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, ""))
So(err, ShouldBeNil)
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -922,35 +982,40 @@ func TestJWTJsonTextResponseServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser(wrongToken)
authenticated, err := hb.GetSuperuser(wrongToken)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientID that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -1036,21 +1101,24 @@ func TestJWTFormJsonResponseServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(token, "", "")
authenticated, err := hb.GetUser(token, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(wrongToken, "", "")
authenticated, err := hb.GetUser(wrongToken, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(token)
authenticated, err := hb.GetSuperuser(token)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
@ -1058,7 +1126,8 @@ func TestJWTFormJsonResponseServer(t *testing.T) {
hb, err := NewJWT(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, ""))
So(err, ShouldBeNil)
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -1066,35 +1135,40 @@ func TestJWTFormJsonResponseServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser(wrongToken)
authenticated, err := hb.GetSuperuser(wrongToken)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientID that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -1162,21 +1236,24 @@ func TestJWTFormStatusOnlyServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(token, "", "")
authenticated, err := hb.GetUser(token, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(wrongToken, "", "")
authenticated, err := hb.GetUser(wrongToken, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(token)
authenticated, err := hb.GetSuperuser(token)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
@ -1184,7 +1261,8 @@ func TestJWTFormStatusOnlyServer(t *testing.T) {
hb, err := NewJWT(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, ""))
So(err, ShouldBeNil)
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -1192,35 +1270,40 @@ func TestJWTFormStatusOnlyServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser(wrongToken)
authenticated, err := hb.GetSuperuser(wrongToken)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientID that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
@ -1291,21 +1374,24 @@ func TestJWTFormTextResponseServer(t *testing.T) {
Convey("Given correct password/username, get user should return true", func() {
authenticated := hb.GetUser(token, "", "")
authenticated, err := hb.GetUser(token, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given incorrect password/username, get user should return false", func() {
authenticated := hb.GetUser(wrongToken, "", "")
authenticated, err := hb.GetUser(wrongToken, "", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username, get superuser should return true", func() {
authenticated := hb.GetSuperuser(token)
authenticated, err := hb.GetSuperuser(token)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
Convey("But disabling superusers by removing superuri should now return false", func() {
@ -1313,7 +1399,8 @@ func TestJWTFormTextResponseServer(t *testing.T) {
hb, err := NewJWT(authOpts, log.DebugLevel, hashing.NewHasher(authOpts, ""))
So(err, ShouldBeNil)
superuser := hb.GetSuperuser(username)
superuser, err := hb.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
@ -1321,35 +1408,40 @@ func TestJWTFormTextResponseServer(t *testing.T) {
Convey("Given incorrect username, get superuser should return false", func() {
authenticated := hb.GetSuperuser(wrongToken)
authenticated, err := hb.GetSuperuser(wrongToken)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct topic, username, client id and acc, acl check should return true", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given an acc that requires more privileges than the user has, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
authenticated, err := hb.CheckAcl(token, topic, clientID, MOSQ_ACL_WRITE)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a topic not present in acls, check acl should return false", func() {
authenticated := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, "fake/topic", clientID, MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a clientID that doesn't match, check acl should return false", func() {
authenticated := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
authenticated, err := hb.CheckAcl(token, topic, "fake_client_id", MOSQ_ACL_READ)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})

View File

@ -128,7 +128,7 @@ func NewMongo(authOpts map[string]string, logLevel log.Level, hasher hashing.Has
}
//GetUser checks that the username exists and the given password hashes to the same password.
func (o Mongo) GetUser(username, password, clientid string) bool {
func (o Mongo) GetUser(username, password, clientid string) (bool, error) {
uc := o.Conn.Database(o.DBName).Collection(o.UsersCollection)
@ -136,23 +136,28 @@ func (o Mongo) GetUser(username, password, clientid string) bool {
err := uc.FindOne(context.TODO(), bson.M{"username": username}).Decode(&user)
if err != nil {
if err == mongo.ErrNoDocuments {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("Mongo get user error: %s", err)
return false
return false, err
}
if o.hasher.Compare(password, user.PasswordHash) {
return true
return true, nil
}
return false
return false, nil
}
//GetSuperuser checks that the key username:su exists and has value "true".
func (o Mongo) GetSuperuser(username string) bool {
func (o Mongo) GetSuperuser(username string) (bool, error) {
if o.disableSuperuser {
return false
return false, nil
}
uc := o.Conn.Database(o.DBName).Collection(o.UsersCollection)
@ -161,16 +166,21 @@ func (o Mongo) GetSuperuser(username string) bool {
err := uc.FindOne(context.TODO(), bson.M{"username": username}).Decode(&user)
if err != nil {
if err == mongo.ErrNoDocuments {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("Mongo get superuser error: %s", err)
return false
return false, err
}
return user.Superuser
return user.Superuser, nil
}
//CheckAcl gets all acls for the username and tries to match against topic, acc, and username/clientid if needed.
func (o Mongo) CheckAcl(username, topic, clientid string, acc int32) bool {
func (o Mongo) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
//Get user and check his acls.
uc := o.Conn.Database(o.DBName).Collection(o.UsersCollection)
@ -179,13 +189,18 @@ func (o Mongo) CheckAcl(username, topic, clientid string, acc int32) bool {
err := uc.FindOne(context.TODO(), bson.M{"username": username}).Decode(&user)
if err != nil {
if err == mongo.ErrNoDocuments {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("Mongo get superuser error: %s", err)
return false
return false, err
}
for _, acl := range user.Acls {
if (acl.Acc == acc || acl.Acc == 3) && TopicsMatch(acl.Topic, topic) {
return true
return true, nil
}
}
@ -196,7 +211,7 @@ func (o Mongo) CheckAcl(username, topic, clientid string, acc int32) bool {
if err != nil {
log.Debugf("Mongo check acl error: %s", err)
return false
return false, err
}
defer cur.Close(context.TODO())
@ -208,14 +223,14 @@ func (o Mongo) CheckAcl(username, topic, clientid string, acc int32) bool {
aclTopic := strings.Replace(acl.Topic, "%c", clientid, -1)
aclTopic = strings.Replace(aclTopic, "%u", username, -1)
if TopicsMatch(aclTopic, topic) {
return true
return true, nil
}
} else {
log.Errorf("mongo cursor decode error: %s", err)
}
}
return false
return false, nil
}

View File

@ -32,6 +32,7 @@ func TestMongoRaw(t *testing.T) {
const username2 = "test2"
const userPass2 = "testpw"
const userPassHash2 = "PBKDF2$sha512$100000$os24lcPr9cJt2QDVWssblQ==$dEOwgFUoMNt+Q8FHWXl03pZTg/RY47JdSTAx/KjhYKpbugOYg1WWG0tW0V2aqBnSCDLYJdRrkNf3p/PUoKLvkA=="
const wrongUsername = "not_present"
//Define Common Mongo Configuration
var authOpts = make(map[string]string)
@ -81,38 +82,57 @@ func TestMongoRaw(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given username1 and a correct password, it should correctly authenticate it", func() {
authenticated := mongo.GetUser(username1, userPass1, "")
authenticated, err := mongo.GetUser(username1, userPass1, "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given username1 and an incorrect password, it should not authenticate it", func() {
authenticated := mongo.GetUser(username1, "wrong_password", "")
authenticated, err := mongo.GetUser(username1, "wrong_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given wrongusername, it should not authenticate it and don't return error", func() {
authenticated, err := mongo.GetUser(wrongUsername, "whatever_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given username1 that is superuser, super user check should pass", func() {
superuser := mongo.GetSuperuser(username1)
superuser, err := mongo.GetSuperuser(username1)
So(err, ShouldBeNil)
So(superuser, ShouldBeTrue)
Convey("But disabling superusers should now return false", func() {
mongo.disableSuperuser = true
superuser := mongo.GetSuperuser(username1)
superuser, err := mongo.GetSuperuser(username1)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
})
Convey("Given wrongusername, super check should no pass and don't return error", func() {
authenticated, err := mongo.GetSuperuser(wrongUsername)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given correct username2 password, but using wrong salt format, user should not authenticate", func() {
authenticated := mongo.GetUser(username2, userPass2, "")
authenticated, err := mongo.GetUser(username2, userPass2, "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
clientID := "test_client"
Convey("Given acls in db, an exact match should work and and inexact one not matching wildcards not", func() {
testTopic1 := `test/topic/1`
testTopic2 := `not/matching/topic`
tt1 := mongo.CheckAcl(username1, testTopic1, clientID, MOSQ_ACL_READ)
tt2 := mongo.CheckAcl(username1, testTopic2, clientID, MOSQ_ACL_READ)
tt1, err1 := mongo.CheckAcl(username1, testTopic1, clientID, MOSQ_ACL_READ)
tt2, err2 := mongo.CheckAcl(username1, testTopic2, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
})
Convey("Given wildcard subscriptions that don't match user acls, acl checks should fail", func() {
tt1 := mongo.CheckAcl(username1, "not/matching/+", clientID, MOSQ_ACL_READ)
tt2 := mongo.CheckAcl(username1, "not/matching/#", clientID, MOSQ_ACL_READ)
tt1, err1 := mongo.CheckAcl(username1, "not/matching/+", clientID, MOSQ_ACL_READ)
tt2, err2 := mongo.CheckAcl(username1, "not/matching/#", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeFalse)
})
@ -127,40 +147,55 @@ func TestMongoRaw(t *testing.T) {
aclsColl.InsertOne(context.TODO(), &userAcl)
aclsColl.InsertOne(context.TODO(), &clientAcl)
Convey("Given a topic that mentions username and subscribes to it, acl check should pass", func() {
tt1 := mongo.CheckAcl(username1, "pattern/test", clientID, MOSQ_ACL_READ)
tt1, err1 := mongo.CheckAcl(username1, "pattern/test", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
Convey("Given a topic that mentions clientid, acl check should pass", func() {
tt1 := mongo.CheckAcl(username1, "pattern/test_client", clientID, MOSQ_ACL_READ)
tt1, err1 := mongo.CheckAcl(username1, "pattern/test_client", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
Convey("Given a topic not strictly present that matches a db single level wildcard, acl check should pass", func() {
tt1 := mongo.CheckAcl(username1, "single/topic/whatever", clientID, MOSQ_ACL_READ)
tt1, err1 := mongo.CheckAcl(username1, "single/topic/whatever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
Convey("Given a topic that matches single level but has more levels, acl check should not pass", func() {
tt1 := mongo.CheckAcl(username1, "single/topic/whatever/extra", clientID, MOSQ_ACL_READ)
tt1, err1 := mongo.CheckAcl(username1, "single/topic/whatever/extra", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass", func() {
tt1 := mongo.CheckAcl(username1, "hierarchy/what/ever", clientID, MOSQ_ACL_READ)
tt1, err1 := mongo.CheckAcl(username1, "hierarchy/what/ever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
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 := mongo.CheckAcl(username1, strictAcl, clientID, MOSQ_ACL_WRITE)
tt1, err1 := mongo.CheckAcl(username1, strictAcl, clientID, MOSQ_ACL_WRITE)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given a subscription attempt on a write only acl, acl check should fail", func() {
tt1 := mongo.CheckAcl(username1, writeAcl, clientID, MOSQ_ACL_READ)
tt1, err1 := mongo.CheckAcl(username1, writeAcl, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given a sub/pub attempt on a readwrite acl, acl check should pass for both", func() {
tt1 := mongo.CheckAcl(username1, readWriteAcl, clientID, MOSQ_ACL_READ)
tt2 := mongo.CheckAcl(username1, readWriteAcl, clientID, MOSQ_ACL_WRITE)
tt1, err1 := mongo.CheckAcl(username1, readWriteAcl, clientID, MOSQ_ACL_READ)
tt2, err2 := mongo.CheckAcl(username1, readWriteAcl, clientID, MOSQ_ACL_WRITE)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeTrue)
})
Convey("Given a bad username, acl check should not return error", func() {
testTopic1 := `test/topic/1`
tt1, err1 := mongo.CheckAcl(wrongUsername, testTopic1, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
mongoDb.Drop(context.TODO())
mongo.Halt()
@ -242,19 +277,23 @@ func TestMongoUtf8(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given username2 and a correct password, it should correctly authenticate it", func() {
authenticated := mongo.GetUser(username2, userPass2, "")
authenticated, err := mongo.GetUser(username2, userPass2, "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given username2 and an incorrect password, it should not authenticate it", func() {
authenticated := mongo.GetUser(username2, "wrong_password", "")
authenticated, err := mongo.GetUser(username2, "wrong_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given username2 that is superuser, super user check should pass", func() {
superuser := mongo.GetSuperuser(username2)
superuser, err := mongo.GetSuperuser(username2)
So(err, ShouldBeNil)
So(superuser, ShouldBeTrue)
})
Convey("Given correct username1 password, but using wrong salt format, user should not authenticate", func() {
authenticated := mongo.GetUser(username1, userPass1, "")
authenticated, err := mongo.GetUser(username1, userPass1, "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})

View File

@ -215,63 +215,73 @@ func NewMysql(authOpts map[string]string, logLevel log.Level, hasher hashing.Has
}
//GetUser checks that the username exists and the given password hashes to the same password.
func (o Mysql) GetUser(username, password, clientid string) bool {
func (o Mysql) GetUser(username, password, clientid string) (bool, error) {
var pwHash sql.NullString
err := o.DB.Get(&pwHash, o.UserQuery, username)
if err != nil {
if err == sql.ErrNoRows {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("MySql get user error: %s", err)
return false
return false, err
}
if !pwHash.Valid {
log.Debugf("MySql get user error: user %s not found", username)
return false
return false, nil
}
if o.hasher.Compare(password, pwHash.String) {
return true
return true, nil
}
return false
return false, nil
}
//GetSuperuser checks that the username meets the superuser query.
func (o Mysql) GetSuperuser(username string) bool {
func (o Mysql) GetSuperuser(username string) (bool, error) {
//If there's no superuser query, return false.
if o.SuperuserQuery == "" {
return false
return false, nil
}
var count sql.NullInt64
err := o.DB.Get(&count, o.SuperuserQuery, username)
if err != nil {
if err == sql.ErrNoRows {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("MySql get superuser error: %s", err)
return false
return false, err
}
if !count.Valid {
log.Debugf("MySql get superuser error: user %s not found", username)
return false
return false, nil
}
if count.Int64 > 0 {
return true
return true, nil
}
return false
return false, nil
}
//CheckAcl gets all acls for the username and tries to match against topic, acc, and username/clientid if needed.
func (o Mysql) CheckAcl(username, topic, clientid string, acc int32) bool {
func (o Mysql) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
//If there's no acl query, assume all privileges for all users.
if o.AclQuery == "" {
return true
return true, nil
}
var acls []string
@ -280,18 +290,18 @@ func (o Mysql) CheckAcl(username, topic, clientid string, acc int32) bool {
if err != nil {
log.Debugf("MySql check acl error: %s", err)
return false
return false, err
}
for _, acl := range acls {
aclTopic := strings.Replace(acl, "%c", clientid, -1)
aclTopic = strings.Replace(aclTopic, "%u", username, -1)
if TopicsMatch(aclTopic, topic) {
return true
return true, nil
}
}
return false
return false, nil
}

View File

@ -43,6 +43,7 @@ func TestMysql(t *testing.T) {
userPass := "testpw"
//Hash generated by the pw utility
userPassHash := "PBKDF2$sha512$100000$os24lcPr9cJt2QDVWssblQ==$BK1BQ2wbwU1zNxv3Ml3wLuu5//hPop3/LvaPYjjCwdBvnpwusnukJPpcXQzyyjOlZdieXTx6sXAcX4WnZRZZnw=="
wrongUsername := "not_present"
insertQuery := "INSERT INTO test_user(username, password_hash, is_admin) values(?, ?, ?)"
@ -58,23 +59,40 @@ func TestMysql(t *testing.T) {
Convey("Given a username and a correct password, it should correctly authenticate it", func() {
authenticated := mysql.GetUser(username, userPass, "")
authenticated, err := mysql.GetUser(username, userPass, "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given a username and an incorrect password, it should not authenticate it", func() {
authenticated := mysql.GetUser(username, "wrong_password", "")
authenticated, err := mysql.GetUser(username, "wrong_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a wrong username, it should not authenticate it and not return error", func() {
authenticated, err := mysql.GetUser(wrongUsername, "whatever_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a username that is admin, super user should pass", func() {
superuser := mysql.GetSuperuser(username)
superuser, err := mysql.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeTrue)
})
Convey("Given a wrong username, super user should not return error", func() {
superuser, err := mysql.GetSuperuser(wrongUsername)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
//Now create some acls and test topics
strictAcl := "test/topic/1"
@ -100,9 +118,11 @@ func TestMysql(t *testing.T) {
testTopic1 := `test/topic/1`
testTopic2 := `test/topic/2`
tt1 := mysql.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2 := mysql.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
tt1, err1 := mysql.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2, err2 := mysql.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
@ -111,16 +131,19 @@ func TestMysql(t *testing.T) {
Convey("Given read only privileges, a pub check should fail", func() {
testTopic1 := "test/topic/1"
tt1 := mysql.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_WRITE)
tt1, err1 := mysql.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_WRITE)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given wildcard subscriptions against strict db acl, acl checks should fail", func() {
tt1 := mysql.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2 := mysql.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
tt1, err1 := mysql.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2, err2 := mysql.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeFalse)
@ -132,7 +155,8 @@ func TestMysql(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic that mentions username, acl check should pass", func() {
tt1 := mysql.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
tt1, err1 := mysql.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -140,7 +164,8 @@ func TestMysql(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic that mentions clientid, acl check should pass", func() {
tt1 := mysql.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
tt1, err1 := mysql.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -150,7 +175,8 @@ func TestMysql(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a db single level wildcard, acl check should pass", func() {
tt1 := mysql.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
tt1, err1 := mysql.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -160,10 +186,18 @@ func TestMysql(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass", func() {
tt1 := mysql.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
tt1, err1 := mysql.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
Convey("Given a bad username, acl check should not return error", func() {
testTopic1 := `test/topic/1`
tt1, err1 := mysql.CheckAcl(wrongUsername, testTopic1, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
//Empty db
mysql.DB.MustExec("delete from test_user where 1 = 1")
mysql.DB.MustExec("delete from test_acl where 1 = 1")

View File

@ -160,64 +160,74 @@ func NewPostgres(authOpts map[string]string, logLevel log.Level, hasher hashing.
}
//GetUser checks that the username exists and the given password hashes to the same password.
func (o Postgres) GetUser(username, password, clientid string) bool {
func (o Postgres) GetUser(username, password, clientid string) (bool, error) {
var pwHash sql.NullString
err := o.DB.Get(&pwHash, o.UserQuery, username)
if err != nil {
if err == sql.ErrNoRows {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("PG get user error: %s", err)
return false
return false, err
}
if !pwHash.Valid {
log.Debugf("PG get user error: user %s not found", username)
return false
return false, err
}
if o.hasher.Compare(password, pwHash.String) {
return true
return true, nil
}
return false
return false, nil
}
//GetSuperuser checks that the username meets the superuser query.
func (o Postgres) GetSuperuser(username string) bool {
func (o Postgres) GetSuperuser(username string) (bool, error) {
//If there's no superuser query, return false.
if o.SuperuserQuery == "" {
return false
return false, nil
}
var count sql.NullInt64
err := o.DB.Get(&count, o.SuperuserQuery, username)
if err != nil {
if err == sql.ErrNoRows {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("PG get superuser error: %s", err)
return false
return false, err
}
if !count.Valid {
log.Debugf("PG get superuser error: user %s not found", username)
return false
return false, nil
}
if count.Int64 > 0 {
return true
return true, nil
}
return false
return false, nil
}
//CheckAcl gets all acls for the username and tries to match against topic, acc, and username/clientid if needed.
func (o Postgres) CheckAcl(username, topic, clientid string, acc int32) bool {
func (o Postgres) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
//If there's no acl query, assume all privileges for all users.
if o.AclQuery == "" {
return true
return true, nil
}
var acls []string
@ -226,18 +236,18 @@ func (o Postgres) CheckAcl(username, topic, clientid string, acc int32) bool {
if err != nil {
log.Debugf("PG check acl error: %s", err)
return false
return false, err
}
for _, acl := range acls {
aclTopic := strings.Replace(acl, "%c", clientid, -1)
aclTopic = strings.Replace(aclTopic, "%u", username, -1)
if TopicsMatch(aclTopic, topic) {
return true
return true, nil
}
}
return false
return false, nil
}

View File

@ -41,6 +41,7 @@ func TestPostgres(t *testing.T) {
userPass := "testpw"
//Hash generated by the pw utility
userPassHash := "PBKDF2$sha512$100000$os24lcPr9cJt2QDVWssblQ==$BK1BQ2wbwU1zNxv3Ml3wLuu5//hPop3/LvaPYjjCwdBvnpwusnukJPpcXQzyyjOlZdieXTx6sXAcX4WnZRZZnw=="
wrongUsername := "not_present"
insertQuery := "INSERT INTO test_user(username, password_hash, is_admin) values($1, $2, $3) returning id"
@ -53,23 +54,40 @@ func TestPostgres(t *testing.T) {
Convey("Given a username and a correct password, it should correctly authenticate it", func() {
authenticated := postgres.GetUser(username, userPass, "")
authenticated, err := postgres.GetUser(username, userPass, "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given a username and an incorrect password, it should not authenticate it", func() {
authenticated := postgres.GetUser(username, "wrong_password", "")
authenticated, err := postgres.GetUser(username, "wrong_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a wrong username, it should not authenticate it and not return error", func() {
authenticated, err := postgres.GetUser(wrongUsername, "whatever_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a username that is admin, super user should pass", func() {
superuser := postgres.GetSuperuser(username)
superuser, err := postgres.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeTrue)
})
Convey("Given a wrong username, super user should not return error", func() {
superuser, err := postgres.GetSuperuser(wrongUsername)
So(err, ShouldBeNil)
So(superuser, ShouldBeFalse)
})
//Now create some acls and test topics
strictAcl := "test/topic/1"
@ -91,9 +109,11 @@ func TestPostgres(t *testing.T) {
testTopic1 := `test/topic/1`
testTopic2 := `test/topic/2`
tt1 := postgres.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2 := postgres.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
tt1, err1 := postgres.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2, err2 := postgres.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
@ -102,16 +122,19 @@ func TestPostgres(t *testing.T) {
Convey("Given read only privileges, a pub check should fail", func() {
testTopic1 := "test/topic/1"
tt1 := postgres.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_WRITE)
tt1, err1 := postgres.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_WRITE)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given wildcard subscriptions against strict db acl, acl checks should fail", func() {
tt1 := postgres.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2 := postgres.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
tt1, err1 := postgres.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2, err2 := postgres.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeFalse)
@ -123,7 +146,8 @@ func TestPostgres(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic that mentions username, acl check should pass", func() {
tt1 := postgres.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
tt1, err1 := postgres.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -131,7 +155,8 @@ func TestPostgres(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic that mentions clientid, acl check should pass", func() {
tt1 := postgres.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
tt1, err1 := postgres.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -141,7 +166,8 @@ func TestPostgres(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a db single level wildcard, acl check should pass", func() {
tt1 := postgres.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
tt1, err1 := postgres.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -151,10 +177,18 @@ func TestPostgres(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass", func() {
tt1 := postgres.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
tt1, err1 := postgres.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
Convey("Given a bad username, acl check should not return error", func() {
testTopic1 := `test/topic/1`
tt1, err1 := postgres.CheckAcl(wrongUsername, testTopic1, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
//Empty db
postgres.DB.MustExec("delete from test_user where 1 = 1")
postgres.DB.MustExec("delete from test_acl where 1 = 1")

View File

@ -137,10 +137,10 @@ func isMovedError(err error) bool {
}
//GetUser checks that the username exists and the given password hashes to the same password.
func (o Redis) GetUser(username, password, _ string) bool {
func (o Redis) GetUser(username, password, _ string) (bool, error) {
ok, err := o.getUser(username, password)
if err == nil {
return ok
return ok, nil
}
//If using Redis Cluster, reload state and attempt once more.
@ -148,7 +148,7 @@ func (o Redis) GetUser(username, password, _ string) bool {
err = o.conn.ReloadState(o.ctx)
if err != nil {
log.Debugf("redis reload state error: %s", err)
return false
return false, err
}
//Retry once.
@ -158,7 +158,7 @@ func (o Redis) GetUser(username, password, _ string) bool {
if err != nil {
log.Debugf("redis get user error: %s", err)
}
return ok
return ok, err
}
func (o Redis) getUser(username, password string) (bool, error) {
@ -175,14 +175,14 @@ func (o Redis) getUser(username, password string) (bool, error) {
}
//GetSuperuser checks that the key username:su exists and has value "true".
func (o Redis) GetSuperuser(username string) bool {
func (o Redis) GetSuperuser(username string) (bool, error) {
if o.disableSuperuser {
return false
return false, nil
}
ok, err := o.getSuperuser(username)
if err == nil {
return ok
return ok, nil
}
//If using Redis Cluster, reload state and attempt once more.
@ -190,7 +190,7 @@ func (o Redis) GetSuperuser(username string) bool {
err = o.conn.ReloadState(o.ctx)
if err != nil {
log.Debugf("redis reload state error: %s", err)
return false
return false, err
}
//Retry once.
@ -200,7 +200,7 @@ func (o Redis) GetSuperuser(username string) bool {
if err != nil {
log.Debugf("redis get superuser error: %s", err)
}
return ok
return ok, err
}
func (o Redis) getSuperuser(username string) (bool, error) {
@ -216,10 +216,10 @@ func (o Redis) getSuperuser(username string) (bool, error) {
return false, nil
}
func (o Redis) CheckAcl(username, topic, clientid string, acc int32) bool {
func (o Redis) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
ok, err := o.checkAcl(username, topic, clientid, acc)
if err == nil {
return ok
return ok, nil
}
//If using Redis Cluster, reload state and attempt once more.
@ -227,7 +227,7 @@ func (o Redis) CheckAcl(username, topic, clientid string, acc int32) bool {
err = o.conn.ReloadState(o.ctx)
if err != nil {
log.Debugf("redis reload state error: %s", err)
return false
return false, err
}
//Retry once.
@ -237,7 +237,7 @@ func (o Redis) CheckAcl(username, topic, clientid string, acc int32) bool {
if err != nil {
log.Debugf("redis check acl error: %s", err)
}
return ok
return ok, err
}
//CheckAcl gets all acls for the username and tries to match against topic, acc, and username/clientid if needed.

View File

@ -49,18 +49,22 @@ func testRedis(ctx context.Context, t *testing.T, authOpts map[string]string) {
userPassHash := "PBKDF2$sha512$100000$os24lcPr9cJt2QDVWssblQ==$BK1BQ2wbwU1zNxv3Ml3wLuu5//hPop3/LvaPYjjCwdBvnpwusnukJPpcXQzyyjOlZdieXTx6sXAcX4WnZRZZnw=="
redis.conn.Set(ctx, username, userPassHash, 0)
authenticated := redis.GetUser(username, userPass, "")
authenticated, err := redis.GetUser(username, userPass, "")
assert.Nil(t, err)
assert.True(t, authenticated)
authenticated = redis.GetUser(username, "wrong_password", "")
authenticated, err = redis.GetUser(username, "wrong_password", "")
assert.Nil(t, err)
assert.False(t, authenticated)
redis.conn.Set(ctx, username+":su", "true", 0)
superuser := redis.GetSuperuser(username)
superuser, err := redis.GetSuperuser(username)
assert.Nil(t, err)
assert.True(t, superuser)
redis.disableSuperuser = true
superuser = redis.GetSuperuser(username)
superuser, err = redis.GetSuperuser(username)
assert.Nil(t, err)
assert.False(t, superuser)
redis.disableSuperuser = false
@ -82,79 +86,99 @@ func testRedis(ctx context.Context, t *testing.T, authOpts map[string]string) {
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)
tt1, err1 := redis.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2, err2 := redis.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.True(t, tt1)
assert.False(t, tt2)
tt1 = redis.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2 = redis.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2, err2 = redis.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.False(t, tt1)
assert.False(t, tt2)
//Now check against common patterns.
redis.conn.SAdd(ctx, "common:racls", userPattern)
tt1 = redis.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.True(t, tt1)
redis.conn.SAdd(ctx, "common:racls", clientPattern)
tt1 = redis.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.True(t, tt1)
redis.conn.SAdd(ctx, username+":racls", singleLevelAcl)
tt1 = redis.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.True(t, tt1)
redis.conn.SAdd(ctx, username+":racls", hierarchyAcl)
tt1 = redis.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.True(t, tt1)
tt1 = redis.CheckAcl(username, "test/test", clientID, MOSQ_ACL_WRITE)
tt1, err1 = redis.CheckAcl(username, "test/test", clientID, MOSQ_ACL_WRITE)
assert.Nil(t, err1)
assert.False(t, tt1)
//Add a write only acl and check for subscription.
redis.conn.SAdd(ctx, username+":wacls", writeAcl)
tt1 = redis.CheckAcl(username, writeAcl, clientID, MOSQ_ACL_READ)
tt2 = redis.CheckAcl(username, writeAcl, clientID, MOSQ_ACL_WRITE)
tt1, err1 = redis.CheckAcl(username, writeAcl, clientID, MOSQ_ACL_READ)
tt2, err2 = redis.CheckAcl(username, writeAcl, clientID, MOSQ_ACL_WRITE)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.False(t, tt1)
assert.True(t, tt2)
//Add a readwrite acl and check for subscription.
redis.conn.SAdd(ctx, username+":rwacls", readWriteAcl)
tt1 = redis.CheckAcl(username, readWriteAcl, clientID, MOSQ_ACL_READ)
tt2 = redis.CheckAcl(username, readWriteAcl, clientID, MOSQ_ACL_WRITE)
tt1, err1 = redis.CheckAcl(username, readWriteAcl, clientID, MOSQ_ACL_READ)
tt2, err2 = redis.CheckAcl(username, readWriteAcl, clientID, MOSQ_ACL_WRITE)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.True(t, tt1)
assert.True(t, tt2)
//Now add a common read acl to check against.
redis.conn.SAdd(ctx, "common:racls", commonTopic)
tt1 = redis.CheckAcl("unknown", commonTopic, clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl("unknown", commonTopic, clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.True(t, tt1)
// Assert that only read works for a given topic in racls.
topic := "readable/topic"
redis.conn.SAdd(ctx, username+":racls", topic)
tt1 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2, err2 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.False(t, tt1)
assert.True(t, tt2)
// Assert that only subscribe works for a given topic in sacls.
topic = "subscribable/topic"
redis.conn.SAdd(ctx, username+":sacls", topic)
tt1 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2, err2 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.True(t, tt1)
assert.False(t, tt2)
topic = "commonsubscribable/topic"
redis.conn.SAdd(ctx, "common:sacls", topic)
tt1 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
tt1, err1 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_SUBSCRIBE)
tt2, err2 = redis.CheckAcl(username, topic, clientID, MOSQ_ACL_READ)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.True(t, tt1)
assert.False(t, tt2)

View File

@ -94,63 +94,73 @@ func NewSqlite(authOpts map[string]string, logLevel log.Level, hasher hashing.Ha
}
//GetUser checks that the username exists and the given password hashes to the same password.
func (o Sqlite) GetUser(username, password, clientid string) bool {
func (o Sqlite) GetUser(username, password, clientid string) (bool, error) {
var pwHash sql.NullString
err := o.DB.Get(&pwHash, o.UserQuery, username)
if err != nil {
if err == sql.ErrNoRows {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("SQlite get user error: %s", err)
return false
return false, err
}
if !pwHash.Valid {
log.Debugf("SQlite get user error: user %s not found.", username)
return false
return false, nil
}
if o.hasher.Compare(password, pwHash.String) {
return true
return true, nil
}
return false
return false, nil
}
//GetSuperuser checks that the username meets the superuser query.
func (o Sqlite) GetSuperuser(username string) bool {
func (o Sqlite) GetSuperuser(username string) (bool, error) {
//If there's no superuser query, return false.
if o.SuperuserQuery == "" {
return false
return false, nil
}
var count sql.NullInt64
err := o.DB.Get(&count, o.SuperuserQuery, username)
if err != nil {
if err == sql.ErrNoRows {
// avoid leaking the fact that user exists or not though error.
return false, nil
}
log.Debugf("sqlite get superuser error: %s", err)
return false
return false, err
}
if !count.Valid {
log.Debugf("sqlite get superuser error: user %s not found", username)
return false
return false, nil
}
if count.Int64 > 0 {
return true
return true, nil
}
return false
return false, nil
}
//CheckAcl gets all acls for the username and tries to match against topic, acc, and username/clientid if needed.
func (o Sqlite) CheckAcl(username, topic, clientid string, acc int32) bool {
func (o Sqlite) CheckAcl(username, topic, clientid string, acc int32) (bool, error) {
//If there's no acl query, assume all privileges for all users.
if o.AclQuery == "" {
return true
return true, nil
}
var acls []string
@ -159,18 +169,18 @@ func (o Sqlite) CheckAcl(username, topic, clientid string, acc int32) bool {
if err != nil {
log.Debugf("sqlite check acl error: %s", err)
return false
return false, err
}
for _, acl := range acls {
aclTopic := strings.Replace(acl, "%c", clientid, -1)
aclTopic = strings.Replace(aclTopic, "%u", username, -1)
if TopicsMatch(aclTopic, topic) {
return true
return true, nil
}
}
return false
return false, nil
}

View File

@ -73,6 +73,8 @@ func TestFileSqlite(t *testing.T) {
//Hash generated by the pw utility
userPassHash := "PBKDF2$sha512$100000$os24lcPr9cJt2QDVWssblQ==$BK1BQ2wbwU1zNxv3Ml3wLuu5//hPop3/LvaPYjjCwdBvnpwusnukJPpcXQzyyjOlZdieXTx6sXAcX4WnZRZZnw=="
wrongUsername := "not_present"
insertQuery := "INSERT INTO test_user(username, password_hash, is_admin) values(?, ?, ?)"
userID := int64(0)
@ -87,22 +89,38 @@ func TestFileSqlite(t *testing.T) {
Convey("Given a username and a correct password, it should correctly authenticate it", func() {
authenticated := sqlite.GetUser(username, userPass, "")
authenticated, err := sqlite.GetUser(username, userPass, "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given a username and an incorrect password, it should not authenticate it", func() {
authenticated := sqlite.GetUser(username, "wrong_password", "")
authenticated, err := sqlite.GetUser(username, "wrong_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given wrongusername, it should not authenticate it and don't return error", func() {
authenticated, err := sqlite.GetUser(wrongUsername, "whatever_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a username that is admin, super user should pass", func() {
superuser := sqlite.GetSuperuser(username)
superuser, err := sqlite.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeTrue)
})
Convey("Given wrongusername, super check should no pass and don't return error", func() {
authenticated, err := sqlite.GetSuperuser(wrongUsername)
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
//Now create some acls and test topics
@ -128,9 +146,11 @@ func TestFileSqlite(t *testing.T) {
testTopic1 := `test/topic/1`
testTopic2 := `test/topic/2`
tt1 := sqlite.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2 := sqlite.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2, err2 := sqlite.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
@ -139,16 +159,19 @@ func TestFileSqlite(t *testing.T) {
Convey("Given read only privileges, a pub check should fail", func() {
testTopic1 := "test/topic/1"
tt1 := sqlite.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_WRITE)
tt1, err1 := sqlite.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_WRITE)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given wildcard subscriptions against strict db acl, acl checks should fail", func() {
tt1 := sqlite.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2 := sqlite.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2, err2 := sqlite.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeFalse)
@ -160,7 +183,8 @@ func TestFileSqlite(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic that mentions username, acl check should pass", func() {
tt1 := sqlite.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -168,7 +192,8 @@ func TestFileSqlite(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic that mentions clientid, acl check should pass", func() {
tt1 := sqlite.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -178,7 +203,8 @@ func TestFileSqlite(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a db single level wildcard, acl check should pass", func() {
tt1 := sqlite.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -188,10 +214,17 @@ func TestFileSqlite(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass", func() {
tt1 := sqlite.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
Convey("Given a bad username, acl check should not return error", func() {
tt1, err1 := sqlite.CheckAcl(wrongUsername, "test/test", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
//Empty db
sqlite.DB.MustExec("delete from test_user where 1 = 1")
sqlite.DB.MustExec("delete from test_acl where 1 = 1")
@ -255,20 +288,23 @@ func TestMemorySqlite(t *testing.T) {
Convey("Given a username and a correct password, it should correctly authenticate it", func() {
authenticated := sqlite.GetUser(username, userPass, "")
authenticated, err := sqlite.GetUser(username, userPass, "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeTrue)
})
Convey("Given a username and an incorrect password, it should not authenticate it", func() {
authenticated := sqlite.GetUser(username, "wrong_password", "")
authenticated, err := sqlite.GetUser(username, "wrong_password", "")
So(err, ShouldBeNil)
So(authenticated, ShouldBeFalse)
})
Convey("Given a username that is admin, super user should pass", func() {
superuser := sqlite.GetSuperuser(username)
superuser, err := sqlite.GetSuperuser(username)
So(err, ShouldBeNil)
So(superuser, ShouldBeTrue)
})
@ -297,9 +333,11 @@ func TestMemorySqlite(t *testing.T) {
testTopic1 := `test/topic/1`
testTopic2 := `test/topic/2`
tt1 := sqlite.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2 := sqlite.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_READ)
tt2, err2 := sqlite.CheckAcl(username, testTopic2, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeTrue)
So(tt2, ShouldBeFalse)
@ -308,16 +346,19 @@ func TestMemorySqlite(t *testing.T) {
Convey("Given read only privileges, a pub check should fail", func() {
testTopic1 := "test/topic/1"
tt1 := sqlite.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_WRITE)
tt1, err1 := sqlite.CheckAcl(username, testTopic1, clientID, MOSQ_ACL_WRITE)
So(err1, ShouldBeNil)
So(tt1, ShouldBeFalse)
})
Convey("Given wildcard subscriptions against strict db acl, acl checks should fail", func() {
tt1 := sqlite.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2 := sqlite.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, singleLevelAcl, clientID, MOSQ_ACL_READ)
tt2, err2 := sqlite.CheckAcl(username, hierarchyAcl, clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(err2, ShouldBeNil)
So(tt1, ShouldBeFalse)
So(tt2, ShouldBeFalse)
@ -329,7 +370,8 @@ func TestMemorySqlite(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic that mentions username, acl check should pass", func() {
tt1 := sqlite.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, "test/test", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -337,7 +379,8 @@ func TestMemorySqlite(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic that mentions clientid, acl check should pass", func() {
tt1 := sqlite.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, "test/test_client", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -347,7 +390,8 @@ func TestMemorySqlite(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a db single level wildcard, acl check should pass", func() {
tt1 := sqlite.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, "test/topic/whatever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})
@ -357,7 +401,8 @@ func TestMemorySqlite(t *testing.T) {
So(err, ShouldBeNil)
Convey("Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass", func() {
tt1 := sqlite.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
tt1, err1 := sqlite.CheckAcl(username, "test/what/ever", clientID, MOSQ_ACL_READ)
So(err1, ShouldBeNil)
So(tt1, ShouldBeTrue)
})

View File

@ -17,9 +17,9 @@ import (
)
type Backend interface {
GetUser(username, password, clientid string) bool
GetSuperuser(username string) bool
CheckAcl(username, topic, clientId string, acc int32) bool
GetUser(username, password, clientid string) (bool, error)
GetSuperuser(username string) (bool, error)
CheckAcl(username, topic, clientId string, acc int32) (bool, error)
GetName() string
Halt()
}
@ -29,9 +29,9 @@ type AuthPlugin struct {
customPlugin *plugin.Plugin
PInit func(map[string]string, log.Level) error
customPluginGetName func() string
customPluginGetUser func(username, password, clientid string) bool
customPluginGetSuperuser func(username string) bool
customPluginCheckAcl func(username, topic, clientid string, acc int) bool
customPluginGetUser func(username, password, clientid string) (bool, error)
customPluginGetSuperuser func(username string) (bool, error)
customPluginCheckAcl func(username, topic, clientid string, acc int) (bool, error)
customPluginHalt func()
useCache bool
checkPrefix bool
@ -58,6 +58,10 @@ const (
pluginBackend = "plugin"
grpcBackend = "grpc"
jsBackend = "js"
AuthRejected = 0
AuthGranted = 1
AuthError = 2
)
// Serves s a check for allowed backends and a map from backend to expected opts prefix.
@ -215,7 +219,13 @@ func AuthPluginInit(keys []string, values []string, authOptsNum int) {
continue
}
getUserFunc := plGetUser.(func(username, password, clientid string) bool)
getUserFunc, ok := plGetUser.(func(username, password, clientid string) (bool, error))
if !ok {
tmp := plGetUser.(func(username, password, clientid string) bool)
getUserFunc = func(username, password, clientid string) (bool, error) {
return tmp(username, password, clientid), nil
}
}
authPlugin.customPluginGetUser = getUserFunc
plGetSuperuser, err := authPlugin.customPlugin.Lookup("GetSuperuser")
@ -226,7 +236,13 @@ func AuthPluginInit(keys []string, values []string, authOptsNum int) {
continue
}
getSuperuserFunc := plGetSuperuser.(func(username string) bool)
getSuperuserFunc, ok := plGetSuperuser.(func(username string) (bool, error))
if !ok {
tmp := plGetSuperuser.(func(username string) bool)
getSuperuserFunc = func(username string) (bool, error) {
return tmp(username), nil
}
}
authPlugin.customPluginGetSuperuser = getSuperuserFunc
plCheckAcl, err := authPlugin.customPlugin.Lookup("CheckAcl")
@ -237,7 +253,13 @@ func AuthPluginInit(keys []string, values []string, authOptsNum int) {
continue
}
checkAclFunc := plCheckAcl.(func(username, topic, clientid string, acc int) bool)
checkAclFunc, ok := plCheckAcl.(func(username, topic, clientid string, acc int) (bool, error))
if !ok {
tmp := plCheckAcl.(func(username, topic, clientid string, acc int) bool)
checkAclFunc = func(username, topic, clientid string, acc int) (bool, error) {
return tmp(username, topic, clientid, acc), nil
}
}
authPlugin.customPluginCheckAcl = checkAclFunc
plHalt, err := authPlugin.customPlugin.Lookup("Halt")
@ -497,16 +519,31 @@ func setCache(authOpts map[string]string) {
}
//export AuthUnpwdCheck
func AuthUnpwdCheck(username, password, clientid string) bool {
func AuthUnpwdCheck(username, password, clientid string) uint8 {
ok, err := authUnpwdCheck(username, password, clientid)
if err != nil {
log.Error(err)
return AuthError
}
if ok {
return AuthGranted
}
return AuthRejected
}
func authUnpwdCheck(username, password, clientid string) (bool, error) {
var authenticated bool
var cached bool
var granted bool
var err error
if authPlugin.useCache {
log.Debugf("checking auth cache for %s", username)
cached, granted = authPlugin.cache.CheckAuthRecord(authPlugin.ctx, username, password)
if cached {
log.Debugf("found in cache: %s", username)
return granted
return granted, nil
}
}
@ -515,7 +552,7 @@ func AuthUnpwdCheck(username, password, clientid string) bool {
validPrefix, bename := CheckPrefix(username)
if validPrefix {
if bename == pluginBackend {
authenticated = CheckPluginAuth(username, password, clientid)
authenticated, err = CheckPluginAuth(username, password, clientid)
} else {
// 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 {
@ -524,52 +561,77 @@ func AuthUnpwdCheck(username, password, clientid string) bool {
}
var backend = authPlugin.backends[bename]
if backend.GetUser(username, password, clientid) {
authenticated = true
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 = CheckBackendsAuth(username, password, clientid)
authenticated, err = CheckBackendsAuth(username, password, clientid)
//If not authenticated, check for a present plugin
if !authenticated {
authenticated = CheckPluginAuth(username, password, clientid)
if ok, checkAuthErr := CheckPluginAuth(username, password, clientid); ok && checkAuthErr == nil {
authenticated = true
err = nil
} else if checkAuthErr != nil && err == nil {
err = checkAuthErr
}
}
}
} else {
authenticated = CheckBackendsAuth(username, password, clientid)
authenticated, err = CheckBackendsAuth(username, password, clientid)
//If not authenticated, check for a present plugin
if !authenticated {
authenticated = CheckPluginAuth(username, password, clientid)
if ok, checkAuthErr := CheckPluginAuth(username, password, clientid); ok && checkAuthErr == nil {
authenticated = true
err = nil
} else if checkAuthErr != nil && err == nil {
err = checkAuthErr
}
}
}
if authPlugin.useCache {
if authPlugin.useCache && err == nil {
authGranted := "false"
if authenticated {
authGranted = "true"
}
log.Debugf("setting auth cache for %s", username)
if err := authPlugin.cache.SetAuthRecord(authPlugin.ctx, username, password, authGranted); err != nil {
log.Errorf("set auth cache: %s", err)
return false
if setAuthErr := authPlugin.cache.SetAuthRecord(authPlugin.ctx, username, password, authGranted); setAuthErr != nil {
log.Errorf("set auth cache: %s", setAuthErr)
return false, setAuthErr
}
}
return authenticated
return authenticated, err
}
//export AuthAclCheck
func AuthAclCheck(clientid, username, topic string, acc int) bool {
func AuthAclCheck(clientid, username, topic string, acc int) uint8 {
ok, err := authAclCheck(clientid, username, topic, acc)
if err != nil {
log.Error(err)
return AuthError
}
if ok {
return AuthGranted
}
return AuthRejected
}
func authAclCheck(clientid, username, topic string, acc int) (bool, error) {
var aclCheck bool
var cached bool
var granted bool
var err error
if authPlugin.useCache {
log.Debugf("checking acl cache for %s", username)
cached, granted = authPlugin.cache.CheckACLRecord(authPlugin.ctx, username, topic, clientid, acc)
if cached {
log.Debugf("found in cache: %s", username)
return granted
return granted, nil
}
}
//If prefixes are enabled, check if username has a valid prefix and use the correct backend if so.
@ -578,7 +640,7 @@ func AuthAclCheck(clientid, username, topic string, acc int) bool {
validPrefix, bename := CheckPrefix(username)
if validPrefix {
if bename == pluginBackend {
aclCheck = CheckPluginAcl(username, topic, clientid, acc)
aclCheck, err = CheckPluginAcl(username, topic, clientid, acc)
} else {
// 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 {
@ -588,49 +650,62 @@ func AuthAclCheck(clientid, username, topic string, acc int) bool {
var backend = authPlugin.backends[bename]
log.Debugf("Superuser check with backend %s", backend.GetName())
// Short circuit checks when superusers are disabled.
if !authPlugin.disableSuperuser && backend.GetSuperuser(username) {
log.Debugf("superuser %s acl authenticated with backend %s", username, backend.GetName())
aclCheck = true
if !authPlugin.disableSuperuser {
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 {
log.Debugf("Acl check with backend %s", backend.GetName())
if backend.CheckAcl(username, topic, clientid, int32(acc)) {
log.Debugf("user %s acl authenticated with backend %s", username, 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 = CheckBackendsAcl(username, topic, clientid, acc)
aclCheck, err = CheckBackendsAcl(username, topic, clientid, acc)
//If acl hasn't passed, check for plugin.
if !aclCheck {
aclCheck = CheckPluginAcl(username, topic, clientid, acc)
if ok, checkACLErr := CheckPluginAcl(username, topic, clientid, acc); ok && checkACLErr == nil {
aclCheck = true
} else if checkACLErr != nil && err == nil {
err = checkACLErr
}
}
}
} else {
aclCheck = CheckBackendsAcl(username, topic, clientid, acc)
aclCheck, err = CheckBackendsAcl(username, topic, clientid, acc)
//If acl hasn't passed, check for plugin.
if !aclCheck {
aclCheck = CheckPluginAcl(username, topic, clientid, acc)
if ok, checkACLErr := CheckPluginAcl(username, topic, clientid, acc); ok && checkACLErr == nil {
aclCheck = true
} else if checkACLErr != nil && err == nil {
err = checkACLErr
}
}
}
if authPlugin.useCache {
if authPlugin.useCache && err == nil {
authGranted := "false"
if aclCheck {
authGranted = "true"
}
log.Debugf("setting acl cache (granted = %s) for %s", authGranted, username)
if err := authPlugin.cache.SetACLRecord(authPlugin.ctx, username, topic, clientid, acc, authGranted); err != nil {
log.Errorf("set acl cache: %s", err)
return false
if setACLErr := authPlugin.cache.SetACLRecord(authPlugin.ctx, username, topic, clientid, acc, authGranted); setACLErr != nil {
log.Errorf("set acl cache: %s", setACLErr)
return false, setACLErr
}
}
log.Debugf("Acl is %t for user %s", aclCheck, username)
return aclCheck
return aclCheck, err
}
//export AuthPskKeyGet
@ -661,8 +736,8 @@ func getPrefixForBackend(backend string) string {
}
//CheckBackendsAuth checks for all backends if a username is authenticated and sets the authenticated param.
func CheckBackendsAuth(username, password, clientid string) bool {
func CheckBackendsAuth(username, password, clientid string) (bool, error) {
var err error
authenticated := false
for _, bename := range backends {
@ -675,20 +750,29 @@ func CheckBackendsAuth(username, password, clientid string) bool {
log.Debugf("checking user %s with backend %s", username, backend.GetName())
if backend.GetUser(username, password, clientid) {
if ok, getUserErr := backend.GetUser(username, password, clientid); ok && getUserErr == nil {
authenticated = true
log.Debugf("user %s authenticated with backend %s", username, backend.GetName())
break
} else if getUserErr != nil && err == nil {
err = getUserErr
}
}
return authenticated
// If authenticated is true, it means at least one backend didn't failed and
// accepted the user. In this case trust this backend and clear the error.
if authenticated {
err = nil
}
return authenticated, err
}
//CheckBackendsAcl checks for all backends if a username is superuser or has acl rights and sets the aclCheck param.
func CheckBackendsAcl(username, topic, clientid string, acc int) bool {
func CheckBackendsAcl(username, topic, clientid string, acc int) (bool, error) {
//Check superusers first
var err error
aclCheck := false
if !authPlugin.disableSuperuser {
for _, bename := range backends {
@ -697,10 +781,12 @@ func CheckBackendsAcl(username, topic, clientid string, acc int) bool {
}
var backend = authPlugin.backends[bename]
log.Debugf("Superuser check with backend %s", backend.GetName())
if backend.GetSuperuser(username) {
if ok, getSuperuserErr := backend.GetSuperuser(username); ok && getSuperuserErr == nil {
log.Debugf("superuser %s acl authenticated with backend %s", username, backend.GetName())
aclCheck = true
break
} else if getSuperuserErr != nil && err == nil {
err = getSuperuserErr
}
}
}
@ -712,36 +798,51 @@ func CheckBackendsAcl(username, topic, clientid string, acc int) bool {
}
var backend = authPlugin.backends[bename]
log.Debugf("Acl check with backend %s", backend.GetName())
if backend.CheckAcl(username, topic, clientid, int32(acc)) {
if ok, checkACLErr := backend.CheckAcl(username, topic, clientid, int32(acc)); ok && checkACLErr == nil {
log.Debugf("user %s acl authenticated with backend %s", username, backend.GetName())
aclCheck = true
break
} else if checkACLErr != nil && err == nil {
err = checkACLErr
}
}
}
return aclCheck
// If aclCheck is true, it means at least one backend didn't fail and
// accepted the access. In this case trust this backend and clear the error.
if aclCheck {
err = nil
}
return aclCheck, err
}
//CheckPluginAuth checks that the plugin is not nil and returns the plugins auth response.
func CheckPluginAuth(username, password, clientid string) bool {
func CheckPluginAuth(username, password, clientid string) (bool, error) {
if authPlugin.customPlugin == nil {
return false
return false, nil
}
return authPlugin.customPluginGetUser(username, password, clientid)
}
//CheckPluginAcl checks that the plugin is not nil and returns the superuser/acl response.
func CheckPluginAcl(username, topic, clientid string, acc int) bool {
func CheckPluginAcl(username, topic, clientid string, acc int) (bool, error) {
var aclCheck bool
var err error
if authPlugin.customPlugin == nil {
return false
return false, nil
}
//If superuser, authorize it unless superusers are disabled.
if !authPlugin.disableSuperuser && authPlugin.customPluginGetSuperuser(username) {
return true
if !authPlugin.disableSuperuser {
aclCheck, err = authPlugin.customPluginGetSuperuser(username)
}
//Check against the plugin's check acl function.
return authPlugin.customPluginCheckAcl(username, topic, clientid, acc)
if !aclCheck && err == nil {
//Check against the plugin's check acl function.
aclCheck, err = authPlugin.customPluginCheckAcl(username, topic, clientid, acc)
}
return aclCheck, err
}
//export AuthPluginCleanup