{-| Module : Servant.Server.Auth.Token.SingleUse Description : Specific functions to work with single usage codes. Copyright : (c) Anton Gushcha, 2016 License : MIT Maintainer : ncrashed@gmail.com Stability : experimental Portability : Portable -} module Servant.Server.Auth.Token.SingleUse( makeSingleUseExpire , registerSingleUseCode , invalideSingleUseCode , validateSingleUseCode , generateSingleUsedCodes ) where import Control.Monad import Control.Monad.IO.Class import Data.Time import Database.Persist.Sql import Servant.API.Auth.Token import Servant.Server.Auth.Token.Common import Servant.Server.Auth.Token.Model -- | Calculate expire date for single usage code makeSingleUseExpire :: MonadIO m => NominalDiffTime -- ^ Duration of code -> m UTCTime -- ^ Time when the code expires makeSingleUseExpire dt = do t <- liftIO getCurrentTime return $ dt `addUTCTime` t -- | Register single use code in DB registerSingleUseCode :: MonadIO m => UserImplId -- ^ Id of user -> SingleUseCode -- ^ Single usage code -> Maybe UTCTime -- ^ Time when the code expires, 'Nothing' is never expiring code -> SqlPersistT m () registerSingleUseCode uid code expire = void $ insert $ UserSingleUseCode code uid expire Nothing -- | Marks single use code that it cannot be used again invalideSingleUseCode :: MonadIO m => UserSingleUseCodeId -- ^ Id of code -> SqlPersistT m () invalideSingleUseCode i = do t <- liftIO getCurrentTime update i [UserSingleUseCodeUsed =. Just t] -- | Check single use code and return 'True' on success. -- -- On success invalidates single use code. validateSingleUseCode :: MonadIO m => UserImplId -- ^ Id of user -> SingleUseCode -- ^ Single usage code -> SqlPersistT m Bool validateSingleUseCode uid code = do t <- liftIO getCurrentTime mcode <- selectFirst ([ UserSingleUseCodeValue ==. code , UserSingleUseCodeUser ==. uid , UserSingleUseCodeUsed ==. Nothing ] ++ ( [UserSingleUseCodeExpire ==. Nothing] ||. [UserSingleUseCodeExpire >=. Just t] )) [Desc UserSingleUseCodeExpire] whenJust mcode $ invalideSingleUseCode . entityKey return $ maybe False (const True) mcode -- | Generates a set single use codes that doesn't expire. -- -- Note: previous codes without expiration are invalidated. generateSingleUsedCodes :: MonadIO m => UserImplId -- ^ Id of user -> IO SingleUseCode -- ^ Generator of codes -> Word -- Count of codes -> SqlPersistT m [SingleUseCode] generateSingleUsedCodes uid gen n = do t <- liftIO getCurrentTime updateWhere [ UserSingleUseCodeUser ==. uid , UserSingleUseCodeUsed ==. Nothing , UserSingleUseCodeExpire ==. Nothing ] [UserSingleUseCodeUsed =. Just t] replicateM (fromIntegral n) $ do code <- liftIO gen _ <- insert $ UserSingleUseCode code uid Nothing Nothing return code