Merge branch 'yesod1.2' of https://github.com/yesodweb/yesod into yesod1.2

This commit is contained in:
Michael Snoyman 2013-04-03 09:28:19 +03:00
commit 09eecb8e79
3 changed files with 68 additions and 26 deletions

View File

@ -13,7 +13,7 @@ import Yesod.Routes.Class
import Blaze.ByteString.Builder (Builder) import Blaze.ByteString.Builder (Builder)
import Blaze.ByteString.Builder.Char.Utf8 (fromText) import Blaze.ByteString.Builder.Char.Utf8 (fromText)
import Control.Arrow ((***)) import Control.Arrow ((***))
import Control.Monad (forM, when) import Control.Monad (forM, when, void)
import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.Logger (LogLevel (LevelInfo, LevelOther), import Control.Monad.Logger (LogLevel (LevelInfo, LevelOther),
LogSource) LogSource)
@ -291,10 +291,14 @@ authorizationCheck = do
master <- getYesod master <- getYesod
case authRoute master of case authRoute master of
Nothing -> Nothing ->
permissionDenied "Authentication required" void $ permissionDenied "Authentication required"
Just url' -> do Just url' -> do
setUltDestCurrent void $ selectRep $ do
redirect url' provideRepType typeJson $ do
void $ permissionDenied "Authentication required"
provideRepType typeHtml $ do
setUltDestCurrent
void $ redirect url'
Unauthorized s' -> permissionDenied s' Unauthorized s' -> permissionDenied s'
-- | Convert a widget to a 'PageContent'. -- | Convert a widget to a 'PageContent'.
@ -407,6 +411,7 @@ defaultErrorHandler NotFound = selectRep $ do
<p>#{path'} <p>#{path'}
|] |]
provideRep $ return $ object ["message" .= ("Not Found" :: Text)] provideRep $ return $ object ["message" .= ("Not Found" :: Text)]
defaultErrorHandler (PermissionDenied msg) = selectRep $ do defaultErrorHandler (PermissionDenied msg) = selectRep $ do
provideRep $ defaultLayout $ do provideRep $ defaultLayout $ do
setTitle "Permission Denied" setTitle "Permission Denied"
@ -414,7 +419,15 @@ defaultErrorHandler (PermissionDenied msg) = selectRep $ do
<h1>Permission denied <h1>Permission denied
<p>#{msg} <p>#{msg}
|] |]
provideRep $ return $ object ["message" .= ("Permission Denied" :: Text)] provideRep $ do
site <- getYesod
rend <- getUrlRender
return $ object $ [
"message" .= ("Permission Denied. " <> msg)
] ++ case authRoute site of
Nothing -> []
Just url -> ["auth_url" .= rend url]
defaultErrorHandler (InvalidArgs ia) = selectRep $ do defaultErrorHandler (InvalidArgs ia) = selectRep $ do
provideRep $ defaultLayout $ do provideRep $ defaultLayout $ do
setTitle "Invalid Arguments" setTitle "Invalid Arguments"

View File

@ -12,24 +12,30 @@ data App = App
mkYesod "App" [parseRoutes| mkYesod "App" [parseRoutes|
/no-auth NoAuthR /no-auth NoAuthR
/needs-login NeedsLoginR /needs-login-json NeedsLoginJsonR
/needs-login-html NeedsLoginHtmlR
/read-only ReadOnlyR /read-only ReadOnlyR
/forbidden ForbiddenR /forbidden ForbiddenR
|] |]
instance Yesod App where instance Yesod App where
isAuthorized NoAuthR _ = return Authorized isAuthorized NoAuthR _ = return Authorized
isAuthorized NeedsLoginR _ = return AuthenticationRequired isAuthorized NeedsLoginJsonR _ = return AuthenticationRequired
isAuthorized NeedsLoginHtmlR _ = return AuthenticationRequired
isAuthorized ReadOnlyR False = return Authorized isAuthorized ReadOnlyR False = return Authorized
isAuthorized ReadOnlyR True = return $ Unauthorized "Read only" isAuthorized ReadOnlyR True = return $ Unauthorized "Read only"
isAuthorized ForbiddenR _ = return $ Unauthorized "Forbidden" isAuthorized ForbiddenR _ = return $ Unauthorized "Forbidden"
authRoute _ = Just NoAuthR authRoute _ = Just NoAuthR
handleNoAuthR, handleNeedsLoginR, handleReadOnlyR, handleForbiddenR :: Handler () handleNoAuthR, handleReadOnlyR, handleForbiddenR :: Handler ()
handleNoAuthR = return () handleNoAuthR = return ()
handleNeedsLoginR = return () handleReadOnlyR = return ()
handleReadOnlyR = return () handleForbiddenR = return ()
handleForbiddenR = return ()
handleNeedsLoginJsonR :: Handler RepJson
handleNeedsLoginJsonR = return $ repJson $ object []
handleNeedsLoginHtmlR :: Handler RepHtml
handleNeedsLoginHtmlR = return ""
test :: String -- ^ method test :: String -- ^ method
-> String -- ^ path -> String -- ^ path
@ -48,8 +54,10 @@ specs :: Spec
specs = describe "Auth" $ do specs = describe "Auth" $ do
test "GET" "no-auth" $ \sres -> assertStatus 200 sres test "GET" "no-auth" $ \sres -> assertStatus 200 sres
test "POST" "no-auth" $ \sres -> assertStatus 200 sres test "POST" "no-auth" $ \sres -> assertStatus 200 sres
test "GET" "needs-login" $ \sres -> assertStatus 303 sres test "GET" "needs-login-html" $ \sres -> assertStatus 303 sres
test "POST" "needs-login" $ \sres -> assertStatus 303 sres test "POST" "needs-login-html" $ \sres -> assertStatus 303 sres
test "GET" "needs-login-json" $ \sres -> assertStatus 403 sres
test "POST" "needs-login-json" $ \sres -> assertStatus 403 sres
test "GET" "read-only" $ \sres -> assertStatus 200 sres test "GET" "read-only" $ \sres -> assertStatus 200 sres
test "POST" "read-only" $ \sres -> assertStatus 403 sres test "POST" "read-only" $ \sres -> assertStatus 403 sres
test "GET" "forbidden" $ \sres -> assertStatus 403 sres test "GET" "forbidden" $ \sres -> assertStatus 403 sres

View File

@ -9,11 +9,15 @@ import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Char8 as S8
import Data.String (IsString) import Data.String (IsString)
import Data.Text (Text) import Data.Text (Text)
import Data.Maybe (fromJust)
import Data.Monoid (Endo (..))
import qualified Control.Monad.Trans.Writer as Writer
data App = App data App = App
mkYesod "App" [parseRoutes| mkYesod "App" [parseRoutes|
/ HomeR GET / HomeR GET
/json JsonR GET
|] |]
instance Yesod App instance Yesod App
@ -23,23 +27,39 @@ specialHtml = "text/html; charset=special"
getHomeR :: Handler TypedContent getHomeR :: Handler TypedContent
getHomeR = selectRep $ do getHomeR = selectRep $ do
let go ct t = provideRepType ct $ return (t :: Text) rep typeHtml "HTML"
go typeHtml "HTML" rep specialHtml "HTMLSPECIAL"
go specialHtml "HTMLSPECIAL" rep typeXml "XML"
go typeJson "JSON" rep typeJson "JSON"
go typeXml "XML"
rep :: Monad m => ContentType -> Text -> Writer.Writer (Data.Monoid.Endo [ProvidedRep m]) ()
rep ct t = provideRepType ct $ return (t :: Text)
getJsonR :: Handler TypedContent
getJsonR = selectRep $ do
rep typeHtml "HTML"
provideRep $ return $ object ["message" .= ("Invalid Login" :: Text)]
testRequest :: Request
-> ByteString -- ^ expected body
-> Spec
testRequest req expected = it (S8.unpack $ fromJust $ lookup "Accept" $ requestHeaders req) $ do
app <- toWaiApp App
flip runSession app $ do
sres <- request req
assertBody expected sres
assertStatus 200 sres
test :: String -- ^ accept header test :: String -- ^ accept header
-> ByteString -- ^ expected body -> ByteString -- ^ expected body
-> Spec -> Spec
test accept expected = it accept $ do test accept expected =
app <- toWaiApp App testRequest (acceptRequest accept) expected
flip runSession app $ do
sres <- request defaultRequest acceptRequest :: String -> Request
acceptRequest accept = defaultRequest
{ requestHeaders = [("Accept", S8.pack accept)] { requestHeaders = [("Accept", S8.pack accept)]
} }
assertBody expected sres
assertStatus 200 sres
specs :: Spec specs :: Spec
specs = describe "selectRep" $ do specs = describe "selectRep" $ do
@ -53,3 +73,4 @@ specs = describe "selectRep" $ do
test (S8.unpack typeHtml) "HTML" test (S8.unpack typeHtml) "HTML"
test "text/html" "HTML" test "text/html" "HTML"
test specialHtml "HTMLSPECIAL" test specialHtml "HTMLSPECIAL"
testRequest (acceptRequest "application/json") { pathInfo = ["json"] } "{\"message\":\"Invalid Login\"}"