Beef up testing
This commit is contained in:
parent
426d3ce39b
commit
24531ac333
1 changed files with 30 additions and 6 deletions
|
@ -26,7 +26,7 @@ import Data.Aeson (FromJSON, ToJSON, Value,
|
||||||
import Data.Aeson.Lens (_JSON)
|
import Data.Aeson.Lens (_JSON)
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
import qualified Data.ByteString.Lazy as BSL
|
||||||
import Data.Text (Text)
|
import Data.Text (Text, pack)
|
||||||
import Data.CaseInsensitive (mk)
|
import Data.CaseInsensitive (mk)
|
||||||
import Data.Foldable (find)
|
import Data.Foldable (find)
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
|
@ -42,6 +42,7 @@ import Network.HTTP.Types (Status, status200,
|
||||||
import Network.Wai (responseLBS)
|
import Network.Wai (responseLBS)
|
||||||
import Network.Wai.Handler.Warp (testWithApplication)
|
import Network.Wai.Handler.Warp (testWithApplication)
|
||||||
import Network.Wreq (Options, auth, basicAuth,
|
import Network.Wreq (Options, auth, basicAuth,
|
||||||
|
checkResponse,
|
||||||
cookieExpiryTime, cookies,
|
cookieExpiryTime, cookies,
|
||||||
defaults, get, getWith, postWith,
|
defaults, get, getWith, postWith,
|
||||||
header, oauth2Bearer,
|
header, oauth2Bearer,
|
||||||
|
@ -185,8 +186,21 @@ cookieAuthSpec
|
||||||
it "fails with no XSRF header or cookie" $ \port -> property
|
it "fails with no XSRF header or cookie" $ \port -> property
|
||||||
$ \(user :: User) -> do
|
$ \(user :: User) -> do
|
||||||
jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user)
|
jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user)
|
||||||
opts <- addJwtToCookie cookieCfg jwt
|
opts' <- addJwtToCookie cookieCfg jwt
|
||||||
getWith opts (url port) `shouldHTTPErrorWith` status401
|
let opts = opts' & checkResponse .~ Just mempty
|
||||||
|
resp <- getWith opts (url port)
|
||||||
|
resp ^. responseStatus `shouldBe` status401
|
||||||
|
(resp ^. responseCookieJar) `shouldNotHaveCookies` ["XSRF-TOKEN"]
|
||||||
|
|
||||||
|
-- Validating that the XSRF cookie isn't added for UVerb routes either.
|
||||||
|
-- These routes can return a 401 response directly without using throwError / throwAll,
|
||||||
|
-- which revealed a bug:
|
||||||
|
--
|
||||||
|
-- https://github.com/haskell-servant/servant/issues/1570#issuecomment-1076374449
|
||||||
|
resp <- getWith opts (url port ++ "/uverb")
|
||||||
|
resp ^. responseStatus `shouldBe` status401
|
||||||
|
(resp ^. responseCookieJar) `shouldNotHaveCookies` ["XSRF-TOKEN"]
|
||||||
|
|
||||||
|
|
||||||
it "succeeds if XSRF header and cookie match, and JWT is valid" $ \port -> property
|
it "succeeds if XSRF header and cookie match, and JWT is valid" $ \port -> property
|
||||||
$ \(user :: User) -> do
|
$ \(user :: User) -> do
|
||||||
|
@ -408,14 +422,14 @@ type API auths
|
||||||
= Auth auths User :>
|
= Auth auths User :>
|
||||||
( Get '[JSON] Int
|
( Get '[JSON] Int
|
||||||
:<|> ReqBody '[JSON] Int :> Post '[JSON] Int
|
:<|> ReqBody '[JSON] Int :> Post '[JSON] Int
|
||||||
:<|> NamedRoutes DummyRoutes
|
:<|> "named" :> NamedRoutes DummyRoutes
|
||||||
:<|> UVerb 'GET '[JSON] '[WithStatus 200 Int, WithStatus 500 Text]
|
|
||||||
:<|> "header" :> Get '[JSON] (Headers '[Header "Blah" Int] Int)
|
:<|> "header" :> Get '[JSON] (Headers '[Header "Blah" Int] Int)
|
||||||
#if MIN_VERSION_servant_server(0,15,0)
|
#if MIN_VERSION_servant_server(0,15,0)
|
||||||
:<|> "stream" :> StreamGet NoFraming OctetStream (SourceIO BS.ByteString)
|
:<|> "stream" :> StreamGet NoFraming OctetStream (SourceIO BS.ByteString)
|
||||||
#endif
|
#endif
|
||||||
:<|> "raw" :> Raw
|
:<|> "raw" :> Raw
|
||||||
)
|
)
|
||||||
|
:<|> "uverb" :> Auth auths User :> UVerb 'GET '[JSON] '[WithStatus 200 Int, WithStatus 401 Text, WithStatus 403 Text]
|
||||||
:<|> "login" :> ReqBody '[JSON] User :> Post '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
|
:<|> "login" :> ReqBody '[JSON] User :> Post '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
|
||||||
, Header "Set-Cookie" SetCookie ] NoContent)
|
, Header "Set-Cookie" SetCookie ] NoContent)
|
||||||
:<|> "logout" :> Get '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
|
:<|> "logout" :> Get '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
|
||||||
|
@ -486,7 +500,6 @@ server ccfg =
|
||||||
Authenticated usr -> getInt usr
|
Authenticated usr -> getInt usr
|
||||||
:<|> postInt usr
|
:<|> postInt usr
|
||||||
:<|> DummyRoutes { dummyInt = getInt usr }
|
:<|> DummyRoutes { dummyInt = getInt usr }
|
||||||
:<|> respond (WithStatus @200 (42 :: Int))
|
|
||||||
:<|> getHeaderInt
|
:<|> getHeaderInt
|
||||||
#if MIN_VERSION_servant_server(0,15,0)
|
#if MIN_VERSION_servant_server(0,15,0)
|
||||||
:<|> return (S.source ["bytestring"])
|
:<|> return (S.source ["bytestring"])
|
||||||
|
@ -494,6 +507,11 @@ server ccfg =
|
||||||
:<|> raw
|
:<|> raw
|
||||||
Indefinite -> throwAll err401
|
Indefinite -> throwAll err401
|
||||||
_ -> throwAll err403
|
_ -> throwAll err403
|
||||||
|
) :<|>
|
||||||
|
(\authResult -> case authResult of
|
||||||
|
Authenticated usr -> respond (WithStatus @200 (42 :: Int))
|
||||||
|
Indefinite -> respond (WithStatus @401 $ pack "Authentication required")
|
||||||
|
_ -> respond (WithStatus @403 $ pack "Forbidden")
|
||||||
)
|
)
|
||||||
:<|> getLogin
|
:<|> getLogin
|
||||||
:<|> getLogout
|
:<|> getLogout
|
||||||
|
@ -574,6 +592,12 @@ shouldMatchCookieNames cj patterns
|
||||||
= fmap cookie_name (destroyCookieJar cj)
|
= fmap cookie_name (destroyCookieJar cj)
|
||||||
`shouldMatchList` patterns
|
`shouldMatchList` patterns
|
||||||
|
|
||||||
|
shouldNotHaveCookies :: HCli.CookieJar -> [BS.ByteString] -> Expectation
|
||||||
|
shouldNotHaveCookies cj patterns =
|
||||||
|
sequence_ $ (\cookieName -> cookieNames `shouldNotContain` [cookieName]) <$> patterns
|
||||||
|
where cookieNames :: [BS.ByteString]
|
||||||
|
cookieNames = cookie_name <$> destroyCookieJar cj
|
||||||
|
|
||||||
shouldMatchCookieNameValues :: HCli.CookieJar -> [(BS.ByteString, BS.ByteString)] -> Expectation
|
shouldMatchCookieNameValues :: HCli.CookieJar -> [(BS.ByteString, BS.ByteString)] -> Expectation
|
||||||
shouldMatchCookieNameValues cj patterns
|
shouldMatchCookieNameValues cj patterns
|
||||||
= fmap ((,) <$> cookie_name <*> cookie_value) (destroyCookieJar cj)
|
= fmap ((,) <$> cookie_name <*> cookie_value) (destroyCookieJar cj)
|
||||||
|
|
Loading…
Reference in a new issue