Make session storage configurable
This commit is contained in:
parent
6b4f181b49
commit
b5b27f2b15
@ -27,6 +27,12 @@ module Yesod.Core
|
|||||||
, logWarn
|
, logWarn
|
||||||
, logError
|
, logError
|
||||||
, logOther
|
, logOther
|
||||||
|
-- * Sessions
|
||||||
|
, SessionBackend (..)
|
||||||
|
, defaultClientSessionBackend
|
||||||
|
, clientSessionBackend
|
||||||
|
, saveClientSession
|
||||||
|
, loadClientSession
|
||||||
-- * Misc
|
-- * Misc
|
||||||
, yesodVersion
|
, yesodVersion
|
||||||
, yesodRender
|
, yesodRender
|
||||||
|
|||||||
@ -38,7 +38,6 @@ import Network.Wai.Middleware.Autohead
|
|||||||
|
|
||||||
import Data.ByteString.Lazy.Char8 ()
|
import Data.ByteString.Lazy.Char8 ()
|
||||||
|
|
||||||
import Web.ClientSession
|
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Text.Encoding (decodeUtf8With)
|
import Data.Text.Encoding (decodeUtf8With)
|
||||||
import Data.Text.Encoding.Error (lenientDecode)
|
import Data.Text.Encoding.Error (lenientDecode)
|
||||||
@ -156,20 +155,20 @@ toWaiApp y = gzip (gzipSettings y) . autohead <$> toWaiAppPlain y
|
|||||||
toWaiAppPlain :: ( Yesod master
|
toWaiAppPlain :: ( Yesod master
|
||||||
, YesodDispatch master master
|
, YesodDispatch master master
|
||||||
) => master -> IO W.Application
|
) => master -> IO W.Application
|
||||||
toWaiAppPlain a = toWaiApp' a <$> encryptKey a
|
toWaiAppPlain a = toWaiApp' a <$> makeSessionBackend a
|
||||||
|
|
||||||
|
|
||||||
toWaiApp' :: ( Yesod master
|
toWaiApp' :: ( Yesod master
|
||||||
, YesodDispatch master master
|
, YesodDispatch master master
|
||||||
)
|
)
|
||||||
=> master
|
=> master
|
||||||
-> Maybe Key
|
-> Maybe (SessionBackend master)
|
||||||
-> W.Application
|
-> W.Application
|
||||||
toWaiApp' y key' env =
|
toWaiApp' y sb env =
|
||||||
case cleanPath y $ W.pathInfo env of
|
case cleanPath y $ W.pathInfo env of
|
||||||
Left pieces -> sendRedirect y pieces env
|
Left pieces -> sendRedirect y pieces env
|
||||||
Right pieces ->
|
Right pieces ->
|
||||||
yesodDispatch y y id app404 handler405 method pieces key' env
|
yesodDispatch y y id app404 handler405 method pieces sb env
|
||||||
where
|
where
|
||||||
app404 = yesodRunner notFound y y Nothing id
|
app404 = yesodRunner notFound y y Nothing id
|
||||||
handler405 route = yesodRunner badMethod y y (Just route) id
|
handler405 route = yesodRunner badMethod y y (Just route) id
|
||||||
|
|||||||
@ -772,14 +772,9 @@ handlerToYAR y s toMasterRoute render errorHandler rr murl sessionMap h =
|
|||||||
types = httpAccept $ reqWaiRequest rr
|
types = httpAccept $ reqWaiRequest rr
|
||||||
errorHandler' = localNoCurrent . errorHandler
|
errorHandler' = localNoCurrent . errorHandler
|
||||||
|
|
||||||
type HeaderRenderer = [Header]
|
yarToResponse :: YesodAppResult -> [(CI H.Ascii, H.Ascii)] -> W.Response
|
||||||
-> ContentType
|
yarToResponse (YARWai a) _ = a
|
||||||
-> SessionMap
|
yarToResponse (YARPlain s hs _ c _) extraHeaders =
|
||||||
-> [(CI H.Ascii, H.Ascii)]
|
|
||||||
|
|
||||||
yarToResponse :: HeaderRenderer -> YesodAppResult -> W.Response
|
|
||||||
yarToResponse _ (YARWai a) = a
|
|
||||||
yarToResponse renderHeaders (YARPlain s hs ct c sessionFinal) =
|
|
||||||
case c of
|
case c of
|
||||||
ContentBuilder b mlen ->
|
ContentBuilder b mlen ->
|
||||||
let hs' = maybe finalHeaders finalHeaders' mlen
|
let hs' = maybe finalHeaders finalHeaders' mlen
|
||||||
@ -787,11 +782,10 @@ yarToResponse renderHeaders (YARPlain s hs ct c sessionFinal) =
|
|||||||
ContentFile fp p -> W.ResponseFile s finalHeaders fp p
|
ContentFile fp p -> W.ResponseFile s finalHeaders fp p
|
||||||
ContentSource body -> W.ResponseSource s finalHeaders body
|
ContentSource body -> W.ResponseSource s finalHeaders body
|
||||||
where
|
where
|
||||||
finalHeaders = renderHeaders hs ct sessionFinal
|
finalHeaders = extraHeaders ++ map headerToPair hs
|
||||||
finalHeaders' len = ("Content-Length", S8.pack $ show len)
|
finalHeaders' len = ("Content-Length", S8.pack $ show len)
|
||||||
: finalHeaders
|
: finalHeaders
|
||||||
|
|
||||||
|
|
||||||
httpAccept :: W.Request -> [ContentType]
|
httpAccept :: W.Request -> [ContentType]
|
||||||
httpAccept = parseHttpAccept
|
httpAccept = parseHttpAccept
|
||||||
. fromMaybe mempty
|
. fromMaybe mempty
|
||||||
|
|||||||
@ -25,6 +25,12 @@ module Yesod.Internal.Core
|
|||||||
, formatLogMessage
|
, formatLogMessage
|
||||||
, fileLocationToString
|
, fileLocationToString
|
||||||
, messageLoggerHandler
|
, messageLoggerHandler
|
||||||
|
-- * Sessions
|
||||||
|
, SessionBackend (..)
|
||||||
|
, defaultClientSessionBackend
|
||||||
|
, clientSessionBackend
|
||||||
|
, saveClientSession
|
||||||
|
, loadClientSession
|
||||||
-- * Misc
|
-- * Misc
|
||||||
, yesodVersion
|
, yesodVersion
|
||||||
, yesodRender
|
, yesodRender
|
||||||
@ -37,6 +43,7 @@ import Yesod.Handler hiding (lift, getExpires)
|
|||||||
|
|
||||||
import Yesod.Routes.Class
|
import Yesod.Routes.Class
|
||||||
|
|
||||||
|
import Control.Applicative ((<$>))
|
||||||
import Control.Arrow ((***))
|
import Control.Arrow ((***))
|
||||||
import Control.Monad (forM)
|
import Control.Monad (forM)
|
||||||
import Yesod.Widget
|
import Yesod.Widget
|
||||||
@ -45,7 +52,6 @@ import qualified Network.Wai as W
|
|||||||
import Yesod.Internal
|
import Yesod.Internal
|
||||||
import Yesod.Internal.Session
|
import Yesod.Internal.Session
|
||||||
import Yesod.Internal.Request
|
import Yesod.Internal.Request
|
||||||
import Web.ClientSession (getKey, defaultKeyFile)
|
|
||||||
import qualified Web.ClientSession as CS
|
import qualified Web.ClientSession as CS
|
||||||
import qualified Data.ByteString.Char8 as S8
|
import qualified Data.ByteString.Char8 as S8
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
@ -56,7 +62,7 @@ import Text.Blaze ((!), customAttribute, textTag, toValue, unsafeLazyByteString)
|
|||||||
import qualified Text.Blaze.Html5 as TBH
|
import qualified Text.Blaze.Html5 as TBH
|
||||||
import Data.Text.Lazy.Builder (toLazyText)
|
import Data.Text.Lazy.Builder (toLazyText)
|
||||||
import Data.Text.Lazy.Encoding (encodeUtf8)
|
import Data.Text.Lazy.Encoding (encodeUtf8)
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe, isJust)
|
||||||
import Control.Monad.IO.Class (MonadIO (liftIO))
|
import Control.Monad.IO.Class (MonadIO (liftIO))
|
||||||
import Web.Cookie (parseCookies)
|
import Web.Cookie (parseCookies)
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
@ -106,11 +112,11 @@ class YesodDispatch sub master where
|
|||||||
=> master
|
=> master
|
||||||
-> sub
|
-> sub
|
||||||
-> (Route sub -> Route master)
|
-> (Route sub -> Route master)
|
||||||
-> (Maybe CS.Key -> W.Application) -- ^ 404 handler
|
-> (Maybe (SessionBackend master) -> W.Application) -- ^ 404 handler
|
||||||
-> (Route sub -> Maybe CS.Key -> W.Application) -- ^ 405 handler
|
-> (Route sub -> Maybe (SessionBackend master) -> W.Application) -- ^ 405 handler
|
||||||
-> Text -- ^ request method
|
-> Text -- ^ request method
|
||||||
-> [Text] -- ^ pieces
|
-> [Text] -- ^ pieces
|
||||||
-> Maybe CS.Key
|
-> Maybe (SessionBackend master)
|
||||||
-> W.Application
|
-> W.Application
|
||||||
|
|
||||||
yesodRunner :: Yesod master
|
yesodRunner :: Yesod master
|
||||||
@ -119,7 +125,7 @@ class YesodDispatch sub master where
|
|||||||
-> sub
|
-> sub
|
||||||
-> Maybe (Route sub)
|
-> Maybe (Route sub)
|
||||||
-> (Route sub -> Route master)
|
-> (Route sub -> Route master)
|
||||||
-> Maybe CS.Key
|
-> Maybe (SessionBackend master)
|
||||||
-> W.Application
|
-> W.Application
|
||||||
yesodRunner = defaultYesodRunner
|
yesodRunner = defaultYesodRunner
|
||||||
|
|
||||||
@ -154,16 +160,6 @@ class RenderRoute a => Yesod a where
|
|||||||
approot :: Approot a
|
approot :: Approot a
|
||||||
approot = ApprootRelative
|
approot = ApprootRelative
|
||||||
|
|
||||||
-- | The encryption key to be used for encrypting client sessions.
|
|
||||||
-- Returning 'Nothing' disables sessions.
|
|
||||||
encryptKey :: a -> IO (Maybe CS.Key)
|
|
||||||
encryptKey _ = fmap Just $ getKey defaultKeyFile
|
|
||||||
|
|
||||||
-- | Number of minutes before a client session times out. Defaults to
|
|
||||||
-- 120 (2 hours).
|
|
||||||
clientSessionDuration :: a -> Int
|
|
||||||
clientSessionDuration = const 120
|
|
||||||
|
|
||||||
-- | Output error response pages.
|
-- | Output error response pages.
|
||||||
errorHandler :: ErrorResponse -> GHandler sub a ChooseRep
|
errorHandler :: ErrorResponse -> GHandler sub a ChooseRep
|
||||||
errorHandler = defaultErrorHandler
|
errorHandler = defaultErrorHandler
|
||||||
@ -318,6 +314,25 @@ class RenderRoute a => Yesod a where
|
|||||||
yepnopeJs :: a -> Maybe (Either Text (Route a))
|
yepnopeJs :: a -> Maybe (Either Text (Route a))
|
||||||
yepnopeJs _ = Nothing
|
yepnopeJs _ = Nothing
|
||||||
|
|
||||||
|
-- | Create a session backend
|
||||||
|
makeSessionBackend :: a -> IO (Maybe (SessionBackend a))
|
||||||
|
makeSessionBackend _ = Just <$> defaultClientSessionBackend
|
||||||
|
|
||||||
|
type Session = [(Text, S8.ByteString)]
|
||||||
|
|
||||||
|
data SessionBackend master = SessionBackend
|
||||||
|
{ sbSaveSession :: master
|
||||||
|
-> W.Request
|
||||||
|
-> UTCTime -- ^ The current time
|
||||||
|
-> Session -- ^ The old session (before running handler)
|
||||||
|
-> Session -- ^ The final session
|
||||||
|
-> IO [Header]
|
||||||
|
, sbLoadSession :: master
|
||||||
|
-> W.Request
|
||||||
|
-> UTCTime -- ^ The current time
|
||||||
|
-> IO Session
|
||||||
|
}
|
||||||
|
|
||||||
messageLoggerHandler :: Yesod m
|
messageLoggerHandler :: Yesod m
|
||||||
=> Loc -> LogLevel -> Text -> GHandler s m ()
|
=> Loc -> LogLevel -> Text -> GHandler s m ()
|
||||||
messageLoggerHandler loc level msg = do
|
messageLoggerHandler loc level msg = do
|
||||||
@ -366,7 +381,7 @@ defaultYesodRunner :: Yesod master
|
|||||||
-> sub
|
-> sub
|
||||||
-> Maybe (Route sub)
|
-> Maybe (Route sub)
|
||||||
-> (Route sub -> Route master)
|
-> (Route sub -> Route master)
|
||||||
-> Maybe CS.Key
|
-> Maybe (SessionBackend master)
|
||||||
-> W.Application
|
-> W.Application
|
||||||
defaultYesodRunner _ master _ murl toMaster _ req
|
defaultYesodRunner _ master _ murl toMaster _ req
|
||||||
| maximumContentLength master (fmap toMaster murl) < len =
|
| maximumContentLength master (fmap toMaster murl) < len =
|
||||||
@ -380,20 +395,11 @@ defaultYesodRunner _ master _ murl toMaster _ req
|
|||||||
case reads $ S8.unpack s of
|
case reads $ S8.unpack s of
|
||||||
[] -> Nothing
|
[] -> Nothing
|
||||||
(x, _):_ -> Just x
|
(x, _):_ -> Just x
|
||||||
defaultYesodRunner handler master sub murl toMasterRoute mkey req = do
|
defaultYesodRunner handler master sub murl toMasterRoute msb req = do
|
||||||
now <- {-# SCC "getCurrentTime" #-} liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let getExpires m = {-# SCC "getExpires" #-} fromIntegral (m * 60) `addUTCTime` now
|
session <- liftIO $
|
||||||
let exp' = {-# SCC "exp'" #-} getExpires $ clientSessionDuration master
|
maybe (return []) (\sb -> sbLoadSession sb master req now) msb
|
||||||
--let rh = {-# SCC "rh" #-} takeWhile (/= ':') $ show $ W.remoteHost req
|
rr <- liftIO $ parseWaiRequest req session (isJust msb)
|
||||||
let host = "" -- FIXME if sessionIpAddress master then S8.pack rh else ""
|
|
||||||
let session' = {-# SCC "session'" #-}
|
|
||||||
case mkey of
|
|
||||||
Nothing -> []
|
|
||||||
Just key -> fromMaybe [] $ do
|
|
||||||
raw <- lookup "Cookie" $ W.requestHeaders req
|
|
||||||
val <- lookup sessionName $ parseCookies raw
|
|
||||||
decodeSession key now host val
|
|
||||||
rr <- liftIO $ parseWaiRequest req session' mkey
|
|
||||||
let h = {-# SCC "h" #-} do
|
let h = {-# SCC "h" #-} do
|
||||||
case murl of
|
case murl of
|
||||||
Nothing -> handler
|
Nothing -> handler
|
||||||
@ -411,39 +417,23 @@ defaultYesodRunner handler master sub murl toMasterRoute mkey req = do
|
|||||||
redirect url'
|
redirect url'
|
||||||
Unauthorized s' -> permissionDenied s'
|
Unauthorized s' -> permissionDenied s'
|
||||||
handler
|
handler
|
||||||
let sessionMap = Map.fromList
|
let sessionMap = Map.fromList . filter ((/=) nonceKey . fst) $ session
|
||||||
$ filter (\(x, _) -> x /= nonceKey) session'
|
|
||||||
let ra = resolveApproot master req
|
let ra = resolveApproot master req
|
||||||
yar <- handlerToYAR master sub toMasterRoute (yesodRender master ra) errorHandler rr murl sessionMap h
|
yar <- handlerToYAR master sub toMasterRoute
|
||||||
let mnonce = reqNonce rr
|
(yesodRender master ra) errorHandler rr murl sessionMap h
|
||||||
-- FIXME should we be caching this IV value and reusing it for efficiency?
|
extraHeaders <- case yar of
|
||||||
iv <- {-# SCC "iv" #-} maybe (return $ error "Should not be used") (const $ liftIO CS.randomIV) mkey
|
(YARPlain _ _ ct _ newSess) -> do
|
||||||
return $ yarToResponse (hr iv mnonce getExpires host exp') yar
|
let nsNonce = Map.toList $ maybe
|
||||||
where
|
newSess
|
||||||
hr iv mnonce getExpires host exp' hs ct sm =
|
(\n -> Map.insert nonceKey (TE.encodeUtf8 n) newSess)
|
||||||
hs'''
|
(reqNonce rr)
|
||||||
where
|
sessionHeaders <- liftIO $ maybe
|
||||||
sessionVal =
|
(return [])
|
||||||
case (mkey, mnonce) of
|
(\sb -> sbSaveSession sb master req now session nsNonce)
|
||||||
(Just key, Just nonce)
|
msb
|
||||||
-> encodeSession key iv exp' host
|
return $ ("Content-Type", ct) : map headerToPair sessionHeaders
|
||||||
$ Map.toList
|
_ -> return []
|
||||||
$ Map.insert nonceKey (TE.encodeUtf8 nonce) sm
|
return $ yarToResponse yar extraHeaders
|
||||||
_ -> mempty
|
|
||||||
hs' =
|
|
||||||
case mkey of
|
|
||||||
Nothing -> hs
|
|
||||||
Just _ -> AddCookie def
|
|
||||||
{ setCookieName = sessionName
|
|
||||||
, setCookieValue = sessionVal
|
|
||||||
, setCookiePath = Just (cookiePath master)
|
|
||||||
, setCookieExpires = Just $ getExpires (clientSessionDuration master)
|
|
||||||
, setCookieDomain = Nothing
|
|
||||||
, setCookieHttpOnly = True
|
|
||||||
}
|
|
||||||
: hs
|
|
||||||
hs'' = map headerToPair hs'
|
|
||||||
hs''' = ("Content-Type", ct) : hs''
|
|
||||||
|
|
||||||
data AuthResult = Authorized | AuthenticationRequired | Unauthorized Text
|
data AuthResult = Authorized | AuthenticationRequired | Unauthorized Text
|
||||||
deriving (Eq, Show, Read)
|
deriving (Eq, Show, Read)
|
||||||
@ -672,3 +662,56 @@ resolveApproot master req =
|
|||||||
ApprootStatic t -> t
|
ApprootStatic t -> t
|
||||||
ApprootMaster f -> f master
|
ApprootMaster f -> f master
|
||||||
ApprootRequest f -> f master req
|
ApprootRequest f -> f master req
|
||||||
|
|
||||||
|
defaultClientSessionBackend :: Yesod master => IO (SessionBackend master)
|
||||||
|
defaultClientSessionBackend = do
|
||||||
|
key <- CS.getKey CS.defaultKeyFile
|
||||||
|
let timeout = 120 -- 120 minutes
|
||||||
|
return $ clientSessionBackend key timeout
|
||||||
|
|
||||||
|
clientSessionBackend :: Yesod master
|
||||||
|
=> CS.Key -- ^ The encryption key
|
||||||
|
-> Int -- ^ Inactive session valitity in minutes
|
||||||
|
-> SessionBackend master
|
||||||
|
clientSessionBackend key timeout = SessionBackend
|
||||||
|
{ sbSaveSession = saveClientSession key timeout
|
||||||
|
, sbLoadSession = loadClientSession key
|
||||||
|
}
|
||||||
|
|
||||||
|
loadClientSession :: Yesod master
|
||||||
|
=> CS.Key
|
||||||
|
-> master
|
||||||
|
-> W.Request
|
||||||
|
-> UTCTime
|
||||||
|
-> IO Session
|
||||||
|
loadClientSession key _ req now = return . fromMaybe [] $ do
|
||||||
|
raw <- lookup "Cookie" $ W.requestHeaders req
|
||||||
|
val <- lookup sessionName $ parseCookies raw
|
||||||
|
let host = "" -- fixme, properly lock sessions to client address
|
||||||
|
decodeClientSession key now host val
|
||||||
|
|
||||||
|
saveClientSession :: Yesod master
|
||||||
|
=> CS.Key
|
||||||
|
-> Int
|
||||||
|
-> master
|
||||||
|
-> W.Request
|
||||||
|
-> UTCTime
|
||||||
|
-> Session
|
||||||
|
-> Session
|
||||||
|
-> IO [Header]
|
||||||
|
saveClientSession key timeout master _ now _ sess = do
|
||||||
|
-- fixme should we be caching this?
|
||||||
|
iv <- liftIO $ CS.randomIV
|
||||||
|
return [AddCookie def
|
||||||
|
{ setCookieName = sessionName
|
||||||
|
, setCookieValue = sessionVal iv
|
||||||
|
, setCookiePath = Just (cookiePath master)
|
||||||
|
, setCookieExpires = Just expires
|
||||||
|
, setCookieDomain = Nothing
|
||||||
|
, setCookieHttpOnly = True
|
||||||
|
}]
|
||||||
|
where
|
||||||
|
host = "" -- fixme, properly lock sessions to client address
|
||||||
|
expires = fromIntegral (timeout * 60) `addUTCTime` now
|
||||||
|
sessionVal iv = encodeClientSession key iv expires host sess
|
||||||
|
|
||||||
|
|||||||
@ -42,17 +42,19 @@ data Request = Request
|
|||||||
|
|
||||||
parseWaiRequest :: W.Request
|
parseWaiRequest :: W.Request
|
||||||
-> [(Text, ByteString)] -- ^ session
|
-> [(Text, ByteString)] -- ^ session
|
||||||
-> Maybe a
|
-> Bool
|
||||||
-> IO Request
|
-> IO Request
|
||||||
parseWaiRequest env session' key' = parseWaiRequest' env session' key' <$> newStdGen
|
parseWaiRequest env session' useNonce =
|
||||||
|
parseWaiRequest' env session' useNonce <$> newStdGen
|
||||||
|
|
||||||
parseWaiRequest' :: RandomGen g
|
parseWaiRequest' :: RandomGen g
|
||||||
=> W.Request
|
=> W.Request
|
||||||
-> [(Text, ByteString)] -- ^ session
|
-> [(Text, ByteString)] -- ^ session
|
||||||
-> Maybe a
|
-> Bool
|
||||||
-> g
|
-> g
|
||||||
-> Request
|
-> Request
|
||||||
parseWaiRequest' env session' key' gen = Request gets'' cookies' env langs'' nonce
|
parseWaiRequest' env session' useNonce gen =
|
||||||
|
Request gets'' cookies' env langs'' nonce
|
||||||
where
|
where
|
||||||
gets' = queryToQueryText $ W.queryString env
|
gets' = queryToQueryText $ W.queryString env
|
||||||
gets'' = map (second $ fromMaybe "") gets'
|
gets'' = map (second $ fromMaybe "") gets'
|
||||||
@ -77,10 +79,10 @@ parseWaiRequest' env session' key' gen = Request gets'' cookies' env langs'' non
|
|||||||
-- nonceKey present in the session is ignored). If sessions
|
-- nonceKey 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 nonceKey a new one is
|
||||||
-- generated.
|
-- generated.
|
||||||
nonce = case (key', lookup nonceKey session') of
|
nonce = case (useNonce, lookup nonceKey session') of
|
||||||
(Nothing, _) -> Nothing
|
(False, _) -> Nothing
|
||||||
(_, Just x) -> Just $ decodeUtf8With lenientDecode x
|
(_, Just x) -> Just $ decodeUtf8With lenientDecode x
|
||||||
_ -> Just $ pack $ randomString 10 gen
|
_ -> Just $ pack $ randomString 10 gen
|
||||||
|
|
||||||
addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text]
|
addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text]
|
||||||
addTwoLetters (toAdd, exist) [] =
|
addTwoLetters (toAdd, exist) [] =
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
module Yesod.Internal.Session
|
module Yesod.Internal.Session
|
||||||
( encodeSession
|
( encodeClientSession
|
||||||
, decodeSession
|
, decodeClientSession
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Web.ClientSession as CS
|
import qualified Web.ClientSession as CS
|
||||||
@ -12,21 +12,21 @@ import Data.Text (Text, pack, unpack)
|
|||||||
import Control.Arrow (first)
|
import Control.Arrow (first)
|
||||||
import Control.Applicative ((<$>))
|
import Control.Applicative ((<$>))
|
||||||
|
|
||||||
encodeSession :: CS.Key
|
encodeClientSession :: CS.Key
|
||||||
-> CS.IV
|
-> CS.IV
|
||||||
-> UTCTime -- ^ expire time
|
-> UTCTime -- ^ expire time
|
||||||
-> ByteString -- ^ remote host
|
-> ByteString -- ^ remote host
|
||||||
-> [(Text, ByteString)] -- ^ session
|
-> [(Text, ByteString)] -- ^ session
|
||||||
-> ByteString -- ^ cookie value
|
-> ByteString -- ^ cookie value
|
||||||
encodeSession key iv expire rhost session' =
|
encodeClientSession key iv expire rhost session' =
|
||||||
CS.encrypt key iv $ encode $ SessionCookie expire rhost session'
|
CS.encrypt key iv $ encode $ SessionCookie expire rhost session'
|
||||||
|
|
||||||
decodeSession :: CS.Key
|
decodeClientSession :: CS.Key
|
||||||
-> UTCTime -- ^ current time
|
-> UTCTime -- ^ current time
|
||||||
-> ByteString -- ^ remote host field
|
-> ByteString -- ^ remote host field
|
||||||
-> ByteString -- ^ cookie value
|
-> ByteString -- ^ cookie value
|
||||||
-> Maybe [(Text, ByteString)]
|
-> Maybe [(Text, ByteString)]
|
||||||
decodeSession key now rhost encrypted = do
|
decodeClientSession key now rhost encrypted = do
|
||||||
decrypted <- CS.decrypt key encrypted
|
decrypted <- CS.decrypt key encrypted
|
||||||
SessionCookie expire rhost' session' <-
|
SessionCookie expire rhost' session' <-
|
||||||
either (const Nothing) Just $ decode decrypted
|
either (const Nothing) Just $ decode decrypted
|
||||||
|
|||||||
@ -40,19 +40,19 @@ nonceSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqNonce)"
|
|||||||
|
|
||||||
noDisabledNonce :: Bool
|
noDisabledNonce :: Bool
|
||||||
noDisabledNonce = reqNonce r == Nothing where
|
noDisabledNonce = reqNonce r == Nothing where
|
||||||
r = parseWaiRequest' defaultRequest [] Nothing g
|
r = parseWaiRequest' defaultRequest [] False g
|
||||||
|
|
||||||
ignoreDisabledNonce :: Bool
|
ignoreDisabledNonce :: Bool
|
||||||
ignoreDisabledNonce = reqNonce r == Nothing where
|
ignoreDisabledNonce = reqNonce r == Nothing where
|
||||||
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] Nothing g
|
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] False g
|
||||||
|
|
||||||
useOldNonce :: Bool
|
useOldNonce :: Bool
|
||||||
useOldNonce = reqNonce r == Just "old" where
|
useOldNonce = reqNonce r == Just "old" where
|
||||||
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] (Just undefined) g
|
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] True g
|
||||||
|
|
||||||
generateNonce :: Bool
|
generateNonce :: Bool
|
||||||
generateNonce = reqNonce r /= Nothing where
|
generateNonce = reqNonce r /= Nothing where
|
||||||
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] (Just undefined) g
|
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] True g
|
||||||
|
|
||||||
|
|
||||||
langSpecs :: [Spec]
|
langSpecs :: [Spec]
|
||||||
@ -67,21 +67,21 @@ langSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqLangs)"
|
|||||||
respectAcceptLangs :: Bool
|
respectAcceptLangs :: Bool
|
||||||
respectAcceptLangs = reqLangs r == ["en-US", "es", "en"] where
|
respectAcceptLangs = reqLangs r == ["en-US", "es", "en"] where
|
||||||
r = parseWaiRequest' defaultRequest
|
r = parseWaiRequest' defaultRequest
|
||||||
{ requestHeaders = [("Accept-Language", "en-US, es")] } [] Nothing g
|
{ requestHeaders = [("Accept-Language", "en-US, es")] } [] False g
|
||||||
|
|
||||||
respectSessionLang :: Bool
|
respectSessionLang :: Bool
|
||||||
respectSessionLang = reqLangs r == ["en"] where
|
respectSessionLang = reqLangs r == ["en"] where
|
||||||
r = parseWaiRequest' defaultRequest [("_LANG", "en")] Nothing g
|
r = parseWaiRequest' defaultRequest [("_LANG", "en")] False g
|
||||||
|
|
||||||
respectCookieLang :: Bool
|
respectCookieLang :: Bool
|
||||||
respectCookieLang = reqLangs r == ["en"] where
|
respectCookieLang = reqLangs r == ["en"] where
|
||||||
r = parseWaiRequest' defaultRequest
|
r = parseWaiRequest' defaultRequest
|
||||||
{ requestHeaders = [("Cookie", "_LANG=en")]
|
{ requestHeaders = [("Cookie", "_LANG=en")]
|
||||||
} [] Nothing g
|
} [] False g
|
||||||
|
|
||||||
respectQueryLang :: Bool
|
respectQueryLang :: Bool
|
||||||
respectQueryLang = reqLangs r == ["en-US", "en"] where
|
respectQueryLang = reqLangs r == ["en-US", "en"] where
|
||||||
r = parseWaiRequest' defaultRequest { queryString = [("_LANG", Just "en-US")] } [] Nothing g
|
r = parseWaiRequest' defaultRequest { queryString = [("_LANG", Just "en-US")] } [] False g
|
||||||
|
|
||||||
prioritizeLangs :: Bool
|
prioritizeLangs :: Bool
|
||||||
prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "es"] where
|
prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "es"] where
|
||||||
@ -90,7 +90,7 @@ prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "e
|
|||||||
, ("Cookie", "_LANG=en-COOKIE")
|
, ("Cookie", "_LANG=en-COOKIE")
|
||||||
]
|
]
|
||||||
, queryString = [("_LANG", Just "en-QUERY")]
|
, queryString = [("_LANG", Just "en-QUERY")]
|
||||||
} [("_LANG", "en-SESSION")] Nothing g
|
} [("_LANG", "en-SESSION")] False g
|
||||||
|
|
||||||
|
|
||||||
internalRequestTest :: [Spec]
|
internalRequestTest :: [Spec]
|
||||||
|
|||||||
@ -85,8 +85,11 @@ type Form x = Html -> MForm ~sitearg~ ~sitearg~ (FormResult x, Widget)
|
|||||||
instance Yesod ~sitearg~ where
|
instance Yesod ~sitearg~ where
|
||||||
approot = ApprootMaster $ appRoot . settings
|
approot = ApprootMaster $ appRoot . settings
|
||||||
|
|
||||||
-- Place the session key file in the config folder
|
-- Store session data on the client in encrypted cookies,
|
||||||
encryptKey _ = fmap Just $ getKey "config/client_session_key.aes"
|
-- place the encryption key file in the config folder
|
||||||
|
makeSessionBackend _ = do
|
||||||
|
key <- getKey "config/client_session_key.aes"
|
||||||
|
return . Just $ clientSessionBackend key 120
|
||||||
|
|
||||||
defaultLayout widget = do
|
defaultLayout widget = do
|
||||||
master <- getYesod
|
master <- getYesod
|
||||||
|
|||||||
@ -62,8 +62,11 @@ mkYesodData "~sitearg~" $(parseRoutesFile "config/routes")
|
|||||||
instance Yesod ~sitearg~ where
|
instance Yesod ~sitearg~ where
|
||||||
approot = ApprootMaster $ appRoot . settings
|
approot = ApprootMaster $ appRoot . settings
|
||||||
|
|
||||||
-- Place the session key file in the config folder
|
-- Store session data on the client in encrypted cookies,
|
||||||
encryptKey _ = fmap Just $ getKey "config/client_session_key.aes"
|
-- place the encryption key file in the config folder
|
||||||
|
makeSessionBackend _ = do
|
||||||
|
key <- getKey "config/client_session_key.aes"
|
||||||
|
return . Just $ clientSessionBackend key 120
|
||||||
|
|
||||||
defaultLayout widget = do
|
defaultLayout widget = do
|
||||||
master <- getYesod
|
master <- getYesod
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user