nonce -> token (#214)
This commit is contained in:
parent
f11656f73a
commit
ec62f6f68c
@ -25,7 +25,7 @@ module Yesod.Internal
|
|||||||
, toUnique
|
, toUnique
|
||||||
-- * Names
|
-- * Names
|
||||||
, sessionName
|
, sessionName
|
||||||
, nonceKey
|
, tokenKey
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Text.Hamlet (HtmlUrl, hamlet, Html)
|
import Text.Hamlet (HtmlUrl, hamlet, Html)
|
||||||
@ -95,8 +95,8 @@ newtype Head url = Head (HtmlUrl url)
|
|||||||
newtype Body url = Body (HtmlUrl url)
|
newtype Body url = Body (HtmlUrl url)
|
||||||
deriving Monoid
|
deriving Monoid
|
||||||
|
|
||||||
nonceKey :: IsString a => a
|
tokenKey :: IsString a => a
|
||||||
nonceKey = "_NONCE"
|
tokenKey = "_TOKEN"
|
||||||
|
|
||||||
sessionName :: IsString a => a
|
sessionName :: IsString a => a
|
||||||
sessionName = "_SESSION"
|
sessionName = "_SESSION"
|
||||||
|
|||||||
@ -416,19 +416,19 @@ defaultYesodRunner handler master sub murl toMasterRoute msb req = do
|
|||||||
redirect url'
|
redirect url'
|
||||||
Unauthorized s' -> permissionDenied s'
|
Unauthorized s' -> permissionDenied s'
|
||||||
handler
|
handler
|
||||||
let sessionMap = Map.fromList . filter ((/=) nonceKey . fst) $ session
|
let sessionMap = Map.fromList . filter ((/=) tokenKey . fst) $ session
|
||||||
let ra = resolveApproot master req
|
let ra = resolveApproot master req
|
||||||
yar <- handlerToYAR master sub toMasterRoute
|
yar <- handlerToYAR master sub toMasterRoute
|
||||||
(yesodRender master ra) errorHandler rr murl sessionMap h
|
(yesodRender master ra) errorHandler rr murl sessionMap h
|
||||||
extraHeaders <- case yar of
|
extraHeaders <- case yar of
|
||||||
(YARPlain _ _ ct _ newSess) -> do
|
(YARPlain _ _ ct _ newSess) -> do
|
||||||
let nsNonce = Map.toList $ maybe
|
let nsToken = Map.toList $ maybe
|
||||||
newSess
|
newSess
|
||||||
(\n -> Map.insert nonceKey (TE.encodeUtf8 n) newSess)
|
(\n -> Map.insert tokenKey (TE.encodeUtf8 n) newSess)
|
||||||
(reqNonce rr)
|
(reqToken rr)
|
||||||
sessionHeaders <- liftIO $ maybe
|
sessionHeaders <- liftIO $ maybe
|
||||||
(return [])
|
(return [])
|
||||||
(\sb -> sbSaveSession sb master req now session nsNonce)
|
(\sb -> sbSaveSession sb master req now session nsToken)
|
||||||
msb
|
msb
|
||||||
return $ ("Content-Type", ct) : map headerToPair sessionHeaders
|
return $ ("Content-Type", ct) : map headerToPair sessionHeaders
|
||||||
_ -> return []
|
_ -> return []
|
||||||
|
|||||||
@ -36,16 +36,16 @@ data Request = Request
|
|||||||
, reqWaiRequest :: W.Request
|
, reqWaiRequest :: W.Request
|
||||||
-- | Languages which the client supports.
|
-- | Languages which the client supports.
|
||||||
, reqLangs :: [Text]
|
, reqLangs :: [Text]
|
||||||
-- | A random, session-specific nonce used to prevent CSRF attacks.
|
-- | A random, session-specific token used to prevent CSRF attacks.
|
||||||
, reqNonce :: Maybe Text
|
, reqToken :: Maybe Text
|
||||||
}
|
}
|
||||||
|
|
||||||
parseWaiRequest :: W.Request
|
parseWaiRequest :: W.Request
|
||||||
-> [(Text, ByteString)] -- ^ session
|
-> [(Text, ByteString)] -- ^ session
|
||||||
-> Bool
|
-> Bool
|
||||||
-> IO Request
|
-> IO Request
|
||||||
parseWaiRequest env session' useNonce =
|
parseWaiRequest env session' useToken =
|
||||||
parseWaiRequest' env session' useNonce <$> newStdGen
|
parseWaiRequest' env session' useToken <$> newStdGen
|
||||||
|
|
||||||
parseWaiRequest' :: RandomGen g
|
parseWaiRequest' :: RandomGen g
|
||||||
=> W.Request
|
=> W.Request
|
||||||
@ -53,8 +53,8 @@ parseWaiRequest' :: RandomGen g
|
|||||||
-> Bool
|
-> Bool
|
||||||
-> g
|
-> g
|
||||||
-> Request
|
-> Request
|
||||||
parseWaiRequest' env session' useNonce gen =
|
parseWaiRequest' env session' useToken gen =
|
||||||
Request gets'' cookies' env langs'' nonce
|
Request gets'' cookies' env langs'' token
|
||||||
where
|
where
|
||||||
gets' = queryToQueryText $ W.queryString env
|
gets' = queryToQueryText $ W.queryString env
|
||||||
gets'' = map (second $ fromMaybe "") gets'
|
gets'' = map (second $ fromMaybe "") gets'
|
||||||
@ -75,16 +75,16 @@ parseWaiRequest' env session' useNonce gen =
|
|||||||
-- language in the list.
|
-- language in the list.
|
||||||
langs'' = addTwoLetters (id, Set.empty) langs'
|
langs'' = addTwoLetters (id, Set.empty) langs'
|
||||||
|
|
||||||
-- If sessions are disabled nonces should not be used (any
|
-- If sessions are disabled tokens should not be used (any
|
||||||
-- nonceKey present in the session is ignored). If sessions
|
-- tokenKey present in the session is ignored). If sessions
|
||||||
-- are enabled and a session has no nonceKey a new one is
|
-- are enabled and a session has no tokenKey a new one is
|
||||||
-- generated.
|
-- generated.
|
||||||
nonce = if not useNonce
|
token = if not useToken
|
||||||
then Nothing
|
then Nothing
|
||||||
else Just $ maybe
|
else Just $ maybe
|
||||||
(pack $ randomString 10 gen)
|
(pack $ randomString 10 gen)
|
||||||
(decodeUtf8With lenientDecode)
|
(decodeUtf8With lenientDecode)
|
||||||
(lookup nonceKey session')
|
(lookup tokenKey session')
|
||||||
|
|
||||||
addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text]
|
addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text]
|
||||||
addTwoLetters (toAdd, exist) [] =
|
addTwoLetters (toAdd, exist) [] =
|
||||||
|
|||||||
@ -27,32 +27,32 @@ noRepeat len n = length (nub $ map (randomString len . mkStdGen) [1..n]) == n
|
|||||||
|
|
||||||
-- For convenience instead of "(undefined :: StdGen)".
|
-- For convenience instead of "(undefined :: StdGen)".
|
||||||
g :: StdGen
|
g :: StdGen
|
||||||
g = undefined
|
g = error "test/YesodCoreTest/InternalRequest.g"
|
||||||
|
|
||||||
|
|
||||||
nonceSpecs :: [Spec]
|
tokenSpecs :: [Spec]
|
||||||
nonceSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqNonce)"
|
tokenSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqToken)"
|
||||||
[ it "is Nothing if sessions are disabled" noDisabledNonce
|
[ it "is Nothing if sessions are disabled" noDisabledToken
|
||||||
, it "ignores pre-existing nonce if sessions are disabled" ignoreDisabledNonce
|
, it "ignores pre-existing token if sessions are disabled" ignoreDisabledToken
|
||||||
, it "uses preexisting nonce in session" useOldNonce
|
, it "uses preexisting token in session" useOldToken
|
||||||
, it "generates a new nonce for sessions without nonce" generateNonce
|
, it "generates a new token for sessions without token" generateToken
|
||||||
]
|
]
|
||||||
|
|
||||||
noDisabledNonce :: Bool
|
noDisabledToken :: Bool
|
||||||
noDisabledNonce = reqNonce r == Nothing where
|
noDisabledToken = reqToken r == Nothing where
|
||||||
r = parseWaiRequest' defaultRequest [] False g
|
r = parseWaiRequest' defaultRequest [] False g
|
||||||
|
|
||||||
ignoreDisabledNonce :: Bool
|
ignoreDisabledToken :: Bool
|
||||||
ignoreDisabledNonce = reqNonce r == Nothing where
|
ignoreDisabledToken = reqToken r == Nothing where
|
||||||
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] False g
|
r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] False g
|
||||||
|
|
||||||
useOldNonce :: Bool
|
useOldToken :: Bool
|
||||||
useOldNonce = reqNonce r == Just "old" where
|
useOldToken = reqToken r == Just "old" where
|
||||||
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] True g
|
r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True g
|
||||||
|
|
||||||
generateNonce :: Bool
|
generateToken :: Bool
|
||||||
generateNonce = reqNonce r /= Nothing where
|
generateToken = reqToken r /= Nothing where
|
||||||
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] True g
|
r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True g
|
||||||
|
|
||||||
|
|
||||||
langSpecs :: [Spec]
|
langSpecs :: [Spec]
|
||||||
@ -95,6 +95,6 @@ prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "e
|
|||||||
|
|
||||||
internalRequestTest :: [Spec]
|
internalRequestTest :: [Spec]
|
||||||
internalRequestTest = descriptions [ randomStringSpecs
|
internalRequestTest = descriptions [ randomStringSpecs
|
||||||
, nonceSpecs
|
, tokenSpecs
|
||||||
, langSpecs
|
, langSpecs
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
|
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
module YesodCoreTest.JsLoader (specs) where
|
module YesodCoreTest.JsLoader (specs, Widget) where
|
||||||
|
|
||||||
import YesodCoreTest.JsLoaderSites.HeadAsync (HA(..))
|
import YesodCoreTest.JsLoaderSites.HeadAsync (HA(..))
|
||||||
import YesodCoreTest.JsLoaderSites.Bottom (B(..))
|
import YesodCoreTest.JsLoaderSites.Bottom (B(..))
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
|
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
module YesodCoreTest.JsLoaderSites.Bottom (B(..)) where
|
module YesodCoreTest.JsLoaderSites.Bottom (B(..), Widget) where
|
||||||
|
|
||||||
import Yesod.Core
|
import Yesod.Core
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
|
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
module YesodCoreTest.JsLoaderSites.HeadAsync (HA(..)) where
|
module YesodCoreTest.JsLoaderSites.HeadAsync (HA(..), Widget) where
|
||||||
|
|
||||||
import Yesod.Core
|
import Yesod.Core
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ mkYesod "Y" [parseRoutes|
|
|||||||
instance Yesod Y
|
instance Yesod Y
|
||||||
|
|
||||||
getRootR :: Handler RepHtml
|
getRootR :: Handler RepHtml
|
||||||
getRootR = defaultLayout $ addHamlet [hamlet|<a href=@{RootR}>|]
|
getRootR = defaultLayout $ toWidget [hamlet|<a href=@{RootR}>|]
|
||||||
|
|
||||||
linksTest :: [Spec]
|
linksTest :: [Spec]
|
||||||
linksTest = describe "Test.Links"
|
linksTest = describe "Test.Links"
|
||||||
|
|||||||
@ -27,9 +27,9 @@ instance Yesod Y where
|
|||||||
|
|
||||||
getRootR :: Handler RepHtml
|
getRootR :: Handler RepHtml
|
||||||
getRootR = defaultLayout $ do
|
getRootR = defaultLayout $ do
|
||||||
addCassius [lucius|foo1{bar:baz}|]
|
toWidget [lucius|foo1{bar:baz}|]
|
||||||
addCassiusMedia "screen" [lucius|foo2{bar:baz}|]
|
addCassiusMedia "screen" [lucius|foo2{bar:baz}|]
|
||||||
addCassius [lucius|foo3{bar:baz}|]
|
toWidget [lucius|foo3{bar:baz}|]
|
||||||
|
|
||||||
getStaticR :: Handler RepHtml
|
getStaticR :: Handler RepHtml
|
||||||
getStaticR = getRootR
|
getStaticR = getRootR
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: yesod-core
|
name: yesod-core
|
||||||
version: 1.0.0
|
version: 1.0.0.20120316
|
||||||
license: BSD3
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ module Yesod.Form.Functions
|
|||||||
, aopt
|
, aopt
|
||||||
-- * Run a form
|
-- * Run a form
|
||||||
, runFormPost
|
, runFormPost
|
||||||
, runFormPostNoNonce
|
, runFormPostNoToken
|
||||||
, runFormGet
|
, runFormGet
|
||||||
-- * Generate a blank form
|
-- * Generate a blank form
|
||||||
, generateFormPost
|
, generateFormPost
|
||||||
@ -48,7 +48,7 @@ import Text.Blaze (Html, toHtml)
|
|||||||
import Yesod.Handler (GHandler, getRequest, runRequestBody, newIdent, getYesod)
|
import Yesod.Handler (GHandler, getRequest, runRequestBody, newIdent, getYesod)
|
||||||
import Yesod.Core (RenderMessage, SomeMessage (..))
|
import Yesod.Core (RenderMessage, SomeMessage (..))
|
||||||
import Yesod.Widget (GWidget, whamlet)
|
import Yesod.Widget (GWidget, whamlet)
|
||||||
import Yesod.Request (reqNonce, reqWaiRequest, reqGetParams, languages, FileInfo (..))
|
import Yesod.Request (reqToken, reqWaiRequest, reqGetParams, languages, FileInfo (..))
|
||||||
import Network.Wai (requestMethod)
|
import Network.Wai (requestMethod)
|
||||||
import Text.Hamlet (shamlet)
|
import Text.Hamlet (shamlet)
|
||||||
import Data.Monoid (mempty)
|
import Data.Monoid (mempty)
|
||||||
@ -178,18 +178,18 @@ postHelper :: RenderMessage master FormMessage
|
|||||||
-> GHandler sub master ((FormResult a, xml), Enctype)
|
-> GHandler sub master ((FormResult a, xml), Enctype)
|
||||||
postHelper form env = do
|
postHelper form env = do
|
||||||
req <- getRequest
|
req <- getRequest
|
||||||
let nonceKey = "_nonce"
|
let tokenKey = "_token"
|
||||||
let nonce =
|
let token =
|
||||||
case reqNonce req of
|
case reqToken req of
|
||||||
Nothing -> mempty
|
Nothing -> mempty
|
||||||
Just n -> [shamlet|<input type=hidden name=#{nonceKey} value=#{n}>|]
|
Just n -> [shamlet|<input type=hidden name=#{tokenKey} value=#{n}>|]
|
||||||
m <- getYesod
|
m <- getYesod
|
||||||
langs <- languages
|
langs <- languages
|
||||||
((res, xml), enctype) <- runFormGeneric (form nonce) m langs env
|
((res, xml), enctype) <- runFormGeneric (form token) m langs env
|
||||||
let res' =
|
let res' =
|
||||||
case (res, env) of
|
case (res, env) of
|
||||||
(FormSuccess{}, Just (params, _))
|
(FormSuccess{}, Just (params, _))
|
||||||
| Map.lookup nonceKey params /= fmap return (reqNonce req) ->
|
| Map.lookup tokenKey params /= fmap return (reqToken req) ->
|
||||||
FormFailure [renderMessage m langs MsgCsrfWarning]
|
FormFailure [renderMessage m langs MsgCsrfWarning]
|
||||||
_ -> res
|
_ -> res
|
||||||
return ((res', xml), enctype)
|
return ((res', xml), enctype)
|
||||||
@ -216,8 +216,8 @@ postEnv = do
|
|||||||
where
|
where
|
||||||
notEmpty = not . L.null . fileContent
|
notEmpty = not . L.null . fileContent
|
||||||
|
|
||||||
runFormPostNoNonce :: (Html -> MForm sub master (FormResult a, xml)) -> GHandler sub master ((FormResult a, xml), Enctype)
|
runFormPostNoToken :: (Html -> MForm sub master (FormResult a, xml)) -> GHandler sub master ((FormResult a, xml), Enctype)
|
||||||
runFormPostNoNonce form = do
|
runFormPostNoToken form = do
|
||||||
langs <- languages
|
langs <- languages
|
||||||
m <- getYesod
|
m <- getYesod
|
||||||
env <- postEnv
|
env <- postEnv
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: yesod-form
|
name: yesod-form
|
||||||
version: 1.0.0
|
version: 1.0.0.20120316
|
||||||
license: BSD3
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user