fradrive/src/Settings/Cookies.hs

73 lines
2.0 KiB
Haskell

-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Settings.Cookies
( CookieSettings(..)
, HasCookieSettings(..)
, cookieSettingsToSetCookie
) where
import ClassyPrelude
import Web.Cookie
import Web.Cookie.Instances ()
import Utils.PathPiece
import Data.Time.Clock
import Data.Time.Clock.Instances ()
import qualified Data.Text as Text
import qualified Data.HashMap.Strict as HashMap
import qualified Data.CaseInsensitive as CI
import Data.Aeson
import Data.Aeson.TH
import Control.Lens ((&))
import Control.Monad.Fail
data CookieSettings = CookieSettings
{ cookieExpires :: Maybe NominalDiffTime
, cookieSameSite :: Maybe SameSiteOption
, cookieHttpOnly
, cookieSecure :: Maybe Bool
} deriving (Eq, Show, Generic)
deriving anyclass (Hashable)
instance FromJSON SameSiteOption where
parseJSON = withText "SameSiteOption" $ \(CI.mk -> ciT) -> HashMap.lookup ciT options
& maybe (fail . unpack $ "Expected one of: " <> Text.intercalate ", " (map CI.original $ HashMap.keys options)) return
where options = mconcat
[ singletonMap "Lax" sameSiteLax
, singletonMap "Strict" sameSiteStrict
, singletonMap "None" sameSiteNone
]
deriveFromJSON defaultOptions
{ omitNothingFields = True
, fieldLabelModifier = camelToPathPiece' 1
} ''CookieSettings
class HasCookieSettings ident app | app -> ident where
getCookieSettings :: app -> ident -> CookieSettings
instance HasCookieSettings ident (ident -> CookieSettings) where
getCookieSettings = id
cookieSettingsToSetCookie :: MonadIO m => CookieSettings -> m SetCookie
cookieSettingsToSetCookie CookieSettings{..} = do
now <- liftIO getCurrentTime
return def
{ setCookieExpires = addUTCTime <$> cookieExpires <*> pure now
, setCookieSameSite = cookieSameSite
, setCookieHttpOnly = fromMaybe (setCookieHttpOnly def) cookieHttpOnly
, setCookieSecure = fromMaybe (setCookieSecure def) cookieSecure
}