Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd3cf9ed97 | ||
|
|
1f28efa9bd | ||
|
|
36eb72c2a0 | ||
|
|
38c596800a | ||
|
|
a7f9181462 | ||
|
|
f68d5944e6 |
@@ -5,6 +5,25 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## [12.2.12] - 2025-05-01
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3956, Fix exposing admin server `/config` by default - @steve-chavez
|
||||||
|
+ The above endpoint is now disabled unless the `admin-server-config-enabled` config is set to `true`
|
||||||
|
|
||||||
|
## [12.2.11] - 2025-04-21
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #4030, Fix regression with parameter `charset=utf-8` in mediatype - @taimoorzaeem
|
||||||
|
|
||||||
|
## [12.2.10] - 2025-04-18
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3889, Fix: JWT cache purging on every request decreases performance - @mkleczek
|
||||||
|
|
||||||
## [12.2.9] - 2025-04-16
|
## [12.2.9] - 2025-04-16
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -55,10 +55,12 @@ Metrics
|
|||||||
|
|
||||||
Provides :ref:`metrics`.
|
Provides :ref:`metrics`.
|
||||||
|
|
||||||
|
.. _runtime_config:
|
||||||
|
|
||||||
Runtime Configuration
|
Runtime Configuration
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
Provides a ``config`` endpoint that returns the runtime :ref:`configuration`.
|
Provides a ``config`` endpoint that returns the runtime :ref:`configuration`. This requires setting :ref:`admin-server-config-enabled`.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@@ -72,6 +74,8 @@ Provides a ``config`` endpoint that returns the runtime :ref:`configuration`.
|
|||||||
db-channel-enabled = false
|
db-channel-enabled = false
|
||||||
...
|
...
|
||||||
|
|
||||||
|
.. _runtime_schema_cache:
|
||||||
|
|
||||||
Runtime Schema Cache
|
Runtime Schema Cache
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,30 @@ admin-server-port
|
|||||||
|
|
||||||
Specifies the port for the :ref:`admin_server`.
|
Specifies the port for the :ref:`admin_server`.
|
||||||
|
|
||||||
|
.. _admin-server-config-enabled:
|
||||||
|
|
||||||
|
admin-server-config-enabled
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. danger::
|
||||||
|
|
||||||
|
The ``/config`` endpoint contains sensitive information, don't enable this if you're exposing the Admin Server publicly.
|
||||||
|
|
||||||
|
To safely enable this you can use a proxy like :ref:`nginx` to:
|
||||||
|
|
||||||
|
- Ensure ``/config`` are only available to local networks.
|
||||||
|
- Only expose ``/live`` and ``/ready`` to public networks.
|
||||||
|
|
||||||
|
=============== =================================
|
||||||
|
**Type** Boolean
|
||||||
|
**Default** False
|
||||||
|
**Reloadable** N
|
||||||
|
**Environment** PGRST_ADMIN_SERVER_CONFIG_ENABLED
|
||||||
|
**In-Database** `n/a`
|
||||||
|
=============== =================================
|
||||||
|
|
||||||
|
Enables the admin server :ref:`runtime_config` and :ref:`runtime_schema_cache` endpoints.
|
||||||
|
|
||||||
.. _app.settings.*:
|
.. _app.settings.*:
|
||||||
|
|
||||||
app.settings.*
|
app.settings.*
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
name: postgrest
|
name: postgrest
|
||||||
version: 12.2.9
|
version: 12.2.12
|
||||||
synopsis: REST API for any Postgres database
|
synopsis: REST API for any Postgres database
|
||||||
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
||||||
for tables, views, and functions, supporting all HTTP methods that security
|
for tables, views, and functions, supporting all HTTP methods that security
|
||||||
|
|||||||
@@ -56,8 +56,11 @@ admin appState req respond = do
|
|||||||
in
|
in
|
||||||
respond $ Wai.responseLBS status [] mempty
|
respond $ Wai.responseLBS status [] mempty
|
||||||
["config"] -> do
|
["config"] -> do
|
||||||
config <- AppState.getConfig appState
|
config@Config.AppConfig{configAdminServerConfigEnabled} <- AppState.getConfig appState
|
||||||
respond $ Wai.responseLBS HTTP.status200 [] (LBS.fromStrict $ encodeUtf8 $ Config.toText config)
|
if configAdminServerConfigEnabled then
|
||||||
|
respond $ Wai.responseLBS HTTP.status200 [] (LBS.fromStrict $ encodeUtf8 $ Config.toText config)
|
||||||
|
else
|
||||||
|
respond $ Wai.responseLBS HTTP.status404 [] mempty
|
||||||
["schema_cache"] -> do
|
["schema_cache"] -> do
|
||||||
sCache <- AppState.getSchemaCache appState
|
sCache <- AppState.getSchemaCache appState
|
||||||
respond $ Wai.responseLBS HTTP.status200 [] (maybe mempty JSON.encode sCache)
|
respond $ Wai.responseLBS HTTP.status200 [] (maybe mempty JSON.encode sCache)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
module PostgREST.AppState
|
module PostgREST.AppState
|
||||||
( AppState
|
( AppState
|
||||||
, AuthResult(..)
|
, AuthResult(..)
|
||||||
|
, JwtCacheState(..)
|
||||||
, destroy
|
, destroy
|
||||||
, getConfig
|
, getConfig
|
||||||
, getSchemaCache
|
, getSchemaCache
|
||||||
@@ -13,7 +14,7 @@ module PostgREST.AppState
|
|||||||
, getNextDelay
|
, getNextDelay
|
||||||
, getNextListenerDelay
|
, getNextListenerDelay
|
||||||
, getTime
|
, getTime
|
||||||
, getJwtCache
|
, getJwtCacheState
|
||||||
, getSocketREST
|
, getSocketREST
|
||||||
, getSocketAdmin
|
, getSocketAdmin
|
||||||
, init
|
, init
|
||||||
@@ -83,6 +84,12 @@ data AuthResult = AuthResult
|
|||||||
, authRole :: BS.ByteString
|
, authRole :: BS.ByteString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | JWT Cache and IO action that triggers purging old entries from the cache
|
||||||
|
data JwtCacheState = JwtCacheState
|
||||||
|
{ jwtCache :: C.Cache ByteString AuthResult
|
||||||
|
, purgeCache :: IO ()
|
||||||
|
}
|
||||||
|
|
||||||
data AppState = AppState
|
data AppState = AppState
|
||||||
-- | Database connection pool
|
-- | Database connection pool
|
||||||
{ statePool :: SQL.Pool
|
{ statePool :: SQL.Pool
|
||||||
@@ -107,7 +114,7 @@ data AppState = AppState
|
|||||||
-- | Keeps track of the next delay for the listener
|
-- | Keeps track of the next delay for the listener
|
||||||
, stateNextListenerDelay :: IORef Int
|
, stateNextListenerDelay :: IORef Int
|
||||||
-- | JWT Cache
|
-- | JWT Cache
|
||||||
, jwtCache :: C.Cache ByteString AuthResult
|
, jwtCacheState :: JwtCacheState
|
||||||
-- | Network socket for REST API
|
-- | Network socket for REST API
|
||||||
, stateSocketREST :: NS.Socket
|
, stateSocketREST :: NS.Socket
|
||||||
-- | Network socket for the admin UI
|
-- | Network socket for the admin UI
|
||||||
@@ -139,6 +146,16 @@ init conf@AppConfig{configLogLevel, configDbPoolSize} = do
|
|||||||
|
|
||||||
initWithPool :: AppSockets -> SQL.Pool -> AppConfig -> Logger.LoggerState -> Metrics.MetricsState -> ObservationHandler -> IO AppState
|
initWithPool :: AppSockets -> SQL.Pool -> AppConfig -> Logger.LoggerState -> Metrics.MetricsState -> ObservationHandler -> IO AppState
|
||||||
initWithPool (sock, adminSock) pool conf loggerState metricsState observer = do
|
initWithPool (sock, adminSock) pool conf loggerState metricsState observer = do
|
||||||
|
cache <- C.newCache Nothing
|
||||||
|
-- purgeExpired has O(n^2) complexity
|
||||||
|
-- so we wrap it in debounce to make sure it:
|
||||||
|
-- 1) is executed asynchronously
|
||||||
|
-- 2) only a single purge operation is running at a time
|
||||||
|
debounce <- mkDebounce defaultDebounceSettings
|
||||||
|
-- debounceFreq is set to default 1 second
|
||||||
|
{ debounceAction = C.purgeExpired cache
|
||||||
|
, debounceEdge = leadingEdge
|
||||||
|
}
|
||||||
|
|
||||||
appState <- AppState pool
|
appState <- AppState pool
|
||||||
<$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
|
<$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
|
||||||
@@ -151,7 +168,7 @@ initWithPool (sock, adminSock) pool conf loggerState metricsState observer = do
|
|||||||
<*> myThreadId
|
<*> myThreadId
|
||||||
<*> newIORef 0
|
<*> newIORef 0
|
||||||
<*> newIORef 1
|
<*> newIORef 1
|
||||||
<*> C.newCache Nothing
|
<*> pure (JwtCacheState cache debounce)
|
||||||
<*> pure sock
|
<*> pure sock
|
||||||
<*> pure adminSock
|
<*> pure adminSock
|
||||||
<*> pure observer
|
<*> pure observer
|
||||||
@@ -314,8 +331,8 @@ putConfig = atomicWriteIORef . stateConf
|
|||||||
getTime :: AppState -> IO UTCTime
|
getTime :: AppState -> IO UTCTime
|
||||||
getTime = stateGetTime
|
getTime = stateGetTime
|
||||||
|
|
||||||
getJwtCache :: AppState -> C.Cache ByteString AuthResult
|
getJwtCacheState :: AppState -> JwtCacheState
|
||||||
getJwtCache = jwtCache
|
getJwtCacheState = jwtCacheState
|
||||||
|
|
||||||
getSocketREST :: AppState -> NS.Socket
|
getSocketREST :: AppState -> NS.Socket
|
||||||
getSocketREST = stateSocketREST
|
getSocketREST = stateSocketREST
|
||||||
@@ -439,7 +456,7 @@ retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThrea
|
|||||||
-- | Reads the in-db config and reads the config file again
|
-- | Reads the in-db config and reads the config file again
|
||||||
-- | We don't retry reading the in-db config after it fails immediately, because it could have user errors. We just report the error and continue.
|
-- | We don't retry reading the in-db config after it fails immediately, because it could have user errors. We just report the error and continue.
|
||||||
readInDbConfig :: Bool -> AppState -> IO ()
|
readInDbConfig :: Bool -> AppState -> IO ()
|
||||||
readInDbConfig startingUp appState@AppState{stateObserver=observer} = do
|
readInDbConfig startingUp appState@AppState{stateObserver=observer, jwtCacheState=JwtCacheState{jwtCache}} = do
|
||||||
conf <- getConfig appState
|
conf <- getConfig appState
|
||||||
pgVer <- getPgVersion appState
|
pgVer <- getPgVersion appState
|
||||||
dbSettings <-
|
dbSettings <-
|
||||||
@@ -476,7 +493,7 @@ readInDbConfig startingUp appState@AppState{stateObserver=observer} = do
|
|||||||
if configJwtSecret conf == configJwtSecret newConf then
|
if configJwtSecret conf == configJwtSecret newConf then
|
||||||
pass
|
pass
|
||||||
else
|
else
|
||||||
C.purge (getJwtCache appState) -- atomic O(1) operation
|
C.purge jwtCache -- atomic O(1) operation
|
||||||
|
|
||||||
if startingUp then
|
if startingUp then
|
||||||
pass
|
pass
|
||||||
|
|||||||
+10
-8
@@ -44,8 +44,9 @@ import System.Clock (TimeSpec (..))
|
|||||||
import System.IO.Unsafe (unsafePerformIO)
|
import System.IO.Unsafe (unsafePerformIO)
|
||||||
import System.TimeIt (timeItT)
|
import System.TimeIt (timeItT)
|
||||||
|
|
||||||
import PostgREST.AppState (AppState, AuthResult (..), getConfig,
|
import PostgREST.AppState (AppState, AuthResult (..),
|
||||||
getJwtCache, getTime)
|
JwtCacheState (..), getConfig,
|
||||||
|
getJwtCacheState, getTime)
|
||||||
import PostgREST.Config (AppConfig (..), JSPath, JSPathExp (..))
|
import PostgREST.Config (AppConfig (..), JSPath, JSPathExp (..))
|
||||||
import PostgREST.Error (Error (..))
|
import PostgREST.Error (Error (..))
|
||||||
|
|
||||||
@@ -131,7 +132,8 @@ middleware appState app req respond = do
|
|||||||
-- | Used to retrieve and insert JWT to JWT Cache
|
-- | Used to retrieve and insert JWT to JWT Cache
|
||||||
getJWTFromCache :: AppState -> ByteString -> Int -> IO (Either Error AuthResult) -> UTCTime -> IO (Either Error AuthResult)
|
getJWTFromCache :: AppState -> ByteString -> Int -> IO (Either Error AuthResult) -> UTCTime -> IO (Either Error AuthResult)
|
||||||
getJWTFromCache appState token maxLifetime parseJwt utc = do
|
getJWTFromCache appState token maxLifetime parseJwt utc = do
|
||||||
checkCache <- C.lookup (getJwtCache appState) token
|
let JwtCacheState{..} = getJwtCacheState appState
|
||||||
|
checkCache <- C.lookup jwtCache token
|
||||||
authResult <- maybe parseJwt (pure . Right) checkCache
|
authResult <- maybe parseJwt (pure . Right) checkCache
|
||||||
|
|
||||||
case (authResult,checkCache) of
|
case (authResult,checkCache) of
|
||||||
@@ -151,17 +153,17 @@ getJWTFromCache appState token maxLifetime parseJwt utc = do
|
|||||||
|
|
||||||
let timeSpec = getTimeSpec res maxLifetime utc
|
let timeSpec = getTimeSpec res maxLifetime utc
|
||||||
|
|
||||||
-- purge expired cache entries
|
|
||||||
C.purgeExpired jwtCache
|
|
||||||
|
|
||||||
-- insert new cache entry
|
-- insert new cache entry
|
||||||
C.insert' jwtCache timeSpec token res
|
C.insert' jwtCache timeSpec token res
|
||||||
|
|
||||||
|
-- Execute IO action to purge the cache
|
||||||
|
-- It is assumed this action returns immidiately
|
||||||
|
-- so that request processing is not blocked.
|
||||||
|
purgeCache
|
||||||
|
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
|
||||||
return authResult
|
return authResult
|
||||||
where
|
|
||||||
jwtCache = getJwtCache appState
|
|
||||||
|
|
||||||
-- Used to extract JWT exp claim and add to JWT Cache
|
-- Used to extract JWT exp claim and add to JWT Cache
|
||||||
getTimeSpec :: AuthResult -> Int -> UTCTime -> Maybe TimeSpec
|
getTimeSpec :: AuthResult -> Int -> UTCTime -> Maybe TimeSpec
|
||||||
|
|||||||
@@ -128,6 +128,9 @@ exampleConfigFile =
|
|||||||
[str|## Admin server used for checks. It's disabled by default unless a port is specified.
|
[str|## Admin server used for checks. It's disabled by default unless a port is specified.
|
||||||
|# admin-server-port = 3001
|
|# admin-server-port = 3001
|
||||||
|
|
|
|
||||||
|
|## Whether to enable the /config endpoint of the admin server
|
||||||
|
|# admin-server-config-enabled = false
|
||||||
|
|
|
||||||
|## The database role to use when no client authentication is provided
|
|## The database role to use when no client authentication is provided
|
||||||
|# db-anon-role = "anon"
|
|# db-anon-role = "anon"
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ data AppConfig = AppConfig
|
|||||||
, configServerUnixSocket :: Maybe FilePath
|
, configServerUnixSocket :: Maybe FilePath
|
||||||
, configServerUnixSocketMode :: FileMode
|
, configServerUnixSocketMode :: FileMode
|
||||||
, configAdminServerPort :: Maybe Int
|
, configAdminServerPort :: Maybe Int
|
||||||
|
, configAdminServerConfigEnabled :: Bool
|
||||||
, configRoleSettings :: RoleSettings
|
, configRoleSettings :: RoleSettings
|
||||||
, configRoleIsoLvl :: RoleIsolationLvl
|
, configRoleIsoLvl :: RoleIsolationLvl
|
||||||
, configInternalSCSleep :: Maybe Int32
|
, configInternalSCSleep :: Maybe Int32
|
||||||
@@ -180,6 +181,7 @@ toText conf =
|
|||||||
,("server-unix-socket", q . maybe mempty T.pack . configServerUnixSocket)
|
,("server-unix-socket", q . maybe mempty T.pack . configServerUnixSocket)
|
||||||
,("server-unix-socket-mode", q . T.pack . showSocketMode)
|
,("server-unix-socket-mode", q . T.pack . showSocketMode)
|
||||||
,("admin-server-port", maybe "\"\"" show . configAdminServerPort)
|
,("admin-server-port", maybe "\"\"" show . configAdminServerPort)
|
||||||
|
,("admin-server-config-enabled", T.toLower . show . configAdminServerConfigEnabled)
|
||||||
]
|
]
|
||||||
|
|
||||||
-- quote all app.settings
|
-- quote all app.settings
|
||||||
@@ -286,6 +288,7 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
|
|||||||
<*> (fmap T.unpack <$> optString "server-unix-socket")
|
<*> (fmap T.unpack <$> optString "server-unix-socket")
|
||||||
<*> parseSocketFileMode "server-unix-socket-mode"
|
<*> parseSocketFileMode "server-unix-socket-mode"
|
||||||
<*> optInt "admin-server-port"
|
<*> optInt "admin-server-port"
|
||||||
|
<*> (fromMaybe False <$> optBool "admin-server-config-enabled")
|
||||||
<*> pure roleSettings
|
<*> pure roleSettings
|
||||||
<*> pure roleIsolationLvl
|
<*> pure roleIsolationLvl
|
||||||
<*> optInt "internal-schema-cache-sleep"
|
<*> optInt "internal-schema-cache-sleep"
|
||||||
|
|||||||
@@ -183,14 +183,14 @@ decodeMediaType mt = decodeMediaType' $ decodeLatin1 mt
|
|||||||
-- >>> P.parse tokenizeMediaType "" "application/vnd.pgrst.plan+text; for=\"text/xml\"; options=analyze|verbose|settings|buffers|wal"
|
-- >>> P.parse tokenizeMediaType "" "application/vnd.pgrst.plan+text; for=\"text/xml\"; options=analyze|verbose|settings|buffers|wal"
|
||||||
-- Right ("application","vnd.pgrst.plan+text",[("for","text/xml"),("options","analyze|verbose|settings|buffers|wal")])
|
-- Right ("application","vnd.pgrst.plan+text",[("for","text/xml"),("options","analyze|verbose|settings|buffers|wal")])
|
||||||
|
|
||||||
|
-- TODO: Improve mediatype parser as per RFC 2045 https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
|
||||||
tokenizeMediaType :: P.Parser (Text, Text, [(Text, Text)])
|
tokenizeMediaType :: P.Parser (Text, Text, [(Text, Text)])
|
||||||
tokenizeMediaType = do
|
tokenizeMediaType = do
|
||||||
mainType <- P.many1 (P.alphaNum <|> P.oneOf ".*")
|
mainType <- P.many1 (P.alphaNum <|> P.oneOf ".*")
|
||||||
P.char '/'
|
P.char '/'
|
||||||
subType <- P.many1 (P.alphaNum <|> P.oneOf ".*+-")
|
subType <- P.many1 (P.alphaNum <|> P.oneOf ".*+-")
|
||||||
params <- P.many pSemicolonSeparatedKeyVals
|
params <- P.many pSemicolonSeparatedKeyVals
|
||||||
P.optional $ P.try $ P.spaces *> P.char ';' -- ending semicolon
|
P.optional $ P.try $ P.spaces *> P.char ';' -- ending semicolon, discard input after that because it has already failed or we have hit EOF
|
||||||
P.eof
|
|
||||||
return (T.pack mainType, T.pack subType, params)
|
return (T.pack mainType, T.pack subType, params)
|
||||||
where
|
where
|
||||||
pSemicolonSeparatedKeyVals :: P.Parser (Text, Text)
|
pSemicolonSeparatedKeyVals :: P.Parser (Text, Text)
|
||||||
@@ -198,12 +198,12 @@ tokenizeMediaType = do
|
|||||||
where
|
where
|
||||||
pKeyVal :: P.Parser (Text, Text)
|
pKeyVal :: P.Parser (Text, Text)
|
||||||
pKeyVal = do
|
pKeyVal = do
|
||||||
key <- P.many1 P.alphaNum
|
key <- P.many1 (P.alphaNum <|> P.oneOf "-")
|
||||||
P.spaces
|
P.spaces
|
||||||
P.char '='
|
P.char '='
|
||||||
P.spaces
|
P.spaces
|
||||||
val <- P.try pQuoted <|> P.try pUnQuoted
|
val <- P.try pQuoted <|> P.try pUnQuoted
|
||||||
return (T.pack key, T.pack val)
|
return (T.pack key, T.pack val)
|
||||||
where
|
where
|
||||||
pUnQuoted = P.many1 (P.alphaNum <|> P.oneOf "|")
|
pUnQuoted = P.many1 (P.alphaNum <|> P.oneOf "|-")
|
||||||
pQuoted = P.char '\"' *> P.manyTill P.anyChar (P.char '\"')
|
pQuoted = P.char '\"' *> P.manyTill P.anyChar (P.char '\"')
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ server-timing-enabled = false
|
|||||||
server-unix-socket = ""
|
server-unix-socket = ""
|
||||||
server-unix-socket-mode = "660"
|
server-unix-socket-mode = "660"
|
||||||
admin-server-port = ""
|
admin-server-port = ""
|
||||||
|
admin-server-config-enabled = false
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ server-timing-enabled = false
|
|||||||
server-unix-socket = ""
|
server-unix-socket = ""
|
||||||
server-unix-socket-mode = "660"
|
server-unix-socket-mode = "660"
|
||||||
admin-server-port = ""
|
admin-server-port = ""
|
||||||
|
admin-server-config-enabled = false
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ server-timing-enabled = false
|
|||||||
server-unix-socket = ""
|
server-unix-socket = ""
|
||||||
server-unix-socket-mode = "660"
|
server-unix-socket-mode = "660"
|
||||||
admin-server-port = ""
|
admin-server-port = ""
|
||||||
|
admin-server-config-enabled = false
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ server-timing-enabled = false
|
|||||||
server-unix-socket = ""
|
server-unix-socket = ""
|
||||||
server-unix-socket-mode = "660"
|
server-unix-socket-mode = "660"
|
||||||
admin-server-port = ""
|
admin-server-port = ""
|
||||||
|
admin-server-config-enabled = false
|
||||||
|
|||||||
@@ -36,5 +36,6 @@ server-timing-enabled = true
|
|||||||
server-unix-socket = "/tmp/pgrst_io_test.sock"
|
server-unix-socket = "/tmp/pgrst_io_test.sock"
|
||||||
server-unix-socket-mode = "777"
|
server-unix-socket-mode = "777"
|
||||||
admin-server-port = 3001
|
admin-server-port = 3001
|
||||||
|
admin-server-config-enabled = true
|
||||||
app.settings.test = "test"
|
app.settings.test = "test"
|
||||||
app.settings.test2 = "test"
|
app.settings.test2 = "test"
|
||||||
|
|||||||
@@ -36,5 +36,6 @@ server-timing-enabled = false
|
|||||||
server-unix-socket = "/tmp/pgrst_io_test.sock"
|
server-unix-socket = "/tmp/pgrst_io_test.sock"
|
||||||
server-unix-socket-mode = "777"
|
server-unix-socket-mode = "777"
|
||||||
admin-server-port = 3001
|
admin-server-port = 3001
|
||||||
|
admin-server-config-enabled = true
|
||||||
app.settings.test = "test"
|
app.settings.test = "test"
|
||||||
app.settings.test2 = "test"
|
app.settings.test2 = "test"
|
||||||
|
|||||||
@@ -36,5 +36,6 @@ server-timing-enabled = true
|
|||||||
server-unix-socket = "/tmp/pgrst_io_test.sock"
|
server-unix-socket = "/tmp/pgrst_io_test.sock"
|
||||||
server-unix-socket-mode = "777"
|
server-unix-socket-mode = "777"
|
||||||
admin-server-port = 3001
|
admin-server-port = 3001
|
||||||
|
admin-server-config-enabled = true
|
||||||
app.settings.test = "test"
|
app.settings.test = "test"
|
||||||
app.settings.test2 = "test"
|
app.settings.test2 = "test"
|
||||||
|
|||||||
@@ -36,4 +36,5 @@ server-timing-enabled = false
|
|||||||
server-unix-socket = ""
|
server-unix-socket = ""
|
||||||
server-unix-socket-mode = "660"
|
server-unix-socket-mode = "660"
|
||||||
admin-server-port = ""
|
admin-server-port = ""
|
||||||
|
admin-server-config-enabled = false
|
||||||
app.settings.test = "Bool False"
|
app.settings.test = "Bool False"
|
||||||
|
|||||||
@@ -39,3 +39,4 @@ PGRST_SERVER_TIMING_ENABLED: true
|
|||||||
PGRST_SERVER_UNIX_SOCKET: /tmp/pgrst_io_test.sock
|
PGRST_SERVER_UNIX_SOCKET: /tmp/pgrst_io_test.sock
|
||||||
PGRST_SERVER_UNIX_SOCKET_MODE: 777
|
PGRST_SERVER_UNIX_SOCKET_MODE: 777
|
||||||
PGRST_ADMIN_SERVER_PORT: 3001
|
PGRST_ADMIN_SERVER_PORT: 3001
|
||||||
|
PGRST_ADMIN_SERVER_CONFIG_ENABLED: true
|
||||||
|
|||||||
@@ -36,5 +36,6 @@ server-timing-enabled = true
|
|||||||
server-unix-socket = "/tmp/pgrst_io_test.sock"
|
server-unix-socket = "/tmp/pgrst_io_test.sock"
|
||||||
server-unix-socket-mode = "777"
|
server-unix-socket-mode = "777"
|
||||||
admin-server-port = 3001
|
admin-server-port = 3001
|
||||||
|
admin-server-config-enabled = true
|
||||||
app.settings.test = "test"
|
app.settings.test = "test"
|
||||||
app.settings.test2 = "test"
|
app.settings.test2 = "test"
|
||||||
|
|||||||
@@ -673,6 +673,15 @@ def test_admin_config(defaultenv):
|
|||||||
"Should get a success response from the admin server containing current configuration"
|
"Should get a success response from the admin server containing current configuration"
|
||||||
|
|
||||||
with run(env=defaultenv) as postgrest:
|
with run(env=defaultenv) as postgrest:
|
||||||
|
response = postgrest.admin.get("/config")
|
||||||
|
assert response.status_code == 404
|
||||||
|
|
||||||
|
env = {
|
||||||
|
**defaultenv,
|
||||||
|
"PGRST_ADMIN_SERVER_CONFIG_ENABLED": "true",
|
||||||
|
}
|
||||||
|
|
||||||
|
with run(env=env) as postgrest:
|
||||||
response = postgrest.admin.get("/config")
|
response = postgrest.admin.get("/config")
|
||||||
print(response.text)
|
print(response.text)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|||||||
@@ -382,3 +382,24 @@ spec = describe "custom media types" $ do
|
|||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json| {"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: undefined"} |]
|
[json| {"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: undefined"} |]
|
||||||
{ matchStatus = 406 }
|
{ matchStatus = 406 }
|
||||||
|
|
||||||
|
context "media type parser allowed characters" $ do
|
||||||
|
it "regression test allowing charset=utf-8" $
|
||||||
|
request methodPost "/rpc/overloaded_default"
|
||||||
|
[("Content-Type", "application/json; charset=utf-8")]
|
||||||
|
[json|{"must_param":1}|]
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json|{"val":1}|]
|
||||||
|
{ matchStatus = 200
|
||||||
|
, matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"]
|
||||||
|
}
|
||||||
|
|
||||||
|
it "handle unrecognized parameters leniently" $ do
|
||||||
|
request methodPost "/rpc/overloaded_default"
|
||||||
|
[("Content-Type", "application/json; $$ unrecognized-chars=ignored $$")]
|
||||||
|
[json|{"must_param":1}|]
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json|{"val":1}|]
|
||||||
|
{ matchStatus = 200
|
||||||
|
, matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"]
|
||||||
|
}
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ baseCfg = let secret = Just $ encodeUtf8 "reallyreallyreallyreallyverysafe" in
|
|||||||
, configDbTxAllowOverride = True
|
, configDbTxAllowOverride = True
|
||||||
, configDbTxRollbackAll = True
|
, configDbTxRollbackAll = True
|
||||||
, configAdminServerPort = Nothing
|
, configAdminServerPort = Nothing
|
||||||
|
, configAdminServerConfigEnabled = False
|
||||||
, configRoleSettings = mempty
|
, configRoleSettings = mempty
|
||||||
, configRoleIsoLvl = mempty
|
, configRoleIsoLvl = mempty
|
||||||
, configInternalSCSleep = Nothing
|
, configInternalSCSleep = Nothing
|
||||||
|
|||||||
Reference in New Issue
Block a user