feat: Make GUC names for headers, cookies and jwt claims compatible with PostgreSQL v14

Getting the value for a header GUC on PostgreSQL v14 is done using `current_setting('request.headers')::json->>'name-of-header'` and in a similar way for `request.cookies` and `request.jwt.claims`

PostgreSQL versions below 14 can opt in to the new JSON GUCs by setting the `db-use-legacy-gucs` config option to false (true by default)
This commit is contained in:
laurenceisla
2021-10-12 12:58:52 -05:00
committed by GitHub
parent ee56dd5db1
commit 627c3c34b7
27 changed files with 250 additions and 34 deletions
+4 -1
View File
@@ -41,13 +41,16 @@ jobs:
- name: Install testing scripts
run: nix-env -f default.nix -iA tests withTools
- name: Run coverage (IO tests and Spec tests against PostgreSQL 13)
- name: Run coverage (IO tests and Spec tests against PostgreSQL 14)
run: postgrest-coverage
- name: Upload coverage to codecov
uses: codecov/codecov-action@v2
with:
files: ./coverage/codecov.json
- name: Run the spec tests against PostgreSQL 13
if: always()
run: postgrest-with-postgresql-13 postgrest-test-spec
- name: Run the spec tests against PostgreSQL 12
if: always()
run: postgrest-with-postgresql-12 postgrest-test-spec
+3
View File
@@ -27,6 +27,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Changed
- #1927, Overloaded Functions: If there's a function "my_func" having a single unnamed json param and other overloaded pairs(with any number of params), PostgREST won't be able to resolve a POST request to "my_func". For solving this, you can name the unnamed json param `my_func(json) -> my_func(prm json)`.
- #1857, Make GUC names for headers, cookies and jwt claims compatible with PostgreSQL v14 - @laurenceisla, @robertsosinski
+ Getting the value for a header GUC on PostgreSQL 14 is done using `current_setting('request.headers')::json->>'name-of-header'` and in a similar way for `request.cookies` and `request.jwt.claims`
+ PostgreSQL versions below 14 can opt in to the new JSON GUCs by setting the `db-use-legacy-gucs` config option to false (true by default)
## [8.0.0] - 2021-07-25
+2
View File
@@ -36,6 +36,7 @@ let
allOverlays.gitignore
allOverlays.postgresql-default
allOverlays.postgresql-legacy
allOverlays.postgresql-future
(allOverlays.haskell-packages { inherit compiler; })
];
@@ -45,6 +46,7 @@ let
postgresqlVersions =
[
{ name = "postgresql-14"; postgresql = pkgs.postgresql_14; }
{ name = "postgresql-13"; postgresql = pkgs.postgresql_13; }
{ name = "postgresql-12"; postgresql = pkgs.postgresql_12; }
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11; }
+1
View File
@@ -5,4 +5,5 @@
haskell-packages = import ./haskell-packages.nix;
postgresql-default = import ./postgresql-default.nix;
postgresql-legacy = import ./postgresql-legacy.nix;
postgresql-future = import ./postgresql-future.nix;
}
+1 -1
View File
@@ -1,5 +1,5 @@
self: super:
# Overlay that sets the default version of PostgreSQL.
{
postgresql = super.postgresql_13;
postgresql = super.postgresql_14;
}
+17
View File
@@ -0,0 +1,17 @@
self: super:
# Overlay that adds future versions of PostgreSQL that are supported by
# PostgREST.
{
postgresql_14 =
let
rev = "76b1e16c6659ccef7187ca69b287525fea133244";
tarballHash = "1vsahpcx80k2bgslspb0sa6j4bmhdx77sw6la455drqcrqhdqj6a";
pinnedPkgs =
builtins.fetchTarball {
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
sha256 = tarballHash;
};
in
(import pinnedPkgs { }).pkgs.postgresql_14;
}
+1
View File
@@ -182,6 +182,7 @@ test-suite spec
Feature.InsertSpec
Feature.IgnorePrivOpenApiSpec
Feature.JsonOperatorSpec
Feature.LegacyGucsSpec
Feature.MultipleSchemaSpec
Feature.NoJwtSpec
Feature.NonexistentSchemaSpec
+1 -1
View File
@@ -198,7 +198,7 @@ postgrestResponse conf maybeDbStructure jsonDbS pgVer pool time req = do
runDbHandler pool (txMode apiRequest) jwtClaims (configDbPreparedStatements conf) .
Middleware.optionalRollback conf apiRequest $
Middleware.runPgLocals conf jwtClaims handleReq apiRequest jsonDbS
Middleware.runPgLocals conf jwtClaims handleReq apiRequest jsonDbS pgVer
runDbHandler :: SQL.Pool -> SQL.Mode -> Auth.JWTClaims -> Bool -> DbHandler a -> Handler IO a
runDbHandler pool mode jwtClaims prepared handler = do
+4
View File
@@ -166,6 +166,10 @@ exampleConfigFile =
|## Enable in-database configuration
|db-config = true
|
|## Determine if GUC request settings for headers, cookies and jwt claims use the legacy names (string with dashes, invalid starting from PostgreSQL v14) with text values instead of the new names (string without dashes, valid on all PostgreSQL versions) with json values.
|## For PostgreSQL v14 and up, this setting will be ignored.
|db-use-legacy-gucs = true
|
|## how to terminate database transactions
|## possible values are:
|## commit (default)
+3
View File
@@ -81,6 +81,7 @@ data AppConfig = AppConfig
, configDbTxRollbackAll :: Bool
, configDbUri :: Text
, configDbEmbedDefaultJoin :: JoinType
, configDbUseLegacyGucs :: Bool
, configFilePath :: Maybe FilePath
, configJWKS :: Maybe JWKSet
, configJwtAudience :: Maybe StringOrURI
@@ -135,6 +136,7 @@ toText conf =
,("db-tx-end", q . showTxEnd)
,("db-uri", q . configDbUri)
,("db-embed-default-join", q . show . configDbEmbedDefaultJoin)
,("db-use-legacy-gucs", T.toLower . show . configDbUseLegacyGucs)
,("jwt-aud", toS . encode . maybe "" toJSON . configJwtAudience)
,("jwt-role-claim-key", q . T.intercalate mempty . fmap show . configJwtRoleClaimKey)
,("jwt-secret", q . toS . showJwtSecret)
@@ -226,6 +228,7 @@ parser optPath env dbSettings =
<*> parseTxEnd "db-tx-end" fst
<*> reqString "db-uri"
<*> parseEmbedDefaultJoin "db-embed-default-join"
<*> (fromMaybe True <$> optBool "db-use-legacy-gucs")
<*> pure optPath
<*> pure Nothing
<*> parseJwtAudience "jwt-aud"
+4
View File
@@ -12,6 +12,7 @@ module PostgREST.Config.PgVersion
, pgVersion114
, pgVersion121
, pgVersion130
, pgVersion140
) where
import qualified Data.Aeson as JSON
@@ -58,3 +59,6 @@ pgVersion121 = PgVersion 120001 "12.1"
pgVersion130 :: PgVersion
pgVersion130 = PgVersion 130000 "13.0"
pgVersion140 :: PgVersion
pgVersion140 = PgVersion 140000 "14.0"
+31 -9
View File
@@ -14,6 +14,7 @@ module PostgREST.Middleware
import qualified Data.Aeson as JSON
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import qualified Data.CaseInsensitive as CI
import qualified Data.HashMap.Strict as M
import qualified Data.Text as T
@@ -30,6 +31,8 @@ import qualified Network.Wai.Middleware.Gzip as Wai
import qualified Network.Wai.Middleware.RequestLogger as Wai
import qualified Network.Wai.Middleware.Static as Wai
import Control.Arrow ((***))
import Data.Function (id)
import Data.List (lookup)
import Data.Scientific (FPFormat (..), formatScientific,
@@ -40,6 +43,7 @@ import System.IO.Unsafe (unsafePerformIO)
import System.Log.FastLogger (toLogStr)
import PostgREST.Config (AppConfig (..), LogLevel (..))
import PostgREST.Config.PgVersion (PgVersion (..), pgVersion140)
import PostgREST.Error (Error, errorResponseFor)
import PostgREST.GucHeader (addHeadersIfNotIncluded)
import PostgREST.Query.SqlFragment (fromQi, intercalateSnippet,
@@ -54,8 +58,8 @@ import Protolude.Conv (toS)
-- | Runs local(transaction scoped) GUCs for every request, plus the pre-request function
runPgLocals :: AppConfig -> M.HashMap Text JSON.Value ->
(ApiRequest -> ExceptT Error H.Transaction Wai.Response) ->
ApiRequest -> ByteString -> ExceptT Error H.Transaction Wai.Response
runPgLocals conf claims app req jsonDbS = do
ApiRequest -> ByteString -> PgVersion -> ExceptT Error H.Transaction Wai.Response
runPgLocals conf claims app req jsonDbS actualPgVersion = do
lift $ H.statement mempty $ H.dynamicallyParameterized
("select " <> intercalateSnippet ", " (searchPathSql : roleSql ++ claimsSql ++ [methodSql, pathSql] ++ headersSql ++ cookiesSql ++ appSettingsSql ++ specSql))
HD.noResult (configDbPreparedStatements conf)
@@ -64,12 +68,18 @@ runPgLocals conf claims app req jsonDbS = do
where
methodSql = setConfigLocal mempty ("request.method", iMethod req)
pathSql = setConfigLocal mempty ("request.path", iPath req)
headersSql = setConfigLocal "request.header." <$> iHeaders req
cookiesSql = setConfigLocal "request.cookie." <$> iCookies req
headersSql = if usesLegacyGucs
then setConfigLocal "request.header." <$> iHeaders req
else setConfigLocalJson "request.headers" (iHeaders req)
cookiesSql = if usesLegacyGucs
then setConfigLocal "request.cookie." <$> iCookies req
else setConfigLocalJson "request.cookies" (iCookies req)
claimsWithRole =
let anon = JSON.String . toS $ configDbAnonRole conf in -- role claim defaults to anon if not specified in jwt
M.union claims (M.singleton "role" anon)
claimsSql = setConfigLocal "request.jwt.claim." <$> [(toS c, toS $ unquoted v) | (c,v) <- M.toList claimsWithRole]
claimsSql = if usesLegacyGucs
then setConfigLocal "request.jwt.claim." <$> [(toS c, toS $ unquoted v) | (c,v) <- M.toList claimsWithRole]
else [setConfigLocal mempty ("request.jwt.claims", BSL.toStrict $ JSON.encode claimsWithRole)]
roleSql = maybeToList $ (\x -> setConfigLocal mempty ("role", toS $ unquoted x)) <$> M.lookup "role" claimsWithRole
appSettingsSql = setConfigLocal mempty <$> (join bimap toS <$> configAppSettings conf)
searchPathSql =
@@ -79,10 +89,7 @@ runPgLocals conf claims app req jsonDbS = do
specSql = case iTarget req of
TargetProc{tpIsRootSpec=True} -> [setConfigLocal mempty ("request.spec", jsonDbS)]
_ -> mempty
-- | Do a pg set_config(setting, value, true) call. This is equivalent to a SET LOCAL.
setConfigLocal :: ByteString -> (ByteString, ByteString) -> H.Snippet
setConfigLocal prefix (k, v) =
"set_config(" <> unknownEncoder (prefix <> k) <> ", " <> unknownEncoder v <> ", true)"
usesLegacyGucs = configDbUseLegacyGucs conf && actualPgVersion < pgVersion140
-- | Log in apache format. Only requests that have a status greater than minStatus are logged.
-- | There's no way to filter logs in the apache format on wai-extra: https://hackage.haskell.org/package/wai-extra-3.0.29.2/docs/Network-Wai-Middleware-RequestLogger.html#t:OutputFormat.
@@ -181,3 +188,18 @@ optionalRollback AppConfig{..} ApiRequest{..} transaction = do
[(HTTP.hPreferenceApplied, BS.pack (show Rollback))]
| otherwise =
identity
-- | Do a pg set_config(setting, value, true) call. This is equivalent to a SET LOCAL.
setConfigLocal :: ByteString -> (ByteString, ByteString) -> H.Snippet
setConfigLocal prefix (k, v) =
"set_config(" <> unknownEncoder (prefix <> k) <> ", " <> unknownEncoder v <> ", true)"
-- | Starting from PostgreSQL v14, some characters are not allowed for config names (mostly affecting headers with "-").
-- | A JSON format string is used to avoid this problem. See https://github.com/PostgREST/postgrest/issues/1857
setConfigLocalJson :: ByteString -> [(ByteString, ByteString)] -> [H.Snippet]
setConfigLocalJson prefix keyVals = [setConfigLocal mempty (prefix, gucJsonVal keyVals)]
where
gucJsonVal :: [(ByteString, ByteString)] -> ByteString
gucJsonVal = BSL.toStrict . JSON.encode . M.fromList . arrayByteStringToText
arrayByteStringToText :: [(ByteString, ByteString)] -> [(Text,Text)]
arrayByteStringToText keyVal = (toS *** toS) <$> keyVal
+68
View File
@@ -0,0 +1,68 @@
module Feature.LegacyGucsSpec where
import Network.Wai (Application)
import Network.HTTP.Types
import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON
import Protolude hiding (get)
import SpecHelper
spec :: SpecWith ((), Application)
spec =
describe "remote procedure call with legacy gucs disabled" $ do
it "custom header is set" $
request methodPost "/rpc/get_guc_value" [("Custom-Header", "test")]
[json| { "prefix": "request.headers", "name": "custom-header" } |]
`shouldRespondWith`
[json|"test"|]
{ matchStatus = 200
, matchHeaders = [ matchContentTypeJson ]
}
it "standard header is set" $
request methodPost "/rpc/get_guc_value" [("Origin", "http://example.com")]
[json| { "prefix": "request.headers", "name": "origin" } |]
`shouldRespondWith`
[json|"http://example.com"|]
{ matchStatus = 200
, matchHeaders = [ matchContentTypeJson ]
}
it "current role is available as GUC claim" $
request methodPost "/rpc/get_guc_value" []
[json| { "prefix": "request.jwt.claims", "name": "role" } |]
`shouldRespondWith`
[json|"postgrest_test_anonymous"|]
{ matchStatus = 200
, matchHeaders = [ matchContentTypeJson ]
}
it "single cookie ends up as claims" $
request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue")]
[json| {"prefix": "request.cookies", "name":"acookie"} |]
`shouldRespondWith`
[json|"cookievalue"|]
{ matchStatus = 200
, matchHeaders = []
}
it "multiple cookies ends up as claims" $
request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue;secondcookie=anothervalue")]
[json| {"prefix": "request.cookies", "name":"secondcookie"} |]
`shouldRespondWith`
[json|"anothervalue"|]
{ matchStatus = 200
, matchHeaders = []
}
it "gets the Authorization value" $
request methodPost "/rpc/get_guc_value" [authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"]
[json| {"prefix": "request.headers", "name":"authorization"} |]
`shouldRespondWith`
[json|"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"|]
{ matchStatus = 200
, matchHeaders = []
}
+37 -7
View File
@@ -15,7 +15,7 @@ import Text.Heredoc
import PostgREST.Config.PgVersion (PgVersion, pgVersion100,
pgVersion109, pgVersion110,
pgVersion112, pgVersion114,
pgVersion96)
pgVersion140, pgVersion96)
import Protolude hiding (get)
import SpecHelper
@@ -798,7 +798,12 @@ spec actualPgVersion =
it "custom header is set" $
request methodPost "/rpc/get_guc_value"
[("Custom-Header", "test")]
[json| { "name": "request.header.custom-header" } |]
(
if actualPgVersion >= pgVersion140 then
[json| { "prefix": "request.headers", "name": "custom-header" } |]
else
[json| { "name": "request.header.custom-header" } |]
)
`shouldRespondWith`
[json|"test"|]
{ matchStatus = 200
@@ -807,7 +812,12 @@ spec actualPgVersion =
it "standard header is set" $
request methodPost "/rpc/get_guc_value"
[("Origin", "http://example.com")]
[json| { "name": "request.header.origin" } |]
(
if actualPgVersion >= pgVersion140 then
[json| { "prefix": "request.headers", "name": "origin" } |]
else
[json| { "name": "request.header.origin" } |]
)
`shouldRespondWith`
[json|"http://example.com"|]
{ matchStatus = 200
@@ -815,7 +825,12 @@ spec actualPgVersion =
}
it "current role is available as GUC claim" $
request methodPost "/rpc/get_guc_value" []
[json| { "name": "request.jwt.claim.role" } |]
(
if actualPgVersion >= pgVersion140 then
[json| { "prefix": "request.jwt.claims", "name": "role" } |]
else
[json| { "name": "request.jwt.claim.role" } |]
)
`shouldRespondWith`
[json|"postgrest_test_anonymous"|]
{ matchStatus = 200
@@ -823,7 +838,12 @@ spec actualPgVersion =
}
it "single cookie ends up as claims" $
request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue")]
[json| {"name":"request.cookie.acookie"} |]
(
if actualPgVersion >= pgVersion140 then
[json| {"prefix": "request.cookies", "name":"acookie"} |]
else
[json| {"name":"request.cookie.acookie"} |]
)
`shouldRespondWith`
[json|"cookievalue"|]
{ matchStatus = 200
@@ -831,7 +851,12 @@ spec actualPgVersion =
}
it "multiple cookies ends up as claims" $
request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue;secondcookie=anothervalue")]
[json| {"name":"request.cookie.secondcookie"} |]
(
if actualPgVersion >= pgVersion140 then
[json| {"prefix": "request.cookies", "name":"secondcookie"} |]
else
[json| {"name":"request.cookie.secondcookie"} |]
)
`shouldRespondWith`
[json|"anothervalue"|]
{ matchStatus = 200
@@ -847,7 +872,12 @@ spec actualPgVersion =
}
it "gets the Authorization value" $
request methodPost "/rpc/get_guc_value" [authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"]
[json| {"name":"request.header.authorization"} |]
(
if actualPgVersion >= pgVersion140 then
[json| {"prefix": "request.headers", "name":"authorization"} |]
else
[json| {"name":"request.header.authorization"} |]
)
`shouldRespondWith`
[json|"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"|]
{ matchStatus = 200
+7
View File
@@ -36,6 +36,7 @@ import qualified Feature.HtmlRawOutputSpec
import qualified Feature.IgnorePrivOpenApiSpec
import qualified Feature.InsertSpec
import qualified Feature.JsonOperatorSpec
import qualified Feature.LegacyGucsSpec
import qualified Feature.MultipleSchemaSpec
import qualified Feature.NoJwtSpec
import qualified Feature.NonexistentSchemaSpec
@@ -88,6 +89,7 @@ main = do
(configDbSchemas config)
(configDbExtraSearchPath config)
appState <- AppState.initWithPool pool config
AppState.putPgVersion appState actualPgVersion
AppState.putDbStructure appState customDbStructure
when (isJust $ configDbRootSpec config) $
AppState.putJsonDbS appState $ toS $ JSON.encode baseDbStructure
@@ -108,6 +110,7 @@ main = do
responseHeadersApp = app testCfgResponseHeaders
disallowRollbackApp = app testCfgDisallowRollback
forceRollbackApp = app testCfgForceRollback
testCfgLegacyGucsApp = app testCfgLegacyGucs
extraSearchPathApp = appDbs testCfgExtraSearchPath
unicodeApp = appDbs testUnicodeCfg
@@ -210,6 +213,10 @@ main = do
parallel $ before multipleSchemaApp $
describe "Feature.MultipleSchemaSpec" $ Feature.MultipleSchemaSpec.spec actualPgVersion
-- this test runs with db-uses-legacy-gucs = false
parallel $ before testCfgLegacyGucsApp $
describe "Feature.LegacyGucsSpec" Feature.LegacyGucsSpec.spec
-- this test runs with db-embed-default-join = inner
before embedInnerJoinApp $
describe "Feature.EmbedInnerJoinSpecNotDefaultConfig" Feature.EmbedInnerJoinSpec.notDefaultConfig
+4
View File
@@ -91,6 +91,7 @@ _baseCfg = let secret = Just $ encodeUtf8 "reallyreallyreallyreallyverysafe" in
, configDbConfig = False
, configDbUri = mempty
, configDbEmbedDefaultJoin = JTLeft
, configDbUseLegacyGucs = True
, configFilePath = Nothing
, configJWKS = parseSecret <$> secret
, configJwtAudience = Nothing
@@ -190,6 +191,9 @@ testCfgResponseHeaders testDbConn = (testCfg testDbConn) { configDbPreRequest =
testMultipleSchemaCfg :: Text -> AppConfig
testMultipleSchemaCfg testDbConn = (testCfg testDbConn) { configDbSchemas = fromList ["v1", "v2"] }
testCfgLegacyGucs :: Text -> AppConfig
testCfgLegacyGucs testDbConn = (testCfg testDbConn) { configDbUseLegacyGucs = False }
resetDb :: Text -> IO ()
resetDb dbConn = loadFixture dbConn "data"
+52 -15
View File
@@ -82,7 +82,10 @@ CREATE FUNCTION set_authors_only_owner() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
NEW.owner = current_setting('request.jwt.claim.id');
NEW.owner = case when current_setting('server_version_num')::int >= 140000
then current_setting('request.jwt.claims')::json->>'id'
else current_setting('request.jwt.claim.id')
end;
RETURN NEW;
end
$$;
@@ -301,7 +304,10 @@ CREATE OR REPLACE FUNCTION switch_role() RETURNS void
declare
user_id text;
Begin
user_id = current_setting('request.jwt.claim.id')::text;
user_id = case when current_setting('server_version_num')::int >= 140000
then (current_setting('request.jwt.claims')::json->>'id')::text
else current_setting('request.jwt.claim.id')::text
end;
if user_id = '1'::text then
execute 'set local role postgrest_test_author';
elseif user_id = '2'::text then
@@ -329,18 +335,33 @@ CREATE FUNCTION reveal_big_jwt() RETURNS TABLE (
iss text, sub text, exp bigint,
nbf bigint, iat bigint, jti text, "http://postgrest.com/foo" boolean
)
LANGUAGE sql SECURITY DEFINER
LANGUAGE plpgsql SECURITY DEFINER
STABLE
AS $$
SELECT current_setting('request.jwt.claim.iss') as iss,
current_setting('request.jwt.claim.sub') as sub,
current_setting('request.jwt.claim.exp')::bigint as exp,
current_setting('request.jwt.claim.nbf')::bigint as nbf,
current_setting('request.jwt.claim.iat')::bigint as iat,
current_setting('request.jwt.claim.jti') as jti,
-- role is not included in the claims list
current_setting('request.jwt.claim.http://postgrest.com/foo')::boolean
as "http://postgrest.com/foo";
BEGIN
-- JWT claims are set in JSON format since v14
IF (current_setting('server_version_num')::INT >= 140000) THEN
RETURN QUERY
SELECT current_setting('request.jwt.claims')::json->>'iss' as iss,
current_setting('request.jwt.claims')::json->>'sub' as sub,
(current_setting('request.jwt.claims')::json->>'exp')::bigint as exp,
(current_setting('request.jwt.claims')::json->>'nbf')::bigint as nbf,
(current_setting('request.jwt.claims')::json->>'iat')::bigint as iat,
current_setting('request.jwt.claims')::json->>'jti' as jti,
(current_setting('request.jwt.claims')::json->>'http://postgrest.com/foo')::boolean
as "http://postgrest.com/foo";
ELSE
RETURN QUERY
SELECT current_setting('request.jwt.claim.iss') as iss,
current_setting('request.jwt.claim.sub') as sub,
current_setting('request.jwt.claim.exp')::bigint as exp,
current_setting('request.jwt.claim.nbf')::bigint as nbf,
current_setting('request.jwt.claim.iat')::bigint as iat,
current_setting('request.jwt.claim.jti') as jti,
current_setting('request.jwt.claim.http://postgrest.com/foo')::boolean
as "http://postgrest.com/foo";
END IF;
END;
$$;
@@ -1093,6 +1114,11 @@ create function test.get_guc_value(name text) returns text as $$
select nullif(current_setting(name), '')::text;
$$ language sql;
-- Get the GUC values for Postgres v14.0 and up
create function test.get_guc_value(prefix text, name text) returns text as $$
select nullif(current_setting(prefix)::json->>name, '')::text;
$$ language sql;
create table w_or_wo_comma_names ( name text );
create table items_with_different_col_types (
@@ -1784,8 +1810,13 @@ openapi json = $$
}
}
$$;
accept text;
begin
case current_setting('request.header.accept', true)
accept = case when current_setting('server_version_num')::int >= 140000
then current_setting('request.headers', true)::json->>'accept'
else current_setting('request.header.accept', true)
end;
case accept
when 'application/openapi+json' then
return openapi;
when 'application/json' then
@@ -1958,9 +1989,15 @@ add constraint snd_shift foreign key (snd_shift_activity_id, snd_shift
-- for a pre-request function
create or replace function custom_headers() returns void as $$
declare
user_agent text := current_setting('request.header.user-agent', true);
user_agent text := case when current_setting('server_version_num')::int >= 140000
then current_setting('request.headers', true)::json->>'user-agent'
else current_setting('request.header.user-agent', true)
end;
req_path text := current_setting('request.path', true);
req_accept text := current_setting('request.header.accept', true);
req_accept text := case when current_setting('server_version_num')::int >= 140000
then current_setting('request.headers', true)::json->>'accept'
else current_setting('request.header.accept', true)
end;
req_method text := current_setting('request.method', true);
begin
if user_agent similar to 'MSIE (6.0|7.0)' then
@@ -13,6 +13,7 @@ db-config = "false"
db-tx-end = "commit"
db-uri = "required"
db-embed-default-join = "left"
db-use-legacy-gucs = true
jwt-aud = ""
jwt-role-claim-key = ".\"aliased\""
jwt-secret = ""
@@ -13,6 +13,7 @@ db-config = "false"
db-tx-end = "commit"
db-uri = "required"
db-embed-default-join = "left"
db-use-legacy-gucs = true
jwt-aud = ""
jwt-role-claim-key = ".\"role\""
jwt-secret = ""
@@ -13,6 +13,7 @@ db-config = "false"
db-tx-end = "commit"
db-uri = "required"
db-embed-default-join = "left"
db-use-legacy-gucs = true
jwt-aud = ""
jwt-role-claim-key = ".\"role\""
jwt-secret = ""
@@ -13,6 +13,7 @@ db-config = "false"
db-tx-end = "commit"
db-uri = "required"
db-embed-default-join = "left"
db-use-legacy-gucs = true
jwt-aud = ""
jwt-role-claim-key = ".\"role\""
jwt-secret = ""
@@ -13,6 +13,7 @@ db-config = "true"
db-tx-end = "rollback-allow-override"
db-uri = "<REPLACED_WITH_DB_URI>"
db-embed-default-join = "inner"
db-use-legacy-gucs = false
jwt-aud = "https://otherexample.org"
jwt-role-claim-key = ".\"other\".\"role\""
jwt-secret = "ODERREALLYREALLYREALLYREALLYVERYSAFE"
@@ -13,6 +13,7 @@ db-config = "true"
db-tx-end = "commit-allow-override"
db-uri = "<REPLACED_WITH_DB_URI>"
db-embed-default-join = "inner"
db-use-legacy-gucs = false
jwt-aud = "https://example.org"
jwt-role-claim-key = ".\"a\".\"role\""
jwt-secret = "OVERRIDEREALLYREALLYREALLYREALLYVERYSAFE"
@@ -13,6 +13,7 @@ db-config = "false"
db-tx-end = "rollback-allow-override"
db-uri = "tmp_db"
db-embed-default-join = "inner"
db-use-legacy-gucs = false
jwt-aud = "https://postgrest.org"
jwt-role-claim-key = ".\"user\"[0].\"real-role\""
jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5"
@@ -13,6 +13,7 @@ db-config = "true"
db-tx-end = "commit"
db-uri = "required"
db-embed-default-join = "left"
db-use-legacy-gucs = true
jwt-aud = ""
jwt-role-claim-key = ".\"role\""
jwt-secret = ""
@@ -15,6 +15,7 @@ PGRST_DB_CONFIG: false
PGRST_DB_TX_END: rollback-allow-override
PGRST_DB_URI: tmp_db
PGRST_DB_EMBED_DEFAULT_JOIN: inner
PGRST_DB_USE_LEGACY_GUCS: false
PGRST_JWT_AUD: 'https://postgrest.org'
PGRST_JWT_ROLE_CLAIM_KEY: '.user[0]."real-role"'
PGRST_JWT_SECRET: c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5
+1
View File
@@ -13,6 +13,7 @@ db-config = "false"
db-tx-end = "rollback-allow-override"
db-uri = "tmp_db"
db-embed-default-join = "inner"
db-use-legacy-gucs = false
jwt-aud = "https://postgrest.org"
jwt-role-claim-key = ".user[0].\"real-role\""
jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5"