From 5feb334191cc8c278b40c72f0028f8770b0b79c4 Mon Sep 17 00:00:00 2001 From: calebmer Date: Sun, 11 Oct 2015 16:26:29 -0400 Subject: [PATCH 1/5] Use postgres connection string --- src/PostgREST/App.hs | 5 ++--- src/PostgREST/Config.hs | 29 ++++++++++------------------- src/PostgREST/Main.hs | 15 +++++++-------- src/PostgREST/Middleware.hs | 9 ++++----- test/SpecHelper.hs | 18 +++++++++++------- 5 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 0729f337c..916098229 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -57,8 +57,8 @@ import PostgREST.Types import Prelude -app :: DbStructure -> AppConfig -> BL.ByteString -> DbRole -> Request -> H.Tx P.Postgres s Response -app dbstructure conf reqBody dbrole req = +app :: DbStructure -> AppConfig -> Text -> BL.ByteString -> DbRole -> Request -> H.Tx P.Postgres s Response +app dbstructure conf authenticator reqBody dbrole req = case (path, verb) of ([], _) -> do @@ -295,7 +295,6 @@ app dbstructure conf reqBody dbrole req = hasPrefer val = any (\(h,v) -> h == "Prefer" && v == val) hdrs accept = lookupHeader hAccept schema = cs $ configSchema conf - authenticator = cs $ configDbUser conf jwtSecret = cs $ configJwtSecret conf range = rangeRequested hdrs allOrigins = ("Access-Control-Allow-Origin", "*") :: Header diff --git a/src/PostgREST/Config.hs b/src/PostgREST/Config.hs index 03fdd05b2..e57ce7128 100644 --- a/src/PostgREST/Config.hs +++ b/src/PostgREST/Config.hs @@ -34,34 +34,25 @@ import Prelude -- | Data type to store all command line options data AppConfig = AppConfig { - configDbName :: String - , configDbPort :: Int - , configDbUser :: String - , configDbPass :: String - , configDbHost :: String - + configDatabase :: String , configPort :: Int , configAnonRole :: String - , configSecure :: Bool - , configPool :: Int , configSchema :: String + , configSecure :: Bool , configJwtSecret :: String + , configPool :: Int } argParser :: Parser AppConfig argParser = AppConfig - <$> strOption (long "db-name" <> short 'd' <> metavar "NAME" <> help "name of database") - <*> option auto (long "db-port" <> short 'P' <> metavar "PORT" <> value 5432 <> help "postgres server port" <> showDefault) - <*> strOption (long "db-user" <> short 'U' <> metavar "ROLE" <> help "postgres authenticator role") - <*> strOption (long "db-pass" <> metavar "PASS" <> value "" <> help "password for authenticator role") - <*> strOption (long "db-host" <> metavar "HOST" <> value "localhost" <> help "postgres server hostname" <> showDefault) + <$> argument str (help "database connection string" <> metavar "URL") - <*> option auto (long "port" <> short 'p' <> metavar "PORT" <> value 3000 <> help "port number on which to run HTTP server" <> showDefault) - <*> strOption (long "anonymous" <> short 'a' <> metavar "ROLE" <> help "postgres role to use for non-authenticated requests") - <*> switch (long "secure" <> short 's' <> help "Redirect all requests to HTTPS") - <*> option auto (long "db-pool" <> metavar "COUNT" <> value 10 <> help "Max connections in database pool" <> showDefault) - <*> strOption (long "schema" <> short 'S' <> metavar "NAME" <> value "public" <> help "Schema to use for API routes" <> showDefault) - <*> strOption (long "jwt-secret" <> metavar "SECRET" <> value "secret" <> help "Secret used to encrypt and decrypt JWT tokens)" <> showDefault) + <*> 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 "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) defaultCorsPolicy :: CorsResourcePolicy defaultCorsPolicy = CorsResourcePolicy Nothing diff --git a/src/PostgREST/Main.hs b/src/PostgREST/Main.hs index 2c1567c51..180bde91a 100644 --- a/src/PostgREST/Main.hs +++ b/src/PostgREST/Main.hs @@ -31,7 +31,7 @@ import PostgREST.Config (AppConfig (..), isServerVersionSupported :: H.Session P.Postgres IO Bool isServerVersionSupported = do - Identity (row :: Text) <- H.tx Nothing $ H.singleEx $ [H.stmt|SHOW server_version_num|] + Identity (row :: Text) <- H.tx Nothing $ H.singleEx [H.stmt|SHOW server_version_num|] return $ read (cs row) >= minimumPgVersion main :: IO () @@ -50,11 +50,7 @@ main = do Prelude.putStrLn $ "Listening on port " ++ (show $ configPort conf :: String) - let pgSettings = P.ParamSettings (cs $ configDbHost conf) - (fromIntegral $ configDbPort conf) - (cs $ configDbUser conf) - (cs $ configDbPass conf) - (cs $ configDbName conf) + let pgSettings = P.StringSettings $ cs (configDatabase conf) appSettings = setPort port . setServerName (cs $ "postgrest/" <> prettyVersion) $ defaultSettings @@ -71,6 +67,10 @@ main = do fail "Cannot run in this PostgreSQL version, PostgREST needs at least 9.2.0" ) supportedOrError + Right authenticator <- H.session pool $ do + Identity (role :: Text) <- H.tx Nothing $ H.singleEx [H.stmt|SELECT SESSION_USER|] + return role + let txSettings = Just (H.ReadCommitted, Just True) metadata <- H.session pool $ H.tx txSettings $ do tabs <- allTables @@ -89,9 +89,8 @@ main = do , primaryKeys=keys } - runSettings appSettings $ middle $ \ req respond -> do body <- strictRequestBody req resOrError <- liftIO $ H.session pool $ H.tx txSettings $ - authenticated conf (app dbstructure conf body) req + authenticated conf authenticator (app dbstructure conf authenticator body) req either (respond . errResponse) respond resOrError diff --git a/src/PostgREST/Middleware.hs b/src/PostgREST/Middleware.hs index b2dfee16a..e841c592c 100644 --- a/src/PostgREST/Middleware.hs +++ b/src/PostgREST/Middleware.hs @@ -33,22 +33,21 @@ import PostgREST.Config (AppConfig (..), corsPolicy) import Prelude -authenticated :: forall s. AppConfig -> +authenticated :: forall s. AppConfig -> Text -> (DbRole -> Request -> H.Tx P.Postgres s Response) -> Request -> H.Tx P.Postgres s Response -authenticated conf app req = do +authenticated conf authenticator app req = do attempt <- httpRequesterRole (requestHeaders req) case attempt of MalformedAuth -> return $ responseLBS status400 [] "Malformed basic auth header" LoginFailed -> return $ responseLBS status401 [] "Invalid username or password" - LoginSuccess role uid -> if role /= currentRole then runInRole role uid else app currentRole req - NoCredentials -> if anon /= currentRole then runInRole anon "" else app currentRole req + LoginSuccess role uid -> if role /= authenticator then runInRole role uid else app authenticator req + NoCredentials -> if anon /= authenticator then runInRole anon "" else app authenticator req where jwtSecret = cs $ configJwtSecret conf - currentRole = cs $ configDbUser conf anon = cs $ configAnonRole conf httpRequesterRole :: RequestHeaders -> H.Tx P.Postgres s LoginAttempt httpRequesterRole hdrs = do diff --git a/test/SpecHelper.hs b/test/SpecHelper.hs index 1587dabd3..c4b8727cc 100644 --- a/test/SpecHelper.hs +++ b/test/SpecHelper.hs @@ -20,6 +20,7 @@ import Network.HTTP.Types.Header (Header, ByteRange, renderByteRange, import Codec.Binary.Base64.String (encode) import Data.CaseInsensitive (CI(..)) import Data.Maybe (fromMaybe) +import Data.Functor.Identity import Text.Regex.TDFA ((=~)) import qualified Data.ByteString.Char8 as BS import System.Process (readProcess) @@ -33,28 +34,31 @@ import PostgREST.Error(errResponse) import PostgREST.PgStructure import PostgREST.Types +dbString :: String +dbString = "postgres://postgrest_test@localhost:5432/postgrest_test" + isLeft :: Either a b -> Bool isLeft (Left _ ) = True isLeft _ = False cfg :: AppConfig -cfg = AppConfig "postgrest_test" 5432 "postgrest_test" "" "localhost" 3000 "postgrest_anonymous" False 10 "test" "safe" +cfg = AppConfig dbString 3000 "postgrest_anonymous" "test" False "safe" 10 testPoolOpts :: PoolSettings testPoolOpts = fromMaybe (error "bad settings") $ H.poolSettings 1 30 pgSettings :: P.Settings -pgSettings = P.ParamSettings (cs $ configDbHost cfg) - (fromIntegral $ configDbPort cfg) - (cs $ configDbUser cfg) - (cs $ configDbPass cfg) - (cs $ configDbName cfg) +pgSettings = P.StringSettings $ cs dbString withApp :: ActionWith Application -> IO () withApp perform = do pool :: H.Pool P.Postgres <- H.acquirePool pgSettings testPoolOpts + Right authenticator <- H.session pool $ do + Identity (role :: Text) <- H.tx Nothing $ H.singleEx [H.stmt|SELECT SESSION_USER|] + return role + let txSettings = Just (H.ReadCommitted, Just True) metadata <- H.session pool $ H.tx txSettings $ do tabs <- allTables @@ -76,7 +80,7 @@ withApp perform = do perform $ middle $ \req resp -> do body <- strictRequestBody req result <- liftIO $ H.session pool $ H.tx txSettings - $ authenticated cfg (app dbstructure cfg body) req + $ authenticated cfg authenticator (app dbstructure cfg authenticator body) req either (resp . errResponse) resp result where middle = defaultMiddle False From f6aa93f09437c99fd2bac188be8d5901595ca5e6 Mon Sep 17 00:00:00 2001 From: calebmer Date: Sun, 11 Oct 2015 16:46:51 -0400 Subject: [PATCH 2/5] Add details to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a4653af7..b52223031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Embed associations, e.g. `/film?select=*,director(*)` - @ruslantalpa - Filter columns, e.g. `?select=col1,col2` - @ruslantalpa - Does not execute the count total if header "Prefer: count=none" - @diogob +- Postgres connection string argument - @calebmer ### Removed - API versioning feature - @calebmer +- `--db-x` command line arguments - @calebmer ### Fixed - Tolerate a missing role in user creation - @calebmer From de43ac52c4f55cc2fffdb8517b569c938800e0e1 Mon Sep 17 00:00:00 2001 From: calebmer Date: Sun, 11 Oct 2015 16:47:09 -0400 Subject: [PATCH 3/5] Rename connection string metavar --- src/PostgREST/Config.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PostgREST/Config.hs b/src/PostgREST/Config.hs index e57ce7128..b83768da8 100644 --- a/src/PostgREST/Config.hs +++ b/src/PostgREST/Config.hs @@ -45,7 +45,7 @@ data AppConfig = AppConfig { argParser :: Parser AppConfig argParser = AppConfig - <$> argument str (help "database connection string" <> metavar "URL") + <$> argument str (help "database connection string" <> metavar "STRING") <*> 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") From fdcf074dfd518c09958e644a6654f46da4c10e6e Mon Sep 17 00:00:00 2001 From: calebmer Date: Sun, 11 Oct 2015 16:50:47 -0400 Subject: [PATCH 4/5] Update readme --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4cd691b14..dd8d92c10 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,16 @@ your own projects. Download the binary ([latest release](https://github.com/begriffs/postgrest/releases/latest)) and invoke like so: ```bash -postgrest --db-host localhost --db-port 5432 \ - --db-name my_db --db-user postgres \ - --db-pass foobar --db-pool 200 \ - --anonymous postgres --port 3000 \ - --schema public +postgrest postgres://postgres:foobar@localhost:5432/my_db + --port 3000 \ + --schema public \ + --anonymous postgres \ + --pool 200 ``` +For more information on valid connection strings see the +[Postgres docs](http://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNSTRING). + In production include the `--secure` option which redirects all requests to HTTPS. Note that PostgREST does not handle the SSL internally and must be put behind another server that does (such From 2cbf2af6c79769c3276161275c51b848c2d06efa Mon Sep 17 00:00:00 2001 From: calebmer Date: Sun, 11 Oct 2015 16:51:52 -0400 Subject: [PATCH 5/5] Fix readme bash syntax --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dd8d92c10..121c4f38d 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ your own projects. Download the binary ([latest release](https://github.com/begriffs/postgrest/releases/latest)) and invoke like so: ```bash -postgrest postgres://postgres:foobar@localhost:5432/my_db - --port 3000 \ - --schema public \ +postgrest postgres://postgres:foobar@localhost:5432/my_db \ + --port 3000 \ + --schema public \ --anonymous postgres \ --pool 200 ```