Set the cookie name on State instead of leaving to frontends.
This commit is contained in:
parent
d462d61b32
commit
7ab8500d22
@ -10,9 +10,9 @@ import Control.Monad (guard)
|
|||||||
import Control.Monad.IO.Class (MonadIO)
|
import Control.Monad.IO.Class (MonadIO)
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.Default (def)
|
import Data.Default (def)
|
||||||
import Web.Cookie (parseCookies, SetCookie(..))
|
|
||||||
import Web.PathPieces (toPathPiece)
|
import Web.PathPieces (toPathPiece)
|
||||||
import Web.ServerSession.Core
|
import Web.ServerSession.Core
|
||||||
|
import Web.ServerSession.Core.Internal (cookieName)
|
||||||
import Yesod.Core (MonadHandler)
|
import Yesod.Core (MonadHandler)
|
||||||
import Yesod.Core.Handler (setSessionBS)
|
import Yesod.Core.Handler (setSessionBS)
|
||||||
import Yesod.Core.Types (Header(AddCookie), SessionBackend(..))
|
import Yesod.Core.Types (Header(AddCookie), SessionBackend(..))
|
||||||
@ -20,6 +20,7 @@ import Yesod.Core.Types (Header(AddCookie), SessionBackend(..))
|
|||||||
import qualified Data.ByteString.Char8 as B8
|
import qualified Data.ByteString.Char8 as B8
|
||||||
import qualified Data.Text.Encoding as TE
|
import qualified Data.Text.Encoding as TE
|
||||||
import qualified Network.Wai as W
|
import qualified Network.Wai as W
|
||||||
|
import qualified Web.Cookie as C
|
||||||
|
|
||||||
|
|
||||||
-- | Construct the server-side session backend using
|
-- | Construct the server-side session backend using
|
||||||
@ -34,51 +35,60 @@ import qualified Network.Wai as W
|
|||||||
--
|
--
|
||||||
-- instance Yesod App where
|
-- instance Yesod App where
|
||||||
-- ...
|
-- ...
|
||||||
-- makeSessionBackend = simpleBackend . SqlStorage . appConnPool
|
-- makeSessionBackend = simpleBackend id . SqlStorage . appConnPool
|
||||||
-- -- Do not forget to add migration code to your Application.hs!
|
-- -- Do not forget to add migration code to your Application.hs!
|
||||||
-- -- Please check serversession-backend-persistent's documentation.
|
-- -- Please check serversession-backend-persistent's documentation.
|
||||||
-- ...
|
-- ...
|
||||||
-- @
|
-- @
|
||||||
|
--
|
||||||
|
-- For example, if you wanted to disable the idle timeout and decrease the
|
||||||
|
-- absolute timeout to one day, you could change that line to:
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- makeSessionBackend = simpleBackend opts . SqlStorage . appConnPool
|
||||||
|
-- where opts = setIdleTimeout Nothing
|
||||||
|
-- . setAbsoluteTimeout (Just $ 60*60*24)
|
||||||
|
-- @
|
||||||
simpleBackend
|
simpleBackend
|
||||||
:: (MonadIO m, Storage s)
|
:: (MonadIO m, Storage s)
|
||||||
=> s -- ^ Storage backend.
|
=> (State s -> State s) -- ^ Set any options on the @serversession@ state.
|
||||||
|
-> s -- ^ Storage backend.
|
||||||
-> m (Maybe SessionBackend) -- ^ Yesod session backend (always @Just@).
|
-> m (Maybe SessionBackend) -- ^ Yesod session backend (always @Just@).
|
||||||
simpleBackend s = do
|
simpleBackend opts s =
|
||||||
state <- createState s
|
return . Just . backend . opts =<< createState s
|
||||||
let cookieName = "JSESSIONID" -- LOL :)
|
|
||||||
return $ Just $ backend state cookieName
|
|
||||||
|
|
||||||
|
|
||||||
-- | Construct the server-side session backend using the given
|
-- | Construct the server-side session backend using the given
|
||||||
-- state and cookie name.
|
-- state.
|
||||||
backend
|
backend
|
||||||
:: Storage s
|
:: Storage s
|
||||||
=> State s -- ^ @serversession@ state, incl. storage backend.
|
=> State s -- ^ @serversession@ state, incl. storage backend.
|
||||||
-> ByteString -- ^ Cookie name.
|
|
||||||
-> SessionBackend -- ^ Yesod session backend.
|
-> SessionBackend -- ^ Yesod session backend.
|
||||||
backend state cookieName =
|
backend state =
|
||||||
SessionBackend {
|
SessionBackend {
|
||||||
sbLoadSession = \req -> do
|
sbLoadSession = \req -> do
|
||||||
let rawSessionId = findSessionId cookieName req
|
let rawSessionId = findSessionId cookieNameBS req
|
||||||
(sessionMap, saveSessionToken) <- loadSession state rawSessionId
|
(sessionMap, saveSessionToken) <- loadSession state rawSessionId
|
||||||
let save =
|
let save =
|
||||||
fmap ((:[]) . createCookie cookieName) .
|
fmap ((:[]) . createCookie cookieNameBS) .
|
||||||
saveSession state saveSessionToken
|
saveSession state saveSessionToken
|
||||||
return (sessionMap, save)
|
return (sessionMap, save)
|
||||||
}
|
}
|
||||||
|
where
|
||||||
|
cookieNameBS = TE.encodeUtf8 $ cookieName state
|
||||||
|
|
||||||
|
|
||||||
-- | Create a cookie for the given session ID.
|
-- | Create a cookie for the given session ID.
|
||||||
createCookie :: ByteString -> SessionId -> Header
|
createCookie :: ByteString -> SessionId -> Header
|
||||||
createCookie cookieName key =
|
createCookie cookieNameBS key =
|
||||||
-- Generate a cookie with the final session ID.
|
-- Generate a cookie with the final session ID.
|
||||||
AddCookie def
|
AddCookie def
|
||||||
{ setCookieName = cookieName
|
{ C.setCookieName = cookieNameBS
|
||||||
, setCookieValue = TE.encodeUtf8 $ toPathPiece key
|
, C.setCookieValue = TE.encodeUtf8 $ toPathPiece key
|
||||||
, setCookiePath = Just "/"
|
, C.setCookiePath = Just "/"
|
||||||
, setCookieExpires = Just undefined
|
, C.setCookieExpires = Just undefined
|
||||||
, setCookieDomain = Nothing
|
, C.setCookieDomain = Nothing
|
||||||
, setCookieHttpOnly = True
|
, C.setCookieHttpOnly = True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -89,11 +99,11 @@ createCookie cookieName key =
|
|||||||
--
|
--
|
||||||
-- * There is more than one cookie with the given name.
|
-- * There is more than one cookie with the given name.
|
||||||
findSessionId :: ByteString -> W.Request -> Maybe ByteString
|
findSessionId :: ByteString -> W.Request -> Maybe ByteString
|
||||||
findSessionId cookieName req = do
|
findSessionId cookieNameBS req = do
|
||||||
[raw] <- return $ do
|
[raw] <- return $ do
|
||||||
("Cookie", header) <- W.requestHeaders req
|
("Cookie", header) <- W.requestHeaders req
|
||||||
(k, v) <- parseCookies header
|
(k, v) <- C.parseCookies header
|
||||||
guard (k == cookieName)
|
guard (k == cookieNameBS)
|
||||||
return v
|
return v
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ module Web.ServerSession.Core
|
|||||||
, SaveSessionToken
|
, SaveSessionToken
|
||||||
, forceInvalidateKey
|
, forceInvalidateKey
|
||||||
-- ** To be re-exported by frontends
|
-- ** To be re-exported by frontends
|
||||||
|
, setCookieName
|
||||||
, ForceInvalidate(..)
|
, ForceInvalidate(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ module Web.ServerSession.Core.Internal
|
|||||||
|
|
||||||
, State(..)
|
, State(..)
|
||||||
, createState
|
, createState
|
||||||
|
, setCookieName
|
||||||
, setAuthKey
|
, setAuthKey
|
||||||
, loadSession
|
, loadSession
|
||||||
, saveSession
|
, saveSession
|
||||||
@ -30,6 +31,7 @@ import Data.ByteString (ByteString)
|
|||||||
import Data.Maybe (isJust)
|
import Data.Maybe (isJust)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Time (UTCTime, getCurrentTime)
|
import Data.Time (UTCTime, getCurrentTime)
|
||||||
|
import Data.Time.Clock (DiffTime, secondsToDiffTime)
|
||||||
import Data.Typeable (Typeable)
|
import Data.Typeable (Typeable)
|
||||||
import Web.PathPieces (PathPiece(..))
|
import Web.PathPieces (PathPiece(..))
|
||||||
|
|
||||||
@ -157,25 +159,45 @@ class MonadIO (TransactionM s) => Storage s where
|
|||||||
--
|
--
|
||||||
-- * A reference to the storage backend.
|
-- * A reference to the storage backend.
|
||||||
--
|
--
|
||||||
|
-- * The name of cookie where the session ID will be saved ('setCookieName').
|
||||||
|
--
|
||||||
-- * Authentication session variable ('setAuthKey').
|
-- * Authentication session variable ('setAuthKey').
|
||||||
--
|
--
|
||||||
|
-- * Idle and absolute timeouts ('setIdleTimeout' and 'setAbsoluteTimeout').
|
||||||
|
--
|
||||||
-- Create a new 'State' using 'createState'.
|
-- Create a new 'State' using 'createState'.
|
||||||
data State s =
|
data State s =
|
||||||
State
|
State
|
||||||
{ generator :: !N.Generator
|
{ generator :: !N.Generator
|
||||||
, storage :: !s
|
, storage :: !s
|
||||||
, authKey :: Text
|
, cookieName :: !Text
|
||||||
|
, authKey :: !Text
|
||||||
|
, idleTimeout :: !(Maybe DiffTime)
|
||||||
|
, absoluteTimeout :: !(Maybe DiffTime)
|
||||||
} deriving (Typeable)
|
} deriving (Typeable)
|
||||||
|
|
||||||
|
|
||||||
-- | Create a new 'State' for the server-side session backend
|
-- | Create a new 'State' for the server-side session backend
|
||||||
-- using the given storage backend.
|
-- using the given storage backend.
|
||||||
createState :: MonadIO m => s -> m (State s)
|
createState :: MonadIO m => s -> m (State s)
|
||||||
createState storage =
|
createState sto = do
|
||||||
State
|
gen <- N.new
|
||||||
<$> N.new
|
return State
|
||||||
<*> return storage
|
{ generator = gen
|
||||||
<*> return "_ID"
|
, storage = sto
|
||||||
|
, cookieName = "JSESSIONID"
|
||||||
|
, authKey = "_ID"
|
||||||
|
, idleTimeout = Just $ secondsToDiffTime $ 60*60*24*7 -- 7 days
|
||||||
|
, absoluteTimeout = Just $ secondsToDiffTime $ 60*60*24*60 -- 60 days
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-- | Set the name of cookie where the session ID will be saved.
|
||||||
|
-- Defaults to \"JSESSIONID\", which is a generic cookie name
|
||||||
|
-- used by many frameworks thus making it harder to fingerprint
|
||||||
|
-- this implementation.
|
||||||
|
setCookieName :: Text -> State s -> State s
|
||||||
|
setCookieName val state = state { cookieName = val }
|
||||||
|
|
||||||
|
|
||||||
-- | Set the name of the session variable that keeps track of the
|
-- | Set the name of the session variable that keeps track of the
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user