@@ -1,5 +1,4 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2−{-# LANGUAGE TypeApplications #-}
3module Servant.Auth.ServerSpec (spec) where
2module Servant.Auth.ServerSpec (spec) where
5#if !MIN_VERSION_servant_server(0,16,0)
4#if !MIN_VERSION_servant_server(0,16,0)
@@ -25,7 +24,6 @@ import Data.Aeson (FromJSON, ToJSON, Value,
25import Data.Aeson.Lens (_JSON)
24import Data.Aeson.Lens (_JSON)
26import qualified Data.ByteString as BS
25import qualified Data.ByteString as BS
27import qualified Data.ByteString.Lazy as BSL
26import qualified Data.ByteString.Lazy as BSL
28−import Data.Text (Text, pack)
29import Data.CaseInsensitive (mk)
27import Data.CaseInsensitive (mk)
30import Data.Foldable (find)
28import Data.Foldable (find)
31import Data.Monoid
29import Data.Monoid
@@ -41,7 +39,6 @@ import Network.HTTP.Types (Status, status200,
41import Network.Wai (responseLBS)
39import Network.Wai (responseLBS)
42import Network.Wai.Handler.Warp (testWithApplication)
40import Network.Wai.Handler.Warp (testWithApplication)
43import Network.Wreq (Options, auth, basicAuth,
41import Network.Wreq (Options, auth, basicAuth,
45 cookieExpiryTime, cookies,
42 cookieExpiryTime, cookies,
46 defaults, get, getWith, postWith,
43 defaults, get, getWith, postWith,
47 header, oauth2Bearer,
44 header, oauth2Bearer,
@@ -185,21 +182,8 @@ cookieAuthSpec
185 it "fails with no XSRF header or cookie" $ \port -> property
182 it "fails with no XSRF header or cookie" $ \port -> property
186 $ \(user :: User) -> do
183 $ \(user :: User) -> do
187 jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user)
184 jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user)
188− opts' <- addJwtToCookie cookieCfg jwt
185+ opts <- addJwtToCookie cookieCfg jwt
189− let opts = opts' & checkResponse .~ Just mempty
190− resp <- getWith opts (url port)
186+ getWith opts (url port) `shouldHTTPErrorWith` status401
191− resp ^. responseStatus `shouldBe` status401
192− (resp ^. responseCookieJar) `shouldNotHaveCookies` ["XSRF-TOKEN"]
194− -- Validating that the XSRF cookie isn't added for UVerb routes either.
195− -- These routes can return a 401 response directly without using throwError / throwAll,
196− -- which revealed a bug:
198− -- https://github.com/haskell-servant/servant/issues/1570#issuecomment-1076374449
199− resp <- getWith opts (url port ++ "/uverb")
200− resp ^. responseStatus `shouldBe` status401
201− (resp ^. responseCookieJar) `shouldNotHaveCookies` ["XSRF-TOKEN"]
204 it "succeeds if XSRF header and cookie match, and JWT is valid" $ \port -> property
188 it "succeeds if XSRF header and cookie match, and JWT is valid" $ \port -> property
205 $ \(user :: User) -> do
189 $ \(user :: User) -> do
@@ -421,14 +405,13 @@ type API auths
421 = Auth auths User :>
405 = Auth auths User :>
422 ( Get '[JSON] Int
406 ( Get '[JSON] Int
423 :<|> ReqBody '[JSON] Int :> Post '[JSON] Int
407 :<|> ReqBody '[JSON] Int :> Post '[JSON] Int
424− :<|> "named" :> NamedRoutes DummyRoutes
408+ :<|> NamedRoutes DummyRoutes
425 :<|> "header" :> Get '[JSON] (Headers '[Header "Blah" Int] Int)
409 :<|> "header" :> Get '[JSON] (Headers '[Header "Blah" Int] Int)
426#if MIN_VERSION_servant_server(0,15,0)
410#if MIN_VERSION_servant_server(0,15,0)
427 :<|> "stream" :> StreamGet NoFraming OctetStream (SourceIO BS.ByteString)
411 :<|> "stream" :> StreamGet NoFraming OctetStream (SourceIO BS.ByteString)
429 :<|> "raw" :> Raw
413 :<|> "raw" :> Raw
431− :<|> "uverb" :> Auth auths User :> UVerb 'GET '[JSON] '[WithStatus 200 Int, WithStatus 401 Text, WithStatus 403 Text]
432 :<|> "login" :> ReqBody '[JSON] User :> Post '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
415 :<|> "login" :> ReqBody '[JSON] User :> Post '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
433 , Header "Set-Cookie" SetCookie ] NoContent)
416 , Header "Set-Cookie" SetCookie ] NoContent)
434 :<|> "logout" :> Get '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
417 :<|> "logout" :> Get '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
@@ -506,11 +489,6 @@ server ccfg =
507 Indefinite -> throwAll err401
490 Indefinite -> throwAll err401
508 _ -> throwAll err403
491 _ -> throwAll err403
510− (\authResult -> case authResult of
511− Authenticated usr -> respond (WithStatus @200 (42 :: Int))
512− Indefinite -> respond (WithStatus @401 $ pack "Authentication required")
513− _ -> respond (WithStatus @403 $ pack "Forbidden")
515 :<|> getLogin
493 :<|> getLogin
516 :<|> getLogout
494 :<|> getLogout
@@ -591,12 +569,6 @@ shouldMatchCookieNames cj patterns
591 = fmap cookie_name (destroyCookieJar cj)
569 = fmap cookie_name (destroyCookieJar cj)
592 `shouldMatchList` patterns
570 `shouldMatchList` patterns
594−shouldNotHaveCookies :: HCli.CookieJar -> [BS.ByteString] -> Expectation
595−shouldNotHaveCookies cj patterns =
596− sequence_ $ (\cookieName -> cookieNames `shouldNotContain` [cookieName]) <$> patterns
597− where cookieNames :: [BS.ByteString]
598− cookieNames = cookie_name <$> destroyCookieJar cj
600shouldMatchCookieNameValues :: HCli.CookieJar -> [(BS.ByteString, BS.ByteString)] -> Expectation
572shouldMatchCookieNameValues :: HCli.CookieJar -> [(BS.ByteString, BS.ByteString)] -> Expectation
601shouldMatchCookieNameValues cj patterns
573shouldMatchCookieNameValues cj patterns
602 = fmap ((,) <$> cookie_name <*> cookie_value) (destroyCookieJar cj)
574 = fmap ((,) <$> cookie_name <*> cookie_value) (destroyCookieJar cj)