change: use RFC 9535 syntax for jwt-role-claim-key config
BREAKING CHANGE Breaks the string comparison operators implemented in #3813. Those can be replaced with regex searches using JSON Path `search()` function. Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
This commit is contained in:
committed by
Wolfgang Walther
parent
a0bb87d693
commit
bb63c3fade
@@ -34,6 +34,19 @@ 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
|
- 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
|
- 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.
|
+ 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
|
||||||
|
|
||||||
|
#### Changed Syntax for JWT Role Extraction
|
||||||
|
|
||||||
|
The `jwt-role-claim-key` config should be updated according to the following:
|
||||||
|
|
||||||
|
- All config values must start with `$` character.
|
||||||
|
+ Example: `.roles.read` -> `$.roles.read`
|
||||||
|
- Keys with special characters, with the exception of `_` char must be quoted.
|
||||||
|
+ Example: `.roles.write-role` -> `$.roles["write-role"]`
|
||||||
|
- String comparison operators (`^==`, `==^` and `*==`) are replaced with regular expression search.
|
||||||
|
+ Example: `.roles[?(@ ^== "postgrest_test_")]` -> `$.roles[?search(@, "^postgrest_test_")]`
|
||||||
|
- Detailed reference for syntax: [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535.html#name-jsonpath-syntax-and-semanti).
|
||||||
|
|
||||||
## [14.13] - 2026-06-04
|
## [14.13] - 2026-06-04
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ CSV
|
|||||||
durations
|
durations
|
||||||
DDL
|
DDL
|
||||||
DOM
|
DOM
|
||||||
DSL
|
|
||||||
DevOps
|
DevOps
|
||||||
Dramatiq
|
Dramatiq
|
||||||
dockerize
|
dockerize
|
||||||
@@ -76,7 +75,6 @@ isdistinct
|
|||||||
JS
|
JS
|
||||||
js
|
js
|
||||||
JSON
|
JSON
|
||||||
JSPath
|
|
||||||
JWK
|
JWK
|
||||||
JWT
|
JWT
|
||||||
jwt
|
jwt
|
||||||
|
|||||||
+11
-20
@@ -224,40 +224,31 @@ It's recommended to leave the JWT cache enabled as our load tests indicate ~20%
|
|||||||
JWT Role Extraction
|
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.
|
A JSON Path (`RFC 9535 <https://www.rfc-editor.org/rfc/rfc9535.html>`_) 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 <https://goessner.net/articles/JsonPath/>`_ expression grammar with extended string comparison operators. Supported operators are:
|
You can quickly try out JSON Path by visiting https://serdejsonpath.live.
|
||||||
|
|
||||||
- ``==`` 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
|
|
||||||
|
|
||||||
Usage examples:
|
Usage examples:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
# {"postgrest":{"roles": ["other", "author"]}}
|
# {"postgrest":{"roles": ["other", "author"]}}
|
||||||
# the DSL accepts characters that are alphanumerical or one of "_$@" as keys
|
jwt-role-claim-key = "$$.postgrest.roles[1]"
|
||||||
jwt-role-claim-key = ".postgrest.roles[1]"
|
|
||||||
|
|
||||||
# {"https://www.example.com/role": { "key": "author" }}
|
# {"https://www.example.com/role": { "key": "author" }}
|
||||||
# non-alphanumerical characters can go inside quotes(escaped in the config value)
|
# non-alphanumerical characters can go inside single quotes
|
||||||
jwt-role-claim-key = ".\"https://www.example.com/role\".key"
|
jwt-role-claim-key = "$$['https://www.example.com/role'].key"
|
||||||
|
|
||||||
# {"postgrest":{"roles": ["other", "author"]}}
|
# {"postgrest":{"roles": ["other", "author"]}}
|
||||||
# `@` represents the current element in the array
|
# filter based on equality or regular expression
|
||||||
# all the these match the string "author"
|
jwt-role-claim-key = "$$.postgrest.roles[?(@ == 'author')]"
|
||||||
jwt-role-claim-key = ".postgrest.roles[?(@ == \"author\")]"
|
jwt-role-claim-key = "$$.postgrest.roles[?search(@, '^au')]"
|
||||||
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\")]"
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
The string comparison operators are implemented as a custom extension to the JSPath and does not strictly follow the `RFC 9535 <https://www.rfc-editor.org/rfc/rfc9535.html>`_.
|
- If JSON Path query returns multiple values, the first one gets selected.
|
||||||
|
- Only when using the :ref:`file_config`, all ``$`` characters in the value must be escaped with an additional ``$`` char. For :ref:`env_variables_config` and :ref:`in_db_config`, only use a single ``$`` char.
|
||||||
|
- In our implementation, only the `search()` function from `JSON Path Functions <https://www.rfc-editor.org/rfc/rfc9535.html#name-function-extensions>`_ is available for filtering.
|
||||||
|
|
||||||
JWT Security
|
JWT Security
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -694,7 +694,7 @@ jwt-role-claim-key
|
|||||||
|
|
||||||
=============== =================================
|
=============== =================================
|
||||||
**Type** String
|
**Type** String
|
||||||
**Default** .role
|
**Default** $.role
|
||||||
**Reloadable** Y
|
**Reloadable** Y
|
||||||
**Environment** PGRST_JWT_ROLE_CLAIM_KEY
|
**Environment** PGRST_JWT_ROLE_CLAIM_KEY
|
||||||
**In-Database** pgrst.jwt_role_claim_key
|
**In-Database** pgrst.jwt_role_claim_key
|
||||||
@@ -704,6 +704,10 @@ jwt-role-claim-key
|
|||||||
|
|
||||||
See :ref:`jwt_role_extract` on how to specify key paths and usage examples.
|
See :ref:`jwt_role_extract` on how to specify key paths and usage examples.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Only when using :ref:`file_config`, the ``$`` char needs to be escaped, so use ``$$`` and PostgREST will interpret it as a single ``$`` character.
|
||||||
|
|
||||||
.. _jwt-secret:
|
.. _jwt-secret:
|
||||||
|
|
||||||
jwt-secret
|
jwt-secret
|
||||||
|
|||||||
@@ -49,6 +49,16 @@ let
|
|||||||
# Before upgrading fuzzyset to 0.3, check: https://github.com/PostgREST/postgrest/issues/3329
|
# Before upgrading fuzzyset to 0.3, check: https://github.com/PostgREST/postgrest/issues/3329
|
||||||
fuzzyset = prev.fuzzyset_0_2_4;
|
fuzzyset = prev.fuzzyset_0_2_4;
|
||||||
|
|
||||||
|
# TODO: Remove once available in nixpkgs
|
||||||
|
aeson-jsonpath =
|
||||||
|
prev.callHackageDirect
|
||||||
|
{
|
||||||
|
pkg = "aeson-jsonpath";
|
||||||
|
ver = "0.4.2.0";
|
||||||
|
sha256 = "sha256-K+3brf1zjSSjojtSCXFrip5rrP7AO/S4zndAxAnvEfc=";
|
||||||
|
}
|
||||||
|
{ };
|
||||||
|
|
||||||
http2 =
|
http2 =
|
||||||
prev.callHackageDirect
|
prev.callHackageDirect
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ library
|
|||||||
, HTTP >= 4000.3.7 && < 4000.5
|
, HTTP >= 4000.3.7 && < 4000.5
|
||||||
, Ranged-sets >= 0.3 && < 0.6
|
, Ranged-sets >= 0.3 && < 0.6
|
||||||
, aeson >= 2.0.3 && < 2.3
|
, aeson >= 2.0.3 && < 2.3
|
||||||
|
, aeson-jsonpath >= 0.4.2 && < 0.5
|
||||||
, auto-update >= 0.1.4 && < 0.3
|
, auto-update >= 0.1.4 && < 0.3
|
||||||
, base64-bytestring >= 1 && < 1.3
|
, base64-bytestring >= 1 && < 1.3
|
||||||
, bytestring >= 0.10.8 && < 0.13
|
, bytestring >= 0.10.8 && < 0.13
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
|
|||||||
|
|
||||||
import PostgREST.Auth.Types (AuthResult (..))
|
import PostgREST.Auth.Types (AuthResult (..))
|
||||||
import PostgREST.Config (AppConfig (..), audMatchesCfg)
|
import PostgREST.Config (AppConfig (..), audMatchesCfg)
|
||||||
import PostgREST.Config.JSPath (walkJSPath)
|
import PostgREST.Config.JSPath (evaluateJSPath)
|
||||||
import PostgREST.Error (Error (..), JwtClaimsError (..),
|
import PostgREST.Error (Error (..), JwtClaimsError (..),
|
||||||
JwtDecodeError (..), JwtError (..))
|
JwtDecodeError (..), JwtError (..))
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ parseClaims cfg@AppConfig{configJwtRoleClaimKey, configDbAnonRole} time mclaims
|
|||||||
validateClaims time (audMatchesCfg cfg) mclaims
|
validateClaims time (audMatchesCfg cfg) mclaims
|
||||||
-- role defaults to anon if not specified in jwt
|
-- role defaults to anon if not specified in jwt
|
||||||
role <- liftEither . maybeToRight (JwtErr JwtTokenRequired) $
|
role <- liftEither . maybeToRight (JwtErr JwtTokenRequired) $
|
||||||
unquoted <$> walkJSPath (Just $ JSON.Object mclaims) configJwtRoleClaimKey <|> configDbAnonRole
|
unquoted <$> evaluateJSPath (Just $ JSON.Object mclaims) configJwtRoleClaimKey <|> configDbAnonRole
|
||||||
pure AuthResult
|
pure AuthResult
|
||||||
{ authClaims = mclaims
|
{ authClaims = mclaims
|
||||||
, authRole = role
|
, authRole = role
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ module PostgREST.Config
|
|||||||
( AppConfig (..)
|
( AppConfig (..)
|
||||||
, Environment
|
, Environment
|
||||||
, JSPath
|
, JSPath
|
||||||
, JSPathExp(..)
|
, defaultRoleJSPathKey
|
||||||
, FilterExp(..)
|
|
||||||
, LogLevel(..)
|
, LogLevel(..)
|
||||||
, OpenAPIMode(..)
|
, OpenAPIMode(..)
|
||||||
, Proxy(..)
|
, Proxy(..)
|
||||||
@@ -63,9 +62,9 @@ import System.Posix.Types (FileMode)
|
|||||||
|
|
||||||
import PostgREST.Config.Database (RoleIsolationLvl,
|
import PostgREST.Config.Database (RoleIsolationLvl,
|
||||||
RoleSettings)
|
RoleSettings)
|
||||||
import PostgREST.Config.JSPath (FilterExp (..), JSPath,
|
import PostgREST.Config.JSPath (JSPath (..),
|
||||||
JSPathExp (..), dumpJSPath,
|
defaultRoleJSPathKey,
|
||||||
pRoleClaimKey)
|
dumpJSPath, pRoleClaimKey)
|
||||||
import PostgREST.Config.Proxy (Proxy (..),
|
import PostgREST.Config.Proxy (Proxy (..),
|
||||||
isMalformedProxyUri, toURI)
|
isMalformedProxyUri, toURI)
|
||||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..),
|
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..),
|
||||||
@@ -192,7 +191,7 @@ toText conf =
|
|||||||
,("db-tx-end", q . showTxEnd)
|
,("db-tx-end", q . showTxEnd)
|
||||||
,("db-uri", q . configDbUri)
|
,("db-uri", q . configDbUri)
|
||||||
,("jwt-aud", q . fromMaybe mempty . configJwtAudience)
|
,("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", q . T.decodeUtf8 . showJwtSecret)
|
||||||
,("jwt-secret-is-base64", T.toLower . show . configJwtSecretIsBase64)
|
,("jwt-secret-is-base64", T.toLower . show . configJwtSecretIsBase64)
|
||||||
,("jwt-cache-max-entries", show . configJwtCacheMaxEntries)
|
,("jwt-cache-max-entries", show . configJwtCacheMaxEntries)
|
||||||
@@ -428,7 +427,7 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
|
|||||||
parseRoleClaimKey :: C.Key -> C.Key -> C.Parser C.Config JSPath
|
parseRoleClaimKey :: C.Key -> C.Key -> C.Parser C.Config JSPath
|
||||||
parseRoleClaimKey k al =
|
parseRoleClaimKey k al =
|
||||||
optWithAlias (optString k) (optString al) >>= \case
|
optWithAlias (optString k) (optString al) >>= \case
|
||||||
Nothing -> pure [JSPKey "role"]
|
Nothing -> pure defaultRoleJSPathKey -- $.role
|
||||||
Just rck -> either (fail . show) pure $ pRoleClaimKey rck
|
Just rck -> either (fail . show) pure $ pRoleClaimKey rck
|
||||||
|
|
||||||
parseCORSAllowedOrigins k =
|
parseCORSAllowedOrigins k =
|
||||||
|
|||||||
@@ -1,123 +1,60 @@
|
|||||||
{-# OPTIONS_GHC -Wno-unused-do-bind #-}
|
{-|
|
||||||
{-# LANGUAGE LambdaCase #-}
|
Module : PostgREST.Config.JSPath
|
||||||
|
Description : Parsing and evaluation logic of JSPath
|
||||||
|
-}
|
||||||
module PostgREST.Config.JSPath
|
module PostgREST.Config.JSPath
|
||||||
( JSPath
|
( JSPath(..)
|
||||||
, JSPathExp(..)
|
, defaultRoleJSPathKey
|
||||||
, FilterExp(..)
|
|
||||||
, dumpJSPath
|
, dumpJSPath
|
||||||
, pRoleClaimKey
|
, pRoleClaimKey
|
||||||
, walkJSPath
|
, evaluateJSPath
|
||||||
) 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.JSONPath as JSP
|
||||||
import qualified Data.Aeson.KeyMap as KM
|
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.Text as T
|
||||||
import qualified Data.Vector as V
|
import qualified Data.Vector as V
|
||||||
import qualified Text.ParserCombinators.Parsec as P
|
import qualified Text.ParserCombinators.Parsec as P
|
||||||
|
|
||||||
import Data.Either.Combinators (mapLeft)
|
import Data.Either.Combinators (mapLeft)
|
||||||
|
import Data.Either.Extra (fromRight')
|
||||||
import Text.ParserCombinators.Parsec ((<?>))
|
import Text.ParserCombinators.Parsec ((<?>))
|
||||||
import Text.Read (read)
|
|
||||||
|
|
||||||
import Protolude
|
import Protolude
|
||||||
|
|
||||||
|
|
||||||
-- | full jspath, e.g. .property[0].attr.detail[?(@ == "role1")]
|
-- | full jspath, e.g. "$.property[0].attr.detail[?(@ == "role1")]"
|
||||||
type JSPath = [JSPathExp]
|
newtype JSPath = JSPath JSP.Query
|
||||||
|
|
||||||
-- NOTE: We only accept one JSPFilter expr (at the end of input)
|
-- | Default value for "jwt-role-claim-key" config
|
||||||
-- | jspath expression
|
defaultRoleJSPathKey :: JSPath
|
||||||
data JSPathExp
|
defaultRoleJSPathKey = fromRight' $ P.parse pJSPath "" "$.role"
|
||||||
= JSPKey Text -- .property or ."property-dash"
|
|
||||||
| JSPIdx Int -- [0]
|
|
||||||
| JSPFilter FilterExp -- [?(@ == "match")]
|
|
||||||
|
|
||||||
data FilterExp
|
-- | Dump JSPath
|
||||||
= EqualsCond Text
|
-- e.g. "$.property[0].attr.detail[?(@ == "role1")]"
|
||||||
| NotEqualsCond Text
|
dumpJSPath :: JSPath -> Text
|
||||||
| StartsWithCond Text
|
dumpJSPath (JSPath query) = (escapeDollarChar . escapeDoubleQuotes) jsPathDump
|
||||||
| EndsWithCond Text
|
where
|
||||||
| ContainsCond Text
|
jsPathDump = JSP.dumpQuery query
|
||||||
|
escapeDoubleQuotes = T.replace "\"" "\\\""
|
||||||
|
-- When dumping, $ must be escaped
|
||||||
|
escapeDollarChar = T.replace "$" "$$"
|
||||||
|
|
||||||
dumpJSPath :: JSPathExp -> Text
|
-- |
|
||||||
-- TODO: this needs to be quoted properly for special chars
|
-- Evaluate JSPath on a JSON
|
||||||
dumpJSPath (JSPKey k) = "." <> show k
|
-- The result of JSON Path query is a Vector, we select the first
|
||||||
dumpJSPath (JSPIdx i) = "[" <> show i <> "]"
|
-- string element as the role.
|
||||||
dumpJSPath (JSPFilter cond) = "[?(@" <> expr <> ")]"
|
evaluateJSPath :: Maybe JSON.Value -> JSPath -> Maybe JSON.Value
|
||||||
where
|
evaluateJSPath Nothing _ = Nothing
|
||||||
expr =
|
evaluateJSPath (Just json) (JSPath query) = JSP.queryQQ query json V.!? 0
|
||||||
case cond of
|
|
||||||
EqualsCond text -> " == " <> show text
|
|
||||||
NotEqualsCond text -> " != " <> show text
|
|
||||||
StartsWithCond text -> " ^== " <> show text
|
|
||||||
EndsWithCond text -> " ==^ " <> show text
|
|
||||||
ContainsCond text -> " *== " <> show text
|
|
||||||
|
|
||||||
-- | 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.Array ar)) [JSPFilter jspFilter] = case jspFilter of
|
|
||||||
EqualsCond txt -> findFirstMatch (==) txt ar
|
|
||||||
NotEqualsCond txt -> findFirstMatch (/=) txt ar
|
|
||||||
StartsWithCond txt -> findFirstMatch T.isPrefixOf txt ar
|
|
||||||
EndsWithCond txt -> findFirstMatch T.isSuffixOf txt ar
|
|
||||||
ContainsCond txt -> findFirstMatch T.isInfixOf txt ar
|
|
||||||
where
|
|
||||||
findFirstMatch matchWith pattern = find (\case
|
|
||||||
JSON.String txt -> pattern `matchWith` txt
|
|
||||||
_ -> False)
|
|
||||||
walkJSPath _ _ = Nothing
|
|
||||||
|
|
||||||
-- Used for the config value "role-claim-key"
|
-- Used for the config value "role-claim-key"
|
||||||
pRoleClaimKey :: Text -> Either Text JSPath
|
pRoleClaimKey :: Text -> Either Text JSPath
|
||||||
pRoleClaimKey selStr =
|
pRoleClaimKey selStr =
|
||||||
mapLeft show $ P.parse pJSPath ("failed to parse role-claim-key value (" <> toS selStr <> ")") (toS 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.Parser JSPath
|
||||||
pJSPath = P.many1 pJSPathExp <* P.eof
|
pJSPath = JSPath <$> JSP.pQuery <?> "pJSPath: JSPath root query"
|
||||||
|
|
||||||
pJSPathExp :: P.Parser JSPathExp
|
|
||||||
pJSPathExp = pJSPKey <|> pJSPFilter <|> pJSPIdx
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
pJSPFilter :: P.Parser JSPathExp
|
|
||||||
pJSPFilter = do
|
|
||||||
P.try $ P.string "[?("
|
|
||||||
condition <- pFilterConditionParser
|
|
||||||
P.char ')'
|
|
||||||
P.char ']'
|
|
||||||
P.eof -- this should be the last jspath expression
|
|
||||||
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 '"')
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ nix:
|
|||||||
- zlib
|
- zlib
|
||||||
|
|
||||||
extra-deps:
|
extra-deps:
|
||||||
|
- aeson-jsonpath-0.4.2.0
|
||||||
- configurator-pg-0.2.11
|
- configurator-pg-0.2.11
|
||||||
- fuzzyset-0.2.4
|
- fuzzyset-0.2.4
|
||||||
- hasql-notifications-0.2.4.0
|
- hasql-notifications-0.2.4.0
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
# https://docs.haskellstack.org/en/stable/topics/lock_files
|
# https://docs.haskellstack.org/en/stable/topics/lock_files
|
||||||
|
|
||||||
packages:
|
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:
|
- completed:
|
||||||
hackage: configurator-pg-0.2.11@sha256:de0c56386591e85159436b0af04a8f15a4f4e156354e99709676c2c2ee959505,2850
|
hackage: configurator-pg-0.2.11@sha256:de0c56386591e85159436b0af04a8f15a4f4e156354e99709676c2c2ee959505,2850
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ db-schema = "provided_through_alias"
|
|||||||
db-pool-timeout = 5
|
db-pool-timeout = 5
|
||||||
max-rows = 1000
|
max-rows = 1000
|
||||||
pre-request = "check_alias"
|
pre-request = "check_alias"
|
||||||
role-claim-key = ".aliased"
|
role-claim-key = "$$.aliased"
|
||||||
root-spec = "open_alias"
|
root-spec = "open_alias"
|
||||||
secret-is-base64 = true
|
secret-is-base64 = true
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ db-tx-end = "commit"
|
|||||||
db-uri = "postgresql://"
|
db-uri = "postgresql://"
|
||||||
jwt-aud = ""
|
jwt-aud = ""
|
||||||
jwt-cache-max-entries = 1000
|
jwt-cache-max-entries = 1000
|
||||||
jwt-role-claim-key = ".\"aliased\""
|
jwt-role-claim-key = "$$.aliased"
|
||||||
jwt-secret = ""
|
jwt-secret = ""
|
||||||
jwt-secret-is-base64 = true
|
jwt-secret-is-base64 = true
|
||||||
log-level = "error"
|
log-level = "error"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ db-tx-end = "commit"
|
|||||||
db-uri = "postgresql://"
|
db-uri = "postgresql://"
|
||||||
jwt-aud = ""
|
jwt-aud = ""
|
||||||
jwt-cache-max-entries = 1000
|
jwt-cache-max-entries = 1000
|
||||||
jwt-role-claim-key = ".\"role\""
|
jwt-role-claim-key = "$$.role"
|
||||||
jwt-secret = ""
|
jwt-secret = ""
|
||||||
jwt-secret-is-base64 = true
|
jwt-secret-is-base64 = true
|
||||||
log-level = "error"
|
log-level = "error"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ db-tx-end = "commit"
|
|||||||
db-uri = "postgresql://"
|
db-uri = "postgresql://"
|
||||||
jwt-aud = ""
|
jwt-aud = ""
|
||||||
jwt-cache-max-entries = 1000
|
jwt-cache-max-entries = 1000
|
||||||
jwt-role-claim-key = ".\"role\""
|
jwt-role-claim-key = "$$.role"
|
||||||
jwt-secret = ""
|
jwt-secret = ""
|
||||||
jwt-secret-is-base64 = true
|
jwt-secret-is-base64 = true
|
||||||
log-level = "error"
|
log-level = "error"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ db-tx-end = "commit"
|
|||||||
db-uri = "postgresql://"
|
db-uri = "postgresql://"
|
||||||
jwt-aud = ""
|
jwt-aud = ""
|
||||||
jwt-cache-max-entries = 1000
|
jwt-cache-max-entries = 1000
|
||||||
jwt-role-claim-key = ".\"role\""
|
jwt-role-claim-key = "$$.role"
|
||||||
jwt-secret = ""
|
jwt-secret = ""
|
||||||
jwt-secret-is-base64 = false
|
jwt-secret-is-base64 = false
|
||||||
log-level = "error"
|
log-level = "error"
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
admin-server-host = "!4"
|
|
||||||
admin-server-port = ""
|
|
||||||
admin-server-unix-socket = ""
|
|
||||||
admin-server-unix-socket-mode = "660"
|
|
||||||
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"
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
admin-server-host = "!4"
|
|
||||||
admin-server-port = ""
|
|
||||||
admin-server-unix-socket = ""
|
|
||||||
admin-server-unix-socket-mode = "660"
|
|
||||||
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"
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
admin-server-host = "!4"
|
|
||||||
admin-server-port = ""
|
|
||||||
admin-server-unix-socket = ""
|
|
||||||
admin-server-unix-socket-mode = "660"
|
|
||||||
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"
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
admin-server-host = "!4"
|
|
||||||
admin-server-port = ""
|
|
||||||
admin-server-unix-socket = ""
|
|
||||||
admin-server-unix-socket-mode = "660"
|
|
||||||
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"
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
admin-server-host = "!4"
|
|
||||||
admin-server-port = ""
|
|
||||||
admin-server-unix-socket = ""
|
|
||||||
admin-server-unix-socket-mode = "660"
|
|
||||||
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"
|
|
||||||
@@ -29,7 +29,7 @@ db-tx-end = "rollback-allow-override"
|
|||||||
db-uri = "postgresql://"
|
db-uri = "postgresql://"
|
||||||
jwt-aud = "https://otherexample.org"
|
jwt-aud = "https://otherexample.org"
|
||||||
jwt-cache-max-entries = 86400
|
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 = "ODERREALLYREALLYREALLYREALLYVERYSAFE"
|
||||||
jwt-secret-is-base64 = false
|
jwt-secret-is-base64 = false
|
||||||
log-level = "info"
|
log-level = "info"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ db-tx-end = "commit-allow-override"
|
|||||||
db-uri = "postgresql://"
|
db-uri = "postgresql://"
|
||||||
jwt-aud = "https://example.org"
|
jwt-aud = "https://example.org"
|
||||||
jwt-cache-max-entries = 86400
|
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 = "OVERRIDE=REALLY=REALLY=REALLY=REALLY=VERY=SAFE"
|
||||||
jwt-secret-is-base64 = false
|
jwt-secret-is-base64 = false
|
||||||
log-level = "info"
|
log-level = "info"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ db-tx-end = "rollback-allow-override"
|
|||||||
db-uri = "tmp_db"
|
db-uri = "tmp_db"
|
||||||
jwt-aud = "https://postgrest.org"
|
jwt-aud = "https://postgrest.org"
|
||||||
jwt-cache-max-entries = 86400
|
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 = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5aW5iYXNlNjQ="
|
||||||
jwt-secret-is-base64 = true
|
jwt-secret-is-base64 = true
|
||||||
log-level = "info"
|
log-level = "info"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ db-tx-end = "commit"
|
|||||||
db-uri = "postgresql://"
|
db-uri = "postgresql://"
|
||||||
jwt-aud = ""
|
jwt-aud = ""
|
||||||
jwt-cache-max-entries = 1000
|
jwt-cache-max-entries = 1000
|
||||||
jwt-role-claim-key = ".\"role\""
|
jwt-role-claim-key = "$$.role"
|
||||||
jwt-secret = ""
|
jwt-secret = ""
|
||||||
jwt-secret-is-base64 = false
|
jwt-secret-is-base64 = false
|
||||||
log-level = "error"
|
log-level = "error"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ db-tx-end = "commit"
|
|||||||
db-uri = "postgresql://"
|
db-uri = "postgresql://"
|
||||||
jwt-aud = ""
|
jwt-aud = ""
|
||||||
jwt-cache-max-entries = 1000
|
jwt-cache-max-entries = 1000
|
||||||
jwt-role-claim-key = ".\"role\""
|
jwt-role-claim-key = "$$.role"
|
||||||
jwt-secret = ""
|
jwt-secret = ""
|
||||||
jwt-secret-is-base64 = false
|
jwt-secret-is-base64 = false
|
||||||
log-level = "crit"
|
log-level = "crit"
|
||||||
|
|||||||
@@ -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\")]"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
jwt-role-claim-key = ".roles[?(@ != \"role1\")]"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
jwt-role-claim-key = ".roles[?(@ ^== \"role1\")]"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
jwt-role-claim-key = ".roles[?(@ ==^ \"role1\")]"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
jwt-role-claim-key = ".roles[?(@ *== \"role1\")]"
|
|
||||||
@@ -25,7 +25,7 @@ PGRST_DB_TX_END: rollback-allow-override
|
|||||||
PGRST_DB_URI: tmp_db
|
PGRST_DB_URI: tmp_db
|
||||||
PGRST_DB_USE_LEGACY_GUCS: false
|
PGRST_DB_USE_LEGACY_GUCS: false
|
||||||
PGRST_JWT_AUD: 'https://postgrest.org'
|
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: c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5aW5iYXNlNjQ=
|
||||||
PGRST_JWT_SECRET_IS_BASE64: true
|
PGRST_JWT_SECRET_IS_BASE64: true
|
||||||
PGRST_JWT_CACHE_MAX_ENTRIES: 86400
|
PGRST_JWT_CACHE_MAX_ENTRIES: 86400
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ db-timezone-enabled = false
|
|||||||
db-tx-end = "rollback-allow-override"
|
db-tx-end = "rollback-allow-override"
|
||||||
db-uri = "tmp_db"
|
db-uri = "tmp_db"
|
||||||
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 = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5aW5iYXNlNjQ="
|
jwt-secret = "c2VjdXJpdHl0aHJvdWdob2JzY3VyaXR5aW5iYXNlNjQ="
|
||||||
jwt-secret-is-base64 = true
|
jwt-secret-is-base64 = true
|
||||||
jwt-cache-max-entries = 86400
|
jwt-cache-max-entries = 86400
|
||||||
|
|||||||
@@ -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.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_aud = 'https://example.org';
|
||||||
ALTER ROLE db_config_authenticator SET pgrst.jwt_cache_max_entries = '86400';
|
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 = '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.jwt_secret_is_base64 = 'false';
|
||||||
ALTER ROLE db_config_authenticator SET pgrst.not_existing = 'should be ignored';
|
ALTER ROLE db_config_authenticator SET pgrst.not_existing = 'should be ignored';
|
||||||
@@ -93,7 +93,7 @@ returns void as $$
|
|||||||
begin
|
begin
|
||||||
if current_user = 'other_authenticator' then
|
if current_user = 'other_authenticator' then
|
||||||
perform
|
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_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_schemas', 'will be overriden with the above ALTER ROLE.. db_schemas', true)
|
||||||
, set_config('pgrst.db_tx_end', 'rollback-allow-override', true);
|
, set_config('pgrst.db_tx_end', 'rollback-allow-override', true);
|
||||||
|
|||||||
@@ -120,13 +120,13 @@ cli:
|
|||||||
PGRST_DB_TX_END: rollback
|
PGRST_DB_TX_END: rollback
|
||||||
|
|
||||||
roleclaims:
|
roleclaims:
|
||||||
- key: '.postgrest.a_role'
|
- key: '$.postgrest.a_role'
|
||||||
data:
|
data:
|
||||||
postgrest:
|
postgrest:
|
||||||
a_role: postgrest_test_author
|
a_role: postgrest_test_author
|
||||||
other: claims
|
other: claims
|
||||||
expected_status: 200
|
expected_status: 200
|
||||||
- key: '.customObject.manyRoles[1]'
|
- key: '$.customObject.manyRoles[1]'
|
||||||
data:
|
data:
|
||||||
customObject:
|
customObject:
|
||||||
manyRoles:
|
manyRoles:
|
||||||
@@ -134,92 +134,60 @@ roleclaims:
|
|||||||
- postgrest_test_author
|
- postgrest_test_author
|
||||||
other: {}
|
other: {}
|
||||||
expected_status: 200
|
expected_status: 200
|
||||||
- key: '."https://www.example.com/roles"[0].value'
|
- key: '$["https://www.example.com/roles"][0].value'
|
||||||
data:
|
data:
|
||||||
'https://www.example.com/roles':
|
'https://www.example.com/roles':
|
||||||
- value: postgrest_test_author
|
- value: postgrest_test_author
|
||||||
other: 666
|
other: 666
|
||||||
expected_status: 200
|
expected_status: 200
|
||||||
- key: '.myDomain[3]'
|
- key: '$.myDomain[3]'
|
||||||
data:
|
data:
|
||||||
myDomain:
|
myDomain:
|
||||||
- other
|
- other
|
||||||
- postgrest_test_author
|
- postgrest_test_author
|
||||||
other: 1.23
|
other: 1.23
|
||||||
expected_status: 401
|
expected_status: 401
|
||||||
- key: '.myRole'
|
- key: '$.myRole'
|
||||||
data:
|
data:
|
||||||
role: postgrest_test_author
|
role: postgrest_test_author
|
||||||
other: true
|
other: true
|
||||||
expected_status: 401
|
expected_status: 401
|
||||||
# https://github.com/PostgREST/postgrest/pull/3813
|
- key: '$.realm_access.roles[?(@ == "postgrest_test_author")]'
|
||||||
- key: '.realm_access.roles[?(@ == "postgrest_test_author")]'
|
|
||||||
data:
|
data:
|
||||||
realm_access:
|
realm_access:
|
||||||
roles:
|
roles:
|
||||||
- other
|
- other
|
||||||
- postgrest_test_author
|
- postgrest_test_author
|
||||||
expected_status: 200
|
expected_status: 200
|
||||||
- key: '.realm_access.roles[?(@ != "other")]'
|
- key: '$.realm_access.roles[?(@ != "other")]'
|
||||||
data:
|
data:
|
||||||
realm_access:
|
realm_access:
|
||||||
roles:
|
roles:
|
||||||
- other
|
- other
|
||||||
- postgrest_test_author
|
- postgrest_test_author
|
||||||
expected_status: 200
|
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
|
|
||||||
|
|
||||||
jwtaudroleclaims:
|
jwtaudroleclaims:
|
||||||
- key: '.aud'
|
- key: '$.aud'
|
||||||
data:
|
data:
|
||||||
aud: postgrest_test_author
|
aud: postgrest_test_author
|
||||||
expected_status: 200
|
expected_status: 200
|
||||||
- key: '.aud'
|
- key: '$.aud'
|
||||||
data:
|
data:
|
||||||
aud: postgrest_test_invalid
|
aud: postgrest_test_invalid
|
||||||
expected_status: 401
|
expected_status: 401
|
||||||
- key: '.aud[0]'
|
- key: '$.aud[0]'
|
||||||
data:
|
data:
|
||||||
aud: [postgrest_test_author]
|
aud: [postgrest_test_author]
|
||||||
expected_status: 200
|
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:
|
data:
|
||||||
aud: [postgrest_test_author, postgrest_test_invalid]
|
aud: [postgrest_test_author, postgrest_test_invalid]
|
||||||
expected_status: 401
|
expected_status: 401
|
||||||
|
|
||||||
invalidroleclaimkeys:
|
invalidroleclaimkeys:
|
||||||
- 'role.other'
|
- '.role.other'
|
||||||
- '.role##'
|
- '$.my_role;;domain'
|
||||||
- '.my_role;;domain'
|
|
||||||
- '.#$$%&$%/'
|
|
||||||
- '1234'
|
|
||||||
- '.role[?(@ =)]'
|
|
||||||
|
|
||||||
invalidopenapimodes:
|
invalidopenapimodes:
|
||||||
- 'follow-'
|
- 'follow-'
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ import qualified Jose.Jwt as JWT
|
|||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
import qualified PostgREST.AppState as AppState
|
import qualified PostgREST.AppState as AppState
|
||||||
import PostgREST.Config (AppConfig (..),
|
import PostgREST.Config (AppConfig (..),
|
||||||
JSPathExp (..),
|
|
||||||
LogLevel (..),
|
LogLevel (..),
|
||||||
OpenAPIMode (..),
|
OpenAPIMode (..),
|
||||||
Verbosity (..),
|
Verbosity (..),
|
||||||
|
defaultRoleJSPathKey,
|
||||||
parseSecret)
|
parseSecret)
|
||||||
import qualified PostgREST.Metrics as Metrics
|
import qualified PostgREST.Metrics as Metrics
|
||||||
import PostgREST.Observation (Observation (..))
|
import PostgREST.Observation (Observation (..))
|
||||||
@@ -96,7 +96,7 @@ baseCfg = let secret = encodeUtf8 "reallyreallyreallyreallyverysafe" in
|
|||||||
, configFilePath = Nothing
|
, configFilePath = Nothing
|
||||||
, configJWKS = rightToMaybe $ parseSecret secret
|
, configJWKS = rightToMaybe $ parseSecret secret
|
||||||
, configJwtAudience = Nothing
|
, configJwtAudience = Nothing
|
||||||
, configJwtRoleClaimKey = [JSPKey "role"]
|
, configJwtRoleClaimKey = defaultRoleJSPathKey -- $.role
|
||||||
, configJwtSecret = Just secret
|
, configJwtSecret = Just secret
|
||||||
, configJwtSecretIsBase64 = False
|
, configJwtSecretIsBase64 = False
|
||||||
, configJwtCacheMaxEntries = 10
|
, configJwtCacheMaxEntries = 10
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ import System.IO.Unsafe (unsafePerformIO)
|
|||||||
import Text.Regex.TDFA ((=~))
|
import Text.Regex.TDFA ((=~))
|
||||||
|
|
||||||
import PostgREST.Config (AppConfig (..),
|
import PostgREST.Config (AppConfig (..),
|
||||||
JSPathExp (..),
|
|
||||||
LogLevel (..),
|
LogLevel (..),
|
||||||
OpenAPIMode (..),
|
OpenAPIMode (..),
|
||||||
Verbosity (..), parseSecret)
|
Verbosity (..),
|
||||||
|
defaultRoleJSPathKey,
|
||||||
|
parseSecret)
|
||||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
|
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
|
||||||
|
|
||||||
import Data.Aeson.Lens
|
import Data.Aeson.Lens
|
||||||
@@ -159,7 +160,7 @@ baseCfg = let secret = encodeUtf8 "reallyreallyreallyreallyverysafe" in
|
|||||||
, configFilePath = Nothing
|
, configFilePath = Nothing
|
||||||
, configJWKS = rightToMaybe $ parseSecret secret
|
, configJWKS = rightToMaybe $ parseSecret secret
|
||||||
, configJwtAudience = Nothing
|
, configJwtAudience = Nothing
|
||||||
, configJwtRoleClaimKey = [JSPKey "role"]
|
, configJwtRoleClaimKey = defaultRoleJSPathKey -- $.role
|
||||||
, configJwtSecret = Just secret
|
, configJwtSecret = Just secret
|
||||||
, configJwtSecretIsBase64 = False
|
, configJwtSecretIsBase64 = False
|
||||||
, configJwtCacheMaxEntries = 10
|
, configJwtCacheMaxEntries = 10
|
||||||
|
|||||||
Reference in New Issue
Block a user