diff --git a/main/Main.hs b/main/Main.hs index f0fe92de15..690e7b4974 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -13,6 +13,8 @@ import PostgREST.DbStructure import Control.Monad import Data.Monoid ((<>)) import Data.String.Conversions (cs) +import Data.Text (stripPrefix, + pack, unpack) import qualified Hasql.Query as H import qualified Hasql.Session as H import qualified Hasql.Decoders as HD @@ -22,7 +24,6 @@ import Network.Wai.Handler.Warp import System.IO (BufferMode (..), hSetBuffering, stderr, stdin, stdout) -import Web.JWT (secret) import Data.IORef #ifndef mingw32_HOST_OS import Control.Monad.IO.Class (liftIO) @@ -46,14 +47,14 @@ main = do hSetBuffering stdin LineBuffering hSetBuffering stderr NoBuffering - conf <- readOptions + conf <- loadSecretFile =<< readOptions let port = configPort conf pgSettings = cs (configDatabase conf) appSettings = setPort port . setServerName (cs $ "postgrest/" <> prettyVersion) $ defaultSettings - unless (secret "secret" /= configJwtSecret conf) $ + unless ("secret" /= configJwtSecret conf) $ putStrLn "WARNING, running in insecure mode, JWT secret is the default value" Prelude.putStrLn $ "Listening on port " ++ (show $ configPort conf :: String) @@ -85,3 +86,11 @@ main = do #endif runSettings appSettings $ postgrest conf refDbStructure pool + +loadSecretFile :: AppConfig -> IO AppConfig +loadSecretFile conf = do + let s = configJwtSecret conf + real <- case stripPrefix "@" s of + Nothing -> return s -- the string is the secret, not a filename + Just filename -> pack <$> readFile ( unpack filename ) + return conf { configJwtSecret = real } diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 3b9f2f7b7d..6c9762b139 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -25,9 +25,10 @@ import Text.ParserCombinators.Parsec (parse) import Network.HTTP.Types.Header import Network.HTTP.Types.Status -import Network.HTTP.Types.URI (renderSimpleQuery) +import Network.HTTP.Types.URI (renderSimpleQuery) import Network.Wai import Network.Wai.Middleware.RequestLogger (logStdout) +import Web.JWT (secret) import Data.Aeson import Data.Aeson.Types (emptyArray) @@ -75,7 +76,7 @@ postgrest conf refDbStructure pool = let schema = cs $ configSchema conf apiRequest = userApiRequest schema req body - eClaims = jwtClaims (configJwtSecret conf) (iJWT apiRequest) time + eClaims = jwtClaims (secret $ configJwtSecret conf) (iJWT apiRequest) time authed = containsRole eClaims handleReq = runWithClaims conf eClaims (app dbStructure conf) apiRequest txMode = transactionMode $ iAction apiRequest @@ -192,7 +193,7 @@ app dbStructure conf apiRequest = if exists then do let p = V.head payload - jwtSecret = configJwtSecret conf + jwtSecret = secret $ configJwtSecret conf respondToRange $ do row <- H.query () (callProc qi p topLevelRange shouldCount) returnJWT <- H.query qi doesProcReturnJWT diff --git a/src/PostgREST/Config.hs b/src/PostgREST/Config.hs index 1f317c1bbc..8f3d1ba7a4 100644 --- a/src/PostgREST/Config.hs +++ b/src/PostgREST/Config.hs @@ -24,7 +24,7 @@ import qualified Data.ByteString.Char8 as BS import qualified Data.CaseInsensitive as CI import Data.List (intercalate) import Data.String.Conversions (cs) -import Data.Text (strip) +import Data.Text (Text, pack, strip) import Data.Version (versionBranch) import Network.Wai import Network.Wai.Middleware.Cors (CorsResourcePolicy (..)) @@ -32,7 +32,6 @@ import Options.Applicative import Paths_postgrest (version) import Prelude import Safe (readMay) -import Web.JWT (Secret, secret) -- | Data type to store all command line options data AppConfig = AppConfig { @@ -40,7 +39,7 @@ data AppConfig = AppConfig { , configAnonRole :: String , configSchema :: String , configPort :: Int - , configJwtSecret :: Secret + , configJwtSecret :: Text , configPool :: Int , configMaxRows :: Maybe Integer , configQuiet :: Bool @@ -52,8 +51,7 @@ argParser = AppConfig <*> strOption (long "anonymous" <> short 'a' <> help "(REQUIRED) 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 "public" <> showDefault) <*> option auto (long "port" <> short 'p' <> help "port number on which to run HTTP server" <> metavar "PORT" <> value 3000 <> showDefault) - <*> (secret . cs <$> - strOption (long "jwt-secret" <> short 'j' <> help "secret used to encrypt and decrypt JWT tokens" <> metavar "SECRET" <> value "secret" <> showDefault)) + <*> (pack <$> 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) <*> (readMay <$> strOption (long "max-rows" <> short 'm' <> help "max rows in response" <> metavar "COUNT" <> value "infinity" <> showDefault)) <*> pure False