feat: support string comparison for jwt-role-claim-key
This commit is contained in:
committed by
Steve Chavez
parent
2df167637d
commit
af6b79d4d7
+15
-1
@@ -27,6 +27,7 @@ import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Lazy.Char8 as LBS
|
||||
import qualified Data.Cache as C
|
||||
import qualified Data.Scientific as Sci
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Vault.Lazy as Vault
|
||||
import qualified Data.Vector as V
|
||||
import qualified Jose.Jwk as JWT
|
||||
@@ -46,7 +47,8 @@ import System.TimeIt (timeItT)
|
||||
|
||||
import PostgREST.AppState (AppState, AuthResult (..), getConfig,
|
||||
getJwtCache, getTime)
|
||||
import PostgREST.Config (AppConfig (..), JSPath, JSPathExp (..))
|
||||
import PostgREST.Config (AppConfig (..), FilterExp (..), JSPath,
|
||||
JSPathExp (..))
|
||||
import PostgREST.Error (Error (..))
|
||||
|
||||
import Protolude
|
||||
@@ -121,8 +123,20 @@ parseClaims AppConfig{..} jclaims@(JSON.Object mclaims) = do
|
||||
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 (EqualsCond txt)] = findFirstMatch (==) txt ar
|
||||
walkJSPath (Just (JSON.Array ar)) [JSPFilter (NotEqualsCond txt)] = findFirstMatch (/=) txt ar
|
||||
walkJSPath (Just (JSON.Array ar)) [JSPFilter (StartsWithCond txt)] = findFirstMatch T.isPrefixOf txt ar
|
||||
walkJSPath (Just (JSON.Array ar)) [JSPFilter (EndsWithCond txt)] = findFirstMatch T.isSuffixOf txt ar
|
||||
walkJSPath (Just (JSON.Array ar)) [JSPFilter (ContainsCond txt)] = findFirstMatch T.isInfixOf txt ar
|
||||
walkJSPath _ _ = Nothing
|
||||
|
||||
findFirstMatch matchWith pattern = foldr checkMatch Nothing
|
||||
where
|
||||
checkMatch (JSON.String txt) acc
|
||||
| pattern `matchWith` txt = Just $ JSON.String txt
|
||||
| otherwise = acc
|
||||
checkMatch _ acc = acc
|
||||
|
||||
unquoted :: JSON.Value -> BS.ByteString
|
||||
unquoted (JSON.String t) = encodeUtf8 t
|
||||
unquoted v = LBS.toStrict $ JSON.encode v
|
||||
|
||||
@@ -15,6 +15,7 @@ module PostgREST.Config
|
||||
, Environment
|
||||
, JSPath
|
||||
, JSPathExp(..)
|
||||
, FilterExp(..)
|
||||
, LogLevel(..)
|
||||
, OpenAPIMode(..)
|
||||
, Proxy(..)
|
||||
@@ -54,8 +55,9 @@ import System.Posix.Types (FileMode)
|
||||
|
||||
import PostgREST.Config.Database (RoleIsolationLvl,
|
||||
RoleSettings)
|
||||
import PostgREST.Config.JSPath (JSPath, JSPathExp (..),
|
||||
dumpJSPath, pRoleClaimKey)
|
||||
import PostgREST.Config.JSPath (FilterExp (..), JSPath,
|
||||
JSPathExp (..), dumpJSPath,
|
||||
pRoleClaimKey)
|
||||
import PostgREST.Config.Proxy (Proxy (..),
|
||||
isMalformedProxyUri, toURI)
|
||||
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier, dumpQi,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{-# OPTIONS_GHC -Wno-unused-do-bind #-}
|
||||
module PostgREST.Config.JSPath
|
||||
( JSPath
|
||||
, JSPathExp(..)
|
||||
, FilterExp(..)
|
||||
, dumpJSPath
|
||||
, pRoleClaimKey
|
||||
) where
|
||||
@@ -14,18 +16,37 @@ import Text.Read (read)
|
||||
import Protolude
|
||||
|
||||
|
||||
-- | full jspath, e.g. .property[0].attr.detail
|
||||
-- | full jspath, e.g. .property[0].attr.detail[?(@ == "role1")]
|
||||
type JSPath = [JSPathExp]
|
||||
|
||||
-- | jspath expression, e.g. .property, .property[0] or ."property-dash"
|
||||
-- NOTE: We only accept one JSPFilter expr (at the end of input)
|
||||
-- | jspath expression
|
||||
data JSPathExp
|
||||
= JSPKey Text
|
||||
| JSPIdx Int
|
||||
= JSPKey Text -- .property or ."property-dash"
|
||||
| JSPIdx Int -- [0]
|
||||
| JSPFilter FilterExp -- [?(@ == "match")]
|
||||
|
||||
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 (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
|
||||
|
||||
|
||||
-- Used for the config value "role-claim-key"
|
||||
pRoleClaimKey :: Text -> Either Text JSPath
|
||||
@@ -33,19 +54,47 @@ pRoleClaimKey selStr =
|
||||
mapLeft show $ P.parse pJSPath ("failed to parse role-claim-key value (" <> toS selStr <> ")") (toS selStr)
|
||||
|
||||
pJSPath :: P.Parser JSPath
|
||||
pJSPath = toJSPath <$> (period *> pPath `P.sepBy` period <* P.eof)
|
||||
where
|
||||
toJSPath :: [(Text, Maybe Int)] -> JSPath
|
||||
toJSPath = concatMap (\(key, idx) -> JSPKey key : maybeToList (JSPIdx <$> idx))
|
||||
period = P.char '.' <?> "period (.)"
|
||||
pPath :: P.Parser (Text, Maybe Int)
|
||||
pPath = (,) <$> pJSPKey <*> P.optionMaybe pJSPIdx
|
||||
pJSPath = P.many1 pJSPathExp <* P.eof
|
||||
|
||||
pJSPKey :: P.Parser Text
|
||||
pJSPKey = toS <$> P.many1 (P.alphaNum <|> P.oneOf "_$@") <|> pQuotedValue <?> "attribute name [a..z0..9_$@])"
|
||||
pJSPathExp :: P.Parser JSPathExp
|
||||
pJSPathExp = pJSPKey <|> pJSPFilter <|> pJSPIdx
|
||||
|
||||
pJSPIdx :: P.Parser Int
|
||||
pJSPIdx = P.char '[' *> (read <$> P.many1 P.digit) <* P.char ']' <?> "array index [0..n]"
|
||||
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 '"')
|
||||
|
||||
Reference in New Issue
Block a user