Fix acid-state's implementation of {insert,replace}Session.

This commit is contained in:
Felipe Lessa 2015-05-28 01:28:07 -03:00
parent 82b7ebdca4
commit 8746b5eb4f

View File

@ -6,6 +6,7 @@ module Web.ServerSession.Backend.Acid.Internal
, ServerSessionAcidState(..) , ServerSessionAcidState(..)
, emptyState , emptyState
, removeSessionFromAuthId , removeSessionFromAuthId
, insertSessionForAuthId
, nothingfy , nothingfy
, getSession , getSession
@ -23,13 +24,13 @@ module Web.ServerSession.Backend.Acid.Internal
, AcidStorage(..) , AcidStorage(..)
) where ) where
import Control.Monad (when)
import Control.Monad.Reader (ask) import Control.Monad.Reader (ask)
import Control.Monad.State (get, modify') import Control.Monad.State (get, modify', put)
import Data.Acid (AcidState, Query, Update, makeAcidic, query, update) import Data.Acid (AcidState, Query, Update, makeAcidic, query, update)
import Data.SafeCopy (deriveSafeCopy, base) import Data.SafeCopy (deriveSafeCopy, base)
import Data.Typeable (Typeable) import Data.Typeable (Typeable)
import qualified Control.Exception as E
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import qualified Data.Set as S import qualified Data.Set as S
import qualified Web.ServerSession.Core as SS import qualified Web.ServerSession.Core as SS
@ -77,6 +78,13 @@ removeSessionFromAuthId :: SS.SessionId -> Maybe SS.AuthId -> AuthIdToSessionId
removeSessionFromAuthId sid = maybe id (M.update (nothingfy . S.delete sid)) removeSessionFromAuthId sid = maybe id (M.update (nothingfy . S.delete sid))
-- | Insert the given session ID as being part of the given auth
-- ID. Conceptually the opposite of 'removeSessionFromAuthId'.
-- Does not do anything if no 'AuthId' is provided.
insertSessionForAuthId :: SS.SessionId -> Maybe SS.AuthId -> AuthIdToSessionId -> AuthIdToSessionId
insertSessionForAuthId sid = maybe id (flip (M.insertWith S.union) (S.singleton sid))
-- | Change a 'S.Set' to 'Nothing' if it's 'S.null'. -- | Change a 'S.Set' to 'Nothing' if it's 'S.null'.
nothingfy :: S.Set a -> Maybe (S.Set a) nothingfy :: S.Set a -> Maybe (S.Set a)
nothingfy s = if S.null s then Nothing else Just s nothingfy s = if S.null s then Nothing else Just s
@ -115,8 +123,10 @@ deleteAllSessionsOfAuthId authId = do
-- | Insert a new session. -- | Insert a new session.
insertSession :: SS.Session -> Update ServerSessionAcidState () insertSession :: SS.Session -> Update ServerSessionAcidState ()
insertSession session = do insertSession session = do
let insertSess = M.insert sid session let insertSess s =
insertAuth = maybe id (flip (M.insertWith S.union) (S.singleton sid)) (SS.sessionAuthId session) let (mold, new) = M.insertLookupWithKey (\_ v _ -> v) sid session s
in maybe new (\old -> E.throw $ SS.SessionAlreadyExists old session) mold
insertAuth = insertSessionForAuthId sid (SS.sessionAuthId session)
sid = SS.sessionKey session sid = SS.sessionKey session
modify' $ \state -> modify' $ \state ->
ServerSessionAcidState ServerSessionAcidState
@ -127,16 +137,22 @@ insertSession session = do
-- | Replace the contents of a session. -- | Replace the contents of a session.
replaceSession :: SS.Session -> Update ServerSessionAcidState () replaceSession :: SS.Session -> Update ServerSessionAcidState ()
replaceSession session = do replaceSession session = do
-- Remove the old auth ID from the map if it has changed. -- Check that the old session exists while replacing it.
let sid = SS.sessionKey session ServerSessionAcidState sits aits <- get
oldSession <- M.lookup sid . sessionIdToSession <$> get let (moldSession, sits') = M.updateLookupWithKey (\_ _ -> Just session) sid sits
let oldAuthId = SS.sessionAuthId =<< oldSession sid = SS.sessionKey session
when (oldAuthId /= SS.sessionAuthId session) $ case moldSession of
modify' $ \state -> state Nothing -> E.throw $ SS.SessionDoesNotExist session
{ authIdToSessionId = removeSessionFromAuthId sid oldAuthId $ authIdToSessionId state Just oldSession -> do
} -- Remove/insert the old auth ID from the map if needed.
-- Otherwise the operation is the same as inserting. let modAits | oldAuthId == newAuthId = id
insertSession session | otherwise = insertSessionForAuthId sid newAuthId
. removeSessionFromAuthId sid oldAuthId
where oldAuthId = SS.sessionAuthId oldSession
newAuthId = SS.sessionAuthId session
aits' = modAits aits
-- Put modified state in place
put (ServerSessionAcidState sits' aits')
---------------------------------------------------------------------- ----------------------------------------------------------------------