@@ -19,6 +19,7 @@ module PostgREST.Config
1919 , LogLevel (.. )
2020 , OpenAPIMode (.. )
2121 , Proxy (.. )
22+ , CfgAud
2223 , toText
2324 , isMalformedProxyUri
2425 , readAppConfig
@@ -29,6 +30,8 @@ module PostgREST.Config
2930 , addTargetSessionAttrs
3031 , exampleConfigFile
3132 , audMatchesCfg
33+ , defaultCfgAud
34+ , parseCfgAud
3235 ) where
3336
3437import qualified Data.Aeson as JSON
@@ -50,7 +53,7 @@ import Data.List.NonEmpty (fromList, toList)
5053import Data.Maybe (fromJust )
5154import Data.Scientific (floatingOrInteger )
5255import Jose.Jwk (Jwk , JwkSet )
53- import Network.URI (escapeURIString , isURI ,
56+ import Network.URI (escapeURIString ,
5457 isUnescapedInURIComponent )
5558import Numeric (readOct , showOct )
5659import System.Environment (getEnvironment )
@@ -66,10 +69,29 @@ import PostgREST.Config.Proxy (Proxy (..),
6669import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier , dumpQi ,
6770 toQi )
6871
69- import Protolude hiding (Proxy , toList )
72+ import Protolude hiding (Proxy , toList )
73+ import qualified Text.Regex.TDFA as R
74+
75+ data ParsedValue a b = ParsedValue {
76+ sourceValue :: a ,
77+ parsedValue :: b
78+ }
79+
80+ newtype CfgAud = CfgAud { unCfgAud :: ParsedValue (Maybe Text ) R. Regex }
81+
82+ parseCfgAud :: MonadFail m => Text -> m CfgAud
83+ parseCfgAud = fmap CfgAud . (fmap . ParsedValue . Just <*> parseRegex)
84+ where
85+ parseRegex = maybe (fail " jwt-aud should be a valid regular expression" ) pure . R. makeRegexM . bounded
86+ -- need start and end of text bounds so that
87+ -- regex does not match parts of text
88+ bounded = (" \\ `(" <> ) . (<> " \\ ')" )
89+
90+ defaultCfgAud :: CfgAud
91+ defaultCfgAud = CfgAud $ ParsedValue Nothing $ R. makeRegex (" .*" :: Text )
7092
7193audMatchesCfg :: AppConfig -> Text -> Bool
72- audMatchesCfg = maybe ( const True ) (==) . configJwtAudience
94+ audMatchesCfg = R. matchTest . parsedValue . unCfgAud . configJwtAudience
7395
7496data AppConfig = AppConfig
7597 { configAppSettings :: [(Text , Text )]
@@ -97,7 +119,7 @@ data AppConfig = AppConfig
97119 , configDbUri :: Text
98120 , configFilePath :: Maybe FilePath
99121 , configJWKS :: Maybe JwkSet
100- , configJwtAudience :: Maybe Text
122+ , configJwtAudience :: CfgAud
101123 , configJwtRoleClaimKey :: JSPath
102124 , configJwtSecret :: Maybe BS. ByteString
103125 , configJwtSecretIsBase64 :: Bool
@@ -171,7 +193,7 @@ toText conf =
171193 ,(" db-pre-config" , q . maybe mempty dumpQi . configDbPreConfig)
172194 ,(" db-tx-end" , q . showTxEnd)
173195 ,(" db-uri" , q . configDbUri)
174- ,(" jwt-aud" , q . fromMaybe mempty . configJwtAudience)
196+ ,(" jwt-aud" , q . fold . sourceValue . unCfgAud . configJwtAudience)
175197 ,(" jwt-role-claim-key" , q . T. intercalate mempty . fmap dumpJSPath . configJwtRoleClaimKey)
176198 ,(" jwt-secret" , q . T. decodeUtf8 . showJwtSecret)
177199 ,(" jwt-secret-is-base64" , T. toLower . show . configJwtSecretIsBase64)
@@ -279,7 +301,7 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
279301 <*> (fromMaybe " postgresql://" <$> optString " db-uri" )
280302 <*> pure optPath
281303 <*> pure Nothing
282- <*> optStringOrURI " jwt-aud"
304+ <*> (optStringEmptyable " jwt-aud" >>= maybe ( pure defaultCfgAud) parseCfgAud)
283305 <*> parseRoleClaimKey " jwt-role-claim-key" " role-claim-key"
284306 <*> (fmap encodeUtf8 <$> optString " jwt-secret" )
285307 <*> (fromMaybe False <$> optWithAlias
@@ -399,20 +421,6 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
399421 optStringEmptyable :: C. Key -> C. Parser C. Config (Maybe Text )
400422 optStringEmptyable k = overrideFromDbOrEnvironment C. optional k coerceText
401423
402- optStringOrURI :: C. Key -> C. Parser C. Config (Maybe Text )
403- optStringOrURI k = do
404- stringOrURI <- mfilter (/= " " ) <$> overrideFromDbOrEnvironment C. optional k coerceText
405- -- If the string contains ':' then it should
406- -- be a valid URI according to RFC 3986
407- case stringOrURI of
408- Just s -> if T. isInfixOf " :" s then validateURI s else return (Just s)
409- Nothing -> return Nothing
410- where
411- validateURI :: Text -> C. Parser C. Config (Maybe Text )
412- validateURI s = if isURI (T. unpack s)
413- then return $ Just s
414- else fail " jwt-aud should be a string or a valid URI"
415-
416424 optInt :: (Read i , Integral i ) => C. Key -> C. Parser C. Config (Maybe i )
417425 optInt k = join <$> overrideFromDbOrEnvironment C. optional k coerceInt
418426
0 commit comments