module Network.Hawk.Server.Nonce
( nonceOpts
, nonceOptsReq
) where
import Data.IORef
import Data.Sequence (Seq, (|>))
import qualified Data.Sequence as Q
import Data.HashSet (HashSet)
import qualified Data.HashSet as S
import Data.Time.Clock.POSIX
import Data.Time.Clock (NominalDiffTime)
import Data.Hashable (Hashable)
import Data.Foldable (toList)
import Network.Hawk.Types (Key)
import Network.Hawk.Server (AuthOpts(..), AuthReqOpts(..), def, Nonce, NonceFunc)
nonceOpts :: NominalDiffTime -> IO AuthOpts
nonceOpts skew = do
ref <- newIORef (Q.empty, S.empty)
let nf = makeNonceFunc skew ref
return $ AuthOpts nf skew 0
nonceOptsReq :: NominalDiffTime -> IO AuthReqOpts
nonceOptsReq skew = do
opts <- nonceOpts skew
return $ def { saOpts = opts }
instance Hashable Key
type Store = (Seq (Key, Nonce, POSIXTime), HashSet (Key, Nonce))
makeNonceFunc :: NominalDiffTime -> IORef Store -> NonceFunc
makeNonceFunc skew ref = \k t n -> do
now <- getPOSIXTime
atomicModifyIORef' ref (update now (abs skew) k n t)
update :: POSIXTime -> NominalDiffTime -> Key -> Nonce -> POSIXTime -> Store -> (Store, Bool)
update now skew k n t (q, s) = ((q'', s''), fresh)
where
fresh = (not $ S.member (k, n) s) && t + skew >= now skew
q' | fresh = q |> (k, n, now + skew)
| otherwise = q
s' | fresh = S.insert (k, n) s
| otherwise = s
(dead, q'') = Q.breakl (\(_, _, t) -> t >= now) q'
s'' = S.difference s' (S.fromList [(k, n) | (k, n, t) <- toList dead])