packages feed

prodapi-userauth-0.1.0.0: src/Prod/UserAuth/Runtime.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE StrictData #-}

module Prod.UserAuth.Runtime (
    Counters (..),
    initRuntime,
    Runtime,
    AugmentSession,
    augmentSession,
    AugmentWhoAmI,
    augmentWhoAmI,
    augmentLoggedInCookieClaims,
    AugmentCookieClaims,
    counters,
    secretstring,
    baseClaimsSet,
    withConn,
    Connection,
    tokenValidityDuration,
    trace,
    traceTransaction,
    traceAttempt,
    traceVerification,
    traceOptionalVerification,
    traceRegistration,
    traceRecoveryRequest,
    traceRecoveryApplication,
    traceAllowed,
    traceDisallowed,
    traceLimited,
    traceJWT,
    traceJWTSigned,
    traceAugmentCookie,
)
where

import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Aeson (Value)
import Data.ByteString (ByteString)
import Data.Text (Text)
import Database.PostgreSQL.Simple (Connection, Only (..), close, connectPostgreSQL, execute, query, rollback, withTransaction)
import Prod.Tracer (Tracer (..))
import Prod.UserAuth.Base (ApplyRecoveryRequest, ErrorMessage, LoggedInCookie, LoginAttempt, LoginResult, Minutes, RecoveryRequest, RecoveryRequestNotification, RecoveryResult, RegistrationRequest, RegistrationResult, SessionData, UserId, WhoAmI)
import Prod.UserAuth.Counters (Counters (..), initCounters)
import Prod.UserAuth.Trace (BackendTrack (..), BehaviourTrack (..), CallbackTrack (..), JwtTrack (..), Track (..))
import Web.JWT

data Runtime info
    = Runtime
    { counters :: !Counters
    , secretstring :: !Text
    , baseClaimsSet :: !JWTClaimsSet
    , connstring :: !ByteString
    , augmentSession :: AugmentSession info
    , augmentWhoAmI :: AugmentWhoAmI info
    , augmentLoggedInCookieClaims :: AugmentCookieClaims
    , tracer :: Tracer IO (Track info)
    }

type AugmentSession info = Connection -> SessionData () -> IO (Maybe info)

type AugmentWhoAmI info = Connection -> WhoAmI UserId -> IO (Maybe info)

type AugmentCookieClaims = UserId -> IO (Either ErrorMessage [(Text, Value)])

trace :: (MonadIO m) => Runtime a -> Track a -> m ()
trace rt v = liftIO $ runTracer (tracer rt) $ v

initRuntime ::
    Text ->
    JWTClaimsSet ->
    ByteString ->
    AugmentSession info ->
    AugmentWhoAmI info ->
    AugmentCookieClaims ->
    Tracer IO (Track info) ->
    IO (Runtime info)
initRuntime skret claims cstring augSession augWhoAmI augCookie tracer =
    Runtime
        <$> initCounters
        <*> pure skret
        <*> pure claims
        <*> pure cstring
        <*> pure augSession
        <*> pure augWhoAmI
        <*> pure augCookie
        <*> pure tracer

withConn :: Runtime b -> (Connection -> IO a) -> IO a
withConn runtime act = do
    conn <- connectPostgreSQL (connstring runtime)
    trace runtime (Backend SQLConnect)
    !ret <- act conn
    close conn
    pure ret

traceTransaction :: Runtime a -> Connection -> IO b -> IO b
traceTransaction runtime conn act =
    withTransaction conn $ do
        trace runtime (Backend SQLTransaction)
        act

traceAttempt :: (MonadIO m) => Runtime a -> LoginAttempt -> LoginResult a -> m ()
traceAttempt runtime l r =
    liftIO $ trace runtime $ Behaviour $ Attempt l r

traceVerification :: (MonadIO m) => Runtime a -> Bool -> m ()
traceVerification runtime bool =
    liftIO $ trace runtime $ Behaviour $ Verification bool

traceOptionalVerification :: (MonadIO m) => Runtime a -> Bool -> m ()
traceOptionalVerification runtime bool =
    liftIO $ trace runtime $ Behaviour $ OptionalVerification bool

traceRegistration :: (MonadIO m) => Runtime a -> RegistrationRequest -> RegistrationResult a -> m ()
traceRegistration runtime req res =
    liftIO $ trace runtime $ Behaviour $ Registration req res

traceRecoveryRequest :: (MonadIO m) => Runtime a -> RecoveryRequest -> RecoveryRequestNotification -> m ()
traceRecoveryRequest runtime req res =
    liftIO $ trace runtime $ Behaviour $ Recovery req res

traceRecoveryApplication :: (MonadIO m) => Runtime a -> ApplyRecoveryRequest -> RecoveryResult -> m ()
traceRecoveryApplication runtime req res =
    liftIO $ trace runtime $ Behaviour $ ApplyRecovery req res

traceAllowed, traceDisallowed, traceLimited :: (MonadIO m) => Runtime a -> m ()
traceAllowed runtime =
    liftIO $ trace runtime $ Bearer $ Allowed (Just True)
traceDisallowed runtime =
    liftIO $ trace runtime $ Bearer $ Allowed (Just False)
traceLimited runtime =
    liftIO $ trace runtime $ Bearer $ Allowed Nothing

traceJWT :: (MonadIO m) => (Runtime a) -> Maybe (JWT (VerifiedJWT)) -> m ()
traceJWT runtime jwt =
    liftIO $ trace runtime $ Bearer $ Extracted jwt

traceJWTSigned :: (MonadIO m) => (Runtime a) -> UserId -> [(Text, Value)] -> m ()
traceJWTSigned runtime uid claims =
    liftIO $ trace runtime $ Bearer $ Signed uid claims

traceAugmentCookie :: (MonadIO m) => (Runtime a) -> Either ErrorMessage LoggedInCookie -> m ()
traceAugmentCookie runtime c =
    liftIO $ trace runtime $ Callbacks $ AugmentCookie c

tokenValidityDuration :: Minutes
tokenValidityDuration = 30