Added jwt http timeout (#285)

* Added jwt http timeout

Co-authored-by: Alessandro Peretti <alessandro.peretti@spindox.it>
This commit is contained in:
alessandroperetti 2023-06-09 14:29:01 +02:00 committed by GitHub
parent a271ad1e70
commit 807e8f25e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 5 deletions

View File

@ -864,6 +864,7 @@ The following options are supported by the `jwt` backend when remote is set to t
| --------------------------- | --------- | :-------: | ------------------------------------------------------------- |
| auth_opt_jwt_host | | Y/N | API server host name or ip |
| auth_opt_jwt_port | | Y | TCP port number |
| auth_opt_jwt_http_timeout | 5 | N | Timeout in seconds for http client |
| auth_opt_jwt_getuser_uri | | Y | URI for check username/password |
| auth_opt_jwt_superuser_uri | | N | URI for check superuser |
| auth_opt_jwt_aclcheck_uri | | Y | URI for check acl |

View File

@ -27,10 +27,10 @@ type remoteJWTChecker struct {
hostWhitelist []string
withTLS bool
verifyPeer bool
paramsMode string
httpMethod string
responseMode string
paramsMode string
httpMethod string
responseMode string
timeout int
options tokenOptions
@ -152,7 +152,16 @@ func NewRemoteJWTChecker(authOpts map[string]string, options tokenOptions, versi
return nil, errors.Errorf("JWT backend error: missing remote options: %s", missingOpts)
}
checker.client = &h.Client{Timeout: 5 * time.Second}
checker.timeout = 5
if timeoutString, ok := authOpts["jwt_http_timeout"]; ok {
if timeout, err := strconv.Atoi(timeoutString); err == nil {
checker.timeout = timeout
} else {
log.Errorf("unable to parse timeout: %s", err)
}
}
checker.client = &h.Client{Timeout: time.Duration(checker.timeout) * time.Second}
if !checker.verifyPeer {
tr := &h.Transport{

View File

@ -1654,3 +1654,88 @@ func TestJWTFormTextResponseServer(t *testing.T) {
})
}
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()
})
}