break:remove the db-use-legacy-gucs config

BREAKING CHANGE

All PostgreSQL versions will use JSON GUCs for headers, cookies and JWT claims.
This commit is contained in:
Laurence Isla
2023-11-02 15:50:22 -05:00
parent b235227119
commit 226400a5bc
22 changed files with 37 additions and 213 deletions
+1
View File
@@ -29,6 +29,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Removed [raw-media-types config](https://postgrest.org/en/v11.1/references/configuration.html#raw-media-types) - @steve-chavez - Removed [raw-media-types config](https://postgrest.org/en/v11.1/references/configuration.html#raw-media-types) - @steve-chavez
- Removed `application/octet-stream`, `text/plain`, `text/xml` [builtin support for scalar results](https://postgrest.org/en/v11.1/references/api/resource_representation.html#scalar-function-response-format) - @steve-chavez - Removed `application/octet-stream`, `text/plain`, `text/xml` [builtin support for scalar results](https://postgrest.org/en/v11.1/references/api/resource_representation.html#scalar-function-response-format) - @steve-chavez
- Removed default `application/openapi+json` media type for [db-root-spec](https://postgrest.org/en/v11.1/references/configuration.html#db-root-spec) - @steve-chavez - Removed default `application/openapi+json` media type for [db-root-spec](https://postgrest.org/en/v11.1/references/configuration.html#db-root-spec) - @steve-chavez
- Removed [db-use-legacy-gucs](https://postgrest.org/en/v11.2/references/configuration.html#db-use-legacy-gucs) - @laurenceisla
## [11.2.2] - 2023-10-25 ## [11.2.2] - 2023-10-25
-1
View File
@@ -192,7 +192,6 @@ test-suite spec
Feature.ConcurrentSpec Feature.ConcurrentSpec
Feature.CorsSpec Feature.CorsSpec
Feature.ExtraSearchPathSpec Feature.ExtraSearchPathSpec
Feature.LegacyGucsSpec
Feature.NoSuperuserSpec Feature.NoSuperuserSpec
Feature.ObservabilitySpec Feature.ObservabilitySpec
Feature.OpenApi.DisabledOpenApiSpec Feature.OpenApi.DisabledOpenApiSpec
+1 -1
View File
@@ -251,7 +251,7 @@ handleRequest AuthResult{..} conf appState authenticated prepared pgVer apiReq@A
roleIsoLvl = HM.findWithDefault SQL.ReadCommitted authRole $ configRoleIsoLvl conf roleIsoLvl = HM.findWithDefault SQL.ReadCommitted authRole $ configRoleIsoLvl conf
runQuery isoLvl mode query = runQuery isoLvl mode query =
runDbHandler appState isoLvl mode authenticated prepared $ do runDbHandler appState isoLvl mode authenticated prepared $ do
Query.setPgLocals conf authClaims authRole (HM.toList roleSettings) apiReq pgVer Query.setPgLocals conf authClaims authRole (HM.toList roleSettings) apiReq
Query.runPreReq conf Query.runPreReq conf
query query
-4
View File
@@ -191,10 +191,6 @@ exampleConfigFile =
|## https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING |## https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
|db-uri = "postgresql://" |db-uri = "postgresql://"
| |
|## 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
|
|# jwt-aud = "your_audience_claim" |# jwt-aud = "your_audience_claim"
| |
|## Jspath to the role claim key |## Jspath to the role claim key
-3
View File
@@ -89,7 +89,6 @@ data AppConfig = AppConfig
, configDbTxAllowOverride :: Bool , configDbTxAllowOverride :: Bool
, configDbTxRollbackAll :: Bool , configDbTxRollbackAll :: Bool
, configDbUri :: Text , configDbUri :: Text
, configDbUseLegacyGucs :: Bool
, configFilePath :: Maybe FilePath , configFilePath :: Maybe FilePath
, configJWKS :: Maybe JWKSet , configJWKS :: Maybe JWKSet
, configJwtAudience :: Maybe StringOrURI , configJwtAudience :: Maybe StringOrURI
@@ -157,7 +156,6 @@ toText conf =
,("db-pre-config", q . maybe mempty dumpQi . configDbPreConfig) ,("db-pre-config", q . maybe mempty dumpQi . configDbPreConfig)
,("db-tx-end", q . showTxEnd) ,("db-tx-end", q . showTxEnd)
,("db-uri", q . configDbUri) ,("db-uri", q . configDbUri)
,("db-use-legacy-gucs", T.toLower . show . configDbUseLegacyGucs)
,("jwt-aud", T.decodeUtf8 . LBS.toStrict . JSON.encode . maybe "" toJSON . configJwtAudience) ,("jwt-aud", T.decodeUtf8 . LBS.toStrict . JSON.encode . maybe "" toJSON . configJwtAudience)
,("jwt-role-claim-key", q . T.intercalate mempty . fmap dumpJSPath . configJwtRoleClaimKey) ,("jwt-role-claim-key", q . T.intercalate mempty . fmap dumpJSPath . configJwtRoleClaimKey)
,("jwt-secret", q . T.decodeUtf8 . showJwtSecret) ,("jwt-secret", q . T.decodeUtf8 . showJwtSecret)
@@ -257,7 +255,6 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
<*> parseTxEnd "db-tx-end" snd <*> parseTxEnd "db-tx-end" snd
<*> parseTxEnd "db-tx-end" fst <*> parseTxEnd "db-tx-end" fst
<*> (fromMaybe "postgresql://" <$> optString "db-uri") <*> (fromMaybe "postgresql://" <$> optString "db-uri")
<*> (fromMaybe True <$> optBool "db-use-legacy-gucs")
<*> pure optPath <*> pure optPath
<*> pure Nothing <*> pure Nothing
<*> parseJwtAudience "jwt-aud" <*> parseJwtAudience "jwt-aud"
-1
View File
@@ -53,7 +53,6 @@ dbSettingsNames =
,"db_root_spec" ,"db_root_spec"
,"db_schemas" ,"db_schemas"
,"db_tx_end" ,"db_tx_end"
,"db_use_legacy_gucs"
,"jwt_aud" ,"jwt_aud"
,"jwt_role_claim_key" ,"jwt_role_claim_key"
,"jwt_secret" ,"jwt_secret"
+12 -31
View File
@@ -14,13 +14,11 @@ module PostgREST.Query
) where ) where
import qualified Data.Aeson as JSON import qualified Data.Aeson as JSON
import qualified Data.Aeson.Key as K
import qualified Data.Aeson.KeyMap as KM import qualified Data.Aeson.KeyMap as KM
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Data.ByteString.Lazy.Char8 as LBS
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import qualified Data.Set as S import qualified Data.Set as S
import qualified Data.Text.Encoding as T
import qualified Hasql.Decoders as HD import qualified Hasql.Decoders as HD
import qualified Hasql.DynamicStatements.Snippet as SQL (Snippet) import qualified Hasql.DynamicStatements.Snippet as SQL (Snippet)
import qualified Hasql.DynamicStatements.Statement as SQL import qualified Hasql.DynamicStatements.Statement as SQL
@@ -33,8 +31,6 @@ import qualified PostgREST.Query.Statements as Statements
import qualified PostgREST.RangeQuery as RangeQuery import qualified PostgREST.RangeQuery as RangeQuery
import qualified PostgREST.SchemaCache as SchemaCache import qualified PostgREST.SchemaCache as SchemaCache
import Data.Scientific (FPFormat (..), formatScientific, isInteger)
import PostgREST.ApiRequest (ApiRequest (..)) import PostgREST.ApiRequest (ApiRequest (..))
import PostgREST.ApiRequest.Preferences (PreferCount (..), import PostgREST.ApiRequest.Preferences (PreferCount (..),
PreferTransaction (..), PreferTransaction (..),
@@ -42,8 +38,7 @@ import PostgREST.ApiRequest.Preferences (PreferCount (..),
shouldCount) shouldCount)
import PostgREST.Config (AppConfig (..), import PostgREST.Config (AppConfig (..),
OpenAPIMode (..)) OpenAPIMode (..))
import PostgREST.Config.PgVersion (PgVersion (..), import PostgREST.Config.PgVersion (PgVersion (..))
pgVersion140)
import PostgREST.Error (Error) import PostgREST.Error (Error)
import PostgREST.MediaType (MediaType (..)) import PostgREST.MediaType (MediaType (..))
import PostgREST.Plan (CallReadPlan (..), import PostgREST.Plan (CallReadPlan (..),
@@ -238,37 +233,23 @@ optionalRollback AppConfig{..} ApiRequest{iPreferences=Preferences{..}} = do
-- | Runs local (transaction scoped) GUCs for every request. -- | Runs local (transaction scoped) GUCs for every request.
setPgLocals :: AppConfig -> KM.KeyMap JSON.Value -> BS.ByteString -> [(ByteString, ByteString)] -> setPgLocals :: AppConfig -> KM.KeyMap JSON.Value -> BS.ByteString -> [(ByteString, ByteString)] ->
ApiRequest -> PgVersion -> DbHandler () ApiRequest -> DbHandler ()
setPgLocals AppConfig{..} claims role roleSettings req actualPgVersion = lift $ setPgLocals AppConfig{..} claims role roleSettings req = lift $
SQL.statement mempty $ SQL.dynamicallyParameterized SQL.statement mempty $ SQL.dynamicallyParameterized
("select " <> intercalateSnippet ", " (searchPathSql : roleSql ++ roleSettingsSql ++ claimsSql ++ [methodSql, pathSql] ++ headersSql ++ cookiesSql ++ appSettingsSql)) ("select " <> intercalateSnippet ", " (searchPathSql : roleSql ++ roleSettingsSql ++ claimsSql ++ [methodSql, pathSql] ++ headersSql ++ cookiesSql ++ appSettingsSql))
HD.noResult configDbPreparedStatements HD.noResult configDbPreparedStatements
where where
methodSql = setConfigLocal mempty ("request.method", iMethod req) methodSql = setConfigLocal ("request.method", iMethod req)
pathSql = setConfigLocal mempty ("request.path", iPath req) pathSql = setConfigLocal ("request.path", iPath req)
headersSql = if usesLegacyGucs headersSql = setConfigLocalJson "request.headers" (iHeaders req)
then setConfigLocal "request.header." <$> iHeaders req cookiesSql = setConfigLocalJson "request.cookies" (iCookies req)
else setConfigLocalJson "request.headers" (iHeaders req) claimsSql = [setConfigLocal ("request.jwt.claims", LBS.toStrict $ JSON.encode claims)]
cookiesSql = if usesLegacyGucs roleSql = [setConfigLocal ("role", role)]
then setConfigLocal "request.cookie." <$> iCookies req roleSettingsSql = setConfigLocal <$> roleSettings
else setConfigLocalJson "request.cookies" (iCookies req) appSettingsSql = setConfigLocal <$> (join bimap toUtf8 <$> configAppSettings)
claimsSql = if usesLegacyGucs
then setConfigLocal "request.jwt.claim." <$> [(toUtf8 $ K.toText c, toUtf8 $ unquoted v) | (c,v) <- KM.toList claims]
else [setConfigLocal mempty ("request.jwt.claims", LBS.toStrict $ JSON.encode claims)]
roleSql = [setConfigLocal mempty ("role", role)]
roleSettingsSql = setConfigLocal mempty <$> roleSettings
appSettingsSql = setConfigLocal mempty <$> (join bimap toUtf8 <$> configAppSettings)
searchPathSql = searchPathSql =
let schemas = escapeIdentList (iSchema req : configDbExtraSearchPath) in let schemas = escapeIdentList (iSchema req : configDbExtraSearchPath) in
setConfigLocal mempty ("search_path", schemas) setConfigLocal ("search_path", schemas)
usesLegacyGucs = configDbUseLegacyGucs && actualPgVersion < pgVersion140
unquoted :: JSON.Value -> Text
unquoted (JSON.String t) = t
unquoted (JSON.Number n) =
toS $ formatScientific Fixed (if isInteger n then Just 0 else Nothing) n
unquoted (JSON.Bool b) = show b
unquoted v = T.decodeUtf8 . LBS.toStrict $ JSON.encode v
-- | Runs the pre-request function. -- | Runs the pre-request function.
runPreReq :: AppConfig -> DbHandler () runPreReq :: AppConfig -> DbHandler ()
+4 -4
View File
@@ -484,14 +484,14 @@ explainF fmt opts snip =
fmtPlanFmt PlanJSON = "FORMAT JSON" fmtPlanFmt PlanJSON = "FORMAT JSON"
-- | Do a pg set_config(setting, value, true) call. This is equivalent to a SET LOCAL. -- | Do a pg set_config(setting, value, true) call. This is equivalent to a SET LOCAL.
setConfigLocal :: ByteString -> (ByteString, ByteString) -> SQL.Snippet setConfigLocal :: (ByteString, ByteString) -> SQL.Snippet
setConfigLocal prefix (k, v) = setConfigLocal (k, v) =
"set_config(" <> unknownEncoder (prefix <> k) <> ", " <> unknownEncoder v <> ", true)" "set_config(" <> unknownEncoder k <> ", " <> unknownEncoder v <> ", true)"
-- | Starting from PostgreSQL v14, some characters are not allowed for config names (mostly affecting headers with "-"). -- | 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 -- | A JSON format string is used to avoid this problem. See https://github.com/PostgREST/postgrest/issues/1857
setConfigLocalJson :: ByteString -> [(ByteString, ByteString)] -> [SQL.Snippet] setConfigLocalJson :: ByteString -> [(ByteString, ByteString)] -> [SQL.Snippet]
setConfigLocalJson prefix keyVals = [setConfigLocal mempty (prefix, gucJsonVal keyVals)] setConfigLocalJson prefix keyVals = [setConfigLocal (prefix, gucJsonVal keyVals)]
where where
gucJsonVal :: [(ByteString, ByteString)] -> ByteString gucJsonVal :: [(ByteString, ByteString)] -> ByteString
gucJsonVal = LBS.toStrict . JSON.encode . HM.fromList . arrayByteStringToText gucJsonVal = LBS.toStrict . JSON.encode . HM.fromList . arrayByteStringToText
-1
View File
@@ -17,7 +17,6 @@ db-config = true
db-pre-config = "" db-pre-config = ""
db-tx-end = "commit" db-tx-end = "commit"
db-uri = "postgresql://" db-uri = "postgresql://"
db-use-legacy-gucs = true
jwt-aud = "" jwt-aud = ""
jwt-role-claim-key = ".\"aliased\"" jwt-role-claim-key = ".\"aliased\""
jwt-secret = "" jwt-secret = ""
@@ -17,7 +17,6 @@ db-config = true
db-pre-config = "" db-pre-config = ""
db-tx-end = "commit" db-tx-end = "commit"
db-uri = "postgresql://" db-uri = "postgresql://"
db-use-legacy-gucs = true
jwt-aud = "" jwt-aud = ""
jwt-role-claim-key = ".\"role\"" jwt-role-claim-key = ".\"role\""
jwt-secret = "" jwt-secret = ""
@@ -17,7 +17,6 @@ db-config = true
db-pre-config = "" db-pre-config = ""
db-tx-end = "commit" db-tx-end = "commit"
db-uri = "postgresql://" db-uri = "postgresql://"
db-use-legacy-gucs = true
jwt-aud = "" jwt-aud = ""
jwt-role-claim-key = ".\"role\"" jwt-role-claim-key = ".\"role\""
jwt-secret = "" jwt-secret = ""
-1
View File
@@ -17,7 +17,6 @@ db-config = false
db-pre-config = "" db-pre-config = ""
db-tx-end = "commit" db-tx-end = "commit"
db-uri = "postgresql://" db-uri = "postgresql://"
db-use-legacy-gucs = true
jwt-aud = "" jwt-aud = ""
jwt-role-claim-key = ".\"role\"" jwt-role-claim-key = ".\"role\""
jwt-secret = "" jwt-secret = ""
@@ -17,7 +17,6 @@ db-config = true
db-pre-config = "postgrest.pre_config" db-pre-config = "postgrest.pre_config"
db-tx-end = "rollback-allow-override" db-tx-end = "rollback-allow-override"
db-uri = "postgresql://" db-uri = "postgresql://"
db-use-legacy-gucs = false
jwt-aud = "https://otherexample.org" jwt-aud = "https://otherexample.org"
jwt-role-claim-key = ".\"other\".\"pre_config_role\"" jwt-role-claim-key = ".\"other\".\"pre_config_role\""
jwt-secret = "ODERREALLYREALLYREALLYREALLYVERYSAFE" jwt-secret = "ODERREALLYREALLYREALLYREALLYVERYSAFE"
@@ -17,7 +17,6 @@ db-config = true
db-pre-config = "postgrest.preconf" db-pre-config = "postgrest.preconf"
db-tx-end = "commit-allow-override" db-tx-end = "commit-allow-override"
db-uri = "postgresql://" db-uri = "postgresql://"
db-use-legacy-gucs = false
jwt-aud = "https://example.org" jwt-aud = "https://example.org"
jwt-role-claim-key = ".\"a\".\"role\"" jwt-role-claim-key = ".\"a\".\"role\""
jwt-secret = "OVERRIDE=REALLY=REALLY=REALLY=REALLY=VERY=SAFE" jwt-secret = "OVERRIDE=REALLY=REALLY=REALLY=REALLY=VERY=SAFE"
@@ -17,7 +17,6 @@ db-config = false
db-pre-config = "postgrest.pre_config" db-pre-config = "postgrest.pre_config"
db-tx-end = "rollback-allow-override" db-tx-end = "rollback-allow-override"
db-uri = "tmp_db" db-uri = "tmp_db"
db-use-legacy-gucs = false
jwt-aud = "https://postgrest.org" jwt-aud = "https://postgrest.org"
jwt-role-claim-key = ".\"user\"[0].\"real-role\"" jwt-role-claim-key = ".\"user\"[0].\"real-role\""
jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5" jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5"
-1
View File
@@ -17,7 +17,6 @@ db-config = true
db-pre-config = "" db-pre-config = ""
db-tx-end = "commit" db-tx-end = "commit"
db-uri = "postgresql://" db-uri = "postgresql://"
db-use-legacy-gucs = true
jwt-aud = "" jwt-aud = ""
jwt-role-claim-key = ".\"role\"" jwt-role-claim-key = ".\"role\""
jwt-secret = "" jwt-secret = ""
-1
View File
@@ -17,7 +17,6 @@ db-config = false
db-pre-config = "postgrest.pre_config" db-pre-config = "postgrest.pre_config"
db-tx-end = "rollback-allow-override" db-tx-end = "rollback-allow-override"
db-uri = "tmp_db" db-uri = "tmp_db"
db-use-legacy-gucs = false
jwt-aud = "https://postgrest.org" jwt-aud = "https://postgrest.org"
jwt-role-claim-key = ".user[0].\"real-role\"" jwt-role-claim-key = ".user[0].\"real-role\""
jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5" jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5"
-68
View File
@@ -1,68 +0,0 @@
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 = []
}
+5 -36
View File
@@ -13,8 +13,7 @@ import Text.Heredoc
import PostgREST.Config.PgVersion (PgVersion, pgVersion100, import PostgREST.Config.PgVersion (PgVersion, pgVersion100,
pgVersion109, pgVersion110, pgVersion109, pgVersion110,
pgVersion112, pgVersion114, pgVersion112, pgVersion114)
pgVersion140)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
@@ -974,12 +973,7 @@ spec actualPgVersion =
it "custom header is set" $ it "custom header is set" $
request methodPost "/rpc/get_guc_value" request methodPost "/rpc/get_guc_value"
[("Custom-Header", "test")] [("Custom-Header", "test")]
( [json| { "prefix": "request.headers", "name": "custom-header" } |]
if actualPgVersion >= pgVersion140 then
[json| { "prefix": "request.headers", "name": "custom-header" } |]
else
[json| { "name": "request.header.custom-header" } |]
)
`shouldRespondWith` `shouldRespondWith`
[json|"test"|] [json|"test"|]
{ matchStatus = 200 { matchStatus = 200
@@ -988,12 +982,7 @@ spec actualPgVersion =
it "standard header is set" $ it "standard header is set" $
request methodPost "/rpc/get_guc_value" request methodPost "/rpc/get_guc_value"
[("Origin", "http://example.com")] [("Origin", "http://example.com")]
( [json| { "prefix": "request.headers", "name": "origin" } |]
if actualPgVersion >= pgVersion140 then
[json| { "prefix": "request.headers", "name": "origin" } |]
else
[json| { "name": "request.header.origin" } |]
)
`shouldRespondWith` `shouldRespondWith`
[json|"http://example.com"|] [json|"http://example.com"|]
{ matchStatus = 200 { matchStatus = 200
@@ -1001,12 +990,7 @@ spec actualPgVersion =
} }
it "current role is available as GUC claim" $ it "current role is available as GUC claim" $
request methodPost "/rpc/get_guc_value" [] request methodPost "/rpc/get_guc_value" []
( [json| { "prefix": "request.jwt.claims", "name": "role" } |]
if actualPgVersion >= pgVersion140 then
[json| { "prefix": "request.jwt.claims", "name": "role" } |]
else
[json| { "name": "request.jwt.claim.role" } |]
)
`shouldRespondWith` `shouldRespondWith`
[json|"postgrest_test_anonymous"|] [json|"postgrest_test_anonymous"|]
{ matchStatus = 200 { matchStatus = 200
@@ -1014,25 +998,15 @@ spec actualPgVersion =
} }
it "single cookie ends up as claims" $ it "single cookie ends up as claims" $
request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue")] request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue")]
(
if actualPgVersion >= pgVersion140 then
[json| {"prefix": "request.cookies", "name":"acookie"} |] [json| {"prefix": "request.cookies", "name":"acookie"} |]
else
[json| {"name":"request.cookie.acookie"} |]
)
`shouldRespondWith` `shouldRespondWith`
[json|"cookievalue"|] [json|"cookievalue"|]
{ matchStatus = 200 { matchStatus = 200
, matchHeaders = [] , matchHeaders = []
} }
it "multiple cookies ends up as claims" $ it "multiple cookies end up as claims" $
request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue;secondcookie=anothervalue")] request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue;secondcookie=anothervalue")]
(
if actualPgVersion >= pgVersion140 then
[json| {"prefix": "request.cookies", "name":"secondcookie"} |] [json| {"prefix": "request.cookies", "name":"secondcookie"} |]
else
[json| {"name":"request.cookie.secondcookie"} |]
)
`shouldRespondWith` `shouldRespondWith`
[json|"anothervalue"|] [json|"anothervalue"|]
{ matchStatus = 200 { matchStatus = 200
@@ -1048,12 +1022,7 @@ spec actualPgVersion =
} }
it "gets the Authorization value" $ it "gets the Authorization value" $
request methodPost "/rpc/get_guc_value" [authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"] request methodPost "/rpc/get_guc_value" [authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"]
(
if actualPgVersion >= pgVersion140 then
[json| {"prefix": "request.headers", "name":"authorization"} |] [json| {"prefix": "request.headers", "name":"authorization"} |]
else
[json| {"name":"request.header.authorization"} |]
)
`shouldRespondWith` `shouldRespondWith`
[json|"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"|] [json|"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"|]
{ matchStatus = 200 { matchStatus = 200
-6
View File
@@ -25,7 +25,6 @@ import qualified Feature.Auth.NoJwtSpec
import qualified Feature.ConcurrentSpec import qualified Feature.ConcurrentSpec
import qualified Feature.CorsSpec import qualified Feature.CorsSpec
import qualified Feature.ExtraSearchPathSpec import qualified Feature.ExtraSearchPathSpec
import qualified Feature.LegacyGucsSpec
import qualified Feature.NoSuperuserSpec import qualified Feature.NoSuperuserSpec
import qualified Feature.ObservabilitySpec import qualified Feature.ObservabilitySpec
import qualified Feature.OpenApi.DisabledOpenApiSpec import qualified Feature.OpenApi.DisabledOpenApiSpec
@@ -106,7 +105,6 @@ main = do
responseHeadersApp = app testCfgResponseHeaders responseHeadersApp = app testCfgResponseHeaders
disallowRollbackApp = app testCfgDisallowRollback disallowRollbackApp = app testCfgDisallowRollback
forceRollbackApp = app testCfgForceRollback forceRollbackApp = app testCfgForceRollback
testCfgLegacyGucsApp = app testCfgLegacyGucs
planEnabledApp = app testPlanEnabledCfg planEnabledApp = app testPlanEnabledCfg
pgSafeUpdateApp = app testPgSafeUpdateEnabledCfg pgSafeUpdateApp = app testPgSafeUpdateEnabledCfg
obsApp = app testObservabilityCfg obsApp = app testObservabilityCfg
@@ -229,10 +227,6 @@ main = do
parallel $ before multipleSchemaApp $ parallel $ before multipleSchemaApp $
describe "Feature.Query.MultipleSchemaSpec" Feature.Query.MultipleSchemaSpec.spec describe "Feature.Query.MultipleSchemaSpec" Feature.Query.MultipleSchemaSpec.spec
-- this test runs with db-uses-legacy-gucs = false
parallel $ before testCfgLegacyGucsApp $
describe "Feature.LegacyGucsSpec" Feature.LegacyGucsSpec.spec
-- this test runs with db-plan-enabled = true -- this test runs with db-plan-enabled = true
parallel $ before planEnabledApp $ parallel $ before planEnabledApp $
describe "Feature.Query.PlanSpec.spec" $ Feature.Query.PlanSpec.spec actualPgVersion describe "Feature.Query.PlanSpec.spec" $ Feature.Query.PlanSpec.spec actualPgVersion
-4
View File
@@ -116,7 +116,6 @@ baseCfg = let secret = Just $ encodeUtf8 "reallyreallyreallyreallyverysafe" in
, configDbConfig = False , configDbConfig = False
, configDbPreConfig = Nothing , configDbPreConfig = Nothing
, configDbUri = "postgresql://" , configDbUri = "postgresql://"
, configDbUseLegacyGucs = True
, configFilePath = Nothing , configFilePath = Nothing
, configJWKS = parseSecret <$> secret , configJWKS = parseSecret <$> secret
, configJwtAudience = Nothing , configJwtAudience = Nothing
@@ -226,9 +225,6 @@ testCfgResponseHeaders = baseCfg { configDbPreRequest = Just $ QualifiedIdentifi
testMultipleSchemaCfg :: AppConfig testMultipleSchemaCfg :: AppConfig
testMultipleSchemaCfg = baseCfg { configDbSchemas = fromList ["v1", "v2", "SPECIAL \"@/\\#~_-"] } testMultipleSchemaCfg = baseCfg { configDbSchemas = fromList ["v1", "v2", "SPECIAL \"@/\\#~_-"] }
testCfgLegacyGucs :: AppConfig
testCfgLegacyGucs = baseCfg { configDbUseLegacyGucs = False }
testPgSafeUpdateEnabledCfg :: AppConfig testPgSafeUpdateEnabledCfg :: AppConfig
testPgSafeUpdateEnabledCfg = baseCfg { configDbPreRequest = Just $ QualifiedIdentifier "test" "load_safeupdate" } testPgSafeUpdateEnabledCfg = baseCfg { configDbPreRequest = Just $ QualifiedIdentifier "test" "load_safeupdate" }
+14 -45
View File
@@ -91,10 +91,7 @@ CREATE FUNCTION set_authors_only_owner() RETURNS trigger
LANGUAGE plpgsql LANGUAGE plpgsql
AS $$ AS $$
begin begin
NEW.owner = case when current_setting('server_version_num')::int >= 140000 NEW.owner = current_setting('request.jwt.claims')::json->>'id';
then current_setting('request.jwt.claims')::json->>'id'
else current_setting('request.jwt.claim.id')
end;
RETURN NEW; RETURN NEW;
end end
$$; $$;
@@ -377,10 +374,7 @@ CREATE OR REPLACE FUNCTION switch_role() RETURNS void
declare declare
user_id text; user_id text;
Begin Begin
user_id = case when current_setting('server_version_num')::int >= 140000 user_id = (current_setting('request.jwt.claims')::json->>'id')::text;
then (current_setting('request.jwt.claims')::json->>'id')::text
else current_setting('request.jwt.claim.id')::text
end;
if user_id = '1'::text then if user_id = '1'::text then
execute 'set local role postgrest_test_author'; execute 'set local role postgrest_test_author';
elseif user_id = '2'::text then elseif user_id = '2'::text then
@@ -408,34 +402,15 @@ CREATE FUNCTION reveal_big_jwt() RETURNS TABLE (
iss text, sub text, exp bigint, iss text, sub text, exp bigint,
nbf bigint, iat bigint, jti text, "http://postgrest.com/foo" boolean nbf bigint, iat bigint, jti text, "http://postgrest.com/foo" boolean
) )
LANGUAGE plpgsql SECURITY DEFINER AS $$
STABLE SELECT current_setting('request.jwt.claims')::json->>'iss' as iss,
AS $$ current_setting('request.jwt.claims')::json->>'sub' as sub,
BEGIN (current_setting('request.jwt.claims')::json->>'exp')::bigint as exp,
-- JWT claims are set in JSON format since v14 (current_setting('request.jwt.claims')::json->>'nbf')::bigint as nbf,
IF (current_setting('server_version_num')::INT >= 140000) THEN (current_setting('request.jwt.claims')::json->>'iat')::bigint as iat,
RETURN QUERY current_setting('request.jwt.claims')::json->>'jti' as jti,
SELECT current_setting('request.jwt.claims')::json->>'iss' as iss, (current_setting('request.jwt.claims')::json->>'http://postgrest.com/foo')::boolean as "http://postgrest.com/foo";
current_setting('request.jwt.claims')::json->>'sub' as sub, $$ LANGUAGE sql SECURITY DEFINER STABLE;
(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;
$$;
CREATE FUNCTION assert() RETURNS void CREATE FUNCTION assert() RETURNS void
@@ -1187,7 +1162,7 @@ create function test.get_guc_value(name text) returns text as $$
select nullif(current_setting(name), '')::text; select nullif(current_setting(name), '')::text;
$$ language sql; $$ language sql;
-- Get the GUC values for Postgres v14.0 and up -- Get the JSON type GUC values
create function test.get_guc_value(prefix text, name text) returns text as $$ create function test.get_guc_value(prefix text, name text) returns text as $$
select nullif(current_setting(prefix)::json->>name, '')::text; select nullif(current_setting(prefix)::json->>name, '')::text;
$$ language sql; $$ language sql;
@@ -2093,15 +2068,9 @@ where fst_shift_activity_id is not null
-- for a pre-request function -- for a pre-request function
create or replace function custom_headers() returns void as $$ create or replace function custom_headers() returns void as $$
declare declare
user_agent text := case when current_setting('server_version_num')::int >= 140000 user_agent text := current_setting('request.headers', true)::json->>'user-agent';
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_path text := current_setting('request.path', true);
req_accept text := case when current_setting('server_version_num')::int >= 140000 req_accept text := current_setting('request.headers', true)::json->>'accept';
then current_setting('request.headers', true)::json->>'accept'
else current_setting('request.header.accept', true)
end;
req_method text := current_setting('request.method', true); req_method text := current_setting('request.method', true);
begin begin
if user_agent similar to 'MSIE (6.0|7.0)' then if user_agent similar to 'MSIE (6.0|7.0)' then