From 32539bfe94c0a5f5ddc4a0a34b74b607b5e82fed Mon Sep 17 00:00:00 2001 From: Anon Ray Date: Tue, 28 Jan 2020 19:47:44 +0530 Subject: [PATCH] add tests --- .circleci/test-server.sh | 22 +++++++++++++ server/src-lib/Hasura/Server/Auth/JWT.hs | 11 ++++--- .../src-lib/Hasura/Server/Auth/JWT/Logging.hs | 33 +++++-------------- server/tests-py/jwk_server.py | 6 ++++ 4 files changed, 43 insertions(+), 29 deletions(-) diff --git a/.circleci/test-server.sh b/.circleci/test-server.sh index e33a5f49d3342..eabd8cd1b4fcc 100755 --- a/.circleci/test-server.sh +++ b/.circleci/test-server.sh @@ -561,6 +561,8 @@ wait_for_port 5001 cache_control_jwk_url='{"type": "RS256", "jwk_url": "http://localhost:5001/jwk-cache-control"}' expires_jwk_url='{"type": "RS256", "jwk_url": "http://localhost:5001/jwk-expires"}' +cc_nomaxage_jwk_url='{"type": "RS256", "jwk_url": "http://localhost:5001/jwk-cache-control?nomaxage"}' +cc_nocache_jwk_url='{"type": "RS256", "jwk_url": "http://localhost:5001/jwk-cache-control?nocache"}' # start HGE with cache control JWK URL export HASURA_GRAPHQL_JWT_SECRET=$cache_control_jwk_url @@ -596,6 +598,26 @@ pytest -n 1 -vv --hge-urls "$HGE_URL" --pg-urls "$HASURA_GRAPHQL_DATABASE_URL" - kill_hge_servers +# start HGE with nomaxage JWK URL +export HASURA_GRAPHQL_JWT_SECRET=$cc_nomaxage_jwk_url +run_hge_with_args serve +wait_for_port 8080 + +pytest -n 1 -vv --hge-urls "$HGE_URL" --pg-urls "$HASURA_GRAPHQL_DATABASE_URL" --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --test-jwk-url test_jwk.py -k 'test_cache_control_header' + +kill_hge_servers +unset HASURA_GRAPHQL_JWT_SECRET + +# start HGE with nocache JWK URL +export HASURA_GRAPHQL_JWT_SECRET=$cc_nocache_jwk_url +run_hge_with_args serve +wait_for_port 8080 + +pytest -n 1 -vv --hge-urls "$HGE_URL" --pg-urls "$HASURA_GRAPHQL_DATABASE_URL" --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --test-jwk-url test_jwk.py -k 'test_cache_control_header' + +kill_hge_servers +unset HASURA_GRAPHQL_JWT_SECRET + kill $JWKS_PID # end jwk url test diff --git a/server/src-lib/Hasura/Server/Auth/JWT.hs b/server/src-lib/Hasura/Server/Auth/JWT.hs index ffb5cac0822a8..59f7157f90277 100644 --- a/server/src-lib/Hasura/Server/Auth/JWT.hs +++ b/server/src-lib/Hasura/Server/Auth/JWT.hs @@ -141,9 +141,10 @@ updateJwkRef (Logger logger) manager url jwkRef = do resp <- either logAndThrowHttp return res let status = resp ^. Wreq.responseStatus respBody = resp ^. Wreq.responseBody + statusCode = status ^. Wreq.statusCode - when (status ^. Wreq.statusCode /= 200) $ do - let errMsg = "Non-200 response on fetching JWK from: " <> urlT + unless (statusCode >= 200 && statusCode < 300) $ do + let errMsg = "Non-2xx response on fetching JWK from: " <> urlT err = JFEHttpError url status respBody errMsg logAndThrow err @@ -173,9 +174,11 @@ updateJwkRef (Logger logger) manager url jwkRef = do parseCacheControlHeader = fmapL (parseCacheControlErr . T.pack) . parseMaxAge parseCacheControlErr e = - JFEExpiryParseError (Just e) "Failed parsing Cache-Control header from JWK response. Could not find max-age or s-maxage" + JFEExpiryParseError (Just e) + "Failed parsing Cache-Control header from JWK response. Could not find max-age or s-maxage" parseTimeErr = - JFEExpiryParseError Nothing "Failed parsing Expires header from JWK response. Value of header is not a valid timestamp" + JFEExpiryParseError Nothing + "Failed parsing Expires header from JWK response. Value of header is not a valid timestamp" timeFmt = "%a, %d %b %Y %T GMT" diff --git a/server/src-lib/Hasura/Server/Auth/JWT/Logging.hs b/server/src-lib/Hasura/Server/Auth/JWT/Logging.hs index 8c62dc52920b8..bd2216a50ac1e 100644 --- a/server/src-lib/Hasura/Server/Auth/JWT/Logging.hs +++ b/server/src-lib/Hasura/Server/Auth/JWT/Logging.hs @@ -17,17 +17,17 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import qualified Network.HTTP.Types as HTTP - -- | Possible errors during fetching and parsing JWK +-- (the 'Text' type at the end is a friendly error message) data JwkFetchError = JFEHttpException !HttpException !Text - -- ^ Exception while making the HTTP request. Text is the error message + -- ^ Exception while making the HTTP request | JFEHttpError !URI !HTTP.Status !BL.ByteString !Text - -- ^ Non-2xx HTTP errors from the upstream server. Url, status, body and error message + -- ^ Non-2xx HTTP errors from the upstream server | JFEJwkParseError !Text !Text - -- ^ Error parsing the JWK response itself. Text: Actual parse error and friendly error message - | JFEExpiryParseError !(Maybe Text) !Text - -- ^ Error parsing the expiry of the JWK. Text: Actual parse error and friendly error message + -- ^ Error parsing the JWK response itself + | JFEExpiryParseError !(Maybe Text) Text + -- ^ Error parsing the expiry of the JWK deriving (Show) instance ToJSON JwkFetchError where @@ -42,27 +42,10 @@ instance ToJSON JwkFetchError where ] JFEJwkParseError e msg -> - object [ "parse_error" .= e, "message" .= msg ] + object [ "error" .= e, "message" .= msg ] JFEExpiryParseError e msg -> - object [ "parse_error" .= e, "message" .= msg ] - - --- data JwkRefreshHttpError --- = JwkRefreshHttpError --- { jrheStatus :: !(Maybe HTTP.Status) --- , jrheUrl :: !T.Text --- , jrheHttpException :: !(Maybe HttpException) --- , jrheResponse :: !(Maybe T.Text) --- } deriving (Show) - --- instance ToJSON JwkRefreshHttpError where --- toJSON jhe = --- object [ "status_code" .= (HTTP.statusCode <$> jrheStatus jhe) --- , "url" .= jrheUrl jhe --- , "response" .= jrheResponse jhe --- , "http_exception" .= (httpExceptToJSON . unHttpException <$> jrheHttpException jhe) --- ] + object [ "error" .= e, "message" .= msg ] data JwkRefreshLog = JwkRefreshLog diff --git a/server/tests-py/jwk_server.py b/server/tests-py/jwk_server.py index e780baac8a0ed..7d9e1fbb09c1d 100644 --- a/server/tests-py/jwk_server.py +++ b/server/tests-py/jwk_server.py @@ -52,8 +52,14 @@ def get(self, request): if request.qs: if 'error' in request.qs and 'true' in request.qs['error']: header_val = 'invalid-header-value=42' + elif 'nocache' in request.qs: + header_val = 'no-cache' + elif 'nomaxage' in request.qs: + header_val = 'public, must-revalidate=123, no-transform' elif 'field' in request.qs and 'smaxage' in request.qs['field']: header_val = 's-maxage=' + self.expires_in_secs + if 'field' in request.qs and 'smaxage' in request.qs['field']: + header_val = 's-maxage=' + self.expires_in_secs resp = mkJSONResp(res) resp.headers['Cache-Control'] = header_val # HGE should always prefer Cache-Control over Expires header