Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e30bc63f49 | ||
|
|
a8f40c4908 | ||
|
|
75972e9ffe | ||
|
|
4ba6b1b30c | ||
|
|
a4927141ee | ||
|
|
1b353590ff | ||
|
|
394aa026d3 |
@@ -49,7 +49,7 @@ jobs:
|
|||||||
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
|
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
|
||||||
run: postgrest-coverage
|
run: postgrest-coverage
|
||||||
- name: Upload coverage to codecov
|
- name: Upload coverage to codecov
|
||||||
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
|
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||||
with:
|
with:
|
||||||
files: ./coverage/codecov.json
|
files: ./coverage/codecov.json
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## [13.0.7] - 2025-09-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Improve the `PGRST106` error when the requested schema is invalid by @laurenceisla in #4089
|
||||||
|
+ It now shows the invalid schema in the `message` field.
|
||||||
|
+ The exposed schemas are now listed in the `hint` instead of the `message` field.
|
||||||
|
- Improve error details of `PGRST301` error by @taimoorzaeem in #4051
|
||||||
|
|
||||||
## [13.0.6] - 2025-08-30
|
## [13.0.6] - 2025-08-30
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
# The x86-64 is a single-static-binary image built via Nix, see:
|
# The x86-64 is a single-static-binary image built via Nix, see:
|
||||||
# nix/tools/docker/README.md
|
# nix/tools/docker/README.md
|
||||||
|
|
||||||
FROM ubuntu:noble@sha256:7c06e91f61fa88c08cc74f7e1b7c69ae24910d745357e0dfe1d2c0322aaf20f9 AS postgrest
|
FROM ubuntu:noble@sha256:9cbed754112939e914291337b5e554b07ad7c392491dba6daf25eef1332a22e8 AS postgrest
|
||||||
|
|
||||||
RUN apt-get update -y \
|
RUN apt-get update -y \
|
||||||
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
|
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
name: postgrest
|
name: postgrest
|
||||||
version: 13.0.6
|
version: 13.0.7
|
||||||
synopsis: REST API for any Postgres database
|
synopsis: REST API for any Postgres database
|
||||||
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
||||||
for tables, views, and functions, supporting all HTTP methods that security
|
for tables, views, and functions, supporting all HTTP methods that security
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ getAction resource schema method =
|
|||||||
getSchema :: AppConfig -> RequestHeaders -> ByteString -> Either ApiRequestError (Schema, Bool)
|
getSchema :: AppConfig -> RequestHeaders -> ByteString -> Either ApiRequestError (Schema, Bool)
|
||||||
getSchema AppConfig{configDbSchemas} hdrs method = do
|
getSchema AppConfig{configDbSchemas} hdrs method = do
|
||||||
case profile of
|
case profile of
|
||||||
Just p | p `notElem` configDbSchemas -> Left $ UnacceptableSchema $ toList configDbSchemas
|
Just p | p `notElem` configDbSchemas -> Left $ UnacceptableSchema p $ toList configDbSchemas
|
||||||
| otherwise -> Right (p, True)
|
| otherwise -> Right (p, True)
|
||||||
Nothing -> Right (defaultSchema, length configDbSchemas /= 1) -- if we have many schemas, assume the default schema was negotiated
|
Nothing -> Right (defaultSchema, length configDbSchemas /= 1) -- if we have many schemas, assume the default schema was negotiated
|
||||||
where
|
where
|
||||||
|
|||||||
+19
-18
@@ -50,7 +50,8 @@ import PostgREST.Auth.JwtCache (lookupJwtCache)
|
|||||||
import PostgREST.Auth.Types (AuthResult (..))
|
import PostgREST.Auth.Types (AuthResult (..))
|
||||||
import PostgREST.Config (AppConfig (..), FilterExp (..),
|
import PostgREST.Config (AppConfig (..), FilterExp (..),
|
||||||
JSPath, JSPathExp (..))
|
JSPath, JSPathExp (..))
|
||||||
import PostgREST.Error (Error (..), JwtError (..))
|
import PostgREST.Error (Error (..), JwtClaimsError (..),
|
||||||
|
JwtDecodeError (..), JwtError (..))
|
||||||
|
|
||||||
import Protolude
|
import Protolude
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ import Protolude
|
|||||||
-- JSON object of JWT claims.
|
-- JSON object of JWT claims.
|
||||||
parseToken :: AppConfig -> Maybe ByteString -> UTCTime -> ExceptT Error IO JSON.Value
|
parseToken :: AppConfig -> Maybe ByteString -> UTCTime -> ExceptT Error IO JSON.Value
|
||||||
parseToken _ Nothing _ = return JSON.emptyObject
|
parseToken _ Nothing _ = return JSON.emptyObject
|
||||||
parseToken _ (Just "") _ = throwE . JwtErr $ JwtDecodeError "Empty JWT is sent in Authorization header"
|
parseToken _ (Just "") _ = throwE . JwtErr $ JwtDecodeErr EmptyAuthHeader
|
||||||
parseToken AppConfig{..} (Just tkn) time = do
|
parseToken AppConfig{..} (Just tkn) time = do
|
||||||
secret <- liftEither . maybeToRight (JwtErr JwtSecretMissing) $ configJWKS
|
secret <- liftEither . maybeToRight (JwtErr JwtSecretMissing) $ configJWKS
|
||||||
tknWith3Parts <- liftEither $ hasThreeParts tkn
|
tknWith3Parts <- liftEither $ hasThreeParts tkn
|
||||||
@@ -69,33 +70,33 @@ parseToken AppConfig{..} (Just tkn) time = do
|
|||||||
hasThreeParts :: ByteString -> Either Error ByteString
|
hasThreeParts :: ByteString -> Either Error ByteString
|
||||||
hasThreeParts token = case length $ BS.split (BS.c2w '.') token of
|
hasThreeParts token = case length $ BS.split (BS.c2w '.') token of
|
||||||
3 -> Right token
|
3 -> Right token
|
||||||
n -> Left $ JwtErr $ JwtDecodeError ("Expected 3 parts in JWT; got " <> show n)
|
n -> Left $ JwtErr $ JwtDecodeErr $ UnexpectedParts n
|
||||||
jwtDecodeError :: JWT.JwtError -> JwtError
|
jwtDecodeError :: JWT.JwtError -> JwtError
|
||||||
-- The only errors we can get from JWT.decode function are:
|
-- The only errors we can get from JWT.decode function are:
|
||||||
-- BadAlgorithm
|
-- BadAlgorithm
|
||||||
-- KeyError
|
-- KeyError
|
||||||
-- BadCrypto
|
-- BadCrypto
|
||||||
jwtDecodeError (JWT.KeyError _) = JwtDecodeError "No suitable key or wrong key type"
|
jwtDecodeError (JWT.KeyError m) = JwtDecodeErr $ KeyError m
|
||||||
jwtDecodeError (JWT.BadAlgorithm _) = JwtDecodeError "Wrong or unsupported encoding algorithm"
|
jwtDecodeError (JWT.BadAlgorithm m) = JwtDecodeErr $ BadAlgorithm m
|
||||||
jwtDecodeError JWT.BadCrypto = JwtDecodeError "JWT cryptographic operation failed"
|
jwtDecodeError JWT.BadCrypto = JwtDecodeErr BadCrypto
|
||||||
-- Control never reaches here, the decode function only returns the above three
|
-- Control never reaches here, the decode function only returns the above three
|
||||||
jwtDecodeError _ = JwtDecodeError "JWT couldn't be decoded"
|
jwtDecodeError _ = JwtDecodeErr UnreachableDecodeError
|
||||||
|
|
||||||
verifyClaims :: JWT.JwtContent -> Either JwtError JSON.Value
|
verifyClaims :: JWT.JwtContent -> Either JwtError JSON.Value
|
||||||
verifyClaims (JWT.Jws (_, claims)) = case JSON.decodeStrict claims of
|
verifyClaims (JWT.Jws (_, claims)) = case JSON.decodeStrict claims of
|
||||||
Just jclaims@(JSON.Object mclaims) ->
|
Just jclaims@(JSON.Object mclaims) ->
|
||||||
verifyClaim mclaims "exp" isValidExpClaim "JWT expired" >>
|
verifyClaim mclaims "exp" isValidExpClaim JWTExpired >>
|
||||||
verifyClaim mclaims "nbf" isValidNbfClaim "JWT not yet valid" >>
|
verifyClaim mclaims "nbf" isValidNbfClaim JWTNotYetValid >>
|
||||||
verifyClaim mclaims "iat" isValidIatClaim "JWT issued at future" >>
|
verifyClaim mclaims "iat" isValidIatClaim JWTIssuedAtFuture >>
|
||||||
verifyClaim mclaims "aud" isValidAudClaim "JWT not in audience" >>
|
verifyClaim mclaims "aud" isValidAudClaim JWTNotInAudience >>
|
||||||
return jclaims
|
return jclaims
|
||||||
_ -> Left $ JwtClaimsError "Parsing claims failed"
|
_ -> Left $ JwtClaimsErr ParsingClaimsFailed
|
||||||
-- TODO: We could enable JWE support here (encrypted tokens)
|
-- TODO: We could enable JWE support here (encrypted tokens)
|
||||||
verifyClaims _ = Left $ JwtDecodeError "Unsupported token type"
|
verifyClaims _ = Left $ JwtDecodeErr UnsupportedTokenType
|
||||||
|
|
||||||
verifyClaim mclaims claim func err = do
|
verifyClaim mclaims claim func err = do
|
||||||
isValid <- maybe (Right True) func (KM.lookup claim mclaims)
|
isValid <- maybe (Right True) func (KM.lookup claim mclaims)
|
||||||
unless isValid $ Left $ JwtClaimsError err
|
unless isValid $ Left $ JwtClaimsErr err
|
||||||
|
|
||||||
allowedSkewSeconds = 30 :: Int64
|
allowedSkewSeconds = 30 :: Int64
|
||||||
now = floor . nominalDiffTimeToSeconds $ utcTimeToPOSIXSeconds time
|
now = floor . nominalDiffTimeToSeconds $ utcTimeToPOSIXSeconds time
|
||||||
@@ -104,15 +105,15 @@ parseToken AppConfig{..} (Just tkn) time = do
|
|||||||
|
|
||||||
isValidExpClaim :: JSON.Value -> Either JwtError Bool
|
isValidExpClaim :: JSON.Value -> Either JwtError Bool
|
||||||
isValidExpClaim (JSON.Number secs) = Right $ now <= (sciToInt secs + allowedSkewSeconds)
|
isValidExpClaim (JSON.Number secs) = Right $ now <= (sciToInt secs + allowedSkewSeconds)
|
||||||
isValidExpClaim _ = Left $ JwtClaimsError "The JWT 'exp' claim must be a number"
|
isValidExpClaim _ = Left $ JwtClaimsErr ExpClaimNotNumber
|
||||||
|
|
||||||
isValidNbfClaim :: JSON.Value -> Either JwtError Bool
|
isValidNbfClaim :: JSON.Value -> Either JwtError Bool
|
||||||
isValidNbfClaim (JSON.Number secs) = Right $ now >= (sciToInt secs - allowedSkewSeconds)
|
isValidNbfClaim (JSON.Number secs) = Right $ now >= (sciToInt secs - allowedSkewSeconds)
|
||||||
isValidNbfClaim _ = Left $ JwtClaimsError "The JWT 'nbf' claim must be a number"
|
isValidNbfClaim _ = Left $ JwtClaimsErr NbfClaimNotNumber
|
||||||
|
|
||||||
isValidIatClaim :: JSON.Value -> Either JwtError Bool
|
isValidIatClaim :: JSON.Value -> Either JwtError Bool
|
||||||
isValidIatClaim (JSON.Number secs) = Right $ now >= (sciToInt secs - allowedSkewSeconds)
|
isValidIatClaim (JSON.Number secs) = Right $ now >= (sciToInt secs - allowedSkewSeconds)
|
||||||
isValidIatClaim _ = Left $ JwtClaimsError "The JWT 'iat' claim must be a number"
|
isValidIatClaim _ = Left $ JwtClaimsErr IatClaimNotNumber
|
||||||
|
|
||||||
isValidAudClaim :: JSON.Value -> Either JwtError Bool
|
isValidAudClaim :: JSON.Value -> Either JwtError Bool
|
||||||
isValidAudClaim JSON.Null = Right True -- {"aud": null} is valid for all audiences
|
isValidAudClaim JSON.Null = Right True -- {"aud": null} is valid for all audiences
|
||||||
@@ -120,7 +121,7 @@ parseToken AppConfig{..} (Just tkn) time = do
|
|||||||
isValidAudClaim (JSON.Array arr)
|
isValidAudClaim (JSON.Array arr)
|
||||||
| null arr = Right True -- {"aud": []} is valid for all audiences
|
| null arr = Right True -- {"aud": []} is valid for all audiences
|
||||||
| allStrings arr = Right $ maybe True (\a -> JSON.String a `elem` arr) configJwtAudience
|
| allStrings arr = Right $ maybe True (\a -> JSON.String a `elem` arr) configJwtAudience
|
||||||
isValidAudClaim _ = Left $ JwtClaimsError "The JWT 'aud' claim must be a string or an array of strings"
|
isValidAudClaim _ = Left $ JwtClaimsErr AudClaimNotStringOrArray
|
||||||
|
|
||||||
parseClaims :: Monad m =>
|
parseClaims :: Monad m =>
|
||||||
AppConfig -> JSON.Value -> ExceptT Error m AuthResult
|
AppConfig -> JSON.Value -> ExceptT Error m AuthResult
|
||||||
|
|||||||
+61
-16
@@ -14,6 +14,8 @@ module PostgREST.Error
|
|||||||
, PgError(..)
|
, PgError(..)
|
||||||
, Error(..)
|
, Error(..)
|
||||||
, JwtError (..)
|
, JwtError (..)
|
||||||
|
, JwtDecodeError(..)
|
||||||
|
, JwtClaimsError(..)
|
||||||
, errorPayload
|
, errorPayload
|
||||||
, status
|
, status
|
||||||
) where
|
) where
|
||||||
@@ -86,7 +88,7 @@ data ApiRequestError
|
|||||||
| QueryParamError QPError
|
| QueryParamError QPError
|
||||||
| RelatedOrderNotToOne Text Text
|
| RelatedOrderNotToOne Text Text
|
||||||
| UnacceptableFilter Text
|
| UnacceptableFilter Text
|
||||||
| UnacceptableSchema [Text]
|
| UnacceptableSchema Text [Text]
|
||||||
| UnsupportedMethod ByteString
|
| UnsupportedMethod ByteString
|
||||||
| GucHeadersError
|
| GucHeadersError
|
||||||
| GucStatusError
|
| GucStatusError
|
||||||
@@ -194,7 +196,7 @@ instance ErrorBody ApiRequestError where
|
|||||||
message (InvalidBody errorMessage) = T.decodeUtf8 errorMessage
|
message (InvalidBody errorMessage) = T.decodeUtf8 errorMessage
|
||||||
message (InvalidRange _) = "Requested range not satisfiable"
|
message (InvalidRange _) = "Requested range not satisfiable"
|
||||||
message InvalidFilters = "Filters must include all and only primary key columns with 'eq' operators"
|
message InvalidFilters = "Filters must include all and only primary key columns with 'eq' operators"
|
||||||
message (UnacceptableSchema schemas) = "The schema must be one of the following: " <> T.intercalate ", " schemas
|
message (UnacceptableSchema sch _) = "Invalid schema: " <> sch
|
||||||
message (MediaTypeError cts) = "None of these media types are available: " <> T.intercalate ", " (map T.decodeUtf8 cts)
|
message (MediaTypeError cts) = "None of these media types are available: " <> T.intercalate ", " (map T.decodeUtf8 cts)
|
||||||
message (NotEmbedded resource) = "'" <> resource <> "' is not an embedded resource in this request"
|
message (NotEmbedded resource) = "'" <> resource <> "' is not an embedded resource in this request"
|
||||||
message GucHeadersError = "response.headers guc must be a JSON array composed of objects with a single key and a string value"
|
message GucHeadersError = "response.headers guc must be a JSON array composed of objects with a single key and a string value"
|
||||||
@@ -234,6 +236,7 @@ instance ErrorBody ApiRequestError where
|
|||||||
-- HINT: Maybe JSON.Value
|
-- HINT: Maybe JSON.Value
|
||||||
hint (NotEmbedded resource) = Just $ JSON.String $ "Verify that '" <> resource <> "' is included in the 'select' query parameter."
|
hint (NotEmbedded resource) = Just $ JSON.String $ "Verify that '" <> resource <> "' is included in the 'select' query parameter."
|
||||||
hint (PGRSTParseError raiseErr) = Just $ JSON.String $ pgrstParseErrorHint raiseErr
|
hint (PGRSTParseError raiseErr) = Just $ JSON.String $ pgrstParseErrorHint raiseErr
|
||||||
|
hint (UnacceptableSchema _ schemas) = Just $ JSON.String $ "Only the following schemas are exposed: " <> T.intercalate ", " schemas
|
||||||
|
|
||||||
hint _ = Nothing
|
hint _ = Nothing
|
||||||
|
|
||||||
@@ -647,10 +650,32 @@ data Error
|
|||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
data JwtError
|
data JwtError
|
||||||
= JwtDecodeError Text
|
= JwtDecodeErr JwtDecodeError
|
||||||
| JwtSecretMissing
|
| JwtSecretMissing
|
||||||
| JwtTokenRequired
|
| JwtTokenRequired
|
||||||
| JwtClaimsError Text
|
| JwtClaimsErr JwtClaimsError
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
data JwtDecodeError
|
||||||
|
= EmptyAuthHeader
|
||||||
|
| UnexpectedParts Int
|
||||||
|
| KeyError Text
|
||||||
|
| BadAlgorithm Text
|
||||||
|
| BadCrypto
|
||||||
|
| UnsupportedTokenType
|
||||||
|
| UnreachableDecodeError
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
data JwtClaimsError
|
||||||
|
= JWTExpired
|
||||||
|
| JWTNotYetValid
|
||||||
|
| JWTIssuedAtFuture
|
||||||
|
| JWTNotInAudience
|
||||||
|
| ParsingClaimsFailed
|
||||||
|
| ExpClaimNotNumber
|
||||||
|
| NbfClaimNotNumber
|
||||||
|
| IatClaimNotNumber
|
||||||
|
| AudClaimNotStringOrArray
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
instance PgrstError Error where
|
instance PgrstError Error where
|
||||||
@@ -696,14 +721,14 @@ instance ErrorBody Error where
|
|||||||
hint (PgErr err) = hint err
|
hint (PgErr err) = hint err
|
||||||
|
|
||||||
instance PgrstError JwtError where
|
instance PgrstError JwtError where
|
||||||
status JwtDecodeError{} = HTTP.unauthorized401
|
status JwtDecodeErr{} = HTTP.unauthorized401
|
||||||
status JwtSecretMissing = HTTP.status500
|
status JwtSecretMissing = HTTP.status500
|
||||||
status JwtTokenRequired = HTTP.unauthorized401
|
status JwtTokenRequired = HTTP.unauthorized401
|
||||||
status JwtClaimsError{} = HTTP.unauthorized401
|
status JwtClaimsErr{} = HTTP.unauthorized401
|
||||||
|
|
||||||
headers (JwtDecodeError m) = [invalidTokenHeader m]
|
headers e@(JwtDecodeErr _) = [invalidTokenHeader $ message e]
|
||||||
headers JwtTokenRequired = [requiredTokenHeader]
|
headers JwtTokenRequired = [requiredTokenHeader]
|
||||||
headers (JwtClaimsError m) = [invalidTokenHeader m]
|
headers e@(JwtClaimsErr _) = [invalidTokenHeader $ message e]
|
||||||
headers _ = mempty
|
headers _ = mempty
|
||||||
|
|
||||||
instance JSON.ToJSON JwtError where
|
instance JSON.ToJSON JwtError where
|
||||||
@@ -711,16 +736,36 @@ instance JSON.ToJSON JwtError where
|
|||||||
(code err) (message err) (details err) (hint err)
|
(code err) (message err) (details err) (hint err)
|
||||||
|
|
||||||
instance ErrorBody JwtError where
|
instance ErrorBody JwtError where
|
||||||
code JwtSecretMissing = "PGRST300"
|
code JwtSecretMissing = "PGRST300"
|
||||||
code (JwtDecodeError _) = "PGRST301"
|
code (JwtDecodeErr _) = "PGRST301"
|
||||||
code JwtTokenRequired = "PGRST302"
|
code JwtTokenRequired = "PGRST302"
|
||||||
code (JwtClaimsError _) = "PGRST303"
|
code (JwtClaimsErr _) = "PGRST303"
|
||||||
|
|
||||||
message JwtSecretMissing = "Server lacks JWT secret"
|
message JwtSecretMissing = "Server lacks JWT secret"
|
||||||
message (JwtDecodeError msg) = msg
|
message (JwtDecodeErr e) = case e of
|
||||||
message JwtTokenRequired = "Anonymous access is disabled"
|
EmptyAuthHeader -> "Empty JWT is sent in Authorization header"
|
||||||
message (JwtClaimsError msg) = msg
|
UnexpectedParts n -> "Expected 3 parts in JWT; got " <> show n
|
||||||
|
KeyError _ -> "No suitable key or wrong key type"
|
||||||
|
BadAlgorithm _ -> "Wrong or unsupported encoding algorithm"
|
||||||
|
BadCrypto -> "JWT cryptographic operation failed"
|
||||||
|
UnsupportedTokenType -> "Unsupported token type"
|
||||||
|
UnreachableDecodeError -> "JWT couldn't be decoded"
|
||||||
|
message JwtTokenRequired = "Anonymous access is disabled"
|
||||||
|
message (JwtClaimsErr e) = case e of
|
||||||
|
JWTExpired -> "JWT expired"
|
||||||
|
JWTNotYetValid -> "JWT not yet valid"
|
||||||
|
JWTIssuedAtFuture -> "JWT issued at future"
|
||||||
|
JWTNotInAudience -> "JWT not in audience"
|
||||||
|
ParsingClaimsFailed -> "Parsing claims failed"
|
||||||
|
ExpClaimNotNumber -> "The JWT 'exp' claim must be a number"
|
||||||
|
NbfClaimNotNumber -> "The JWT 'nbf' claim must be a number"
|
||||||
|
IatClaimNotNumber -> "The JWT 'iat' claim must be a number"
|
||||||
|
AudClaimNotStringOrArray -> "The JWT 'aud' claim must be a string or an array of strings"
|
||||||
|
|
||||||
|
details (JwtDecodeErr jde) = case jde of
|
||||||
|
KeyError dets -> Just $ JSON.String dets
|
||||||
|
BadAlgorithm dets -> Just $ JSON.String dets
|
||||||
|
_ -> Nothing
|
||||||
details _ = Nothing
|
details _ = Nothing
|
||||||
|
|
||||||
hint _ = Nothing
|
hint _ = Nothing
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ def test_jwt_errors(defaultenv):
|
|||||||
response = postgrest.session.get("/", headers=headers)
|
response = postgrest.session.get("/", headers=headers)
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
assert response.json()["message"] == "No suitable key or wrong key type"
|
assert response.json()["message"] == "No suitable key or wrong key type"
|
||||||
|
assert (
|
||||||
|
response.json()["details"] == "None of the keys was able to decode the JWT"
|
||||||
|
)
|
||||||
|
|
||||||
headers = jwtauthheader({"role": "not_existing"}, SECRET)
|
headers = jwtauthheader({"role": "not_existing"}, SECRET)
|
||||||
response = postgrest.session.get("/", headers=headers)
|
response = postgrest.session.get("/", headers=headers)
|
||||||
@@ -141,6 +144,10 @@ def test_jwt_errors(defaultenv):
|
|||||||
response = postgrest.session.get("/", headers=headers)
|
response = postgrest.session.get("/", headers=headers)
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
assert response.json()["message"] == "Wrong or unsupported encoding algorithm"
|
assert response.json()["message"] == "Wrong or unsupported encoding algorithm"
|
||||||
|
assert (
|
||||||
|
response.json()["details"]
|
||||||
|
== "JWT is unsecured but expected 'alg' was not 'none'"
|
||||||
|
)
|
||||||
|
|
||||||
env = {
|
env = {
|
||||||
**defaultenv,
|
**defaultenv,
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ spec =
|
|||||||
, matchHeaders = []
|
, matchHeaders = []
|
||||||
}
|
}
|
||||||
|
|
||||||
it "fails trying to read table from unkown schema" $
|
it "fails trying to read table from unknown schema" $
|
||||||
request methodGet "/parents" [("Accept-Profile", "unkown")] "" `shouldRespondWith`
|
request methodGet "/parents" [("Accept-Profile", "unknown")] "" `shouldRespondWith`
|
||||||
[json|{"message":"The schema must be one of the following: v1, v2, SPECIAL \"@/\\#~_-","code":"PGRST106","details":null,"hint":null}|]
|
[json|{"message":"Invalid schema: unknown","code":"PGRST106","details":null,"hint":"Only the following schemas are exposed: v1, v2, SPECIAL \"@/\\#~_-"}|]
|
||||||
{
|
{
|
||||||
matchStatus = 406
|
matchStatus = 406
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ spec =
|
|||||||
request methodPost "/children" [("Content-Profile", "unknown")]
|
request methodPost "/children" [("Content-Profile", "unknown")]
|
||||||
[json|{"name": "child 4", "parent_id": 4}|]
|
[json|{"name": "child 4", "parent_id": 4}|]
|
||||||
`shouldRespondWith`
|
`shouldRespondWith`
|
||||||
[json|{"message":"The schema must be one of the following: v1, v2, SPECIAL \"@/\\#~_-","code":"PGRST106","details":null,"hint":null}|]
|
[json|{"message":"Invalid schema: unknown","code":"PGRST106","details":null,"hint":"Only the following schemas are exposed: v1, v2, SPECIAL \"@/\\#~_-"}|]
|
||||||
{
|
{
|
||||||
matchStatus = 406
|
matchStatus = 406
|
||||||
}
|
}
|
||||||
@@ -389,9 +389,9 @@ spec =
|
|||||||
let def = simpleBody r ^? key "definitions" . key "another_table"
|
let def = simpleBody r ^? key "definitions" . key "another_table"
|
||||||
def `shouldBe` Nothing
|
def `shouldBe` Nothing
|
||||||
|
|
||||||
it "fails trying to read definitions from unkown schema" $
|
it "fails trying to read definitions from unknown schema" $
|
||||||
request methodGet "/" [("Accept-Profile", "unkown")] "" `shouldRespondWith`
|
request methodGet "/" [("Accept-Profile", "unknown")] "" `shouldRespondWith`
|
||||||
[json|{"message":"The schema must be one of the following: v1, v2, SPECIAL \"@/\\#~_-","code":"PGRST106","details":null,"hint":null}|]
|
[json|{"message":"Invalid schema: unknown","code":"PGRST106","details":null,"hint":"Only the following schemas are exposed: v1, v2, SPECIAL \"@/\\#~_-"}|]
|
||||||
{
|
{
|
||||||
matchStatus = 406
|
matchStatus = 406
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user