packages feed

shomei-core-0.2.0.0: src/Shomei/Account/Password/Breach/Workflow.hs

-- | EP-3: the effectful breach-policy guard, appended to every password-accepting workflow
-- after the pure 'Shomei.Account.Password.Domain.validatePassword' step. Honors the EP-1 policy flags:
-- no-op when disabled; rejects breached passwords; on an unreachable checker, fails open or
-- closed per 'breachCheckFailClosed'.
module Shomei.Account.Password.Breach.Workflow (enforceBreachPolicy) where

import Effectful (Eff, (:>))
import Effectful.Error.Static (Error, throwError)
import Shomei.Account.Password.Breach.Store (BreachResult (..), PasswordBreachChecker, checkPasswordBreached)
import Shomei.Account.Password.Domain (PasswordPolicy (..), PlainPassword)
import Shomei.Error (AuthError (..), PasswordPolicyViolation (..))

-- | Run the opt-in breach check for a password. A no-op unless @breachCheckEnabled@ is set.
-- A 'Breached' result always rejects; an unreachable checker rejects only under
-- @breachCheckFailClosed@ (the default is fail-open). The 'Error AuthError' effect is supplied
-- by each workflow's enclosing 'runErrorNoCallStack', so the guard is callable from inside the
-- workflow @do@ blocks.
enforceBreachPolicy ::
  (PasswordBreachChecker :> es, Error AuthError :> es) =>
  PasswordPolicy ->
  PlainPassword ->
  Eff es ()
enforceBreachPolicy policy pw
  | not policy.breachCheckEnabled = pure ()
  | otherwise = do
      r <- checkPasswordBreached pw
      case r of
        NotBreached -> pure ()
        Breached -> throwError (WeakPassword PasswordBreached)
        BreachCheckUnavailable ->
          if policy.breachCheckFailClosed
            then throwError (WeakPassword PasswordBreached)
            else pure ()