From 34c153086ca24a8a77adf7216fd7526888febf39 Mon Sep 17 00:00:00 2001 From: calebmer Date: Thu, 5 Nov 2015 17:15:50 -0500 Subject: [PATCH 1/3] Do not redirect insecure requests --- CHANGELOG.md | 1 + src/PostgREST/Middleware.hs | 36 ++++++++++-------------------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b52223031..80d6887a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Removed - API versioning feature - @calebmer - `--db-x` command line arguments - @calebmer +- Secure flag responds with 403 instead of redirect - @calebmer ### Fixed - Tolerate a missing role in user creation - @calebmer diff --git a/src/PostgREST/Middleware.hs b/src/PostgREST/Middleware.hs index 5787ca3a2..03ba3dcb7 100644 --- a/src/PostgREST/Middleware.hs +++ b/src/PostgREST/Middleware.hs @@ -4,19 +4,15 @@ module PostgREST.Middleware where import Data.Maybe (fromMaybe, isNothing) -import Data.Monoid import Data.Text import Data.String.Conversions (cs) import qualified Hasql as H import qualified Hasql.Postgres as P -import Network.HTTP.Types.Header (hAccept, hAuthorization, - hLocation) -import Network.HTTP.Types.Status (status301, status400, status415) -import Network.URI (URI (..), parseURI) +import Network.HTTP.Types.Header (hAccept, hAuthorization) +import Network.HTTP.Types.Status (status403, status415) import Network.Wai (Application, Request (..), - Response, isSecure, rawPathInfo, - rawQueryString, requestHeaders, + Response, isSecure, requestHeaders, responseLBS) import Network.Wai.Middleware.Cors (cors) import Network.Wai.Middleware.Gzip (def, gzip) @@ -54,26 +50,14 @@ runWithClaims conf app req = do else setRole anon : jwtEnv jwtEnv = claimsToSQL claims -redirectInsecure :: Application -> Application -redirectInsecure app req respond = do - let hdrs = requestHeaders req - host = lookup "host" hdrs - uriM = parseURI . cs =<< mconcat [ - Just "https://", - host, - Just $ rawPathInfo req, - Just $ rawQueryString req] - isHerokuSecure = lookup "x-forwarded-proto" hdrs == Just "https" - +checkInsecure :: Application -> Application +checkInsecure app req respond = if not (isSecure req || isHerokuSecure) - then case uriM of - Just uri -> - respond $ responseLBS status301 [ - (hLocation, cs . show $ uri { uriScheme = "https:" }) - ] "" - Nothing -> - respond $ responseLBS status400 [] "SSL is required" + then respond $ responseLBS status403 [] "SSL is required" else app req respond + where + hdrs = requestHeaders req + isHerokuSecure = lookup "x-forwarded-proto" hdrs == Just "https" unsupportedAccept :: Application -> Application unsupportedAccept app req respond = do @@ -84,7 +68,7 @@ unsupportedAccept app req respond = do else app req respond defaultMiddle :: Bool -> Application -> Application -defaultMiddle secure = (if secure then redirectInsecure else id) +defaultMiddle secure = (if secure then checkInsecure else id) . gzip def . cors corsPolicy . staticPolicy (only [("favicon.ico", "static/favicon.ico")]) . unsupportedAccept From 3d2a78e962e7fc77febf0ea6774b7b4e0fef0878 Mon Sep 17 00:00:00 2001 From: Joe Nelson Date: Thu, 5 Nov 2015 17:18:22 -0800 Subject: [PATCH 2/3] Encode JWT when proc return types end in jwt_claims Fixes it when the jwt_claims type is defined in a non-default schema --- src/PostgREST/PgStructure.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PostgREST/PgStructure.hs b/src/PostgREST/PgStructure.hs index d7e1710db..70d804f7d 100644 --- a/src/PostgREST/PgStructure.hs +++ b/src/PostgREST/PgStructure.hs @@ -45,7 +45,7 @@ doesProcReturnJWT = doesProc [H.stmt| ON pronamespace = n.oid WHERE nspname = ? AND proname = ? - AND pg_catalog.pg_get_function_result(p.oid) = 'jwt_claims' + AND pg_catalog.pg_get_function_result(p.oid) like '%jwt_claims' |] tableFromRow :: (Text, Text, Bool, Maybe Text) -> Table From c02468762940cbf4dc83c4d553aeaf42047c5e51 Mon Sep 17 00:00:00 2001 From: calebmer Date: Sun, 8 Nov 2015 13:03:50 -0500 Subject: [PATCH 3/3] Remove secure flag entirely --- CHANGELOG.md | 2 +- src/PostgREST/Config.hs | 4 +--- src/PostgREST/Main.hs | 4 +--- src/PostgREST/MainTest.hs | 4 +--- src/PostgREST/Middleware.hs | 23 +++++++---------------- test/SpecHelper.hs | 4 ++-- 6 files changed, 13 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80d6887a0..56fb3b9ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Removed - API versioning feature - @calebmer - `--db-x` command line arguments - @calebmer -- Secure flag responds with 403 instead of redirect - @calebmer +- Remove secure flag - @calebmer ### Fixed - Tolerate a missing role in user creation - @calebmer diff --git a/src/PostgREST/Config.hs b/src/PostgREST/Config.hs index efb090fa5..5edf05718 100644 --- a/src/PostgREST/Config.hs +++ b/src/PostgREST/Config.hs @@ -38,7 +38,6 @@ data AppConfig = AppConfig { , configPort :: Int , configAnonRole :: String , configSchema :: String - , configSecure :: Bool , configJwtSecret :: String , configPool :: Int } @@ -49,8 +48,7 @@ argParser = AppConfig <*> option auto (long "port" <> short 'p' <> help "port number on which to run HTTP server" <> metavar "PORT" <> value 3000 <> showDefault) <*> strOption (long "anonymous" <> short 'a' <> help "postgres role to use for non-authenticated requests" <> metavar "ROLE") - <*> strOption (long "schema" <> short 'S' <> help "schema to use for API routes" <> metavar "NAME" <> value "1" <> showDefault) - <*> switch (long "secure" <> short 's' <> help "redirect all requests to HTTPS") + <*> strOption (long "schema" <> short 's' <> help "schema to use for API routes" <> metavar "NAME" <> value "1" <> showDefault) <*> strOption (long "jwt-secret" <> short 'j' <> help "secret used to encrypt and decrypt JWT tokens" <> metavar "SECRET" <> value "secret" <> showDefault) <*> option auto (long "pool" <> short 'o' <> help "max connections in database pool" <> metavar "COUNT" <> value 10 <> showDefault) diff --git a/src/PostgREST/Main.hs b/src/PostgREST/Main.hs index af6f395fb..44fbd841a 100644 --- a/src/PostgREST/Main.hs +++ b/src/PostgREST/Main.hs @@ -46,8 +46,6 @@ main = do conf <- readOptions let port = configPort conf - unless (configSecure conf) $ - putStrLn "WARNING, running in insecure mode, auth will be in plaintext" unless ("secret" /= configJwtSecret conf) $ putStrLn "WARNING, running in insecure mode, JWT secret is the default value" Prelude.putStrLn $ "Listening on port " ++ @@ -57,7 +55,7 @@ main = do appSettings = setPort port . setServerName (cs $ "postgrest/" <> prettyVersion) $ defaultSettings - middle = logStdout . defaultMiddle (configSecure conf) + middle = logStdout . defaultMiddle poolSettings <- maybe (fail "Improper session settings") return $ H.poolSettings (fromIntegral $ configPool conf) 30 diff --git a/src/PostgREST/MainTest.hs b/src/PostgREST/MainTest.hs index 89a2397c9..55ebec5bc 100644 --- a/src/PostgREST/MainTest.hs +++ b/src/PostgREST/MainTest.hs @@ -54,8 +54,6 @@ main = do conf <- readOptions let port = configPort conf - unless (configSecure conf) $ - putStrLn "WARNING, running in insecure mode, auth will be in plaintext" unless ("secret" /= configJwtSecret conf) $ putStrLn "WARNING, running in insecure mode, JWT secret is the default value" Prelude.putStrLn $ "Listening on port " ++ @@ -65,7 +63,7 @@ main = do appSettings = setPort port . setServerName (cs $ "postgrest/" <> prettyVersion) $ defaultSettings - middle = logStdout . defaultMiddle (configSecure conf) + middle = logStdout . defaultMiddle poolSettings <- maybe (fail "Improper session settings") return $ H.poolSettings (fromIntegral $ configPool conf) 30 diff --git a/src/PostgREST/Middleware.hs b/src/PostgREST/Middleware.hs index 03ba3dcb7..cc9fb3fa8 100644 --- a/src/PostgREST/Middleware.hs +++ b/src/PostgREST/Middleware.hs @@ -10,10 +10,9 @@ import qualified Hasql as H import qualified Hasql.Postgres as P import Network.HTTP.Types.Header (hAccept, hAuthorization) -import Network.HTTP.Types.Status (status403, status415) -import Network.Wai (Application, Request (..), - Response, isSecure, requestHeaders, - responseLBS) +import Network.HTTP.Types.Status (status415) +import Network.Wai (Application, Request (..), Response, + requestHeaders, responseLBS) import Network.Wai.Middleware.Cors (cors) import Network.Wai.Middleware.Gzip (def, gzip) import Network.Wai.Middleware.Static (only, staticPolicy) @@ -50,15 +49,6 @@ runWithClaims conf app req = do else setRole anon : jwtEnv jwtEnv = claimsToSQL claims -checkInsecure :: Application -> Application -checkInsecure app req respond = - if not (isSecure req || isHerokuSecure) - then respond $ responseLBS status403 [] "SSL is required" - else app req respond - where - hdrs = requestHeaders req - isHerokuSecure = lookup "x-forwarded-proto" hdrs == Just "https" - unsupportedAccept :: Application -> Application unsupportedAccept app req respond = do let @@ -67,8 +57,9 @@ unsupportedAccept app req respond = do then respond $ responseLBS status415 [] "Unsupported Accept header, try: application/json" else app req respond -defaultMiddle :: Bool -> Application -> Application -defaultMiddle secure = (if secure then checkInsecure else id) - . gzip def . cors corsPolicy +defaultMiddle :: Application -> Application +defaultMiddle = + gzip def + . cors corsPolicy . staticPolicy (only [("favicon.ico", "static/favicon.ico")]) . unsupportedAccept diff --git a/test/SpecHelper.hs b/test/SpecHelper.hs index 1a0dae557..f99af00de 100644 --- a/test/SpecHelper.hs +++ b/test/SpecHelper.hs @@ -41,7 +41,7 @@ isLeft (Left _ ) = True isLeft _ = False cfg :: AppConfig -cfg = AppConfig dbString 3000 "postgrest_anonymous" "test" False "safe" 10 +cfg = AppConfig dbString 3000 "postgrest_anonymous" "test" "safe" 10 testPoolOpts :: PoolSettings testPoolOpts = fromMaybe (error "bad settings") $ H.poolSettings 1 30 @@ -78,7 +78,7 @@ withApp perform = do $ runWithClaims cfg (app dbstructure cfg body) req either (resp . errResponse) resp result - where middle = defaultMiddle False + where middle = defaultMiddle resetDb :: IO ()