diff --git a/main/Main.hs b/main/Main.hs index a66c9c07ef..203a3aa3e8 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -66,8 +66,15 @@ main = do -- read command/path from commad line CLI{cliCommand, cliPath} <- readCLIShowHelp env - -- build the 'AppConfig' from the config file path - conf <- either panic identity <$> readConfig mempty env cliPath + -- build the 'AppConfig' from the config file path and env vars + pathEnvConf <- either panic identity <$> readAppConfig mempty env cliPath Nothing Nothing + + -- read external files + dbUriFile <- readDbUriFile $ configDbUri pathEnvConf + secretFile <- readSecretFile $ configJwtSecret pathEnvConf + + -- add the external files to AppConfig + conf <- either panic identity <$> readAppConfig mempty env cliPath dbUriFile secretFile -- These are config values that can't be reloaded at runtime. Reloading some of them would imply restarting the web server. let @@ -87,7 +94,7 @@ main = do poolSize = configDbPoolSize conf poolTimeout = configDbPoolTimeout' conf logLevel = configLogLevel conf - gucConfigEnabled = configDbLoadGucConfig conf + dbConfigEnabled = configDbConfig conf -- create connection pool with the provided settings, returns either a 'Connection' or a 'ConnectionError'. Does not throw. pool <- P.acquire (poolSize, poolTimeout, dbUri) @@ -104,10 +111,19 @@ main = do -- Config that can change at runtime refConf <- newIORef conf - let configRereader startingUp = reReadConfig startingUp pool gucConfigEnabled env cliPath refConf - - -- re-read and override the config if db-load-guc-config is true - when gucConfigEnabled $ configRereader True + let + -- re-reads config file + db config + dbConfigReReader startingUp = when dbConfigEnabled $ + reReadConfig startingUp pool dbConfigEnabled env cliPath refConf dbUriFile secretFile + -- re-reads jwt-secret external file + config file + db config + fullConfigReReader = + reReadConfig False pool dbConfigEnabled env cliPath refConf + dbUriFile =<< -- db-uri external file could be re-read, but it doesn't make sense as db-uri is not reloadable + readSecretFile (configJwtSecret pathEnvConf) + + -- Override the config with config options from the db + -- TODO: the same operation is repeated on connectionWorker, ideally this would be done only once, but dump CmdDumpConfig needs it for tests. + dbConfigReReader True case cliCommand of CmdDumpConfig -> @@ -126,7 +142,8 @@ main = do -- This is passed to the connectionWorker method so it can kill the main thread if the PostgreSQL's version is not supported. mainTid <- myThreadId - let connWorker = connectionWorker mainTid pool refConf refDbStructure refIsWorkerOn (dbChannelEnabled, mvarConnectionStatus) + let connWorker = connectionWorker mainTid pool refConf refDbStructure refIsWorkerOn (dbChannelEnabled, mvarConnectionStatus) $ + dbConfigReReader False -- Sets the initial refDbStructure connWorker @@ -149,13 +166,13 @@ main = do -- Re-read the config on SIGUSR2 void $ installHandler sigUSR2 ( - Catch $ configRereader False + Catch fullConfigReReader ) Nothing #endif -- reload schema cache + config on NOTIFY when dbChannelEnabled $ - listener dbUri dbChannel pool refConf refDbStructure mvarConnectionStatus connWorker $ configRereader False + listener dbUri dbChannel pool refConf refDbStructure mvarConnectionStatus connWorker fullConfigReReader -- ask for the OS time at most once per second getTime <- mkAutoUpdate defaultUpdateSettings {updateAction = getCurrentTime} @@ -211,7 +228,8 @@ connectionWorker -> IORef Bool -- ^ Used as a binary Semaphore -> (Bool, MVar ConnectionStatus) -- ^ For interacting with the LISTEN channel -> IO () -connectionWorker mainTid pool refConf refDbStructure refIsWorkerOn (dbChannelEnabled, mvarConnectionStatus) = do + -> IO () +connectionWorker mainTid pool refConf refDbStructure refIsWorkerOn (dbChannelEnabled, mvarConnectionStatus) dbCfReader = do isWorkerOn <- readIORef refIsWorkerOn unless isWorkerOn $ do -- Prevents multiple workers to be running at the same time. Could happen on too many SIGUSR1s. atomicWriteIORef refIsWorkerOn True @@ -227,6 +245,7 @@ connectionWorker mainTid pool refConf refDbStructure refIsWorkerOn (dbChannelEna NotConnected -> return () -- Unreachable because connectionStatus will keep trying to connect Connected actualPgVersion -> do -- Procede with initialization putStrLn ("Connection successful" :: Text) + dbCfReader -- this could be fail because the connection drops, but the loadSchemaCache will pick the error and retry again scStatus <- loadSchemaCache pool actualPgVersion refConf refDbStructure case scStatus of SCLoaded -> pure () -- do nothing and proceed if the load was successful @@ -272,15 +291,6 @@ connectionStatus pool = putStrLn $ "Attempting to reconnect to the database in " <> (show delay::Text) <> " seconds..." return itShould -loadDbSettings :: P.Pool -> IO [(Text, Text)] -loadDbSettings pool = do - result <- P.use pool $ HT.transaction HT.ReadCommitted HT.Read $ HT.statement mempty dbSettingsStatement - case result of - Left e -> do - hPutStrLn stderr ("An error ocurred when trying to query database settings for the config parameters:\n" <> show e :: Text) - pure [] - Right x -> pure x - -- | Load the DbStructure by using a connection from the pool. loadSchemaCache :: P.Pool -> PgVersion -> IORef AppConfig -> IORef (Maybe DbStructure) -> IO SCacheStatus loadSchemaCache pool actualPgVersion refConf refDbStructure = do @@ -340,20 +350,29 @@ listener dbUri dbChannel pool refConf refDbStructure mvarConnectionStatus connWo errorMessage = "Could not listen for notifications on the " <> dbChannel <> " channel" :: Text retryMessage = "Retrying listening for notifications on the " <> dbChannel <> " channel.." :: Text --- | Re-reads the config at runtime. -reReadConfig :: Bool -> P.Pool -> Bool -> Environment -> Maybe FilePath -> IORef AppConfig -> IO () -reReadConfig startingUp pool gucConfigEnabled env path refConf = do - dbSettings <- if gucConfigEnabled then loadDbSettings pool else pure [] - readConfig dbSettings env path >>= \case +-- | Re-reads the config plus config options from the db +reReadConfig :: Bool -> P.Pool -> Bool -> Environment -> Maybe FilePath -> IORef AppConfig -> Maybe Text -> Maybe BS.ByteString -> IO () +reReadConfig startingUp pool dbConfigEnabled env path refConf dbUriFile secretFile = do + dbSettings <- if dbConfigEnabled then loadDbSettings else pure [] + readAppConfig dbSettings env path dbUriFile secretFile >>= \case Left err -> if startingUp then panic err -- die on invalid config if the program is starting up - else hPutStrLn stderr $ "Failed config reload. " <> err + else hPutStrLn stderr $ "Failed loading in-database config. " <> err Right conf -> do atomicWriteIORef refConf conf if startingUp then pass - else putStrLn ("Config reloaded" :: Text) + else putStrLn ("In-database config loaded" :: Text) + where + loadDbSettings :: IO [(Text, Text)] + loadDbSettings = do + result <- P.use pool $ HT.transaction HT.ReadCommitted HT.Read $ HT.statement mempty dbSettingsStatement + case result of + Left e -> do + hPutStrLn stderr ("An error ocurred when trying to query database settings for the config parameters:\n" <> show e :: Text) + pure [] + Right x -> pure x -- | Dump DbStructure schema to JSON dumpSchema :: P.Pool -> AppConfig -> IO LBS.ByteString diff --git a/src/PostgREST/Config.hs b/src/PostgREST/Config.hs index 554b6022b3..dd9f162d95 100644 --- a/src/PostgREST/Config.hs +++ b/src/PostgREST/Config.hs @@ -31,7 +31,9 @@ module PostgREST.Config , Environment , readCLIShowHelp , readEnvironment - , readConfig + , readAppConfig + , readDbUriFile + , readSecretFile , parseSecret ) where @@ -103,7 +105,7 @@ data AppConfig = AppConfig { , configDbPreparedStatements :: Bool , configDbRootSpec :: Maybe Text , configDbSchemas :: NonEmpty Text - , configDbLoadGucConfig :: Bool + , configDbConfig :: Bool , configDbTxAllowOverride :: Bool , configDbTxRollbackAll :: Bool , configDbUri :: Text @@ -218,8 +220,8 @@ readCLIShowHelp env = customExecParser parserPrefs opts |## Enable or disable the notification channel |db-channel-enabled = false | - |## Enable loading config parameters from the database by changing the connection role settings - |db-load-guc-config = true + |## Enable in-database configuration + |db-config = true | |## how to terminate database transactions |## possible values are: @@ -286,7 +288,7 @@ dumpAppConfig conf = ,("db-prepared-statements", toLower . show . configDbPreparedStatements) ,("db-root-spec", q . fromMaybe mempty . configDbRootSpec) ,("db-schemas", q . intercalate "," . toList . configDbSchemas) - ,("db-load-guc-config", q . toLower . show . configDbLoadGucConfig) + ,("db-config", q . toLower . show . configDbConfig) ,("db-tx-end", q . showTxEnd) ,("db-uri", q . configDbUri) ,("jwt-aud", toS . encode . maybe "" toJSON . configJwtAudience) @@ -331,9 +333,9 @@ instance JustIfMaybe a a where instance JustIfMaybe a (Maybe a) where justIfMaybe a = Just a --- | Parse the config file -readAppConfig :: [(Text, Text)] -> Environment -> Maybe FilePath -> IO (Either Text AppConfig) -readAppConfig dbSettings env optPath = do +-- | Reads and parses the config and overrides its parameters from env vars, files or db settings. +readAppConfig :: [(Text, Text)] -> Environment -> Maybe FilePath -> Maybe Text -> Maybe B.ByteString -> IO (Either Text AppConfig) +readAppConfig dbSettings env optPath dbUriFile secretFile = do -- Now read the actual config file conf <- case optPath of -- Both C.ParseError and IOError are shown here @@ -345,6 +347,10 @@ readAppConfig dbSettings env optPath = do where parseConfig = + let pB64 = fromMaybe False <$> optWithAlias (optBool "jwt-secret-is-base64") + (optBool "secret-is-base64") + pSec = parseJwtSecret "jwt-secret" =<< pB64 + in AppConfig <$> parseAppSettings "app.settings" <*> reqString "db-anon-role" @@ -363,16 +369,15 @@ readAppConfig dbSettings env optPath = do <*> (fromList . splitOnCommas <$> reqWithAlias (optValue "db-schemas") (optValue "db-schema") "missing key: either db-schemas or db-schema must be set") - <*> (fromMaybe True <$> optBool "db-load-guc-config") + <*> (fromMaybe True <$> optBool "db-config") <*> parseTxEnd "db-tx-end" snd <*> parseTxEnd "db-tx-end" fst - <*> reqString "db-uri" - <*> pure Nothing + <*> parseDbUri "db-uri" + <*> (fmap parseSecret <$> pSec) <*> parseJwtAudience "jwt-aud" <*> parseRoleClaimKey "jwt-role-claim-key" "role-claim-key" - <*> (fmap encodeUtf8 <$> optString "jwt-secret") - <*> (fromMaybe False <$> optWithAlias (optBool "jwt-secret-is-base64") - (optBool "secret-is-base64")) + <*> pSec + <*> pB64 <*> parseLogLevel "log-level" <*> parseOpenAPIServerProxyURI "openapi-server-proxy-uri" <*> (maybe [] (fmap encodeUtf8 . splitOnCommas) <$> optValue "raw-media-types") @@ -381,6 +386,25 @@ readAppConfig dbSettings env optPath = do <*> (fmap unpack <$> optString "server-unix-socket") <*> parseSocketFileMode "server-unix-socket-mode" + parseDbUri :: C.Key -> C.Parser C.Config Text + parseDbUri k = flip fromMaybe dbUriFile <$> reqString k + + parseJwtSecret :: C.Key -> Bool -> C.Parser C.Config (Maybe B.ByteString) + parseJwtSecret k isB64 = optString k >>= \case + Nothing -> pure Nothing + Just sec -> + let secStr = encodeUtf8 sec + secFile = fromMaybe secStr secretFile + -- replace because the JWT is actually base64url encoded which must be turned into just base64 before decoding. + replaceUrlChars = replace "_" "/" . replace "-" "+" . replace "." "=" + willBeFile = isPrefixOf "@" (toS secStr) && isNothing secretFile + in + if isB64 && not willBeFile -- don't decode in bas64 if the secret will be a file or it will err. The secFile will be filled with the file contents in a later stage. + then case B64.decode $ encodeUtf8 $ strip $ replaceUrlChars $ decodeUtf8 secFile of + Left errMsg -> fail errMsg + Right bs -> pure $ Just bs + else pure $ Just secFile + parseAppSettings :: C.Key -> C.Parser C.Config [(Text, Text)] parseAppSettings key = addFromEnv . fmap (fmap coerceText) <$> C.subassocs key C.value where @@ -477,18 +501,19 @@ readAppConfig dbSettings env optPath = do (C.Key -> C.Parser C.Value a -> C.Parser C.Config b) -> C.Key -> (C.Value -> a) -> C.Parser C.Config b overrideFromDbOrEnvironment necessity key coercion = - case reloadableDbSetting <|> M.lookup name env of + case reloadableDbSetting <|> M.lookup envVarName env of Just dbOrEnvVal -> pure $ justIfMaybe $ coercion $ C.String dbOrEnvVal Nothing -> necessity key (coercion <$> C.value) where - name = "PGRST_" <> map capitalize (toS key) - capitalize '-' = '_' - capitalize c = toUpper c + dashToUnderscore '-' = '_' + dashToUnderscore c = c + envVarName = "PGRST_" <> (toUpper . dashToUnderscore <$> toS key) reloadableDbSetting = - if key `notElem` [ - "server-host", "server-port", "server-unix-socket", "server-unix-socket-mode", "log-level", - "db-anon-role", "db-uri", "db-channel-enabled", "db-channel", "db-pool", "db-pool-timeout", "db-load-guc-config"] - then lookup key dbSettings + let dbSettingName = pack $ dashToUnderscore <$> toS key in + if dbSettingName `notElem` [ + "server_host", "server_port", "server_unix_socket", "server_unix_socket_mode", "log_level", + "db_anon_role", "db_uri", "db_channel_enabled", "db_channel", "db_pool", "db_pool_timeout", "db_config"] + then lookup dbSettingName dbSettings else Nothing coerceText :: C.Value -> Text @@ -514,90 +539,6 @@ readAppConfig dbSettings env optPath = do splitOnCommas (C.String s) = strip <$> splitOn "," s splitOnCommas _ = [] --- | Reads the config and overrides its parameters from files, env vars or db settings. -readConfig :: [(Text, Text)] -> Environment -> Maybe FilePath -> IO (Either Text AppConfig) -readConfig dbSettings env path = - readAppConfig dbSettings env path >>= \case - Left err -> pure $ Left err - Right appConf -> do - conf <- loadDbUriFile =<< loadSecretFile appConf - pure $ Right $ conf { configJWKS = parseSecret <$> configJwtSecret conf} - -type Environment = M.Map [Char] Text - -readEnvironment :: IO Environment -readEnvironment = getEnvironment <&> pgrst - where - pgrst env = M.filterWithKey (\k _ -> "PGRST_" `isPrefixOf` k) $ M.map pack $ M.fromList env - -{-| - The purpose of this function is to load the JWT secret from a file if - configJwtSecret is actually a filepath and replaces some characters if the JWT - is base64 encoded. - - The reason some characters need to be replaced is because JWT is actually - base64url encoded which must be turned into just base64 before decoding. - - To check if the JWT secret is provided is in fact a file path, it must be - decoded as 'Text' to be processed. - - decodeUtf8: Decode a ByteString containing UTF-8 encoded text that is known to - be valid. --} -loadSecretFile :: AppConfig -> IO AppConfig -loadSecretFile conf = extractAndTransform mSecret - where - mSecret = decodeUtf8 <$> configJwtSecret conf - isB64 = configJwtSecretIsBase64 conf - -- - -- The Text (variable name secret) here is mSecret from above which is the JWT - -- decoded as Utf8 - -- - -- stripPrefix: Return the suffix of the second string if its prefix matches - -- the entire first string. - -- - -- The configJwtSecret is a filepath instead of the JWT secret itself if the - -- secret has @ as its prefix. - extractAndTransform :: Maybe Text -> IO AppConfig - extractAndTransform Nothing = return conf - extractAndTransform (Just secret) = - fmap setSecret $ - transformString isB64 =<< - case stripPrefix "@" secret of - Nothing -> return . encodeUtf8 $ secret - Just filename -> chomp <$> BS.readFile (toS filename) - where - chomp bs = fromMaybe bs (BS.stripSuffix "\n" bs) - -- - -- Turns the Base64url encoded JWT into Base64 - transformString :: Bool -> ByteString -> IO ByteString - transformString False t = return t - transformString True t = - case B64.decode $ encodeUtf8 $ strip $ replaceUrlChars $ decodeUtf8 t of - Left errMsg -> panic $ pack errMsg - Right bs -> return bs - setSecret bs = conf {configJwtSecret = Just bs} - -- - -- replace: Replace every occurrence of one substring with another - replaceUrlChars = - replace "_" "/" . replace "-" "+" . replace "." "=" - -{- - Load database uri from a separate file if `db-uri` is a filepath. --} -loadDbUriFile :: AppConfig -> IO AppConfig -loadDbUriFile conf = extractDbUri mDbUri - where - mDbUri = configDbUri conf - extractDbUri :: Text -> IO AppConfig - extractDbUri dbUri = - fmap setDbUri $ - case stripPrefix "@" dbUri of - Nothing -> return dbUri - Just filename -> strip <$> readFile (toS filename) - setDbUri dbUri = conf {configDbUri = dbUri} - - {-| Parse `jwt-secret` configuration option and turn into a JWKSet. @@ -614,3 +555,26 @@ parseSecret bytes = maybeJWK = JSON.decode (toS bytes) :: Maybe JWK secret = JWT.JWKSet [JWT.fromKeyMaterial keyMaterial] keyMaterial = JWT.OctKeyMaterial . JWT.OctKeyParameters $ JOSE.Base64Octets bytes + +type Environment = M.Map [Char] Text + +readEnvironment :: IO Environment +readEnvironment = getEnvironment <&> pgrst + where + pgrst env = M.filterWithKey (\k _ -> "PGRST_" `isPrefixOf` k) $ M.map pack $ M.fromList env + +-- | Read the JWT secret from a file if configJwtSecret is actually a filepath(has @ as its prefix). +-- | To check if the JWT secret is provided is in fact a file path, it must be decoded as 'Text' to be processed. +readSecretFile :: Maybe B.ByteString -> IO (Maybe B.ByteString) +readSecretFile mSecret = + case (stripPrefix "@" . decodeUtf8) =<< mSecret of + Nothing -> return Nothing + Just filename -> Just . chomp <$> BS.readFile (toS filename) + where + chomp bs = fromMaybe bs (BS.stripSuffix "\n" bs) + +-- | Read database uri from a separate file if `db-uri` is a filepath. +readDbUriFile :: Text -> IO (Maybe Text) +readDbUriFile dbUri = case stripPrefix "@" dbUri of + Nothing -> return Nothing + Just filename -> Just . strip <$> readFile (toS filename) diff --git a/src/PostgREST/Statements.hs b/src/PostgREST/Statements.hs index 7bfebfa3cb..9c2422db0d 100644 --- a/src/PostgREST/Statements.hs +++ b/src/PostgREST/Statements.hs @@ -195,18 +195,23 @@ decodeGucHeaders = first (const GucHeadersError) . JSON.eitherDecode . toS <$> H decodeGucStatus :: HD.Value (Either Error (Maybe Status)) decodeGucStatus = first (const GucStatusError) . fmap (Just . toEnum . fst) . decimal <$> HD.text --- | Get db settings from the connection role. Only used for configuration. +-- | Get db settings from the connection role. Global settings will be overridden by database specific settings. dbSettingsStatement :: H.Statement () [(Text, Text)] dbSettingsStatement = H.Statement sql HE.noParams decodeSettings False where sql = [q| with role_setting as ( - select unnest(setconfig) as setting from pg_catalog.pg_db_role_setting where setrole = current_user::regrole::oid + select setdatabase, unnest(setconfig) as setting from pg_catalog.pg_db_role_setting + where setrole = current_user::regrole::oid + and setdatabase in (0, (select oid from pg_catalog.pg_database where datname = current_catalog)) ), kv_settings as ( - select split_part(setting, '=', 1) as key, split_part(setting, '=', 2) as value from role_setting + select setdatabase, split_part(setting, '=', 1) as k, split_part(setting, '=', 2) as value from role_setting + where setting like 'pgrst.%' ) - select replace(key, 'pgrst.', '') as key, value from kv_settings where key like 'pgrst.%'; + select distinct on (key) replace(k, 'pgrst.', '') as key, value + from kv_settings + order by key, setdatabase desc; |] decodeSettings = HD.rowList $ (,) <$> column HD.text <*> column HD.text diff --git a/test/SpecHelper.hs b/test/SpecHelper.hs index 80ebe3deca..8f0464c3eb 100644 --- a/test/SpecHelper.hs +++ b/test/SpecHelper.hs @@ -77,7 +77,7 @@ _baseCfg = let secret = Just $ encodeUtf8 "reallyreallyreallyreallyverysafe" in , configDbPreparedStatements = True , configDbRootSpec = Nothing , configDbSchemas = fromList ["test"] - , configDbLoadGucConfig = False + , configDbConfig = False , configDbUri = mempty , configJWKS = parseSecret <$> secret , configJwtAudience = Nothing diff --git a/test/fixtures/roles.sql b/test/fixtures/roles.sql index 64f6f61601..2e566cdafe 100644 --- a/test/fixtures/roles.sql +++ b/test/fixtures/roles.sql @@ -7,46 +7,55 @@ CREATE ROLE postgrest_test_author; GRANT postgrest_test_anonymous, postgrest_test_default_role, postgrest_test_author TO :USER; -- reloadable config options for io tests -ALTER ROLE postgrest_test_authenticator SET pgrst."jwt-aud" = 'https://example.org'; -ALTER ROLE postgrest_test_authenticator SET pgrst."openapi-server-proxy-uri" = 'https://example.org/api'; -ALTER ROLE postgrest_test_authenticator SET pgrst."raw-media-types" = 'application/vnd.pgrst.db-config'; -ALTER ROLE postgrest_test_authenticator SET pgrst."jwt-secret" = 'REALLYREALLYREALLYREALLYVERYSAFE'; -ALTER ROLE postgrest_test_authenticator SET pgrst."jwt-secret-is-base64" = 'true'; -ALTER ROLE postgrest_test_authenticator SET pgrst."jwt-role-claim-key" = '."a"."role"'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-tx-end" = 'commit-allow-override'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-schemas" = 'test, tenant1, tenant2'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-root-spec" = 'root'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-prepared-statements" = 'false'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-pre-request" = 'test.custom_headers'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-max-rows" = '1000'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-extra-search-path" = 'public, extensions'; +ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_aud = 'https://example.org'; +ALTER ROLE postgrest_test_authenticator SET pgrst.openapi_server_proxy_uri = 'https://example.org/api'; +ALTER ROLE postgrest_test_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.db-config'; +ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_secret = 'REALLYREALLYREALLYREALLYVERYSAFE'; +ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_secret_is_base64 = 'true'; +ALTER ROLE postgrest_test_authenticator SET pgrst.jwt_role_claim_key = '."a"."role"'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_tx_end = 'commit-allow-override'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_schemas = 'test, tenant1, tenant2'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_root_spec = 'root'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_prepared_statements = 'false'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_pre_request = 'test.custom_headers'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_max_rows = '1000'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_extra_search_path = 'public, extensions'; + +-- override with database specific setting +ALTER ROLE postgrest_test_authenticator IN DATABASE :DBNAME SET pgrst.jwt_secret = 'OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE'; +ALTER ROLE postgrest_test_authenticator IN DATABASE :DBNAME SET pgrst.db_extra_search_path = 'public, extensions, private'; + +-- other database settings that should be ignored +DROP DATABASE IF EXISTS other; +CREATE DATABASE other; +ALTER ROLE postgrest_test_authenticator IN DATABASE other SET pgrst.db_max_rows = '1111'; -- non-reloadable configs for io tests -ALTER ROLE postgrest_test_authenticator SET pgrst."server-host" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."server-port" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."server-unix-socket" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."server-unix-socket-mode" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."log-level" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-anon-role" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-uri" = 'postgresql://ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-channel-enabled" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-channel" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-pool" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-pool-timeout" = 'ignored'; -ALTER ROLE postgrest_test_authenticator SET pgrst."db-load-guc-config" = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.server_host = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.server_port = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.server_unix_socket = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.server_unix_socket_mode = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.log_level = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_anon_role = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_uri = 'postgresql://ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_channel_enabled = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_channel = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_pool = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_pool_timeout = 'ignored'; +ALTER ROLE postgrest_test_authenticator SET pgrst.db_config = 'ignored'; -- other authenticator reloadable config options for io tests CREATE ROLE other_authenticator LOGIN NOINHERIT; -ALTER ROLE other_authenticator SET pgrst."jwt-aud" = 'https://otherexample.org'; -ALTER ROLE other_authenticator SET pgrst."openapi-server-proxy-uri" = 'https://otherexample.org/api'; -ALTER ROLE other_authenticator SET pgrst."raw-media-types" = 'application/vnd.pgrst.other-db-config'; -ALTER ROLE other_authenticator SET pgrst."jwt-secret" = 'ODERREALLYREALLYREALLYREALLYVERYSAFE'; -ALTER ROLE other_authenticator SET pgrst."jwt-secret-is-base64" = 'true'; -ALTER ROLE other_authenticator SET pgrst."jwt-role-claim-key" = '."other"."role"'; -ALTER ROLE other_authenticator SET pgrst."db-tx-end" = 'rollback-allow-override'; -ALTER ROLE other_authenticator SET pgrst."db-schemas" = 'test, other_tenant1, other_tenant2'; -ALTER ROLE other_authenticator SET pgrst."db-root-spec" = 'other_root'; -ALTER ROLE other_authenticator SET pgrst."db-prepared-statements" = 'false'; -ALTER ROLE other_authenticator SET pgrst."db-pre-request" = 'test.other_custom_headers'; -ALTER ROLE other_authenticator SET pgrst."db-max-rows" = '100'; -ALTER ROLE other_authenticator SET pgrst."db-extra-search-path" = 'public, extensions, other'; +ALTER ROLE other_authenticator SET pgrst.jwt_aud = 'https://otherexample.org'; +ALTER ROLE other_authenticator SET pgrst.openapi_server_proxy_uri = 'https://otherexample.org/api'; +ALTER ROLE other_authenticator SET pgrst.raw_media_types = 'application/vnd.pgrst.other-db-config'; +ALTER ROLE other_authenticator SET pgrst.jwt_secret = 'ODERREALLYREALLYREALLYREALLYVERYSAFE'; +ALTER ROLE other_authenticator SET pgrst.jwt_secret_is_base64 = 'true'; +ALTER ROLE other_authenticator SET pgrst.jwt_role_claim_key = '."other"."role"'; +ALTER ROLE other_authenticator SET pgrst.db_tx_end = 'rollback-allow-override'; +ALTER ROLE other_authenticator SET pgrst.db_schemas = 'test, other_tenant1, other_tenant2'; +ALTER ROLE other_authenticator SET pgrst.db_root_spec = 'other_root'; +ALTER ROLE other_authenticator SET pgrst.db_prepared_statements = 'false'; +ALTER ROLE other_authenticator SET pgrst.db_pre_request = 'test.other_custom_headers'; +ALTER ROLE other_authenticator SET pgrst.db_max_rows = '100'; +ALTER ROLE other_authenticator SET pgrst.db_extra_search_path = 'public, extensions, other'; diff --git a/test/fixtures/schema.sql b/test/fixtures/schema.sql index 47fed95a07..637d0a5e7a 100644 --- a/test/fixtures/schema.sql +++ b/test/fixtures/schema.sql @@ -1928,7 +1928,7 @@ select * from pg_catalog.pg_prepared_statements; create or replace function change_max_rows_config(val int, notify bool default false) returns void as $_$ begin execute format($$ - alter role postgrest_test_authenticator set pgrst."db-max-rows" = %L; + alter role postgrest_test_authenticator set pgrst.db_max_rows = %L; $$, val); if notify then perform pg_notify('pgrst', 'reload config'); @@ -1937,13 +1937,13 @@ end $_$ volatile security definer language plpgsql ; create or replace function reset_max_rows_config() returns void as $_$ begin - alter role postgrest_test_authenticator set pgrst."db-max-rows" = '1000'; + alter role postgrest_test_authenticator set pgrst.db_max_rows = '1000'; end $_$ volatile security definer language plpgsql ; create or replace function change_db_schema_and_full_reload(schemas text) returns void as $_$ begin execute format($$ - alter role postgrest_test_authenticator set pgrst."db-schemas" = %L; + alter role postgrest_test_authenticator set pgrst.db_schemas = %L; $$, schemas); perform pg_notify('pgrst', 'reload config'); perform pg_notify('pgrst', 'reload schema'); @@ -1951,19 +1951,24 @@ end $_$ volatile security definer language plpgsql ; create or replace function v1.reset_db_schema_config() returns void as $_$ begin - alter role postgrest_test_authenticator set pgrst."db-schemas" = 'test'; + alter role postgrest_test_authenticator set pgrst.db_schemas = 'test'; perform pg_notify('pgrst', 'reload config'); perform pg_notify('pgrst', 'reload schema'); end $_$ volatile security definer language plpgsql ; create or replace function test.invalid_role_claim_key_reload() returns void as $_$ begin - alter role postgrest_test_authenticator set pgrst."jwt-role-claim-key" = 'test'; + alter role postgrest_test_authenticator set pgrst.jwt_role_claim_key = 'test'; perform pg_notify('pgrst', 'reload config'); end $_$ volatile security definer language plpgsql ; create or replace function test.reset_invalid_role_claim_key() returns void as $_$ begin - alter role postgrest_test_authenticator set pgrst."jwt-role-claim-key" = '."a"."role"'; + alter role postgrest_test_authenticator set pgrst.jwt_role_claim_key = '."a"."role"'; perform pg_notify('pgrst', 'reload config'); end $_$ volatile security definer language plpgsql ; + +create or replace function test.reload_pgrst_config() returns void as $_$ +begin + perform pg_notify('pgrst', 'reload config'); +end $_$ language plpgsql ; diff --git a/test/io-tests/configs/aliases.config b/test/io-tests/configs/aliases.config index b3e1f1e7cb..d22999fc25 100644 --- a/test/io-tests/configs/aliases.config +++ b/test/io-tests/configs/aliases.config @@ -7,4 +7,4 @@ pre-request = "check_alias" role-claim-key = ".aliased" root-spec = "open_alias" secret-is-base64 = true -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/app-settings.config b/test/io-tests/configs/app-settings.config index 9ff1ca19d9..3542ae82eb 100644 --- a/test/io-tests/configs/app-settings.config +++ b/test/io-tests/configs/app-settings.config @@ -2,4 +2,4 @@ db-pool = 1 db-pool-timeout = 1 app.settings.external_api_secret = "0123456789abcdef" -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/base64-secret-from-file.config b/test/io-tests/configs/base64-secret-from-file.config index 95f421004f..2a5eab44f2 100644 --- a/test/io-tests/configs/base64-secret-from-file.config +++ b/test/io-tests/configs/base64-secret-from-file.config @@ -3,4 +3,4 @@ db-pool = 1 # Read secret from a file: /dev/stdin (alias for standard input) jwt-secret = "@/dev/stdin" jwt-secret-is-base64 = true -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/boolean-numeric.config b/test/io-tests/configs/boolean-numeric.config index 5505721178..315e546c14 100644 --- a/test/io-tests/configs/boolean-numeric.config +++ b/test/io-tests/configs/boolean-numeric.config @@ -5,4 +5,4 @@ db-anon-role = "required" db-channel-enabled = "1" db-prepared-statements = "0" jwt-secret-is-base64 = "2" -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/boolean-string.config b/test/io-tests/configs/boolean-string.config index 26f53d4959..0a4c68ff4a 100644 --- a/test/io-tests/configs/boolean-string.config +++ b/test/io-tests/configs/boolean-string.config @@ -5,4 +5,4 @@ db-anon-role = "required" db-channel-enabled = "true" db-prepared-statements = "FALSE" jwt-secret-is-base64 = "\"true\"" -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/dburi-from-file.config b/test/io-tests/configs/dburi-from-file.config index 8352d2d1a0..33586354e6 100644 --- a/test/io-tests/configs/dburi-from-file.config +++ b/test/io-tests/configs/dburi-from-file.config @@ -1,4 +1,4 @@ db-uri = "@/dev/stdin" db-pool = 1 jwt-secret = "reallyreallyreallyreallyverysafe" -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/defaults.config b/test/io-tests/configs/defaults.config index 539810bfa5..6cec3a718f 100644 --- a/test/io-tests/configs/defaults.config +++ b/test/io-tests/configs/defaults.config @@ -2,4 +2,4 @@ db-uri = "required" db-schemas = "required" db-anon-role = "required" # Not the default, but only works with proper db-uri -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/expected/aliases.config b/test/io-tests/configs/expected/aliases.config index 8e82dbd769..e8bd68a755 100644 --- a/test/io-tests/configs/expected/aliases.config +++ b/test/io-tests/configs/expected/aliases.config @@ -9,7 +9,7 @@ db-pre-request = "check_alias" db-prepared-statements = true db-root-spec = "open_alias" db-schemas = "provided_through_alias" -db-load-guc-config = "false" +db-config = "false" db-tx-end = "commit" db-uri = "required" jwt-aud = "" diff --git a/test/io-tests/configs/expected/boolean-numeric.config b/test/io-tests/configs/expected/boolean-numeric.config index dd47e8742c..dfa34781ef 100644 --- a/test/io-tests/configs/expected/boolean-numeric.config +++ b/test/io-tests/configs/expected/boolean-numeric.config @@ -9,7 +9,7 @@ db-pre-request = "" db-prepared-statements = false db-root-spec = "" db-schemas = "required" -db-load-guc-config = "false" +db-config = "false" db-tx-end = "commit" db-uri = "required" jwt-aud = "" diff --git a/test/io-tests/configs/expected/boolean-string.config b/test/io-tests/configs/expected/boolean-string.config index dd47e8742c..dfa34781ef 100644 --- a/test/io-tests/configs/expected/boolean-string.config +++ b/test/io-tests/configs/expected/boolean-string.config @@ -9,7 +9,7 @@ db-pre-request = "" db-prepared-statements = false db-root-spec = "" db-schemas = "required" -db-load-guc-config = "false" +db-config = "false" db-tx-end = "commit" db-uri = "required" jwt-aud = "" diff --git a/test/io-tests/configs/expected/defaults.config b/test/io-tests/configs/expected/defaults.config index 8f18c2e2e6..45aa14ce8b 100644 --- a/test/io-tests/configs/expected/defaults.config +++ b/test/io-tests/configs/expected/defaults.config @@ -9,7 +9,7 @@ db-pre-request = "" db-prepared-statements = true db-root-spec = "" db-schemas = "required" -db-load-guc-config = "false" +db-config = "false" db-tx-end = "commit" db-uri = "required" jwt-aud = "" diff --git a/test/io-tests/configs/expected/no-defaults-with-db-other-authenticator.config b/test/io-tests/configs/expected/no-defaults-with-db-other-authenticator.config index 604c33d6ff..5729bb10ab 100644 --- a/test/io-tests/configs/expected/no-defaults-with-db-other-authenticator.config +++ b/test/io-tests/configs/expected/no-defaults-with-db-other-authenticator.config @@ -9,7 +9,7 @@ db-pre-request = "test.other_custom_headers" db-prepared-statements = false db-root-spec = "other_root" db-schemas = "test,other_tenant1,other_tenant2" -db-load-guc-config = "true" +db-config = "true" db-tx-end = "rollback-allow-override" db-uri = "" jwt-aud = "https://otherexample.org" diff --git a/test/io-tests/configs/expected/no-defaults-with-db.config b/test/io-tests/configs/expected/no-defaults-with-db.config index 670178ff31..4339c24ecb 100644 --- a/test/io-tests/configs/expected/no-defaults-with-db.config +++ b/test/io-tests/configs/expected/no-defaults-with-db.config @@ -1,7 +1,7 @@ db-anon-role = "postgrest_test_anonymous" db-channel = "postgrest" db-channel-enabled = true -db-extra-search-path = "public,extensions" +db-extra-search-path = "public,extensions,private" db-max-rows = 1000 db-pool = 1 db-pool-timeout = 100 @@ -9,12 +9,12 @@ db-pre-request = "test.custom_headers" db-prepared-statements = false db-root-spec = "root" db-schemas = "test,tenant1,tenant2" -db-load-guc-config = "true" +db-config = "true" db-tx-end = "commit-allow-override" db-uri = "" jwt-aud = "https://example.org" jwt-role-claim-key = ".\"a\".\"role\"" -jwt-secret = "REALLYREALLYREALLYREALLYVERYSAFE" +jwt-secret = "OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE" jwt-secret-is-base64 = true log-level = "info" openapi-server-proxy-uri = "https://example.org/api" diff --git a/test/io-tests/configs/expected/no-defaults.config b/test/io-tests/configs/expected/no-defaults.config index 1e3acaaef9..55ef2fc772 100644 --- a/test/io-tests/configs/expected/no-defaults.config +++ b/test/io-tests/configs/expected/no-defaults.config @@ -9,7 +9,7 @@ db-pre-request = "please_run_fast" db-prepared-statements = false db-root-spec = "openapi_v3" db-schemas = "multi,tenant,setup" -db-load-guc-config = "false" +db-config = "false" db-tx-end = "rollback-allow-override" db-uri = "tmp_db" jwt-aud = "https://postgrest.org" diff --git a/test/io-tests/configs/expected/types.config b/test/io-tests/configs/expected/types.config index 7f17d62346..86bf81747c 100644 --- a/test/io-tests/configs/expected/types.config +++ b/test/io-tests/configs/expected/types.config @@ -9,7 +9,7 @@ db-pre-request = "" db-prepared-statements = true db-root-spec = "" db-schemas = "required" -db-load-guc-config = "true" +db-config = "true" db-tx-end = "commit" db-uri = "required" jwt-aud = "" diff --git a/test/io-tests/configs/no-defaults-env.yaml b/test/io-tests/configs/no-defaults-env.yaml index e4b4045c0a..7078afc6f3 100644 --- a/test/io-tests/configs/no-defaults-env.yaml +++ b/test/io-tests/configs/no-defaults-env.yaml @@ -11,7 +11,7 @@ PGRST_DB_PREPARED_STATEMENTS: false PGRST_DB_PRE_REQUEST: please_run_fast PGRST_DB_ROOT_SPEC: openapi_v3 PGRST_DB_SCHEMAS: multi, tenant,setup -PGRST_DB_LOAD_GUC_CONFIG: false +PGRST_DB_CONFIG: false PGRST_DB_TX_END: rollback-allow-override PGRST_DB_URI: tmp_db PGRST_JWT_AUD: 'https://postgrest.org' diff --git a/test/io-tests/configs/no-defaults.config b/test/io-tests/configs/no-defaults.config index 68d8f1d062..1f2cfa0ec4 100644 --- a/test/io-tests/configs/no-defaults.config +++ b/test/io-tests/configs/no-defaults.config @@ -9,7 +9,7 @@ db-pre-request = "please_run_fast" db-prepared-statements = false db-root-spec = "openapi_v3" db-schemas = "multi, tenant,setup" -db-load-guc-config = "false" +db-config = "false" db-tx-end = "rollback-allow-override" db-uri = "tmp_db" jwt-aud = "https://postgrest.org" diff --git a/test/io-tests/configs/role-claim-key.config b/test/io-tests/configs/role-claim-key.config index 658ae9c6a4..d51d5fb647 100644 --- a/test/io-tests/configs/role-claim-key.config +++ b/test/io-tests/configs/role-claim-key.config @@ -1,4 +1,4 @@ db-pool = 1 jwt-role-claim-key = "$(ROLE_CLAIM_KEY)" jwt-secret = "reallyreallyreallyreallyverysafe" -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/secret-from-file.config b/test/io-tests/configs/secret-from-file.config index 09d7754538..df373590e1 100644 --- a/test/io-tests/configs/secret-from-file.config +++ b/test/io-tests/configs/secret-from-file.config @@ -3,4 +3,4 @@ db-pool = 1 # Read secret from a file: /dev/stdin (alias for standard input) jwt-secret = "@/dev/stdin" jwt-secret-is-base64 = false -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/sigusr2-settings-external-secret.config b/test/io-tests/configs/sigusr2-settings-external-secret.config new file mode 100644 index 0000000000..2dfda4a63d --- /dev/null +++ b/test/io-tests/configs/sigusr2-settings-external-secret.config @@ -0,0 +1,5 @@ +db-pool = 1 + +jwt-secret = "$(JWT_SECRET_FILE)" +jwt-secret-is-base64 = false +db-config = false diff --git a/test/io-tests/configs/sigusr2-settings.config b/test/io-tests/configs/sigusr2-settings.config index 5c129b0c76..69784f06c8 100644 --- a/test/io-tests/configs/sigusr2-settings.config +++ b/test/io-tests/configs/sigusr2-settings.config @@ -3,4 +3,4 @@ db-pool = 1 app.settings.name_var = "John" jwt-secret = "invalidinvalidinvalidinvalidinvalid" -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/simple.config b/test/io-tests/configs/simple.config index 73da75b9f1..97512d3ece 100644 --- a/test/io-tests/configs/simple.config +++ b/test/io-tests/configs/simple.config @@ -1,3 +1,3 @@ db-pool = 1 jwt-secret = "reallyreallyreallyreallyverysafe" -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/configs/unix-socket.config b/test/io-tests/configs/unix-socket.config index 904d0e0292..1d34f1ea0d 100644 --- a/test/io-tests/configs/unix-socket.config +++ b/test/io-tests/configs/unix-socket.config @@ -1,4 +1,4 @@ db-pool = 1 server-unix-socket = "$(POSTGREST_TEST_SOCKET)" jwt-secret = "reallyreallyreallyreallyverysafe" -db-load-guc-config = false +db-config = false diff --git a/test/io-tests/test_io.py b/test/io-tests/test_io.py index a53bb17209..5aa18b4360 100644 --- a/test/io-tests/test_io.py +++ b/test/io-tests/test_io.py @@ -87,7 +87,8 @@ def defaultenv(): "PGRST_DB_URI": os.environ["PGRST_DB_URI"], "PGRST_DB_SCHEMAS": os.environ["PGRST_DB_SCHEMAS"], "PGRST_DB_ANON_ROLE": os.environ["PGRST_DB_ANON_ROLE"], - "PGRST_DB_LOAD_GUC_CONFIG": "false", + "PGRST_DB_CONFIG": "false", + "PGRST_LOG_LEVEL": "info", } @@ -287,7 +288,7 @@ def test_expected_config_from_db_settings(defaultenv, role, expectedconfig): env = { **defaultenv, "PGRST_DB_URI": db_uri, - "PGRST_DB_LOAD_GUC_CONFIG": "true", + "PGRST_DB_CONFIG": "true", } expected = ( (CONFIGSDIR / "expected" / expectedconfig) @@ -298,6 +299,23 @@ def test_expected_config_from_db_settings(defaultenv, role, expectedconfig): assert dumpconfig(configpath=config, env=env) == expected +def test_read_db_setting(defaultenv): + """ + Should be able to read db settings with current_setting. + + See: https://github.com/PostgREST/postgrest/pull/1729#discussion_r572946461 + """ + env = { + **defaultenv, + "PGRST_DB_CONFIG": "true", + } + with run(env=env) as postgrest: + uri = "/rpc/get_guc_value?name=pgrst.jwt_secret" + response = postgrest.session.get(uri) + + assert response.text == '"OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE"' + + @pytest.mark.parametrize( "config", [conf for conf in CONFIGSDIR.iterdir() if conf.suffix == ".config"], @@ -319,6 +337,7 @@ def test_stable_config(tmp_path, config, defaultenv): "ROLE_CLAIM_KEY": '."https://www.example.com/roles"[0].value', "POSTGREST_TEST_SOCKET": "/tmp/postgrest.sock", "POSTGREST_TEST_PORT": "80", + "JWT_SECRET_FILE": "a_file", } # Some configs expect input from stdin, at least on base64. @@ -491,6 +510,53 @@ def test_jwt_secret_reload(tmp_path, defaultenv): assert response.status_code == 200 +def test_jwt_secret_external_file_reload(tmp_path, defaultenv): + "JWT secret external file should be reloaded when PostgREST is sent a SIGUSR2 or a NOTIFY." + config = CONFIGSDIR / "sigusr2-settings-external-secret.config" + + headers = jwtauthheader({"role": "postgrest_test_author"}, SECRET) + + external_secret_file = tmp_path / "jwt-secret-config" + external_secret_file.write_text("invalid" * 5) + + env = { + **defaultenv, + "JWT_SECRET_FILE": f"@{external_secret_file}", + "PGRST_DB_CHANNEL_ENABLED": "true", + } + + with run(config, env=env) as postgrest: + response = postgrest.session.get("/authors_only", headers=headers) + assert response.status_code == 401 + + # change external file + external_secret_file.write_text(SECRET) + + # SIGUSR1 doesn't reload external files + postgrest.process.send_signal(signal.SIGUSR1) + time.sleep(0.1) + + response = postgrest.session.get("/authors_only", headers=headers) + assert response.status_code == 401 + + # reload config and external file with SIGUSR2 + postgrest.process.send_signal(signal.SIGUSR2) + time.sleep(0.1) + + response = postgrest.session.get("/authors_only", headers=headers) + assert response.status_code == 200 + + # change external file to wrong value again + external_secret_file.write_text("invalid" * 5) + + # reload config and external file with NOTIFY + postgrest.session.post("/rpc/reload_pgrst_config") + time.sleep(0.1) + + response = postgrest.session.get("/authors_only", headers=headers) + assert response.status_code == 401 + + def test_db_schema_reload(tmp_path, defaultenv): "DB schema should be reloaded when PostgREST is sent SIGUSR2." config = (CONFIGSDIR / "sigusr2-settings.config").read_text() @@ -526,7 +592,7 @@ def test_db_schema_notify_reload(defaultenv): env = { **defaultenv, - "PGRST_DB_LOAD_GUC_CONFIG": "true", + "PGRST_DB_CONFIG": "true", "PGRST_DB_CHANNEL_ENABLED": "true", "PGRST_DB_SCHEMAS": "test", } @@ -555,7 +621,7 @@ def test_max_rows_reload(defaultenv): env = { **defaultenv, - "PGRST_DB_LOAD_GUC_CONFIG": "true", + "PGRST_DB_CONFIG": "true", } with run(config, env=env) as postgrest: @@ -583,7 +649,7 @@ def test_max_rows_notify_reload(defaultenv): env = { **defaultenv, - "PGRST_DB_LOAD_GUC_CONFIG": "true", + "PGRST_DB_CONFIG": "true", "PGRST_DB_CHANNEL_ENABLED": "true", } @@ -611,7 +677,7 @@ def test_invalid_role_claim_key_notify_reload(defaultenv): env = { **defaultenv, - "PGRST_DB_LOAD_GUC_CONFIG": "true", + "PGRST_DB_CONFIG": "true", "PGRST_DB_CHANNEL_ENABLED": "true", } diff --git a/test/memory-tests.sh b/test/memory-tests.sh index ebc4a01e8b..edd4d5f5c3 100755 --- a/test/memory-tests.sh +++ b/test/memory-tests.sh @@ -12,7 +12,7 @@ export PGRST_DB_POOL="1" export PGRST_SERVER_HOST="127.0.0.1" export PGRST_SERVER_PORT="$pgrPort" export PGRST_JWT_SECRET="reallyreallyreallyreallyverysafe" -export PGRST_DB_LOAD_GUC_CONFIG="false" +export PGRST_DB_CONFIG="false" trap "kill 0" int term exit