2017-12-09 11:10:37 +08:00
package backends
import (
2017-12-27 23:59:08 +08:00
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
2021-04-04 06:21:29 +08:00
"path/filepath"
2017-12-27 23:59:08 +08:00
"strconv"
"strings"
2017-12-09 11:10:37 +08:00
"testing"
"time"
2017-12-27 23:59:08 +08:00
2021-09-16 02:16:49 +08:00
"github.com/golang-jwt/jwt"
2021-04-04 06:21:29 +08:00
. "github.com/iegomez/mosquitto-go-auth/backends/constants"
2020-06-28 16:15:17 +08:00
"github.com/iegomez/mosquitto-go-auth/hashing"
2018-01-04 02:39:11 +08:00
log "github.com/sirupsen/logrus"
2017-12-27 23:59:08 +08:00
. "github.com/smartystreets/goconvey/convey"
2017-12-09 11:10:37 +08:00
)
2017-12-27 23:59:08 +08:00
var username = "test"
2017-12-09 11:10:37 +08:00
2021-04-04 06:21:29 +08:00
// Hash generated by the pw utility
2017-12-27 23:59:08 +08:00
var userPassHash = "PBKDF2$sha512$100000$os24lcPr9cJt2QDVWssblQ==$BK1BQ2wbwU1zNxv3Ml3wLuu5//hPop3/LvaPYjjCwdBvnpwusnukJPpcXQzyyjOlZdieXTx6sXAcX4WnZRZZnw=="
2017-12-09 11:10:37 +08:00
2017-12-27 23:59:08 +08:00
var jwtSecret = "some_jwt_secret"
2017-12-09 11:10:37 +08:00
2017-12-27 23:59:08 +08:00
// Generate the token.
var now = time . Now ( )
var nowSecondsSinceEpoch = now . Unix ( )
var expSecondsSinceEpoch int64 = nowSecondsSinceEpoch + int64 ( time . Hour * 24 / time . Second )
2017-12-09 11:10:37 +08:00
2017-12-27 23:59:08 +08:00
var jwtToken = jwt . NewWithClaims ( jwt . SigningMethodHS256 , jwt . MapClaims {
"iss" : "jwt-test" ,
"aud" : "jwt-test" ,
"nbf" : nowSecondsSinceEpoch ,
"exp" : expSecondsSinceEpoch ,
"sub" : "user" ,
"username" : username ,
} )
2017-12-09 11:10:37 +08:00
2017-12-27 23:59:08 +08:00
var wrongJwtToken = jwt . NewWithClaims ( jwt . SigningMethodHS256 , jwt . MapClaims {
"iss" : "jwt-test" ,
"aud" : "jwt-test" ,
"nbf" : nowSecondsSinceEpoch ,
"exp" : expSecondsSinceEpoch ,
"sub" : "user" ,
"username" : "wrong_user" ,
} )
2020-10-30 07:47:05 +08:00
var expiredToken = jwt . NewWithClaims ( jwt . SigningMethodHS256 , jwt . MapClaims {
"iss" : "jwt-test" ,
"aud" : "jwt-test" ,
"nbf" : nowSecondsSinceEpoch ,
"exp" : nowSecondsSinceEpoch - int64 ( time . Hour * 24 / time . Second ) ,
"sub" : "user" ,
"username" : username ,
} )
2021-04-04 06:21:29 +08:00
var notPresentJwtToken = jwt . NewWithClaims ( jwt . SigningMethodHS256 , jwt . MapClaims {
"iss" : "jwt-test" ,
"aud" : "jwt-test" ,
"nbf" : nowSecondsSinceEpoch ,
"exp" : expSecondsSinceEpoch ,
"sub" : "user" ,
"username" : "not_present" ,
} )
2020-10-31 09:33:53 +08:00
var tkOptions = tokenOptions {
2022-05-04 01:18:20 +08:00
secret : jwtSecret ,
userFieldKey : "username" ,
2020-10-31 09:33:53 +08:00
}
2020-10-30 07:47:05 +08:00
2020-10-31 09:33:53 +08:00
func TestJWTClaims ( t * testing . T ) {
Convey ( "When getting claims" , t , func ( ) {
2020-10-30 07:47:05 +08:00
Convey ( "Correct token should give no error" , func ( ) {
token , err := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
_ , err = getJWTClaims ( jwtSecret , token , false )
2020-10-30 07:47:05 +08:00
So ( err , ShouldBeNil )
} )
Convey ( "A token signed with a different secret should give an error" , func ( ) {
token , err := jwtToken . SignedString ( [ ] byte ( "wrong-secret" ) )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
_ , err = getJWTClaims ( jwtSecret , token , false )
2020-10-30 07:47:05 +08:00
So ( err , ShouldNotBeNil )
} )
Convey ( "Wrong user token should give no error" , func ( ) {
token , err := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
_ , err = getJWTClaims ( jwtSecret , token , false )
2020-10-30 07:47:05 +08:00
So ( err , ShouldBeNil )
} )
Convey ( "Expired token should give an error when getting claims" , func ( ) {
token , err := expiredToken . SignedString ( [ ] byte ( jwtSecret ) )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
_ , err = getJWTClaims ( jwtSecret , token , false )
2020-10-30 07:47:05 +08:00
So ( err , ShouldNotBeNil )
} )
2020-10-31 09:33:53 +08:00
Convey ( "When skipping expiration, expired token should not give an error" , func ( ) {
token , err := expiredToken . SignedString ( [ ] byte ( jwtSecret ) )
2020-10-30 07:47:05 +08:00
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
_ , err = getJWTClaims ( jwtSecret , token , true )
2020-10-30 07:47:05 +08:00
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
} )
} )
}
func TestJsJWTChecker ( t * testing . T ) {
authOpts := make ( map [ string ] string )
2021-02-11 08:38:24 +08:00
authOpts [ "jwt_js_user_script_path" ] = "../test-files/jwt/user_script.js"
authOpts [ "jwt_js_superuser_script_path" ] = "../test-files/jwt/superuser_script.js"
authOpts [ "jwt_js_acl_script_path" ] = "../test-files/jwt/acl_script.js"
2020-10-31 09:33:53 +08:00
Convey ( "Creating a js checker should succeed" , t , func ( ) {
checker , err := NewJsJWTChecker ( authOpts , tkOptions )
So ( err , ShouldBeNil )
2021-02-11 22:56:42 +08:00
userResponse , err := checker . GetUser ( "correct" )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( userResponse , ShouldBeTrue )
2021-02-11 22:56:42 +08:00
userResponse , err = checker . GetUser ( "bad" )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( userResponse , ShouldBeFalse )
2020-10-30 07:47:05 +08:00
2021-02-11 22:56:42 +08:00
superuserResponse , err := checker . GetSuperuser ( "admin" )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( superuserResponse , ShouldBeTrue )
2021-02-11 22:56:42 +08:00
superuserResponse , err = checker . GetSuperuser ( "non-admin" )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( superuserResponse , ShouldBeFalse )
2021-02-11 22:56:42 +08:00
aclResponse , err := checker . CheckAcl ( "correct" , "test/topic" , "id" , 1 )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( aclResponse , ShouldBeTrue )
2021-02-11 22:56:42 +08:00
aclResponse , err = checker . CheckAcl ( "incorrect" , "test/topic" , "id" , 1 )
So ( err , ShouldBeNil )
2021-04-04 06:21:29 +08:00
So ( aclResponse , ShouldBeFalse )
2020-10-31 09:33:53 +08:00
2021-02-11 22:56:42 +08:00
aclResponse , err = checker . CheckAcl ( "correct" , "bad/topic" , "id" , 1 )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( aclResponse , ShouldBeFalse )
2021-02-11 22:56:42 +08:00
aclResponse , err = checker . CheckAcl ( "correct" , "test/topic" , "wrong-id" , 1 )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( aclResponse , ShouldBeFalse )
2021-02-11 22:56:42 +08:00
aclResponse , err = checker . CheckAcl ( "correct" , "test/topic" , "id" , 2 )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( aclResponse , ShouldBeFalse )
Convey ( "Tokens may be pre-parsed and passed to the scripts" , func ( ) {
jsTokenOptions := tokenOptions {
2022-05-04 01:18:20 +08:00
parseToken : true ,
secret : jwtSecret ,
userFieldKey : "username" ,
2020-10-31 09:33:53 +08:00
}
2021-02-11 08:38:24 +08:00
authOpts [ "jwt_js_user_script_path" ] = "../test-files/jwt/parsed_user_script.js"
2022-05-04 01:18:20 +08:00
authOpts [ "jwt_js_pass_claims" ] = "true"
2020-10-31 09:33:53 +08:00
checker , err = NewJsJWTChecker ( authOpts , jsTokenOptions )
2020-10-30 07:47:05 +08:00
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
token , err := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
2020-10-30 07:47:05 +08:00
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
2021-02-11 22:56:42 +08:00
userResponse , err := checker . GetUser ( token )
2020-10-30 07:47:05 +08:00
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( userResponse , ShouldBeTrue )
2020-10-30 07:47:05 +08:00
} )
} )
}
2021-04-04 06:21:29 +08:00
func TestFilesJWTChecker ( t * testing . T ) {
// The bulk of files testing is done in the internal files checker.
// Neverthelss, we'll check that tokens are effectively parsed and correct usernames get the expected access.
authOpts := make ( map [ string ] string )
logLevel := log . DebugLevel
hasher := hashing . NewHasher ( authOpts , "files" )
Convey ( "Given empty opts NewFilesJWTChecker should fail" , t , func ( ) {
_ , err := NewFilesJWTChecker ( authOpts , logLevel , hasher , tkOptions )
So ( err , ShouldNotBeNil )
} )
Convey ( "When files backend is set, missing acl path should make NewFilesJWTChecker fail" , t , func ( ) {
authOpts [ "backends" ] = "files"
_ , err := NewFilesJWTChecker ( authOpts , logLevel , hasher , tkOptions )
So ( err , ShouldNotBeNil )
} )
Convey ( "When acl path is given, NewFilesJWTChecker should succeed" , t , func ( ) {
pwPath , err := filepath . Abs ( "../test-files/acls" )
So ( err , ShouldBeNil )
authOpts [ "backends" ] = "files"
authOpts [ "jwt_acl_path" ] = pwPath
filesChecker , err := NewFilesJWTChecker ( authOpts , logLevel , hasher , tkOptions )
So ( err , ShouldBeNil )
token , err := notPresentJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
So ( err , ShouldBeNil )
Convey ( "Access should be granted for ACL mentioned users" , func ( ) {
tt , err := filesChecker . CheckAcl ( token , "test/not_present" , "id" , 1 )
So ( err , ShouldBeNil )
So ( tt , ShouldBeTrue )
} )
Convey ( "Access should be granted for general ACL rules on non mentioned users" , func ( ) {
tt1 , err1 := filesChecker . CheckAcl ( token , "test/general" , "id" , 1 )
tt2 , err2 := filesChecker . CheckAcl ( token , "test/general_denied" , "id" , 1 )
So ( err1 , ShouldBeNil )
So ( tt1 , ShouldBeTrue )
So ( err2 , ShouldBeNil )
So ( tt2 , ShouldBeFalse )
} )
} )
}
2017-12-27 23:59:08 +08:00
func TestLocalPostgresJWT ( t * testing . T ) {
2017-12-09 11:10:37 +08:00
Convey ( "Creating a token should return a nil error" , t , func ( ) {
token , err := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
So ( err , ShouldBeNil )
2020-10-30 07:47:05 +08:00
// Initialize JWT in local mode.
2017-12-09 11:10:37 +08:00
authOpts := make ( map [ string ] string )
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mode" ] = "local"
2017-12-27 23:59:08 +08:00
authOpts [ "jwt_db" ] = "postgres"
2017-12-09 11:10:37 +08:00
authOpts [ "jwt_secret" ] = jwtSecret
2018-10-01 21:54:30 +08:00
authOpts [ "jwt_userfield" ] = "Username"
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_userquery" ] = "select count(*) from test_user where username = $1 limit 1"
2017-12-09 11:10:37 +08:00
2020-10-31 09:33:53 +08:00
// Give necessary postgres options.
authOpts [ "jwt_pg_host" ] = "localhost"
authOpts [ "jwt_pg_port" ] = "5432"
2022-11-11 19:27:55 +08:00
authOpts [ "jwt_pg_sslmode" ] = "disable"
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_pg_dbname" ] = "go_auth_test"
authOpts [ "jwt_pg_user" ] = "go_auth_test"
authOpts [ "jwt_pg_password" ] = "go_auth_test"
authOpts [ "jwt_pg_superquery" ] = "select count(*) from test_user where username = $1 and is_admin = true"
authOpts [ "jwt_pg_aclquery" ] = "SELECT test_acl.topic FROM test_acl, test_user WHERE test_user.username = $1 AND test_acl.test_user_id = test_user.id AND rw >= $2"
// Set regular PG options just to create a PG instance and create the records.
pgAuthOpts := make ( map [ string ] string )
pgAuthOpts [ "pg_host" ] = "localhost"
pgAuthOpts [ "pg_port" ] = "5432"
2022-11-11 19:27:55 +08:00
pgAuthOpts [ "pg_sslmode" ] = "disable"
2020-10-31 09:33:53 +08:00
pgAuthOpts [ "pg_dbname" ] = "go_auth_test"
pgAuthOpts [ "pg_user" ] = "go_auth_test"
pgAuthOpts [ "pg_password" ] = "go_auth_test"
pgAuthOpts [ "pg_userquery" ] = "mock"
pgAuthOpts [ "pg_superquery" ] = "mock"
pgAuthOpts [ "pg_aclquery" ] = "mock"
db , err := NewPostgres ( pgAuthOpts , log . DebugLevel , hashing . NewHasher ( pgAuthOpts , "" ) )
So ( err , ShouldBeNil )
2017-12-09 11:10:37 +08:00
Convey ( "Given correct option NewJWT returns an instance of jwt backend" , func ( ) {
2020-10-31 09:33:53 +08:00
jwt , err := NewLocalJWTChecker ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , tkOptions )
2017-12-09 11:10:37 +08:00
So ( err , ShouldBeNil )
2020-05-15 11:34:45 +08:00
//Empty db
2020-10-31 09:33:53 +08:00
db . DB . MustExec ( "delete from test_user where 1 = 1" )
db . DB . MustExec ( "delete from test_acl where 1 = 1" )
2017-12-09 11:10:37 +08:00
//Now test everything.
insertQuery := "INSERT INTO test_user(username, password_hash, is_admin) values($1, $2, $3) returning id"
userID := 0
2020-10-31 09:33:53 +08:00
err = db . DB . Get ( & userID , insertQuery , username , userPassHash , true )
2017-12-09 11:10:37 +08:00
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( userID , ShouldBeGreaterThan , 0 )
Convey ( "Given a correct token, it should correctly authenticate it" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := jwt . GetUser ( token )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given an incorrect token, it should not authenticate it" , func ( ) {
wrongToken , err := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
So ( err , ShouldBeNil )
2021-02-11 22:56:42 +08:00
authenticated , err := jwt . GetUser ( wrongToken )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given a token that is admin, super user should pass" , func ( ) {
2020-11-13 22:41:22 +08:00
superuser , err := jwt . GetSuperuser ( token )
So ( err , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( superuser , ShouldBeTrue )
2020-10-31 09:33:53 +08:00
2020-05-14 10:44:23 +08:00
Convey ( "But disabling superusers by removing superuri should now return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authOpts [ "jwt_pg_superquery" ] = ""
2020-10-31 09:33:53 +08:00
jwt , err := NewLocalJWTChecker ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , tkOptions )
So ( err , ShouldBeNil )
2020-11-13 22:41:22 +08:00
superuser , err := jwt . GetSuperuser ( token )
So ( err , ShouldBeNil )
2020-05-14 10:44:23 +08:00
So ( superuser , ShouldBeFalse )
} )
2017-12-09 11:10:37 +08:00
} )
//Now create some acls and test topics
2020-10-31 09:33:53 +08:00
strictACL := "test/topic/1"
singleLevelACL := "test/topic/+"
hierarchyACL := "test/#"
2017-12-09 11:10:37 +08:00
clientID := "test_client"
aclID := 0
aclQuery := "INSERT INTO test_acl(test_user_id, topic, rw) values($1, $2, $3) returning id"
2020-10-31 09:33:53 +08:00
err = db . DB . Get ( & aclID , aclQuery , userID , strictACL , MOSQ_ACL_READ )
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
2017-12-09 11:10:37 +08:00
2020-05-15 11:34:45 +08:00
Convey ( "Given only strict acl in db, an exact match should work and and inexact one not" , func ( ) {
2017-12-09 11:10:37 +08:00
testTopic1 := ` test/topic/1 `
testTopic2 := ` test/topic/2 `
2020-11-13 22:41:22 +08:00
tt1 , err1 := jwt . CheckAcl ( token , testTopic1 , clientID , MOSQ_ACL_READ )
tt2 , err2 := jwt . CheckAcl ( token , testTopic2 , clientID , MOSQ_ACL_READ )
2017-12-09 11:10:37 +08:00
2020-11-13 22:41:22 +08:00
So ( err1 , ShouldBeNil )
So ( err2 , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( tt1 , ShouldBeTrue )
So ( tt2 , ShouldBeFalse )
} )
Convey ( "Given read only privileges, a pub check should fail" , func ( ) {
testTopic1 := "test/topic/1"
2020-11-13 22:41:22 +08:00
tt1 , err1 := jwt . CheckAcl ( token , testTopic1 , clientID , MOSQ_ACL_WRITE )
So ( err1 , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( tt1 , ShouldBeFalse )
} )
Convey ( "Given wildcard subscriptions against strict db acl, acl checks should fail" , func ( ) {
2021-02-11 22:56:42 +08:00
tt1 , err1 := jwt . CheckAcl ( token , singleLevelACL , clientID , MOSQ_ACL_READ )
tt2 , err2 := jwt . CheckAcl ( token , hierarchyACL , clientID , MOSQ_ACL_READ )
2017-12-09 11:10:37 +08:00
2020-11-13 22:41:22 +08:00
So ( err1 , ShouldBeNil )
So ( err2 , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( tt1 , ShouldBeFalse )
So ( tt2 , ShouldBeFalse )
} )
//Now insert single level topic to check against.
2020-10-31 09:33:53 +08:00
err = db . DB . Get ( & aclID , aclQuery , userID , singleLevelACL , MOSQ_ACL_READ )
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
2017-12-09 11:10:37 +08:00
Convey ( "Given a topic not strictly present that matches a db single level wildcard, acl check should pass" , func ( ) {
2020-11-13 22:41:22 +08:00
tt1 , err1 := jwt . CheckAcl ( token , "test/topic/whatever" , clientID , MOSQ_ACL_READ )
So ( err1 , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( tt1 , ShouldBeTrue )
} )
//Now insert hierarchy wildcard to check against.
2020-10-31 09:33:53 +08:00
err = db . DB . Get ( & aclID , aclQuery , userID , hierarchyACL , MOSQ_ACL_READ )
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
2017-12-09 11:10:37 +08:00
Convey ( "Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass" , func ( ) {
2020-11-13 22:41:22 +08:00
tt1 , err1 := jwt . CheckAcl ( token , "test/what/ever" , clientID , MOSQ_ACL_READ )
So ( err1 , ShouldBeNil )
2017-12-09 11:10:37 +08:00
So ( tt1 , ShouldBeTrue )
} )
2020-10-31 09:33:53 +08:00
Convey ( "Deleting superuser and acl queries should work fine" , func ( ) {
authOpts [ "jwt_pg_superquery" ] = ""
authOpts [ "jwt_pg_aclquery" ] = ""
jwt , err := NewLocalJWTChecker ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , tkOptions )
So ( err , ShouldBeNil )
Convey ( "So checking against them should give false and true for any user" , func ( ) {
2021-02-11 22:56:42 +08:00
tt1 , err1 := jwt . CheckAcl ( token , singleLevelACL , clientID , MOSQ_ACL_READ )
tt2 , err2 := jwt . CheckAcl ( token , hierarchyACL , clientID , MOSQ_ACL_READ )
2020-10-31 09:33:53 +08:00
2021-02-11 22:56:42 +08:00
So ( err1 , ShouldBeNil )
So ( err2 , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( tt1 , ShouldBeTrue )
So ( tt2 , ShouldBeTrue )
2021-02-11 22:56:42 +08:00
superuser , err := jwt . GetSuperuser ( token )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
So ( superuser , ShouldBeFalse )
} )
} )
2017-12-09 11:10:37 +08:00
//Empty db
2020-10-31 09:33:53 +08:00
db . DB . MustExec ( "delete from test_user where 1 = 1" )
db . DB . MustExec ( "delete from test_acl where 1 = 1" )
2017-12-09 11:10:37 +08:00
2018-01-09 23:13:18 +08:00
jwt . Halt ( )
2017-12-09 11:10:37 +08:00
} )
} )
}
2017-12-27 23:59:08 +08:00
func TestLocalMysqlJWT ( t * testing . T ) {
Convey ( "Creating a token should return a nil error" , t , func ( ) {
token , err := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
So ( err , ShouldBeNil )
2020-10-31 09:33:53 +08:00
// Initialize JWT in local mode.
2017-12-27 23:59:08 +08:00
authOpts := make ( map [ string ] string )
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mode" ] = "local"
2017-12-27 23:59:08 +08:00
authOpts [ "jwt_db" ] = "mysql"
authOpts [ "jwt_secret" ] = jwtSecret
2018-10-01 21:54:30 +08:00
authOpts [ "jwt_userfield" ] = "Username"
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_userquery" ] = "select count(*) from test_user where username = ? limit 1"
2017-12-27 23:59:08 +08:00
2020-10-31 09:33:53 +08:00
// Give necessary postgres options.
authOpts [ "jwt_mysql_host" ] = "localhost"
authOpts [ "jwt_mysql_port" ] = "3306"
authOpts [ "jwt_mysql_dbname" ] = "go_auth_test"
authOpts [ "jwt_mysql_user" ] = "go_auth_test"
authOpts [ "jwt_mysql_password" ] = "go_auth_test"
authOpts [ "jwt_mysql_allow_native_passwords" ] = "true"
authOpts [ "jwt_mysql_superquery" ] = "select count(*) from test_user where username = ? and is_admin = true"
authOpts [ "jwt_mysql_aclquery" ] = "SELECT test_acl.topic FROM test_acl, test_user WHERE test_user.username = ? AND test_acl.test_user_id = test_user.id AND rw >= ?"
// Set options for our MySQL instance used to create test records.
mysqlAuthOpts := make ( map [ string ] string )
mysqlAuthOpts [ "mysql_host" ] = "localhost"
mysqlAuthOpts [ "mysql_port" ] = "3306"
mysqlAuthOpts [ "mysql_dbname" ] = "go_auth_test"
mysqlAuthOpts [ "mysql_user" ] = "go_auth_test"
mysqlAuthOpts [ "mysql_password" ] = "go_auth_test"
mysqlAuthOpts [ "mysql_allow_native_passwords" ] = "true"
mysqlAuthOpts [ "mysql_userquery" ] = "mock"
mysqlAuthOpts [ "mysql_superquery" ] = "mock"
mysqlAuthOpts [ "mysql_aclquery" ] = "mock"
db , err := NewMysql ( mysqlAuthOpts , log . DebugLevel , hashing . NewHasher ( mysqlAuthOpts , "" ) )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
Convey ( "Given correct option NewJWT returns an instance of jwt backend" , func ( ) {
2020-10-31 09:33:53 +08:00
jwt , err := NewLocalJWTChecker ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , tkOptions )
2017-12-27 23:59:08 +08:00
So ( err , ShouldBeNil )
2020-05-15 11:34:45 +08:00
//Empty db
2020-10-31 09:33:53 +08:00
db . DB . MustExec ( "delete from test_user where 1 = 1" )
db . DB . MustExec ( "delete from test_acl where 1 = 1" )
2017-12-27 23:59:08 +08:00
//Now test everything.
insertQuery := "INSERT INTO test_user(username, password_hash, is_admin) values(?, ?, ?)"
userID := int64 ( 0 )
2020-10-31 09:33:53 +08:00
res , err := db . DB . Exec ( insertQuery , username , userPassHash , true )
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
2020-02-22 12:16:41 +08:00
userID , err = res . LastInsertId ( )
2017-12-27 23:59:08 +08:00
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( userID , ShouldBeGreaterThan , 0 )
Convey ( "Given a correct token, it should correctly authenticate it" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := jwt . GetUser ( token )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given an incorrect token, it should not authenticate it" , func ( ) {
wrongToken , err := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
So ( err , ShouldBeNil )
2021-02-11 22:56:42 +08:00
authenticated , err := jwt . GetUser ( wrongToken )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given a token that is admin, super user should pass" , func ( ) {
2020-11-13 22:41:22 +08:00
superuser , err := jwt . GetSuperuser ( token )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( superuser , ShouldBeTrue )
2020-05-14 10:44:23 +08:00
Convey ( "But disabling superusers by removing superuri should now return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authOpts [ "jwt_mysql_superquery" ] = ""
2020-10-31 09:33:53 +08:00
jwt , err := NewLocalJWTChecker ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , tkOptions )
So ( err , ShouldBeNil )
2020-11-13 22:41:22 +08:00
superuser , err := jwt . GetSuperuser ( token )
So ( err , ShouldBeNil )
2020-05-14 10:44:23 +08:00
So ( superuser , ShouldBeFalse )
} )
2017-12-27 23:59:08 +08:00
} )
2020-10-31 09:33:53 +08:00
strictACL := "test/topic/1"
singleLevelACL := "test/topic/+"
hierarchyACL := "test/#"
2017-12-27 23:59:08 +08:00
clientID := "test_client"
aclID := int64 ( 0 )
aclQuery := "INSERT INTO test_acl(test_user_id, topic, rw) values(?, ?, ?)"
2020-10-31 09:33:53 +08:00
res , err = db . DB . Exec ( aclQuery , userID , strictACL , MOSQ_ACL_READ )
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
aclID , err = res . LastInsertId ( )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( aclID , ShouldBeGreaterThan , 0 )
2020-05-15 11:34:45 +08:00
Convey ( "Given only strict acl in db, an exact match should work and and inexact one not" , func ( ) {
2017-12-27 23:59:08 +08:00
testTopic1 := ` test/topic/1 `
testTopic2 := ` test/topic/2 `
2020-11-13 22:41:22 +08:00
tt1 , err1 := jwt . CheckAcl ( token , testTopic1 , clientID , MOSQ_ACL_READ )
tt2 , err2 := jwt . CheckAcl ( token , testTopic2 , clientID , MOSQ_ACL_READ )
2017-12-27 23:59:08 +08:00
2020-11-13 22:41:22 +08:00
So ( err1 , ShouldBeNil )
So ( err2 , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( tt1 , ShouldBeTrue )
So ( tt2 , ShouldBeFalse )
} )
Convey ( "Given read only privileges, a pub check should fail" , func ( ) {
testTopic1 := "test/topic/1"
2020-11-13 22:41:22 +08:00
tt1 , err1 := jwt . CheckAcl ( token , testTopic1 , clientID , MOSQ_ACL_WRITE )
So ( err1 , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( tt1 , ShouldBeFalse )
} )
Convey ( "Given wildcard subscriptions against strict db acl, acl checks should fail" , func ( ) {
2021-02-11 22:56:42 +08:00
tt1 , err1 := jwt . CheckAcl ( token , singleLevelACL , clientID , MOSQ_ACL_READ )
tt2 , err2 := jwt . CheckAcl ( token , hierarchyACL , clientID , MOSQ_ACL_READ )
2017-12-27 23:59:08 +08:00
2020-11-13 22:41:22 +08:00
So ( err1 , ShouldBeNil )
So ( err2 , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( tt1 , ShouldBeFalse )
So ( tt2 , ShouldBeFalse )
} )
//Now insert single level topic to check against.
2020-10-31 09:33:53 +08:00
_ , err = db . DB . Exec ( aclQuery , userID , singleLevelACL , MOSQ_ACL_READ )
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
Convey ( "Given a topic not strictly present that matches a db single level wildcard, acl check should pass" , func ( ) {
2020-11-13 22:41:22 +08:00
tt1 , err1 := jwt . CheckAcl ( token , "test/topic/whatever" , clientID , MOSQ_ACL_READ )
So ( err1 , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( tt1 , ShouldBeTrue )
} )
//Now insert hierarchy wildcard to check against.
2020-10-31 09:33:53 +08:00
_ , err = db . DB . Exec ( aclQuery , userID , hierarchyACL , MOSQ_ACL_READ )
2020-02-22 12:16:41 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
Convey ( "Given a topic not strictly present that matches a hierarchy wildcard, acl check should pass" , func ( ) {
2020-11-13 22:41:22 +08:00
tt1 , err1 := jwt . CheckAcl ( token , "test/what/ever" , clientID , MOSQ_ACL_READ )
So ( err1 , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( tt1 , ShouldBeTrue )
} )
2018-02-08 22:17:54 +08:00
Convey ( "Deleting superuser and acl queries should work fine" , func ( ) {
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mysql_superquery" ] = ""
authOpts [ "jwt_mysql_aclquery" ] = ""
jwt , err := NewLocalJWTChecker ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , tkOptions )
So ( err , ShouldBeNil )
2018-02-08 22:17:54 +08:00
Convey ( "So checking against them should give false and true for any user" , func ( ) {
2021-02-11 22:56:42 +08:00
tt1 , err1 := jwt . CheckAcl ( token , singleLevelACL , clientID , MOSQ_ACL_READ )
tt2 , err2 := jwt . CheckAcl ( token , hierarchyACL , clientID , MOSQ_ACL_READ )
2018-02-08 22:17:54 +08:00
2020-11-13 22:41:22 +08:00
So ( err1 , ShouldBeNil )
So ( err2 , ShouldBeNil )
2018-02-08 22:17:54 +08:00
So ( tt1 , ShouldBeTrue )
So ( tt2 , ShouldBeTrue )
2020-11-13 22:41:22 +08:00
superuser , err := jwt . GetSuperuser ( token )
So ( err , ShouldBeNil )
2018-02-08 22:17:54 +08:00
So ( superuser , ShouldBeFalse )
} )
} )
2020-10-31 09:33:53 +08:00
//Empty db
db . DB . MustExec ( "delete from test_user where 1 = 1" )
db . DB . MustExec ( "delete from test_acl where 1 = 1" )
2018-01-09 23:13:18 +08:00
jwt . Halt ( )
2017-12-27 23:59:08 +08:00
} )
} )
}
func TestJWTAllJsonServer ( t * testing . T ) {
topic := "test/topic"
var acc = int64 ( 1 )
2020-10-31 09:33:53 +08:00
clientID := "test_client"
2017-12-27 23:59:08 +08:00
2021-07-12 09:52:22 +08:00
version := "2.0.0"
2017-12-27 23:59:08 +08:00
token , _ := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
wrongToken , _ := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
mockServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
httpResponse := & HTTPResponse {
Ok : true ,
Error : "" ,
}
var jsonResponse [ ] byte
w . WriteHeader ( http . StatusOK )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2020-05-14 10:44:23 +08:00
gToken := r . Header . Get ( "Authorization" )
gToken = strings . TrimPrefix ( gToken , "Bearer " )
2017-12-27 23:59:08 +08:00
2020-05-14 10:44:23 +08:00
if token != gToken {
httpResponse . Ok = false
httpResponse . Error = "Wrong token."
} else {
switch r . URL . Path {
case "/user" , "/superuser" :
2017-12-27 23:59:08 +08:00
httpResponse . Ok = true
httpResponse . Error = ""
2020-05-14 10:44:23 +08:00
case "/acl" :
var data interface { }
var params map [ string ] interface { }
2017-12-27 23:59:08 +08:00
2020-05-14 10:44:23 +08:00
body , _ := ioutil . ReadAll ( r . Body )
defer r . Body . Close ( )
2017-12-27 23:59:08 +08:00
2020-05-14 10:44:23 +08:00
err := json . Unmarshal ( body , & data )
2017-12-27 23:59:08 +08:00
2020-05-14 10:44:23 +08:00
if err != nil {
httpResponse . Ok = false
httpResponse . Error = "Json unmarshal error"
break
}
2017-12-27 23:59:08 +08:00
params = data . ( map [ string ] interface { } )
paramsAcc := int64 ( params [ "acc" ] . ( float64 ) )
2020-10-31 09:33:53 +08:00
if params [ "topic" ] . ( string ) == topic && params [ "clientid" ] . ( string ) == clientID && paramsAcc <= acc {
2017-12-27 23:59:08 +08:00
httpResponse . Ok = true
httpResponse . Error = ""
2020-05-14 10:44:23 +08:00
break
2017-12-27 23:59:08 +08:00
}
2020-05-14 10:44:23 +08:00
httpResponse . Ok = false
httpResponse . Error = "Acl check failed."
2017-12-27 23:59:08 +08:00
}
}
2020-02-22 12:16:41 +08:00
jsonResponse , err := json . Marshal ( httpResponse )
if err != nil {
2017-12-27 23:59:08 +08:00
w . Write ( [ ] byte ( "error" ) )
}
w . Write ( jsonResponse )
} ) )
defer mockServer . Close ( )
authOpts := make ( map [ string ] string )
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mode" ] = "remote"
2017-12-27 23:59:08 +08:00
authOpts [ "jwt_params_mode" ] = "json"
authOpts [ "jwt_response_mode" ] = "json"
authOpts [ "jwt_port" ] = ""
authOpts [ "jwt_getuser_uri" ] = "/user"
authOpts [ "jwt_superuser_uri" ] = "/superuser"
authOpts [ "jwt_aclcheck_uri" ] = "/acl"
2022-05-21 18:52:43 +08:00
parseTkOptions := tkOptions
parseTkOptions . parseToken = true
Convey ( "Given inconsistent auth options, NewRemoteJWTChecker should fail" , t , func ( ) {
Convey ( "Given jwt_host is not set, jwt_host_whitelist should be set and valid" , func ( ) {
authOpts [ "jwt_host_whitelist" ] = ""
_ , err := NewRemoteJWTChecker ( authOpts , parseTkOptions , version )
So ( err , ShouldNotBeNil )
authOpts [ "jwt_host_whitelist" ] = "good-host:8000, bad_host"
_ , err = NewRemoteJWTChecker ( authOpts , parseTkOptions , version )
So ( err , ShouldNotBeNil )
} )
authOpts [ "jwt_host_whitelist" ] = "*"
Convey ( "Given jwt_host is not set, jwt_parse_token should be true" , func ( ) {
_ , err := NewRemoteJWTChecker ( authOpts , tkOptions , version )
So ( err , ShouldNotBeNil )
} )
} )
Convey ( "Given consistent auth options, NewRemoteJWTChecker should be created" , t , func ( ) {
authOpts [ "jwt_host_whitelist" ] = "good-host:8000, 10.0.0.1:10, some.good.host, 10.0.0.2"
_ , err := NewRemoteJWTChecker ( authOpts , parseTkOptions , version )
So ( err , ShouldBeNil )
} )
authOpts [ "jwt_host" ] = strings . Replace ( mockServer . URL , "http://" , "" , - 1 )
2017-12-27 23:59:08 +08:00
Convey ( "Given correct options an http backend instance should be returned" , t , func ( ) {
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2017-12-27 23:59:08 +08:00
So ( err , ShouldBeNil )
Convey ( "Given correct password/username, get user should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( token , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given incorrect password/username, get user should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( wrongToken , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct username, get superuser should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( token )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
2020-05-14 10:44:23 +08:00
Convey ( "But disabling superusers by removing superuri should now return false" , func ( ) {
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_superuser_uri" ] = ""
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2020-10-31 09:33:53 +08:00
So ( err , ShouldBeNil )
2020-11-13 22:41:22 +08:00
superuser , err := hb . GetSuperuser ( username )
So ( err , ShouldBeNil )
2020-05-14 10:44:23 +08:00
So ( superuser , ShouldBeFalse )
} )
2017-12-27 23:59:08 +08:00
} )
Convey ( "Given incorrect username, get superuser should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( wrongToken )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct topic, username, client id and acc, acl check should return true" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given an acc that requires more privileges than the user has, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_WRITE )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given a topic not present in acls, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , "fake/topic" , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2020-10-31 09:33:53 +08:00
Convey ( "Given a clientID that doesn't match, check acl should return false" , func ( ) {
2017-12-27 23:59:08 +08:00
2020-11-13 22:41:22 +08:00
authenticated , err := hb . CheckAcl ( token , topic , "fake_client_id" , MOSQ_ACL_READ )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2018-01-09 23:13:18 +08:00
hb . Halt ( )
2017-12-27 23:59:08 +08:00
} )
}
func TestJWTJsonStatusOnlyServer ( t * testing . T ) {
topic := "test/topic"
var acc = int64 ( 1 )
2020-10-31 09:33:53 +08:00
clientID := "test_client"
2017-12-27 23:59:08 +08:00
token , _ := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
wrongToken , _ := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
2021-07-12 09:52:22 +08:00
version := "2.0.0"
2017-12-27 23:59:08 +08:00
mockServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
var data interface { }
var params map [ string ] interface { }
body , _ := ioutil . ReadAll ( r . Body )
defer r . Body . Close ( )
2020-02-22 12:16:41 +08:00
err := json . Unmarshal ( body , & data )
2017-12-27 23:59:08 +08:00
2020-02-22 12:16:41 +08:00
if err != nil {
2017-12-27 23:59:08 +08:00
w . WriteHeader ( http . StatusBadRequest )
}
2020-05-14 10:44:23 +08:00
gToken := r . Header . Get ( "Authorization" )
gToken = strings . TrimPrefix ( gToken , "Bearer " )
2017-12-27 23:59:08 +08:00
2020-05-14 10:44:23 +08:00
if token != gToken {
w . WriteHeader ( http . StatusNotFound )
return
}
switch r . URL . Path {
case "/user" , "/superuser" :
w . WriteHeader ( http . StatusOK )
case "/acl" :
2017-12-27 23:59:08 +08:00
params = data . ( map [ string ] interface { } )
paramsAcc := int64 ( params [ "acc" ] . ( float64 ) )
2020-10-31 09:33:53 +08:00
if params [ "topic" ] . ( string ) == topic && params [ "clientid" ] . ( string ) == clientID && paramsAcc <= acc {
2017-12-27 23:59:08 +08:00
w . WriteHeader ( http . StatusOK )
2020-05-14 10:44:23 +08:00
break
2017-12-27 23:59:08 +08:00
}
2020-05-14 10:44:23 +08:00
w . WriteHeader ( http . StatusNotFound )
2017-12-27 23:59:08 +08:00
}
} ) )
defer mockServer . Close ( )
authOpts := make ( map [ string ] string )
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mode" ] = "remote"
2017-12-27 23:59:08 +08:00
authOpts [ "jwt_params_mode" ] = "json"
authOpts [ "jwt_response_mode" ] = "status"
authOpts [ "jwt_host" ] = strings . Replace ( mockServer . URL , "http://" , "" , - 1 )
authOpts [ "jwt_port" ] = ""
authOpts [ "jwt_getuser_uri" ] = "/user"
authOpts [ "jwt_superuser_uri" ] = "/superuser"
authOpts [ "jwt_aclcheck_uri" ] = "/acl"
Convey ( "Given correct options an http backend instance should be returned" , t , func ( ) {
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2017-12-27 23:59:08 +08:00
So ( err , ShouldBeNil )
2023-05-24 11:31:32 +08:00
So ( hb . GetName ( ) , ShouldEqual , "JWT remote" )
2017-12-27 23:59:08 +08:00
Convey ( "Given correct password/username, get user should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( token , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given incorrect password/username, get user should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( wrongToken , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct username, get superuser should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( token )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
2020-05-14 10:44:23 +08:00
Convey ( "But disabling superusers by removing superuri should now return false" , func ( ) {
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_superuser_uri" ] = ""
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2020-10-31 09:33:53 +08:00
So ( err , ShouldBeNil )
2020-11-13 22:41:22 +08:00
superuser , err := hb . GetSuperuser ( username )
So ( err , ShouldBeNil )
2020-05-14 10:44:23 +08:00
So ( superuser , ShouldBeFalse )
} )
2017-12-27 23:59:08 +08:00
} )
Convey ( "Given incorrect username, get superuser should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( wrongToken )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct topic, username, client id and acc, acl check should return true" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given an acc that requires more privileges than the user has, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_WRITE )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given a topic not present in acls, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , "fake/topic" , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2020-10-31 09:33:53 +08:00
Convey ( "Given a clientID that doesn't match, check acl should return false" , func ( ) {
2017-12-27 23:59:08 +08:00
2020-11-13 22:41:22 +08:00
authenticated , err := hb . CheckAcl ( token , topic , "fake_client_id" , MOSQ_ACL_READ )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2018-01-09 23:13:18 +08:00
hb . Halt ( )
2017-12-27 23:59:08 +08:00
} )
}
func TestJWTJsonTextResponseServer ( t * testing . T ) {
topic := "test/topic"
var acc = int64 ( 1 )
2020-10-31 09:33:53 +08:00
clientID := "test_client"
2017-12-27 23:59:08 +08:00
token , _ := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
wrongToken , _ := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
2021-07-12 09:52:22 +08:00
version := "2.0.0"
2017-12-27 23:59:08 +08:00
mockServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
var data interface { }
var params map [ string ] interface { }
body , _ := ioutil . ReadAll ( r . Body )
defer r . Body . Close ( )
2020-02-22 12:16:41 +08:00
err := json . Unmarshal ( body , & data )
2017-12-27 23:59:08 +08:00
w . WriteHeader ( http . StatusOK )
2020-02-22 12:16:41 +08:00
if err != nil {
w . Write ( [ ] byte ( err . Error ( ) ) )
2017-12-27 23:59:08 +08:00
}
2020-05-14 10:44:23 +08:00
gToken := r . Header . Get ( "Authorization" )
gToken = strings . TrimPrefix ( gToken , "Bearer " )
2017-12-27 23:59:08 +08:00
2020-05-14 10:44:23 +08:00
if token != gToken {
w . Write ( [ ] byte ( "Wrong credentials." ) )
return
}
switch r . URL . Path {
case "/user" , "/superuser" :
w . Write ( [ ] byte ( "ok" ) )
case "/acl" :
2017-12-27 23:59:08 +08:00
params = data . ( map [ string ] interface { } )
paramsAcc := int64 ( params [ "acc" ] . ( float64 ) )
2020-10-31 09:33:53 +08:00
if params [ "topic" ] . ( string ) == topic && params [ "clientid" ] . ( string ) == clientID && paramsAcc <= acc {
2017-12-27 23:59:08 +08:00
w . Write ( [ ] byte ( "ok" ) )
2020-05-14 10:44:23 +08:00
break
2017-12-27 23:59:08 +08:00
}
2020-05-14 10:44:23 +08:00
w . Write ( [ ] byte ( "Acl check failed." ) )
2017-12-27 23:59:08 +08:00
}
} ) )
defer mockServer . Close ( )
authOpts := make ( map [ string ] string )
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mode" ] = "remote"
2017-12-27 23:59:08 +08:00
authOpts [ "jwt_params_mode" ] = "json"
authOpts [ "jwt_response_mode" ] = "text"
authOpts [ "jwt_host" ] = strings . Replace ( mockServer . URL , "http://" , "" , - 1 )
authOpts [ "jwt_port" ] = ""
authOpts [ "jwt_getuser_uri" ] = "/user"
authOpts [ "jwt_superuser_uri" ] = "/superuser"
authOpts [ "jwt_aclcheck_uri" ] = "/acl"
Convey ( "Given correct options an http backend instance should be returned" , t , func ( ) {
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2017-12-27 23:59:08 +08:00
So ( err , ShouldBeNil )
2023-05-24 11:31:32 +08:00
So ( hb . GetName ( ) , ShouldEqual , "JWT remote" )
2017-12-27 23:59:08 +08:00
Convey ( "Given correct password/username, get user should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( token , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given incorrect password/username, get user should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( wrongToken , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct username, get superuser should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( token )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
2020-05-14 10:44:23 +08:00
Convey ( "But disabling superusers by removing superuri should now return false" , func ( ) {
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_superuser_uri" ] = ""
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2020-10-31 09:33:53 +08:00
So ( err , ShouldBeNil )
2020-11-13 22:41:22 +08:00
superuser , err := hb . GetSuperuser ( username )
So ( err , ShouldBeNil )
2020-05-14 10:44:23 +08:00
So ( superuser , ShouldBeFalse )
} )
2017-12-27 23:59:08 +08:00
} )
Convey ( "Given incorrect username, get superuser should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( wrongToken )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct topic, username, client id and acc, acl check should return true" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given an acc that requires more privileges than the user has, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_WRITE )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given a topic not present in acls, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , "fake/topic" , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2020-10-31 09:33:53 +08:00
Convey ( "Given a clientID that doesn't match, check acl should return false" , func ( ) {
2017-12-27 23:59:08 +08:00
2020-11-13 22:41:22 +08:00
authenticated , err := hb . CheckAcl ( token , topic , "fake_client_id" , MOSQ_ACL_READ )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2018-01-09 23:13:18 +08:00
hb . Halt ( )
2017-12-27 23:59:08 +08:00
} )
}
func TestJWTFormJsonResponseServer ( t * testing . T ) {
topic := "test/topic"
var acc = int64 ( 1 )
2020-10-31 09:33:53 +08:00
clientID := "test_client"
2017-12-27 23:59:08 +08:00
token , _ := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
wrongToken , _ := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
2021-07-12 09:52:22 +08:00
version := "2.0.0"
2017-12-27 23:59:08 +08:00
mockServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
httpResponse := & HTTPResponse {
Ok : true ,
Error : "" ,
}
2020-02-22 12:16:41 +08:00
err := r . ParseForm ( )
if err != nil {
2017-12-27 23:59:08 +08:00
w . WriteHeader ( http . StatusBadRequest )
return
}
var params = r . Form
w . WriteHeader ( http . StatusOK )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2020-05-14 10:44:23 +08:00
gToken := r . Header . Get ( "Authorization" )
gToken = strings . TrimPrefix ( gToken , "Bearer " )
2017-12-27 23:59:08 +08:00
2020-05-14 10:44:23 +08:00
if token != gToken {
httpResponse . Ok = false
httpResponse . Error = "Wrong credentials."
} else {
switch r . URL . Path {
case "/user" , "/superuser" :
2017-12-27 23:59:08 +08:00
httpResponse . Ok = true
httpResponse . Error = ""
2020-05-14 10:44:23 +08:00
case "/acl" :
paramsAcc , _ := strconv . ParseInt ( params [ "acc" ] [ 0 ] , 10 , 64 )
2020-10-31 09:33:53 +08:00
if params [ "topic" ] [ 0 ] == topic && params [ "clientid" ] [ 0 ] == clientID && paramsAcc <= acc {
2020-05-14 10:44:23 +08:00
httpResponse . Ok = true
httpResponse . Error = ""
break
}
2017-12-27 23:59:08 +08:00
httpResponse . Ok = false
httpResponse . Error = "Acl check failed."
}
}
2020-02-22 12:16:41 +08:00
jsonResponse , err := json . Marshal ( httpResponse )
if err != nil {
2017-12-27 23:59:08 +08:00
w . Write ( [ ] byte ( "error" ) )
}
w . Write ( jsonResponse )
} ) )
defer mockServer . Close ( )
authOpts := make ( map [ string ] string )
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mode" ] = "remote"
2017-12-27 23:59:08 +08:00
authOpts [ "jwt_params_mode" ] = "form"
authOpts [ "jwt_response_mode" ] = "json"
authOpts [ "jwt_host" ] = strings . Replace ( mockServer . URL , "http://" , "" , - 1 )
authOpts [ "jwt_port" ] = ""
authOpts [ "jwt_getuser_uri" ] = "/user"
authOpts [ "jwt_superuser_uri" ] = "/superuser"
authOpts [ "jwt_aclcheck_uri" ] = "/acl"
Convey ( "Given correct options an http backend instance should be returned" , t , func ( ) {
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2017-12-27 23:59:08 +08:00
So ( err , ShouldBeNil )
2023-05-24 11:31:32 +08:00
So ( hb . GetName ( ) , ShouldEqual , "JWT remote" )
2017-12-27 23:59:08 +08:00
Convey ( "Given correct password/username, get user should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( token , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given incorrect password/username, get user should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( wrongToken , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct username, get superuser should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( token )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
2020-05-14 10:44:23 +08:00
Convey ( "But disabling superusers by removing superuri should now return false" , func ( ) {
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_superuser_uri" ] = ""
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2020-10-31 09:33:53 +08:00
So ( err , ShouldBeNil )
2020-11-13 22:41:22 +08:00
superuser , err := hb . GetSuperuser ( username )
So ( err , ShouldBeNil )
2020-05-14 10:44:23 +08:00
So ( superuser , ShouldBeFalse )
} )
2017-12-27 23:59:08 +08:00
} )
Convey ( "Given incorrect username, get superuser should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( wrongToken )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct topic, username, client id and acc, acl check should return true" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given an acc that requires more privileges than the user has, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_WRITE )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given a topic not present in acls, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , "fake/topic" , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2020-10-31 09:33:53 +08:00
Convey ( "Given a clientID that doesn't match, check acl should return false" , func ( ) {
2017-12-27 23:59:08 +08:00
2020-11-13 22:41:22 +08:00
authenticated , err := hb . CheckAcl ( token , topic , "fake_client_id" , MOSQ_ACL_READ )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2018-01-09 23:13:18 +08:00
hb . Halt ( )
2017-12-27 23:59:08 +08:00
} )
}
func TestJWTFormStatusOnlyServer ( t * testing . T ) {
topic := "test/topic"
var acc = int64 ( 1 )
2020-10-31 09:33:53 +08:00
clientID := "test_client"
2017-12-27 23:59:08 +08:00
token , _ := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
wrongToken , _ := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
2021-07-12 09:52:22 +08:00
version := "2.0.0"
2022-05-21 18:52:43 +08:00
rightToken := token
2017-12-27 23:59:08 +08:00
mockServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2020-02-22 12:16:41 +08:00
err := r . ParseForm ( )
if err != nil {
2017-12-27 23:59:08 +08:00
w . WriteHeader ( http . StatusBadRequest )
return
}
var params = r . Form
2020-05-14 10:44:23 +08:00
gToken := r . Header . Get ( "Authorization" )
gToken = strings . TrimPrefix ( gToken , "Bearer " )
2017-12-27 23:59:08 +08:00
2022-05-21 18:52:43 +08:00
if rightToken != gToken {
2020-05-14 10:44:23 +08:00
w . WriteHeader ( http . StatusNotFound )
return
}
switch r . URL . Path {
case "/user" , "/superuser" :
w . WriteHeader ( http . StatusOK )
case "/acl" :
2017-12-27 23:59:08 +08:00
paramsAcc , _ := strconv . ParseInt ( params [ "acc" ] [ 0 ] , 10 , 64 )
2020-10-31 09:33:53 +08:00
if params [ "topic" ] [ 0 ] == topic && params [ "clientid" ] [ 0 ] == clientID && paramsAcc <= acc {
2017-12-27 23:59:08 +08:00
w . WriteHeader ( http . StatusOK )
2020-05-14 10:44:23 +08:00
break
2017-12-27 23:59:08 +08:00
}
2020-05-14 10:44:23 +08:00
w . WriteHeader ( http . StatusNotFound )
2017-12-27 23:59:08 +08:00
}
} ) )
defer mockServer . Close ( )
authOpts := make ( map [ string ] string )
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mode" ] = "remote"
2017-12-27 23:59:08 +08:00
authOpts [ "jwt_params_mode" ] = "form"
authOpts [ "jwt_response_mode" ] = "status"
authOpts [ "jwt_host" ] = strings . Replace ( mockServer . URL , "http://" , "" , - 1 )
authOpts [ "jwt_port" ] = ""
authOpts [ "jwt_getuser_uri" ] = "/user"
authOpts [ "jwt_superuser_uri" ] = "/superuser"
authOpts [ "jwt_aclcheck_uri" ] = "/acl"
Convey ( "Given correct options an http backend instance should be returned" , t , func ( ) {
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2017-12-27 23:59:08 +08:00
So ( err , ShouldBeNil )
2023-05-24 11:31:32 +08:00
So ( hb . GetName ( ) , ShouldEqual , "JWT remote" )
2017-12-27 23:59:08 +08:00
Convey ( "Given correct password/username, get user should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( token , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given incorrect password/username, get user should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( wrongToken , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct username, get superuser should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( token )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
2020-05-14 10:44:23 +08:00
Convey ( "But disabling superusers by removing superuri should now return false" , func ( ) {
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_superuser_uri" ] = ""
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2020-10-31 09:33:53 +08:00
So ( err , ShouldBeNil )
2020-11-13 22:41:22 +08:00
superuser , err := hb . GetSuperuser ( username )
So ( err , ShouldBeNil )
2020-05-14 10:44:23 +08:00
So ( superuser , ShouldBeFalse )
} )
2017-12-27 23:59:08 +08:00
} )
Convey ( "Given incorrect username, get superuser should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( wrongToken )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct topic, username, client id and acc, acl check should return true" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given an acc that requires more privileges than the user has, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_WRITE )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given a topic not present in acls, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , "fake/topic" , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2020-10-31 09:33:53 +08:00
Convey ( "Given a clientID that doesn't match, check acl should return false" , func ( ) {
2017-12-27 23:59:08 +08:00
2020-11-13 22:41:22 +08:00
authenticated , err := hb . CheckAcl ( token , topic , "fake_client_id" , MOSQ_ACL_READ )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2018-01-09 23:13:18 +08:00
hb . Halt ( )
2017-12-27 23:59:08 +08:00
} )
2022-05-21 18:52:43 +08:00
serverHostAddr := strings . Replace ( mockServer . URL , "http://" , "" , - 1 )
authOpts [ "jwt_host" ] = ""
authOpts [ "jwt_parse_token" ] = "true"
authOpts [ "jwt_secret" ] = jwtSecret
tokenWithIss , _ := jwt . NewWithClaims ( jwt . SigningMethodHS256 , jwt . MapClaims {
"iss" : serverHostAddr ,
"aud" : "jwt-test" ,
"nbf" : nowSecondsSinceEpoch ,
"exp" : expSecondsSinceEpoch ,
"sub" : "user" ,
"username" : username ,
} ) . SignedString ( [ ] byte ( jwtSecret ) )
wrongIssToken , _ := jwt . NewWithClaims ( jwt . SigningMethodHS256 , jwt . MapClaims {
"iss" : serverHostAddr ,
"aud" : "jwt-test" ,
"nbf" : nowSecondsSinceEpoch ,
"exp" : expSecondsSinceEpoch ,
"sub" : "user" ,
"username" : "wrong_user" ,
} ) . SignedString ( [ ] byte ( jwtSecret ) )
rightToken = tokenWithIss
Convey ( "Given empty jwt_host field and correct iss claim authorization should work" , t , func ( ) {
authOpts [ "jwt_host_whitelist" ] = serverHostAddr + ", sometherhost"
hbWhitelistedHost , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
So ( err , ShouldBeNil )
Convey ( "Given correct password/username and iss host is whitelisted, get user should return true" , func ( ) {
authenticated , err := hbWhitelistedHost . GetUser ( tokenWithIss , "" , "" )
So ( err , ShouldBeNil )
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given incorrect password/username, get user should return false" , func ( ) {
authenticated , err := hbWhitelistedHost . GetUser ( wrongIssToken , "" , "" )
So ( err , ShouldBeNil )
So ( authenticated , ShouldBeFalse )
} )
authOpts [ "jwt_port" ] = "12345"
hbWhitelistedHostBadConfigPort , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
So ( err , ShouldBeNil )
Convey ( "Given jwt_port is present in config, port from iss field should be used anyway" , func ( ) {
authenticated , err := hbWhitelistedHostBadConfigPort . GetUser ( tokenWithIss , "" , "" )
So ( err , ShouldBeNil )
So ( authenticated , ShouldBeTrue )
} )
authOpts [ "jwt_host_whitelist" ] = "*"
hbAnyHost , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
So ( err , ShouldBeNil )
Convey ( "Given correct password/username and all hosts are allowed, get user should return true" , func ( ) {
authenticated , err := hbAnyHost . GetUser ( tokenWithIss , "" , "" )
So ( err , ShouldBeNil )
So ( authenticated , ShouldBeTrue )
} )
authOpts [ "jwt_host_whitelist" ] = "otherhost1, otherhost2"
hbBadHost , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
So ( err , ShouldBeNil )
Convey ( "Given host from iss is not whitelisted, get user should fail even if the credentials are correct" , func ( ) {
authenticated , err := hbBadHost . GetUser ( tokenWithIss , "" , "" )
So ( err , ShouldNotBeNil )
So ( authenticated , ShouldBeFalse )
} )
} )
2017-12-27 23:59:08 +08:00
}
func TestJWTFormTextResponseServer ( t * testing . T ) {
topic := "test/topic"
var acc = int64 ( 1 )
2020-10-31 09:33:53 +08:00
clientID := "test_client"
2017-12-27 23:59:08 +08:00
token , _ := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
wrongToken , _ := wrongJwtToken . SignedString ( [ ] byte ( jwtSecret ) )
2021-07-12 09:52:22 +08:00
version := "2.0.0"
2017-12-27 23:59:08 +08:00
mockServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( http . StatusOK )
2020-02-22 12:16:41 +08:00
err := r . ParseForm ( )
if err != nil {
2017-12-27 23:59:08 +08:00
w . WriteHeader ( http . StatusBadRequest )
return
}
var params = r . Form
2020-05-14 10:44:23 +08:00
gToken := r . Header . Get ( "Authorization" )
gToken = strings . TrimPrefix ( gToken , "Bearer " )
2017-12-27 23:59:08 +08:00
2020-05-14 10:44:23 +08:00
if token != gToken {
w . Write ( [ ] byte ( "Wrong credentials." ) )
return
}
switch r . URL . Path {
case "/user" , "/superuser" :
w . Write ( [ ] byte ( "ok" ) )
case "/acl" :
2017-12-27 23:59:08 +08:00
paramsAcc , _ := strconv . ParseInt ( params [ "acc" ] [ 0 ] , 10 , 64 )
2020-10-31 09:33:53 +08:00
if params [ "topic" ] [ 0 ] == topic && params [ "clientid" ] [ 0 ] == clientID && paramsAcc <= acc {
2017-12-27 23:59:08 +08:00
w . Write ( [ ] byte ( "ok" ) )
2020-05-14 10:44:23 +08:00
break
2017-12-27 23:59:08 +08:00
}
2020-05-14 10:44:23 +08:00
w . Write ( [ ] byte ( "Acl check failed." ) )
2017-12-27 23:59:08 +08:00
}
} ) )
defer mockServer . Close ( )
authOpts := make ( map [ string ] string )
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_mode" ] = "remote"
2017-12-27 23:59:08 +08:00
authOpts [ "jwt_params_mode" ] = "form"
authOpts [ "jwt_response_mode" ] = "text"
authOpts [ "jwt_host" ] = strings . Replace ( mockServer . URL , "http://" , "" , - 1 )
authOpts [ "jwt_port" ] = ""
authOpts [ "jwt_getuser_uri" ] = "/user"
authOpts [ "jwt_superuser_uri" ] = "/superuser"
authOpts [ "jwt_aclcheck_uri" ] = "/acl"
Convey ( "Given correct options an http backend instance should be returned" , t , func ( ) {
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2017-12-27 23:59:08 +08:00
So ( err , ShouldBeNil )
Convey ( "Given correct password/username, get user should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( token , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given incorrect password/username, get user should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetUser ( wrongToken , "" , "" )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct username, get superuser should return true" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( token )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
2020-05-14 10:44:23 +08:00
Convey ( "But disabling superusers by removing superuri should now return false" , func ( ) {
2020-10-31 09:33:53 +08:00
authOpts [ "jwt_superuser_uri" ] = ""
2021-07-12 09:52:22 +08:00
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
2020-10-31 09:33:53 +08:00
So ( err , ShouldBeNil )
2020-11-13 22:41:22 +08:00
superuser , err := hb . GetSuperuser ( username )
So ( err , ShouldBeNil )
2020-05-14 10:44:23 +08:00
So ( superuser , ShouldBeFalse )
} )
2017-12-27 23:59:08 +08:00
} )
Convey ( "Given incorrect username, get superuser should return false" , func ( ) {
2020-11-13 22:41:22 +08:00
authenticated , err := hb . GetSuperuser ( wrongToken )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given correct topic, username, client id and acc, acl check should return true" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeTrue )
} )
Convey ( "Given an acc that requires more privileges than the user has, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_WRITE )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
Convey ( "Given a topic not present in acls, check acl should return false" , func ( ) {
2021-02-11 22:56:42 +08:00
authenticated , err := hb . CheckAcl ( token , "fake/topic" , clientID , MOSQ_ACL_READ )
2020-11-13 22:41:22 +08:00
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2020-10-31 09:33:53 +08:00
Convey ( "Given a clientID that doesn't match, check acl should return false" , func ( ) {
2017-12-27 23:59:08 +08:00
2020-11-13 22:41:22 +08:00
authenticated , err := hb . CheckAcl ( token , topic , "fake_client_id" , MOSQ_ACL_READ )
So ( err , ShouldBeNil )
2017-12-27 23:59:08 +08:00
So ( authenticated , ShouldBeFalse )
} )
2018-01-09 23:13:18 +08:00
hb . Halt ( )
2017-12-27 23:59:08 +08:00
} )
}
2023-06-09 20:29:01 +08:00
func TestJWTHttpTimeout ( t * testing . T ) {
topic := "test/topic"
var acc = int64 ( 1 )
clientID := "test_client"
token , _ := jwtToken . SignedString ( [ ] byte ( jwtSecret ) )
version := "2.0.0"
mockServer := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( http . StatusOK )
err := r . ParseForm ( )
if err != nil {
w . WriteHeader ( http . StatusBadRequest )
return
}
var params = r . Form
gToken := r . Header . Get ( "Authorization" )
gToken = strings . TrimPrefix ( gToken , "Bearer " )
if token != gToken {
w . Write ( [ ] byte ( "Wrong credentials." ) )
return
}
switch r . URL . Path {
case "/user" , "/superuser" :
w . Write ( [ ] byte ( "ok" ) )
case "/acl" :
time . Sleep ( time . Duration ( 1200 ) * time . Millisecond )
paramsAcc , _ := strconv . ParseInt ( params [ "acc" ] [ 0 ] , 10 , 64 )
if params [ "topic" ] [ 0 ] == topic && params [ "clientid" ] [ 0 ] == clientID && paramsAcc <= acc {
w . Write ( [ ] byte ( "ok" ) )
break
}
w . Write ( [ ] byte ( "Acl check failed." ) )
}
} ) )
defer mockServer . Close ( )
authOpts := make ( map [ string ] string )
authOpts [ "jwt_mode" ] = "remote"
authOpts [ "jwt_params_mode" ] = "form"
authOpts [ "jwt_response_mode" ] = "text"
authOpts [ "jwt_host" ] = strings . Replace ( mockServer . URL , "http://" , "" , - 1 )
authOpts [ "jwt_port" ] = ""
authOpts [ "jwt_getuser_uri" ] = "/user"
authOpts [ "jwt_superuser_uri" ] = "/superuser"
authOpts [ "jwt_aclcheck_uri" ] = "/acl"
authOpts [ "jwt_http_timeout" ] = "1"
Convey ( "Given correct options an http backend instance should be returned" , t , func ( ) {
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
So ( err , ShouldBeNil )
Convey ( "JWT remote test timeout parameter: YES TIMEOUT" , func ( ) {
_ , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_READ )
So ( err , ShouldBeError )
So ( err . Error ( ) , ShouldContainSubstring , "acl" )
} )
hb . Halt ( )
} )
authOpts [ "jwt_http_timeout" ] = "2"
Convey ( "Given correct options an http backend instance should be returned" , t , func ( ) {
hb , err := NewJWT ( authOpts , log . DebugLevel , hashing . NewHasher ( authOpts , "" ) , version )
So ( err , ShouldBeNil )
Convey ( "JWT remote test timeout parameter: NO TIMEOUT" , func ( ) {
_ , err := hb . CheckAcl ( token , topic , clientID , MOSQ_ACL_READ )
So ( err , ShouldBeNil )
} )
hb . Halt ( )
} )
}