Merge pull request #315 from calebmer/feature/pg-string-settings

Use postgres connection string instead of 5+ options
This commit is contained in:
Joe Nelson
2015-10-12 16:22:27 -07:00
7 changed files with 44 additions and 47 deletions
+2
View File
@@ -9,9 +9,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Embed associations, e.g. `/film?select=*,director(*)` - @ruslantalpa - Embed associations, e.g. `/film?select=*,director(*)` - @ruslantalpa
- Filter columns, e.g. `?select=col1,col2` - @ruslantalpa - Filter columns, e.g. `?select=col1,col2` - @ruslantalpa
- Does not execute the count total if header "Prefer: count=none" - @diogob - Does not execute the count total if header "Prefer: count=none" - @diogob
- Postgres connection string argument - @calebmer
### Removed ### Removed
- API versioning feature - @calebmer - API versioning feature - @calebmer
- `--db-x` command line arguments - @calebmer
### Fixed ### Fixed
- Tolerate a missing role in user creation - @calebmer - Tolerate a missing role in user creation - @calebmer
+8 -5
View File
@@ -24,13 +24,16 @@ your own projects.
Download the binary ([latest release](https://github.com/begriffs/postgrest/releases/latest)) and invoke like so: Download the binary ([latest release](https://github.com/begriffs/postgrest/releases/latest)) and invoke like so:
```bash ```bash
postgrest --db-host localhost --db-port 5432 \ postgrest postgres://postgres:foobar@localhost:5432/my_db \
--db-name my_db --db-user postgres \ --port 3000 \
--db-pass foobar --db-pool 200 \ --schema public \
--anonymous postgres --port 3000 \ --anonymous postgres \
--schema public --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 In production include the `--secure` option which redirects all
requests to HTTPS. Note that PostgREST does not handle the SSL requests to HTTPS. Note that PostgREST does not handle the SSL
internally and must be put behind another server that does (such internally and must be put behind another server that does (such
+2 -3
View File
@@ -57,8 +57,8 @@ import PostgREST.Types
import Prelude import Prelude
app :: DbStructure -> AppConfig -> BL.ByteString -> DbRole -> Request -> H.Tx P.Postgres s Response app :: DbStructure -> AppConfig -> Text -> BL.ByteString -> DbRole -> Request -> H.Tx P.Postgres s Response
app dbstructure conf reqBody dbrole req = app dbstructure conf authenticator reqBody dbrole req =
case (path, verb) of case (path, verb) of
([], _) -> do ([], _) -> do
@@ -295,7 +295,6 @@ app dbstructure conf reqBody dbrole req =
hasPrefer val = any (\(h,v) -> h == "Prefer" && v == val) hdrs hasPrefer val = any (\(h,v) -> h == "Prefer" && v == val) hdrs
accept = lookupHeader hAccept accept = lookupHeader hAccept
schema = cs $ configSchema conf schema = cs $ configSchema conf
authenticator = cs $ configDbUser conf
jwtSecret = cs $ configJwtSecret conf jwtSecret = cs $ configJwtSecret conf
range = rangeRequested hdrs range = rangeRequested hdrs
allOrigins = ("Access-Control-Allow-Origin", "*") :: Header allOrigins = ("Access-Control-Allow-Origin", "*") :: Header
+10 -19
View File
@@ -34,34 +34,25 @@ import Prelude
-- | Data type to store all command line options -- | Data type to store all command line options
data AppConfig = AppConfig { data AppConfig = AppConfig {
configDbName :: String configDatabase :: String
, configDbPort :: Int
, configDbUser :: String
, configDbPass :: String
, configDbHost :: String
, configPort :: Int , configPort :: Int
, configAnonRole :: String , configAnonRole :: String
, configSecure :: Bool
, configPool :: Int
, configSchema :: String , configSchema :: String
, configSecure :: Bool
, configJwtSecret :: String , configJwtSecret :: String
, configPool :: Int
} }
argParser :: Parser AppConfig argParser :: Parser AppConfig
argParser = AppConfig argParser = AppConfig
<$> strOption (long "db-name" <> short 'd' <> metavar "NAME" <> help "name of database") <$> argument str (help "database connection string" <> metavar "STRING")
<*> 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)
<*> option auto (long "port" <> short 'p' <> metavar "PORT" <> value 3000 <> help "port number on which to run HTTP server" <> 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' <> metavar "ROLE" <> help "postgres role to use for non-authenticated requests") <*> strOption (long "anonymous" <> short 'a' <> help "postgres role to use for non-authenticated requests" <> metavar "ROLE")
<*> 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)
<*> option auto (long "db-pool" <> metavar "COUNT" <> value 10 <> help "Max connections in database pool" <> showDefault) <*> switch (long "secure" <> short 's' <> help "redirect all requests to HTTPS")
<*> strOption (long "schema" <> short 'S' <> metavar "NAME" <> value "public" <> help "Schema to use for API routes" <> showDefault) <*> strOption (long "jwt-secret" <> short 'j' <> help "secret used to encrypt and decrypt JWT tokens" <> metavar "SECRET" <> value "secret" <> showDefault)
<*> strOption (long "jwt-secret" <> metavar "SECRET" <> value "secret" <> help "Secret used to encrypt and decrypt JWT tokens)" <> showDefault) <*> option auto (long "pool" <> short 'o' <> help "max connections in database pool" <> metavar "COUNT" <> value 10 <> showDefault)
defaultCorsPolicy :: CorsResourcePolicy defaultCorsPolicy :: CorsResourcePolicy
defaultCorsPolicy = CorsResourcePolicy Nothing defaultCorsPolicy = CorsResourcePolicy Nothing
+7 -8
View File
@@ -31,7 +31,7 @@ import PostgREST.Config (AppConfig (..),
isServerVersionSupported :: H.Session P.Postgres IO Bool isServerVersionSupported :: H.Session P.Postgres IO Bool
isServerVersionSupported = do 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 return $ read (cs row) >= minimumPgVersion
main :: IO () main :: IO ()
@@ -50,11 +50,7 @@ main = do
Prelude.putStrLn $ "Listening on port " ++ Prelude.putStrLn $ "Listening on port " ++
(show $ configPort conf :: String) (show $ configPort conf :: String)
let pgSettings = P.ParamSettings (cs $ configDbHost conf) let pgSettings = P.StringSettings $ cs (configDatabase conf)
(fromIntegral $ configDbPort conf)
(cs $ configDbUser conf)
(cs $ configDbPass conf)
(cs $ configDbName conf)
appSettings = setPort port appSettings = setPort port
. setServerName (cs $ "postgrest/" <> prettyVersion) . setServerName (cs $ "postgrest/" <> prettyVersion)
$ defaultSettings $ defaultSettings
@@ -71,6 +67,10 @@ main = do
fail "Cannot run in this PostgreSQL version, PostgREST needs at least 9.2.0" fail "Cannot run in this PostgreSQL version, PostgREST needs at least 9.2.0"
) supportedOrError ) 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) let txSettings = Just (H.ReadCommitted, Just True)
metadata <- H.session pool $ H.tx txSettings $ do metadata <- H.session pool $ H.tx txSettings $ do
tabs <- allTables tabs <- allTables
@@ -89,9 +89,8 @@ main = do
, primaryKeys=keys , primaryKeys=keys
} }
runSettings appSettings $ middle $ \ req respond -> do runSettings appSettings $ middle $ \ req respond -> do
body <- strictRequestBody req body <- strictRequestBody req
resOrError <- liftIO $ H.session pool $ H.tx txSettings $ 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 either (respond . errResponse) respond resOrError
+4 -5
View File
@@ -33,22 +33,21 @@ import PostgREST.Config (AppConfig (..), corsPolicy)
import Prelude import Prelude
authenticated :: forall s. AppConfig -> authenticated :: forall s. AppConfig -> Text ->
(DbRole -> Request -> H.Tx P.Postgres s Response) -> (DbRole -> Request -> H.Tx P.Postgres s Response) ->
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) attempt <- httpRequesterRole (requestHeaders req)
case attempt of case attempt of
MalformedAuth -> MalformedAuth ->
return $ responseLBS status400 [] "Malformed basic auth header" return $ responseLBS status400 [] "Malformed basic auth header"
LoginFailed -> LoginFailed ->
return $ responseLBS status401 [] "Invalid username or password" return $ responseLBS status401 [] "Invalid username or password"
LoginSuccess role uid -> if role /= currentRole then runInRole role uid else app currentRole req LoginSuccess role uid -> if role /= authenticator then runInRole role uid else app authenticator req
NoCredentials -> if anon /= currentRole then runInRole anon "" else app currentRole req NoCredentials -> if anon /= authenticator then runInRole anon "" else app authenticator req
where where
jwtSecret = cs $ configJwtSecret conf jwtSecret = cs $ configJwtSecret conf
currentRole = cs $ configDbUser conf
anon = cs $ configAnonRole conf anon = cs $ configAnonRole conf
httpRequesterRole :: RequestHeaders -> H.Tx P.Postgres s LoginAttempt httpRequesterRole :: RequestHeaders -> H.Tx P.Postgres s LoginAttempt
httpRequesterRole hdrs = do httpRequesterRole hdrs = do
+11 -7
View File
@@ -20,6 +20,7 @@ import Network.HTTP.Types.Header (Header, ByteRange, renderByteRange,
import Codec.Binary.Base64.String (encode) import Codec.Binary.Base64.String (encode)
import Data.CaseInsensitive (CI(..)) import Data.CaseInsensitive (CI(..))
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Functor.Identity
import Text.Regex.TDFA ((=~)) import Text.Regex.TDFA ((=~))
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
import System.Process (readProcess) import System.Process (readProcess)
@@ -33,28 +34,31 @@ import PostgREST.Error(errResponse)
import PostgREST.PgStructure import PostgREST.PgStructure
import PostgREST.Types import PostgREST.Types
dbString :: String
dbString = "postgres://postgrest_test@localhost:5432/postgrest_test"
isLeft :: Either a b -> Bool isLeft :: Either a b -> Bool
isLeft (Left _ ) = True isLeft (Left _ ) = True
isLeft _ = False isLeft _ = False
cfg :: AppConfig 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 :: PoolSettings
testPoolOpts = fromMaybe (error "bad settings") $ H.poolSettings 1 30 testPoolOpts = fromMaybe (error "bad settings") $ H.poolSettings 1 30
pgSettings :: P.Settings pgSettings :: P.Settings
pgSettings = P.ParamSettings (cs $ configDbHost cfg) pgSettings = P.StringSettings $ cs dbString
(fromIntegral $ configDbPort cfg)
(cs $ configDbUser cfg)
(cs $ configDbPass cfg)
(cs $ configDbName cfg)
withApp :: ActionWith Application -> IO () withApp :: ActionWith Application -> IO ()
withApp perform = do withApp perform = do
pool :: H.Pool P.Postgres pool :: H.Pool P.Postgres
<- H.acquirePool pgSettings testPoolOpts <- 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) let txSettings = Just (H.ReadCommitted, Just True)
metadata <- H.session pool $ H.tx txSettings $ do metadata <- H.session pool $ H.tx txSettings $ do
tabs <- allTables tabs <- allTables
@@ -76,7 +80,7 @@ withApp perform = do
perform $ middle $ \req resp -> do perform $ middle $ \req resp -> do
body <- strictRequestBody req body <- strictRequestBody req
result <- liftIO $ H.session pool $ H.tx txSettings 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 either (resp . errResponse) resp result
where middle = defaultMiddle False where middle = defaultMiddle False