content: {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE DoAndIfThenElse #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE RankNTypes #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  
  module Web.Spock.SessionActions
    ( SessionId,
      sessionRegenerateId,
      getSessionId,
      readSession,
      writeSession,
      modifySession,
      modifySession',
      modifyReadSession,
      mapAllSessions,
      clearAllSessions,
    )
  where
  
  import Web.Spock.Action
  import Web.Spock.Internal.Monad ()
  import Web.Spock.Internal.SessionManager
  import Web.Spock.Internal.Types
  
  -- | Regenerate the users sessionId. This preserves all stored data. Call this prior
  -- to logging in a user to prevent session fixation attacks.
  sessionRegenerateId :: SpockActionCtx ctx conn sess st ()
  sessionRegenerateId =
    runInContext () $
      getSessMgr >>= sm_regenerateSessionId
  
  -- | Get the current users sessionId. Note that this ID should only be
  -- shown to it's owner as otherwise sessions can be hijacked.
  getSessionId :: SpockActionCtx ctx conn sess st SessionId
  getSessionId =
    runInContext () $
      getSessMgr >>= sm_getSessionId
  
  -- | Write to the current session. Note that all data is stored on the server.
  -- The user only reciedes a sessionId to be identified.
  writeSession :: forall sess ctx conn st. sess -> SpockActionCtx ctx conn sess st ()
  writeSession d =
    do
      mgr <- getSessMgr
      runInContext () $ sm_writeSession mgr d
  
  -- | Modify the stored session
  modifySession :: (sess -> sess) -> SpockActionCtx ctx conn sess st ()
  modifySession f =
    modifySession' $ \sess -> (f sess, ())
  
  -- | Modify the stored session and return a value
  modifySession' :: (sess -> (sess, a)) -> SpockActionCtx ctx conn sess st a
  modifySession' f =
    do
      mgr <- getSessMgr
      runInContext () $ sm_modifySession mgr f
  
  -- | Modify the stored session and return the new value after modification
  modifyReadSession :: (sess -> sess) -> SpockActionCtx ctx conn sess st sess
  modifyReadSession f =
    modifySession' $ \sess ->
      let x = f sess
       in (x, x)
  
  -- | Read the stored session
  readSession :: SpockActionCtx ctx conn sess st sess
  readSession =
    runInContext () $
      do
        mgr <- getSessMgr
        sm_readSession mgr
  
  -- | Globally delete all existing sessions. This is useful for example if you want
  -- to require all users to relogin
  clearAllSessions :: SpockActionCtx ctx conn sess st ()
  clearAllSessions =
    do
      mgr <- getSessMgr
      runInContext () $ sm_clearAllSessions mgr
  
  -- | Apply a transformation to all sessions. Be careful with this, as this
  -- may cause many STM transaction retries.
  mapAllSessions :: (forall m. Monad m => sess -> m sess) -> SpockActionCtx ctx conn sess st ()
  mapAllSessions f =
    do
      mgr <- getSessMgr
      runInContext () $ sm_mapSessions mgr f
  