From a871fcfe0bd2da3dc3c1a049a57b6009a6bdc083 Mon Sep 17 00:00:00 2001 From: Taimoor Zaeem Date: Sat, 6 Jun 2026 00:39:53 +0500 Subject: [PATCH] change: use RFC 9535 syntax for `jwt-role-claim-key` config BREAKING CHANGE On top of it, slice operator can be used by adding the pipe symbol at the end like `$.roles[0] | [1:]`. Signed-off-by: Taimoor Zaeem --- CHANGELOG.md | 1 + cabal.project.freeze | 2 +- docs/postgrest.dict | 2 - docs/references/auth.rst | 36 ++-- docs/references/configuration.rst | 2 +- nix/overlays/haskell-packages.nix | 9 + postgrest.cabal | 1 + src/PostgREST/App.hs | 6 +- src/PostgREST/Auth/Jwt.hs | 4 +- src/PostgREST/Config.hs | 13 +- src/PostgREST/Config/JSPath.hs | 165 ++++++------------ stack.yaml | 1 + stack.yaml.lock | 7 + test/io/configs/aliases.config | 2 +- test/io/configs/expected/aliases.config | 2 +- .../configs/expected/boolean-numeric.config | 2 +- .../io/configs/expected/boolean-string.config | 2 +- test/io/configs/expected/defaults.config | 2 +- ...mp1.config => jspath-slice-op-dump.config} | 2 +- .../expected/jspath-str-op-dump2.config | 42 ----- .../expected/jspath-str-op-dump3.config | 42 ----- .../expected/jspath-str-op-dump4.config | 42 ----- .../expected/jspath-str-op-dump5.config | 42 ----- ...efaults-with-db-other-authenticator.config | 2 +- .../expected/no-defaults-with-db.config | 2 +- test/io/configs/expected/no-defaults.config | 2 +- test/io/configs/expected/types.config | 2 +- test/io/configs/expected/utf-8.config | 2 +- test/io/configs/jspath-slice-op-dump.config | 1 + test/io/configs/jspath-str-op-dump1.config | 2 - test/io/configs/jspath-str-op-dump2.config | 1 - test/io/configs/jspath-str-op-dump3.config | 1 - test/io/configs/jspath-str-op-dump4.config | 1 - test/io/configs/jspath-str-op-dump5.config | 1 - test/io/configs/no-defaults-env.yaml | 2 +- test/io/configs/no-defaults.config | 2 +- test/io/fixtures/db_config.sql | 4 +- test/io/fixtures/fixtures.yaml | 94 ++-------- test/io/test_auth.py | 1 + test/observability/ObsHelper.hs | 4 +- test/spec/SpecHelper.hs | 7 +- 41 files changed, 144 insertions(+), 416 deletions(-) rename test/io/configs/expected/{jspath-str-op-dump1.config => jspath-slice-op-dump.config} (94%) delete mode 100644 test/io/configs/expected/jspath-str-op-dump2.config delete mode 100644 test/io/configs/expected/jspath-str-op-dump3.config delete mode 100644 test/io/configs/expected/jspath-str-op-dump4.config delete mode 100644 test/io/configs/expected/jspath-str-op-dump5.config create mode 100644 test/io/configs/jspath-slice-op-dump.config delete mode 100644 test/io/configs/jspath-str-op-dump1.config delete mode 100644 test/io/configs/jspath-str-op-dump2.config delete mode 100644 test/io/configs/jspath-str-op-dump3.config delete mode 100644 test/io/configs/jspath-str-op-dump4.config delete mode 100644 test/io/configs/jspath-str-op-dump5.config diff --git a/CHANGELOG.md b/CHANGELOG.md index c2fd8a2786..cbb8fc78e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ All notable changes to this project will be documented in this file. From versio - Build the minimal docker image for aarch64-linux by @wolfgangwalther in #4193 - The name of an embedded table can no longer be used in filters if it has an alias by @laurenceisla in #4075 + e.g. `?select=alias:table(*)&table.id=eq.1` is not possible anymore, use `?select=alias:table(*)&alias.id=eq.1` instead. +- Config `jwt-role-claim-key` now uses RFC 9535 syntax for JSON Path by @taimoorzaeem in #4984 ## [14.13] - 2026-06-04 diff --git a/cabal.project.freeze b/cabal.project.freeze index 026d4be91f..f141bb58e0 100644 --- a/cabal.project.freeze +++ b/cabal.project.freeze @@ -1 +1 @@ -index-state: hackage.haskell.org 2026-04-18T18:42:36Z +index-state: hackage.haskell.org 2026-06-03T09:50:41Z diff --git a/docs/postgrest.dict b/docs/postgrest.dict index 324d51b888..5941b782b0 100644 --- a/docs/postgrest.dict +++ b/docs/postgrest.dict @@ -30,7 +30,6 @@ CSV durations DDL DOM -DSL DevOps Dramatiq dockerize @@ -76,7 +75,6 @@ isdistinct JS js JSON -JSPath JWK JWT jwt diff --git a/docs/references/auth.rst b/docs/references/auth.rst index 30551abeb9..86e95e6462 100644 --- a/docs/references/auth.rst +++ b/docs/references/auth.rst @@ -224,17 +224,11 @@ It's recommended to leave the JWT cache enabled as our load tests indicate ~20% JWT Role Extraction ------------------- -A JSPath DSL that specifies the location of the :code:`role` key in the JWT claims. It's configured by :ref:`jwt-role-claim-key`. This can be used to consume a JWT provided by a third party service like Auth0, Okta, Microsoft Entra or Keycloak. +JSON Path (`RFC 9535 `_) can be specified for the location of the :code:`role` key in the JWT claims. It's configured by :ref:`jwt-role-claim-key`. This can be used to consume a JWT provided by a third party service like Auth0, Okta, Microsoft Entra or Keycloak. -The DSL follows the `JSONPath `_ expression grammar with extended string comparison operators. Supported operators are: +You can quickly try out JSON Path by visiting https://serdejsonpath.live/. If result has multiple values, first one gets selected. -- ``==`` selects the first array element that exactly matches the right operand -- ``!=`` selects the first array element that does not match the right operand -- ``^==`` selects the first array element that starts with the right operand -- ``==^`` selects the first array element that ends with the right operand -- ``*==`` selects the first array element that contains the right operand - -The selected role value can also be sliced using the slice operator ``[a:b]``. It is similar to `slice operator in python `_. Negative index values are also supported. The syntax is as: +Optionally, the result can be sliced by using the slice operator ``[a:b]`` after putting a pipe symbol like ``$.roles[0] | [1:]``. It is similar to `slice operator in python `_. Negative index values are also supported. The syntax is as: - ``[a:b]`` take slice from index ``a`` up to ``b`` - ``[a:]`` take slice from index ``a`` to end @@ -250,30 +244,26 @@ Usage examples: .. code:: bash # {"postgrest":{"roles": ["other", "author"]}} - # the DSL accepts characters that are alphanumerical or one of "_$@" as keys - jwt-role-claim-key = ".postgrest.roles[1]" + # Escape the dollar with another $ sign in the config file + jwt-role-claim-key = "$$.postgrest.roles[1]" # {"https://www.example.com/role": { "key": "author" }} - # non-alphanumerical characters can go inside quotes(escaped in the config value) - jwt-role-claim-key = ".\"https://www.example.com/role\".key" + # non-alphanumerical characters can go inside single quotes + jwt-role-claim-key = "$$.'https://www.example.com/role'.key" # {"postgrest":{"roles": ["other", "author"]}} - # `@` represents the current element in the array - # all the these match the string "author" - jwt-role-claim-key = ".postgrest.roles[?(@ == \"author\")]" - jwt-role-claim-key = ".postgrest.roles[?(@ != \"other\")]" - jwt-role-claim-key = ".postgrest.roles[?(@ ^== \"aut\")]" - jwt-role-claim-key = ".postgrest.roles[?(@ ==^ \"hor\")]" - jwt-role-claim-key = ".postgrest.roles[?(@ *== \"utho\")]" + # filter based on equality or regular expression + jwt-role-claim-key = "$$.postgrest.roles[?(@ == 'author')]" + jwt-role-claim-key = "$$.postgrest.roles[?@.search(@, '^au')]" # {"postgrest":{"wlcg": ["/groupa", "/groupb/"]}} # skip the "/" character using slice operator - jwt-role-claim-key = ".postgrest.wlcg[0][1:]" - jwt-role-claim-key = ".postgrest.wlcg[1][1:-1]" + jwt-role-claim-key = "$$.postgrest.wlcg[0] | [1:]" + jwt-role-claim-key = "$$.postgrest.wlcg[1] | [1:-1]" .. note:: - The string comparison operators are implemented as a custom extension to the JSPath and does not strictly follow the `RFC 9535 `_. + Note that in our implementation, only `search()` function from JSON Path is available for filtering. JWT Security ------------ diff --git a/docs/references/configuration.rst b/docs/references/configuration.rst index 48e1a92753..facc8cf017 100644 --- a/docs/references/configuration.rst +++ b/docs/references/configuration.rst @@ -654,7 +654,7 @@ jwt-role-claim-key =============== ================================= **Type** String - **Default** .role + **Default** $.role **Reloadable** Y **Environment** PGRST_JWT_ROLE_CLAIM_KEY **In-Database** pgrst.jwt_role_claim_key diff --git a/nix/overlays/haskell-packages.nix b/nix/overlays/haskell-packages.nix index 288a7d48cc..aac508bcac 100644 --- a/nix/overlays/haskell-packages.nix +++ b/nix/overlays/haskell-packages.nix @@ -49,6 +49,15 @@ let # Before upgrading fuzzyset to 0.3, check: https://github.com/PostgREST/postgrest/issues/3329 fuzzyset = prev.fuzzyset_0_2_4; + aeson-jsonpath = + prev.callHackageDirect + { + pkg = "aeson-jsonpath"; + ver = "0.4.2.0"; + sha256 = "sha256-K+3brf1zjSSjojtSCXFrip5rrP7AO/S4zndAxAnvEfc="; + } + { }; + http2 = prev.callHackageDirect { diff --git a/postgrest.cabal b/postgrest.cabal index 63840ec096..9cccaad41e 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -103,6 +103,7 @@ library , HTTP >= 4000.3.7 && < 4000.5 , Ranged-sets >= 0.3 && < 0.6 , aeson >= 2.0.3 && < 2.3 + , aeson-jsonpath >= 0.4.2 && < 0.5 , auto-update >= 0.1.4 && < 0.3 , base64-bytestring >= 1 && < 1.3 , bytestring >= 0.10.8 && < 0.13 diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 0103625733..ada773c231 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -68,7 +68,7 @@ import qualified Data.List as L import Data.Streaming.Network (bindPortTCP) import qualified Data.Text as T import qualified Network.HTTP.Types as HTTP -import qualified Network.HTTP.Types.Header as HTTP (hVary) +import Network.HTTP.Types.Header (hVary) import qualified Network.Socket as NS import PostgREST.Unix (createAndBindDomainSocket) import Protolude hiding (Handler) @@ -223,10 +223,10 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jwtTime authResul serverTimingHeaders timing = [serverTimingHeader timing | configServerTimingEnabled] varyHeader :: HTTP.Header - varyHeader = (HTTP.hVary, "Accept, Prefer, Range") + varyHeader = (hVary, "Accept, Prefer, Range") varyHeaderPresent :: [HTTP.Header] -> Bool - varyHeaderPresent = any (\(h, _v) -> h == HTTP.hVary) + varyHeaderPresent = any (\(h, _v) -> h == hVary) withTiming :: (MonadError e m, MonadIO m) => AppConfig -> m a -> m (Maybe Double, a) withTiming AppConfig{configServerTimingEnabled} f = if configServerTimingEnabled diff --git a/src/PostgREST/Auth/Jwt.hs b/src/PostgREST/Auth/Jwt.hs index e63664e045..bc02e0a250 100644 --- a/src/PostgREST/Auth/Jwt.hs +++ b/src/PostgREST/Auth/Jwt.hs @@ -31,7 +31,7 @@ import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds) import PostgREST.Auth.Types (AuthResult (..)) import PostgREST.Config (AppConfig (..), audMatchesCfg) -import PostgREST.Config.JSPath (walkJSPath) +import PostgREST.Config.JSPath (evaluateJSPath) import PostgREST.Error (Error (..), JwtClaimsError (..), JwtDecodeError (..), JwtError (..)) @@ -114,7 +114,7 @@ parseClaims cfg@AppConfig{configJwtRoleClaimKey, configDbAnonRole} time mclaims validateClaims time (audMatchesCfg cfg) mclaims -- role defaults to anon if not specified in jwt role <- liftEither . maybeToRight (JwtErr JwtTokenRequired) $ - unquoted <$> walkJSPath (Just $ JSON.Object mclaims) configJwtRoleClaimKey <|> configDbAnonRole + unquoted <$> evaluateJSPath (Just $ JSON.Object mclaims) configJwtRoleClaimKey <|> configDbAnonRole pure AuthResult { authClaims = mclaims , authRole = role diff --git a/src/PostgREST/Config.hs b/src/PostgREST/Config.hs index bf48d156bd..1949b55307 100644 --- a/src/PostgREST/Config.hs +++ b/src/PostgREST/Config.hs @@ -15,8 +15,7 @@ module PostgREST.Config ( AppConfig (..) , Environment , JSPath - , JSPathExp(..) - , FilterExp(..) + , defaultRoleJSPathKey , LogLevel(..) , OpenAPIMode(..) , Proxy(..) @@ -63,9 +62,9 @@ import System.Posix.Types (FileMode) import PostgREST.Config.Database (RoleIsolationLvl, RoleSettings) -import PostgREST.Config.JSPath (FilterExp (..), JSPath, - JSPathExp (..), dumpJSPath, - pRoleClaimKey) +import PostgREST.Config.JSPath (JSPath (..), + defaultRoleJSPathKey, + dumpJSPath, pRoleClaimKey) import PostgREST.Config.Proxy (Proxy (..), isMalformedProxyUri, toURI) import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..), @@ -189,7 +188,7 @@ toText conf = ,("db-tx-end", q . showTxEnd) ,("db-uri", q . configDbUri) ,("jwt-aud", q . fromMaybe mempty . configJwtAudience) - ,("jwt-role-claim-key", q . T.intercalate mempty . fmap dumpJSPath . configJwtRoleClaimKey) + ,("jwt-role-claim-key", q . dumpJSPath . configJwtRoleClaimKey) ,("jwt-secret", q . T.decodeUtf8 . showJwtSecret) ,("jwt-secret-is-base64", T.toLower . show . configJwtSecretIsBase64) ,("jwt-cache-max-entries", show . configJwtCacheMaxEntries) @@ -419,7 +418,7 @@ parser optPath env dbSettings roleSettings roleIsolationLvl = parseRoleClaimKey :: C.Key -> C.Key -> C.Parser C.Config JSPath parseRoleClaimKey k al = optWithAlias (optString k) (optString al) >>= \case - Nothing -> pure [JSPKey "role"] + Nothing -> pure defaultRoleJSPathKey -- $.role Just rck -> either (fail . show) pure $ pRoleClaimKey rck parseCORSAllowedOrigins k = diff --git a/src/PostgREST/Config/JSPath.hs b/src/PostgREST/Config/JSPath.hs index 8116a2d21f..756f2cd735 100644 --- a/src/PostgREST/Config/JSPath.hs +++ b/src/PostgREST/Config/JSPath.hs @@ -1,17 +1,17 @@ {-# OPTIONS_GHC -Wno-unused-do-bind #-} -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RecordWildCards #-} module PostgREST.Config.JSPath - ( JSPath - , JSPathExp(..) - , FilterExp(..) + ( JSPath(..) + , defaultRoleJSPathKey , dumpJSPath , pRoleClaimKey - , walkJSPath + , evaluateJSPath ) where import qualified Data.Aeson as JSON -import qualified Data.Aeson.Key as K -import qualified Data.Aeson.KeyMap as KM +import qualified Data.Aeson.JSONPath as JSP +import qualified Data.Aeson.JSONPath.Parser as JSP +import qualified Data.Aeson.JSONPath.Types as JSP import qualified Data.Text as T import qualified Data.Vector as V import qualified Text.ParserCombinators.Parsec as P @@ -23,94 +23,69 @@ import Text.Read (read) import Protolude --- | full jspath, e.g. .property[0].attr.detail[?(@ == "role1")] -type JSPath = [JSPathExp] +-- | full jspath, e.g. "$.property[0].attr.detail[?(@ == "role1")] | [1:0]" +data JSPath = JSPath + { rootJSPath :: JSP.Query + , sliceJSPath :: Maybe (Maybe Int, Maybe Int) + } --- NOTE: We only accept one JSPFilter expr (at the end of input) --- | jspath expression -data JSPathExp - = JSPKey Text -- .property or ."property-dash" - | JSPIdx Int -- [0] - | JSPSlice (Maybe Int) (Maybe Int) -- [0:5] or [0:] or [:5] or [:] - | JSPFilter FilterExp -- [?(@ == "match")] +defaultRoleJSPathKey :: JSPath +defaultRoleJSPathKey = JSPath (JSP.Query JSP.Root [JSP.QuerySegment JSP.Child $ JSP.Dotted "role"]) Nothing -data FilterExp - = EqualsCond Text - | NotEqualsCond Text - | StartsWithCond Text - | EndsWithCond Text - | ContainsCond Text - -dumpJSPath :: JSPathExp -> Text --- TODO: this needs to be quoted properly for special chars -dumpJSPath (JSPKey k) = "." <> show k -dumpJSPath (JSPIdx i) = "[" <> show i <> "]" -dumpJSPath (JSPSlice s e) = "[" <> maybe "" show s <> ":" <> maybe "" show e <> "]" -dumpJSPath (JSPFilter cond) = "[?(@" <> expr <> ")]" - where - expr = - case cond of - EqualsCond text -> " == " <> show text - NotEqualsCond text -> " != " <> show text - StartsWithCond text -> " ^== " <> show text - EndsWithCond text -> " ==^ " <> show text - ContainsCond text -> " *== " <> show text +-- | Dump JSPath +-- e.g. "$$.property[0].attr.detail[?(@ == "role1")] | [1:0]" +dumpJSPath :: JSPath -> Text +dumpJSPath jspath = + T.replace "\"" "\\\"" (jsPathDump <> jsPathSliceDump (sliceJSPath jspath)) + where + jsPathDump = "$" <> JSP.dumpQuery (rootJSPath jspath) + jsPathSliceDump (Just (s, e)) = T.pack (" | " <> "[" <> maybe "" show s <> ":" <> maybe "" show e <> "]") + jsPathSliceDump Nothing = mempty +-- | TODO: DO NOT MERGE: I want to clean up the code a bit -- | Evaluate JSPath on a JSON -walkJSPath :: Maybe JSON.Value -> JSPath -> Maybe JSON.Value -walkJSPath x [] = x -walkJSPath (Just (JSON.Object o)) (JSPKey key:rest) = walkJSPath (KM.lookup (K.fromText key) o) rest -walkJSPath (Just (JSON.Array ar)) (JSPIdx idx:rest) = walkJSPath (ar V.!? idx) rest -walkJSPath (Just (JSON.String str)) (JSPSlice start end:rest) = - let - len = T.length str +evaluateJSPath :: Maybe JSON.Value -> JSPath -> Maybe JSON.Value +evaluateJSPath Nothing _ = Nothing +evaluateJSPath (Just json) JSPath{..} = + case sliceJSPath of + Nothing -> role + Just indices -> getSlice indices role + where + jsPathResult = JSP.queryQQ rootJSPath json + role = case jsPathResult V.!? 0 of -- get the first index + -- If the result is a string, return it. Otherwise return Nothing + Just (JSON.String r) -> Just $ JSON.String r + _ -> Nothing + + getSlice :: (Maybe Int, Maybe Int) -> Maybe JSON.Value -> Maybe JSON.Value + getSlice (start, end) (Just (JSON.String str)) = Just $ JSON.String slicedString + where + len = T.length str - norm :: Maybe Int -> Maybe Int -- Normalize negative indices to positive - norm = fmap (\i -> max 0 $ min len $ if i < 0 then len + i else i) + norm :: Maybe Int -> Maybe Int -- Normalize negative indices to positive + norm = fmap (\i -> max 0 $ min len $ if i < 0 then len + i else i) - s = fromMaybe 0 $ norm start -- normalized start index - e = fromMaybe len $ norm end -- normalized end index - slicedString = if s >= e then T.empty else T.take (e-s) $ T.drop s str - in - walkJSPath (Just $ JSON.String slicedString) rest + s = fromMaybe 0 $ norm start -- normalized start index + e = fromMaybe len $ norm end -- normalized end index + slicedString = if s >= e then T.empty else T.take (e-s) $ T.drop s str -walkJSPath (Just (JSON.Array ar)) (JSPFilter jspFilter:rest) = case jspFilter of - EqualsCond txt -> walkJSPath (findFirstMatch (==) txt ar) rest - NotEqualsCond txt -> walkJSPath (findFirstMatch (/=) txt ar) rest - StartsWithCond txt -> walkJSPath (findFirstMatch T.isPrefixOf txt ar) rest - EndsWithCond txt -> walkJSPath (findFirstMatch T.isSuffixOf txt ar) rest - ContainsCond txt -> walkJSPath (findFirstMatch T.isInfixOf txt ar) rest - where - findFirstMatch matchWith pattern = find (\case - JSON.String txt -> pattern `matchWith` txt - _ -> False) -walkJSPath _ _ = Nothing + getSlice _ _ = Nothing -- Used for the config value "role-claim-key" pRoleClaimKey :: Text -> Either Text JSPath pRoleClaimKey selStr = mapLeft show $ P.parse pJSPath ("failed to parse role-claim-key value (" <> toS selStr <> ")") (toS selStr) +-- | Parse RFC 9535 JSPath: $.roles[0] pJSPath :: P.Parser JSPath -pJSPath = P.many1 pJSPathExp <* P.eof - -pJSPathExp :: P.Parser JSPathExp -pJSPathExp = P.try pJSPKey <|> P.try pJSPFilter <|> P.try pJSPIdx <|> pJSPSlice - -pJSPKey :: P.Parser JSPathExp -pJSPKey = do - P.char '.' - val <- toS <$> P.many1 (P.alphaNum <|> P.oneOf "_$@") <|> pQuotedValue - return (JSPKey val) "pJSPKey: JSPath attribute key" - -pJSPIdx :: P.Parser JSPathExp -pJSPIdx = do - P.char '[' - num <- read <$> P.many1 P.digit - P.char ']' - return (JSPIdx num) "pJSPIdx: JSPath array index" - -pJSPSlice :: P.Parser JSPathExp +pJSPath = do + jsPath' <- JSP.pRootQuery + jsPathSlice' <- P.optionMaybe (P.try (P.spaces *> P.char '|' *> P.spaces *> pJSPSlice)) + P.eof + return (JSPath jsPath' jsPathSlice') "pJSPath: JSPath root" + +-- | Parse slice operator: [1:] +pJSPSlice :: P.Parser (Maybe Int, Maybe Int) pJSPSlice = do P.char '[' startSign <- P.optionMaybe $ P.char '-' @@ -121,30 +96,4 @@ pJSPSlice = do P.char ']' let start' = if isJust startSign then ((-1) *) <$> startIndex else startIndex end' = if isJust endSign then ((-1) *) <$> endIndex else endIndex - return (JSPSlice start' end') "pJSPSlice: JSPath string slice" - -pJSPFilter :: P.Parser JSPathExp -pJSPFilter = do - P.try $ P.string "[?(" - condition <- pFilterConditionParser - P.char ')' - P.char ']' - return (JSPFilter condition) "pJSPFilter: JSPath filter exp" - -pFilterConditionParser :: P.Parser FilterExp -pFilterConditionParser = do - P.char '@' - P.spaces - filt <- matchOperator - P.spaces - filt <$> pQuotedValue - where - matchOperator = - P.try (P.string "==^" $> EndsWithCond) - <|> P.try (P.string "==" $> EqualsCond) - <|> P.try (P.string "!=" $> NotEqualsCond) - <|> P.try (P.string "^==" $> StartsWithCond) - <|> P.try (P.string "*==" $> ContainsCond) - -pQuotedValue :: P.Parser Text -pQuotedValue = toS <$> (P.char '"' *> P.many (P.noneOf "\"") <* P.char '"') + return (start', end') "pJSPSlice: JSPath string slice" diff --git a/stack.yaml b/stack.yaml index 34ef0f6c8c..013980021e 100644 --- a/stack.yaml +++ b/stack.yaml @@ -9,6 +9,7 @@ nix: - zlib extra-deps: + - aeson-jsonpath-0.4.2.0 - configurator-pg-0.2.11 - fuzzyset-0.2.4 - hasql-notifications-0.2.4.0 diff --git a/stack.yaml.lock b/stack.yaml.lock index 5afe87881c..b7dfccbdd8 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -4,6 +4,13 @@ # https://docs.haskellstack.org/en/stable/topics/lock_files packages: +- completed: + hackage: aeson-jsonpath-0.4.2.0@sha256:e582474eba0ea4cbaa21cc75355c36f1b767d240fde75cfd929a8feaf223dfa9,4235 + pantry-tree: + sha256: c5992b0319ac43f179438fc1965d565b5d0e710a04355639843fb085d44f2f55 + size: 2160 + original: + hackage: aeson-jsonpath-0.4.2.0 - completed: hackage: configurator-pg-0.2.11@sha256:de0c56386591e85159436b0af04a8f15a4f4e156354e99709676c2c2ee959505,2850 pantry-tree: diff --git a/test/io/configs/aliases.config b/test/io/configs/aliases.config index cd66cc2992..a83c3bac23 100644 --- a/test/io/configs/aliases.config +++ b/test/io/configs/aliases.config @@ -2,6 +2,6 @@ db-schema = "provided_through_alias" db-pool-timeout = 5 max-rows = 1000 pre-request = "check_alias" -role-claim-key = ".aliased" +role-claim-key = "$$.aliased" root-spec = "open_alias" secret-is-base64 = true diff --git a/test/io/configs/expected/aliases.config b/test/io/configs/expected/aliases.config index 62b361a799..3d42b062ff 100644 --- a/test/io/configs/expected/aliases.config +++ b/test/io/configs/expected/aliases.config @@ -25,7 +25,7 @@ db-tx-end = "commit" db-uri = "postgresql://" jwt-aud = "" jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"aliased\"" +jwt-role-claim-key = "$$.aliased" jwt-secret = "" jwt-secret-is-base64 = true log-level = "error" diff --git a/test/io/configs/expected/boolean-numeric.config b/test/io/configs/expected/boolean-numeric.config index 9af8ff8b75..9661d8f350 100644 --- a/test/io/configs/expected/boolean-numeric.config +++ b/test/io/configs/expected/boolean-numeric.config @@ -25,7 +25,7 @@ db-tx-end = "commit" db-uri = "postgresql://" jwt-aud = "" jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"role\"" +jwt-role-claim-key = "$$.role" jwt-secret = "" jwt-secret-is-base64 = true log-level = "error" diff --git a/test/io/configs/expected/boolean-string.config b/test/io/configs/expected/boolean-string.config index 9af8ff8b75..9661d8f350 100644 --- a/test/io/configs/expected/boolean-string.config +++ b/test/io/configs/expected/boolean-string.config @@ -25,7 +25,7 @@ db-tx-end = "commit" db-uri = "postgresql://" jwt-aud = "" jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"role\"" +jwt-role-claim-key = "$$.role" jwt-secret = "" jwt-secret-is-base64 = true log-level = "error" diff --git a/test/io/configs/expected/defaults.config b/test/io/configs/expected/defaults.config index 4d1fa0302c..5260c7bd6f 100644 --- a/test/io/configs/expected/defaults.config +++ b/test/io/configs/expected/defaults.config @@ -25,7 +25,7 @@ db-tx-end = "commit" db-uri = "postgresql://" jwt-aud = "" jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"role\"" +jwt-role-claim-key = "$$.role" jwt-secret = "" jwt-secret-is-base64 = false log-level = "error" diff --git a/test/io/configs/expected/jspath-str-op-dump1.config b/test/io/configs/expected/jspath-slice-op-dump.config similarity index 94% rename from test/io/configs/expected/jspath-str-op-dump1.config rename to test/io/configs/expected/jspath-slice-op-dump.config index ffb3ff5b77..2ca03795a7 100644 --- a/test/io/configs/expected/jspath-str-op-dump1.config +++ b/test/io/configs/expected/jspath-slice-op-dump.config @@ -25,7 +25,7 @@ db-tx-end = "commit" db-uri = "postgresql://" jwt-aud = "" jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"roles\"[?(@ == \"role1\")]" +jwt-role-claim-key = "$$.roles[?@.name == 'role1'] | [1:]" jwt-secret = "" jwt-secret-is-base64 = false log-level = "error" diff --git a/test/io/configs/expected/jspath-str-op-dump2.config b/test/io/configs/expected/jspath-str-op-dump2.config deleted file mode 100644 index ce36e6d450..0000000000 --- a/test/io/configs/expected/jspath-str-op-dump2.config +++ /dev/null @@ -1,42 +0,0 @@ -admin-server-host = "!4" -admin-server-port = "" -client-error-verbosity = "verbose" -db-aggregates-enabled = false -db-anon-role = "" -db-channel = "pgrst" -db-channel-enabled = true -db-config = true -db-extra-search-path = "public" -db-hoisted-tx-settings = "statement_timeout,plan_filter.statement_cost_limit,default_transaction_isolation" -db-max-rows = "" -db-plan-enabled = false -db-pool = 10 -db-pool-acquisition-timeout = 10 -db-pool-automatic-recovery = true -db-pool-max-idletime = 30 -db-pool-max-lifetime = 1800 -db-pre-config = "" -db-pre-request = "" -db-prepared-statements = true -db-root-spec = "" -db-schemas = "public" -db-timezone-enabled = true -db-tx-end = "commit" -db-uri = "postgresql://" -jwt-aud = "" -jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"roles\"[?(@ != \"role1\")]" -jwt-secret = "" -jwt-secret-is-base64 = false -log-level = "error" -log-query = false -openapi-mode = "follow-privileges" -openapi-security-active = false -openapi-server-proxy-uri = "" -server-cors-allowed-origins = "" -server-host = "!4" -server-port = 3000 -server-timing-enabled = false -server-trace-header = "" -server-unix-socket = "" -server-unix-socket-mode = "660" diff --git a/test/io/configs/expected/jspath-str-op-dump3.config b/test/io/configs/expected/jspath-str-op-dump3.config deleted file mode 100644 index 7762993f36..0000000000 --- a/test/io/configs/expected/jspath-str-op-dump3.config +++ /dev/null @@ -1,42 +0,0 @@ -admin-server-host = "!4" -admin-server-port = "" -client-error-verbosity = "verbose" -db-aggregates-enabled = false -db-anon-role = "" -db-channel = "pgrst" -db-channel-enabled = true -db-config = true -db-extra-search-path = "public" -db-hoisted-tx-settings = "statement_timeout,plan_filter.statement_cost_limit,default_transaction_isolation" -db-max-rows = "" -db-plan-enabled = false -db-pool = 10 -db-pool-acquisition-timeout = 10 -db-pool-automatic-recovery = true -db-pool-max-idletime = 30 -db-pool-max-lifetime = 1800 -db-pre-config = "" -db-pre-request = "" -db-prepared-statements = true -db-root-spec = "" -db-schemas = "public" -db-timezone-enabled = true -db-tx-end = "commit" -db-uri = "postgresql://" -jwt-aud = "" -jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"roles\"[?(@ ^== \"role1\")]" -jwt-secret = "" -jwt-secret-is-base64 = false -log-level = "error" -log-query = false -openapi-mode = "follow-privileges" -openapi-security-active = false -openapi-server-proxy-uri = "" -server-cors-allowed-origins = "" -server-host = "!4" -server-port = 3000 -server-timing-enabled = false -server-trace-header = "" -server-unix-socket = "" -server-unix-socket-mode = "660" diff --git a/test/io/configs/expected/jspath-str-op-dump4.config b/test/io/configs/expected/jspath-str-op-dump4.config deleted file mode 100644 index f98c84ffe1..0000000000 --- a/test/io/configs/expected/jspath-str-op-dump4.config +++ /dev/null @@ -1,42 +0,0 @@ -admin-server-host = "!4" -admin-server-port = "" -client-error-verbosity = "verbose" -db-aggregates-enabled = false -db-anon-role = "" -db-channel = "pgrst" -db-channel-enabled = true -db-config = true -db-extra-search-path = "public" -db-hoisted-tx-settings = "statement_timeout,plan_filter.statement_cost_limit,default_transaction_isolation" -db-max-rows = "" -db-plan-enabled = false -db-pool = 10 -db-pool-acquisition-timeout = 10 -db-pool-automatic-recovery = true -db-pool-max-idletime = 30 -db-pool-max-lifetime = 1800 -db-pre-config = "" -db-pre-request = "" -db-prepared-statements = true -db-root-spec = "" -db-schemas = "public" -db-timezone-enabled = true -db-tx-end = "commit" -db-uri = "postgresql://" -jwt-aud = "" -jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"roles\"[?(@ ==^ \"role1\")]" -jwt-secret = "" -jwt-secret-is-base64 = false -log-level = "error" -log-query = false -openapi-mode = "follow-privileges" -openapi-security-active = false -openapi-server-proxy-uri = "" -server-cors-allowed-origins = "" -server-host = "!4" -server-port = 3000 -server-timing-enabled = false -server-trace-header = "" -server-unix-socket = "" -server-unix-socket-mode = "660" diff --git a/test/io/configs/expected/jspath-str-op-dump5.config b/test/io/configs/expected/jspath-str-op-dump5.config deleted file mode 100644 index 9e59c96311..0000000000 --- a/test/io/configs/expected/jspath-str-op-dump5.config +++ /dev/null @@ -1,42 +0,0 @@ -admin-server-host = "!4" -admin-server-port = "" -client-error-verbosity = "verbose" -db-aggregates-enabled = false -db-anon-role = "" -db-channel = "pgrst" -db-channel-enabled = true -db-config = true -db-extra-search-path = "public" -db-hoisted-tx-settings = "statement_timeout,plan_filter.statement_cost_limit,default_transaction_isolation" -db-max-rows = "" -db-plan-enabled = false -db-pool = 10 -db-pool-acquisition-timeout = 10 -db-pool-automatic-recovery = true -db-pool-max-idletime = 30 -db-pool-max-lifetime = 1800 -db-pre-config = "" -db-pre-request = "" -db-prepared-statements = true -db-root-spec = "" -db-schemas = "public" -db-timezone-enabled = true -db-tx-end = "commit" -db-uri = "postgresql://" -jwt-aud = "" -jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"roles\"[?(@ *== \"role1\")]" -jwt-secret = "" -jwt-secret-is-base64 = false -log-level = "error" -log-query = false -openapi-mode = "follow-privileges" -openapi-security-active = false -openapi-server-proxy-uri = "" -server-cors-allowed-origins = "" -server-host = "!4" -server-port = 3000 -server-timing-enabled = false -server-trace-header = "" -server-unix-socket = "" -server-unix-socket-mode = "660" diff --git a/test/io/configs/expected/no-defaults-with-db-other-authenticator.config b/test/io/configs/expected/no-defaults-with-db-other-authenticator.config index 150924128b..e722be9e15 100644 --- a/test/io/configs/expected/no-defaults-with-db-other-authenticator.config +++ b/test/io/configs/expected/no-defaults-with-db-other-authenticator.config @@ -27,7 +27,7 @@ db-tx-end = "rollback-allow-override" db-uri = "postgresql://" jwt-aud = "https://otherexample.org" jwt-cache-max-entries = 86400 -jwt-role-claim-key = ".\"other\".\"pre_config_role\"" +jwt-role-claim-key = "$$.other.pre_config_role" jwt-secret = "ODERREALLYREALLYREALLYREALLYVERYSAFE" jwt-secret-is-base64 = false log-level = "info" diff --git a/test/io/configs/expected/no-defaults-with-db.config b/test/io/configs/expected/no-defaults-with-db.config index 05eb4e0fac..d3c9159f05 100644 --- a/test/io/configs/expected/no-defaults-with-db.config +++ b/test/io/configs/expected/no-defaults-with-db.config @@ -27,7 +27,7 @@ db-tx-end = "commit-allow-override" db-uri = "postgresql://" jwt-aud = "https://example.org" jwt-cache-max-entries = 86400 -jwt-role-claim-key = ".\"a\".\"role\"" +jwt-role-claim-key = "$$.a.role" jwt-secret = "OVERRIDE=REALLY=REALLY=REALLY=REALLY=VERY=SAFE" jwt-secret-is-base64 = false log-level = "info" diff --git a/test/io/configs/expected/no-defaults.config b/test/io/configs/expected/no-defaults.config index 887c5fce32..320cbebc1a 100644 --- a/test/io/configs/expected/no-defaults.config +++ b/test/io/configs/expected/no-defaults.config @@ -27,7 +27,7 @@ db-tx-end = "rollback-allow-override" db-uri = "tmp_db" jwt-aud = "https://postgrest.org" jwt-cache-max-entries = 86400 -jwt-role-claim-key = ".\"user\"[0].\"real-role\"" +jwt-role-claim-key = "$$.user[0].real_role" jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5aW5iYXNlNjQ=" jwt-secret-is-base64 = true log-level = "info" diff --git a/test/io/configs/expected/types.config b/test/io/configs/expected/types.config index ecabdea000..bb85d130b1 100644 --- a/test/io/configs/expected/types.config +++ b/test/io/configs/expected/types.config @@ -26,7 +26,7 @@ db-tx-end = "commit" db-uri = "postgresql://" jwt-aud = "" jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"role\"" +jwt-role-claim-key = "$$.role" jwt-secret = "" jwt-secret-is-base64 = false log-level = "error" diff --git a/test/io/configs/expected/utf-8.config b/test/io/configs/expected/utf-8.config index 06e428a394..123af16eae 100644 --- a/test/io/configs/expected/utf-8.config +++ b/test/io/configs/expected/utf-8.config @@ -25,7 +25,7 @@ db-tx-end = "commit" db-uri = "postgresql://" jwt-aud = "" jwt-cache-max-entries = 1000 -jwt-role-claim-key = ".\"role\"" +jwt-role-claim-key = "$$.role" jwt-secret = "" jwt-secret-is-base64 = false log-level = "crit" diff --git a/test/io/configs/jspath-slice-op-dump.config b/test/io/configs/jspath-slice-op-dump.config new file mode 100644 index 0000000000..9ecd550907 --- /dev/null +++ b/test/io/configs/jspath-slice-op-dump.config @@ -0,0 +1 @@ +jwt-role-claim-key = "$$.roles[?@.name == 'role1'] | [1:]" diff --git a/test/io/configs/jspath-str-op-dump1.config b/test/io/configs/jspath-str-op-dump1.config deleted file mode 100644 index 770d8c7d89..0000000000 --- a/test/io/configs/jspath-str-op-dump1.config +++ /dev/null @@ -1,2 +0,0 @@ -# For coverage of config dumping with jspath string comparison operator. We allow 5 different operators, so each file test 1 operator. -jwt-role-claim-key = ".roles[?(@ == \"role1\")]" diff --git a/test/io/configs/jspath-str-op-dump2.config b/test/io/configs/jspath-str-op-dump2.config deleted file mode 100644 index d92b49fe48..0000000000 --- a/test/io/configs/jspath-str-op-dump2.config +++ /dev/null @@ -1 +0,0 @@ -jwt-role-claim-key = ".roles[?(@ != \"role1\")]" diff --git a/test/io/configs/jspath-str-op-dump3.config b/test/io/configs/jspath-str-op-dump3.config deleted file mode 100644 index d55e8376fe..0000000000 --- a/test/io/configs/jspath-str-op-dump3.config +++ /dev/null @@ -1 +0,0 @@ -jwt-role-claim-key = ".roles[?(@ ^== \"role1\")]" diff --git a/test/io/configs/jspath-str-op-dump4.config b/test/io/configs/jspath-str-op-dump4.config deleted file mode 100644 index 52c4ae3e15..0000000000 --- a/test/io/configs/jspath-str-op-dump4.config +++ /dev/null @@ -1 +0,0 @@ -jwt-role-claim-key = ".roles[?(@ ==^ \"role1\")]" diff --git a/test/io/configs/jspath-str-op-dump5.config b/test/io/configs/jspath-str-op-dump5.config deleted file mode 100644 index 16838837c8..0000000000 --- a/test/io/configs/jspath-str-op-dump5.config +++ /dev/null @@ -1 +0,0 @@ -jwt-role-claim-key = ".roles[?(@ *== \"role1\")]" diff --git a/test/io/configs/no-defaults-env.yaml b/test/io/configs/no-defaults-env.yaml index ce9280e1e5..d1bcba9dc6 100644 --- a/test/io/configs/no-defaults-env.yaml +++ b/test/io/configs/no-defaults-env.yaml @@ -25,7 +25,7 @@ PGRST_DB_TX_END: rollback-allow-override PGRST_DB_URI: tmp_db PGRST_DB_USE_LEGACY_GUCS: false PGRST_JWT_AUD: 'https://postgrest.org' -PGRST_JWT_ROLE_CLAIM_KEY: '.user[0]."real-role"' +PGRST_JWT_ROLE_CLAIM_KEY: '$.user[0].real_role' PGRST_JWT_SECRET: c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5aW5iYXNlNjQ= PGRST_JWT_SECRET_IS_BASE64: true PGRST_JWT_CACHE_MAX_ENTRIES: 86400 diff --git a/test/io/configs/no-defaults.config b/test/io/configs/no-defaults.config index 6bb1cec158..82c6064b56 100644 --- a/test/io/configs/no-defaults.config +++ b/test/io/configs/no-defaults.config @@ -22,7 +22,7 @@ db-timezone-enabled = false db-tx-end = "rollback-allow-override" db-uri = "tmp_db" jwt-aud = "https://postgrest.org" -jwt-role-claim-key = ".user[0].\"real-role\"" +jwt-role-claim-key = "$$.user[0].real_role" jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5aW5iYXNlNjQ=" jwt-secret-is-base64 = true jwt-cache-max-entries = 86400 diff --git a/test/io/fixtures/db_config.sql b/test/io/fixtures/db_config.sql index 112271919f..e76558757a 100644 --- a/test/io/fixtures/db_config.sql +++ b/test/io/fixtures/db_config.sql @@ -17,7 +17,7 @@ ALTER ROLE db_config_authenticator SET pgrst.db_timezone_enabled = 'false'; ALTER ROLE db_config_authenticator SET pgrst.db_tx_end = 'commit-allow-override'; ALTER ROLE db_config_authenticator SET pgrst.jwt_aud = 'https://example.org'; ALTER ROLE db_config_authenticator SET pgrst.jwt_cache_max_entries = '86400'; -ALTER ROLE db_config_authenticator SET pgrst.jwt_role_claim_key = '."a"."role"'; +ALTER ROLE db_config_authenticator SET pgrst.jwt_role_claim_key = '$.a.role'; ALTER ROLE db_config_authenticator SET pgrst.jwt_secret = 'REALLY=REALLY=REALLY=REALLY=VERY=SAFE'; ALTER ROLE db_config_authenticator SET pgrst.jwt_secret_is_base64 = 'false'; ALTER ROLE db_config_authenticator SET pgrst.not_existing = 'should be ignored'; @@ -91,7 +91,7 @@ returns void as $$ begin if current_user = 'other_authenticator' then perform - set_config('pgrst.jwt_role_claim_key', '."other"."pre_config_role"', true) + set_config('pgrst.jwt_role_claim_key', '$.other.pre_config_role', true) , set_config('pgrst.db_anon_role', 'pre_config_role', true) , set_config('pgrst.db_schemas', 'will be overriden with the above ALTER ROLE.. db_schemas', true) , set_config('pgrst.db_tx_end', 'rollback-allow-override', true); diff --git a/test/io/fixtures/fixtures.yaml b/test/io/fixtures/fixtures.yaml index 50fe14cdc1..c1d1c77685 100644 --- a/test/io/fixtures/fixtures.yaml +++ b/test/io/fixtures/fixtures.yaml @@ -120,13 +120,13 @@ cli: PGRST_DB_TX_END: rollback roleclaims: - - key: '.postgrest.a_role' + - key: '$.postgrest.a_role' data: postgrest: a_role: postgrest_test_author other: claims expected_status: 200 - - key: '.customObject.manyRoles[1]' + - key: '$.customObject.manyRoles[1]' data: customObject: manyRoles: @@ -134,130 +134,74 @@ roleclaims: - postgrest_test_author other: {} expected_status: 200 - - key: '."https://www.example.com/roles"[0].value' + - key: '$["https://www.example.com/roles"][0].value' data: 'https://www.example.com/roles': - value: postgrest_test_author other: 666 expected_status: 200 - - key: '.myDomain[3]' + - key: '$.myDomain[3]' data: myDomain: - other - postgrest_test_author other: 1.23 expected_status: 401 - - key: '.myRole' + - key: '$.myRole' data: role: postgrest_test_author other: true expected_status: 401 - # https://github.com/PostgREST/postgrest/pull/3813 - - key: '.realm_access.roles[?(@ == "postgrest_test_author")]' - data: - realm_access: - roles: - - other - - postgrest_test_author - expected_status: 200 - - key: '.realm_access.roles[?(@ != "other")]' - data: - realm_access: - roles: - - other - - postgrest_test_author - expected_status: 200 - - key: '.realm_access.roles[?(@ ^== "postgrest_te")]' - data: - realm_access: - roles: - - other - - postgrest_test_author - expected_status: 200 - - key: '.realm_access.roles[?(@ ==^ "st_test_author")]' - data: - realm_access: - roles: - - other - - postgrest_test_author - expected_status: 200 - - key: '.realm_access.roles[?(@ *== "_test_")]' - data: - realm_access: - roles: - - other - - postgrest_test_author - expected_status: 200 - - key: '.realm_access.roles[?(@ == "string")]' - data: - realm_access: - roles: - - obj_key: obj_value - expected_status: 401 # fails because it compares an object with a string - - key: '.realm_access.roles[0][7:]' + - key: '$.realm_access.roles[0] | [7:]' data: realm_access: roles: - prefix_postgrest_test_author expected_status: 200 # passes because it removes the "prefix_" part using slice - - key: '.realm_access.roles[0][:-7]' + - key: '$.realm_access.roles[0] | [:-7]' data: realm_access: roles: - postgrest_test_author_suffix expected_status: 200 # passes because it removes the "_suffix" part using slice - - key: '.realm_access.roles[0][7:-7]' + - key: '$.realm_access.roles[0] | [7:-7]' data: realm_access: roles: - prefix_postgrest_test_author_suffix expected_status: 200 # passes because it removes the "prefix_" and "_suffix" part using slice - - key: '.realm_access.roles[0][:]' + - key: '$.realm_access.roles[0] | [:]' data: realm_access: roles: - postgrest_test_author expected_status: 200 # passes because nothing gets sliced - - key: '.realm_access.roles[?(@ *== "_test_")][7:]' - data: - realm_access: - roles: - - other - - prefix_postgrest_test_author - expected_status: 200 # passes on both comparison operators and slicing - - key: '.realm_access.roles[?(@ *== "_test_")][200:]' - data: - realm_access: - roles: - - other - - prefix_postgrest_test_author - expected_status: 401 # fails due to slicing results in empty string jwtaudroleclaims: - - key: '.aud' + - key: '$.aud' data: aud: postgrest_test_author expected_status: 200 - - key: '.aud' + - key: '$.aud' data: aud: postgrest_test_invalid expected_status: 401 - - key: '.aud[0]' + - key: '$.aud[0]' data: aud: [postgrest_test_author] expected_status: 200 - - key: '.aud[1]' # succeeds the aud claims check, but fail when hits the db + - key: '$.aud[1]' # succeeds the aud claims check, but fail when hits the db data: aud: [postgrest_test_author, postgrest_test_invalid] expected_status: 401 invalidroleclaimkeys: - - 'role.other' - - '.role##' - - '.my_role;;domain' - - '.#$$%&$%/' - - '1234' - - '.role[?(@ =)]' + - '$role.other' + - '$.role##' + - '$.my_role;;domain' + - '$.#$$%&$%/' + - '$1234' + - '$.role[?(@ =)]' invalidopenapimodes: - 'follow-' diff --git a/test/io/test_auth.py b/test/io/test_auth.py index 351785549f..79d7b3faf3 100644 --- a/test/io/test_auth.py +++ b/test/io/test_auth.py @@ -25,6 +25,7 @@ def test_read_secret_from_file(secretpath, defaultenv): "Authorization should succeed when the secret is read from a file." env = {**defaultenv, "PGRST_JWT_SECRET": f"@{secretpath}"} + print(env) if secretpath.suffix == ".b64": env["PGRST_JWT_SECRET_IS_BASE64"] = "true" diff --git a/test/observability/ObsHelper.hs b/test/observability/ObsHelper.hs index 268bdbf67c..a903061f40 100644 --- a/test/observability/ObsHelper.hs +++ b/test/observability/ObsHelper.hs @@ -24,10 +24,10 @@ import qualified Jose.Jwt as JWT import Network.HTTP.Types import qualified PostgREST.AppState as AppState import PostgREST.Config (AppConfig (..), - JSPathExp (..), LogLevel (..), OpenAPIMode (..), Verbosity (..), + defaultRoleJSPathKey, parseSecret) import qualified PostgREST.Metrics as Metrics import PostgREST.Observation (Observation (..)) @@ -96,7 +96,7 @@ baseCfg = let secret = encodeUtf8 "reallyreallyreallyreallyverysafe" in , configFilePath = Nothing , configJWKS = rightToMaybe $ parseSecret secret , configJwtAudience = Nothing - , configJwtRoleClaimKey = [JSPKey "role"] + , configJwtRoleClaimKey = defaultRoleJSPathKey -- $.role , configJwtSecret = Just secret , configJwtSecretIsBase64 = False , configJwtCacheMaxEntries = 10 diff --git a/test/spec/SpecHelper.hs b/test/spec/SpecHelper.hs index 4e45a5a31b..06ae922da3 100644 --- a/test/spec/SpecHelper.hs +++ b/test/spec/SpecHelper.hs @@ -29,10 +29,11 @@ import Test.Hspec.Wai import Data.String (String) import PostgREST.Config (AppConfig (..), - JSPathExp (..), LogLevel (..), OpenAPIMode (..), - Verbosity (..), parseSecret) + Verbosity (..), + defaultRoleJSPathKey, + parseSecret) import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..)) import Protolude hiding (get, toS) import Protolude.Conv (toS) @@ -137,7 +138,7 @@ baseCfg = let secret = encodeUtf8 "reallyreallyreallyreallyverysafe" in , configFilePath = Nothing , configJWKS = rightToMaybe $ parseSecret secret , configJwtAudience = Nothing - , configJwtRoleClaimKey = [JSPKey "role"] + , configJwtRoleClaimKey = defaultRoleJSPathKey -- $.role , configJwtSecret = Just secret , configJwtSecretIsBase64 = False , configJwtCacheMaxEntries = 10