use std::sync::Arc;
use arcly_http_core::resilience::distributed_rate_limit::{RateDecision, RateLimitBackend};
use crate::error::{IdentityError, Result};
pub struct LockoutPolicy {
backend: Arc<dyn RateLimitBackend>,
max_attempts: u32,
window_secs: u32,
fail_closed: bool,
}
impl LockoutPolicy {
pub fn new(backend: Arc<dyn RateLimitBackend>, max_attempts: u32, window_secs: u32) -> Self {
Self {
backend,
max_attempts,
window_secs,
fail_closed: true,
}
}
pub fn fail_open(mut self) -> Self {
self.fail_closed = false;
self
}
pub async fn check(&self, principal: &str) -> Result<()> {
let key = format!("lockout::{principal}");
match self
.backend
.hit(&key, self.max_attempts, self.window_secs)
.await
{
RateDecision::Allow { .. } => Ok(()),
RateDecision::Deny { .. } => Err(IdentityError::AccountLocked),
RateDecision::Unavailable => {
if self.fail_closed {
Err(IdentityError::Unavailable)
} else {
Ok(())
}
}
}
}
}