Skip to main content

rusmes_auth/
security.rs

1//! Authentication security module
2//!
3//! Provides comprehensive security features for authentication:
4//! - Brute force protection with account lockout
5//! - Password strength validation (length, complexity, entropy)
6//! - Audit logging for authentication events
7//! - Rate limiting on authentication attempts per IP
8//! - Configurable lockout policies
9
10use anyhow::{anyhow, Result};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::net::IpAddr;
14use std::sync::Arc;
15use std::time::{SystemTime, UNIX_EPOCH};
16use tokio::sync::RwLock;
17
18// ============================================================================
19// Configuration Structures
20// ============================================================================
21
22/// Configuration for brute force protection
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct BruteForceConfig {
25    /// Maximum failed attempts before lockout
26    pub max_attempts: u32,
27    /// Lockout duration in seconds
28    pub lockout_duration_secs: u64,
29    /// Time window for counting failed attempts (in seconds)
30    pub attempt_window_secs: u64,
31    /// Whether to enable progressive lockout (increasing duration)
32    pub progressive_lockout: bool,
33}
34
35impl Default for BruteForceConfig {
36    fn default() -> Self {
37        Self {
38            max_attempts: 5,
39            lockout_duration_secs: 900, // 15 minutes
40            attempt_window_secs: 300,   // 5 minutes
41            progressive_lockout: true,
42        }
43    }
44}
45
46/// Configuration for password strength validation
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct PasswordStrengthConfig {
49    /// Minimum password length
50    pub min_length: usize,
51    /// Require at least one uppercase letter
52    pub require_uppercase: bool,
53    /// Require at least one lowercase letter
54    pub require_lowercase: bool,
55    /// Require at least one digit
56    pub require_digit: bool,
57    /// Require at least one special character
58    pub require_special: bool,
59    /// Minimum entropy bits (Shannon entropy)
60    pub min_entropy_bits: f64,
61    /// List of common/banned passwords
62    pub banned_passwords: Vec<String>,
63}
64
65impl Default for PasswordStrengthConfig {
66    fn default() -> Self {
67        Self {
68            min_length: 8,
69            require_uppercase: true,
70            require_lowercase: true,
71            require_digit: true,
72            require_special: true,
73            min_entropy_bits: 3.0,
74            banned_passwords: vec![
75                "password".to_string(),
76                "123456".to_string(),
77                "qwerty".to_string(),
78                "admin".to_string(),
79                "letmein".to_string(),
80            ],
81        }
82    }
83}
84
85/// Configuration for rate limiting
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct RateLimitConfig {
88    /// Maximum requests per time window
89    pub max_requests: u32,
90    /// Time window in seconds
91    pub window_secs: u64,
92}
93
94impl Default for RateLimitConfig {
95    fn default() -> Self {
96        Self {
97            max_requests: 10,
98            window_secs: 60, // 1 minute
99        }
100    }
101}
102
103/// Overall security configuration
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct SecurityConfig {
106    /// Brute force protection settings
107    pub brute_force: BruteForceConfig,
108    /// Password strength validation settings
109    pub password_strength: PasswordStrengthConfig,
110    /// Rate limiting settings
111    pub rate_limit: RateLimitConfig,
112    /// Enable audit logging
113    pub enable_audit_log: bool,
114}
115
116impl Default for SecurityConfig {
117    fn default() -> Self {
118        Self {
119            brute_force: BruteForceConfig::default(),
120            password_strength: PasswordStrengthConfig::default(),
121            rate_limit: RateLimitConfig::default(),
122            enable_audit_log: true,
123        }
124    }
125}
126
127// ============================================================================
128// Audit Logging
129// ============================================================================
130
131/// Authentication event type
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub enum AuthEvent {
134    /// Successful authentication
135    Success,
136    /// Failed authentication (wrong password)
137    Failure,
138    /// Account locked due to brute force
139    AccountLocked,
140    /// Rate limit exceeded
141    RateLimitExceeded,
142    /// Password changed
143    PasswordChanged,
144    /// User created
145    UserCreated,
146    /// User deleted
147    UserDeleted,
148}
149
150/// Authentication audit log entry
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct AuditLogEntry {
153    /// Timestamp (Unix epoch seconds)
154    pub timestamp: u64,
155    /// Event type
156    pub event: AuthEvent,
157    /// Username involved
158    pub username: String,
159    /// Source IP address
160    pub ip_address: Option<IpAddr>,
161    /// Additional details
162    pub details: Option<String>,
163}
164
165/// Audit logger for authentication events
166pub struct AuditLogger {
167    /// In-memory log entries (for demonstration; production would use external storage)
168    entries: Arc<RwLock<Vec<AuditLogEntry>>>,
169    /// Maximum entries to keep in memory
170    max_entries: usize,
171}
172
173impl AuditLogger {
174    /// Create a new audit logger
175    pub fn new(max_entries: usize) -> Self {
176        Self {
177            entries: Arc::new(RwLock::new(Vec::new())),
178            max_entries,
179        }
180    }
181
182    /// Log an authentication event
183    pub async fn log(
184        &self,
185        event: AuthEvent,
186        username: String,
187        ip_address: Option<IpAddr>,
188        details: Option<String>,
189    ) {
190        let timestamp = SystemTime::now()
191            .duration_since(UNIX_EPOCH)
192            .unwrap_or_default()
193            .as_secs();
194
195        let entry = AuditLogEntry {
196            timestamp,
197            event,
198            username,
199            ip_address,
200            details,
201        };
202
203        let mut entries = self.entries.write().await;
204        entries.push(entry);
205
206        // Keep only the most recent entries
207        if entries.len() > self.max_entries {
208            let start = entries.len() - self.max_entries;
209            *entries = entries[start..].to_vec();
210        }
211    }
212
213    /// Get recent audit log entries
214    pub async fn get_recent(&self, count: usize) -> Vec<AuditLogEntry> {
215        let entries = self.entries.read().await;
216        let start = entries.len().saturating_sub(count);
217        entries[start..].to_vec()
218    }
219
220    /// Get audit log entries for a specific username
221    pub async fn get_for_user(&self, username: &str) -> Vec<AuditLogEntry> {
222        let entries = self.entries.read().await;
223        entries
224            .iter()
225            .filter(|e| e.username == username)
226            .cloned()
227            .collect()
228    }
229}
230
231// ============================================================================
232// Brute Force Protection
233// ============================================================================
234
235/// Failed authentication attempt record
236#[derive(Debug, Clone)]
237struct FailedAttempt {
238    /// Timestamp of the attempt
239    timestamp: u64,
240}
241
242/// Account lockout information
243#[derive(Debug, Clone)]
244struct LockoutInfo {
245    /// When the account was locked
246    locked_at: u64,
247    /// Duration of the lockout in seconds
248    duration_secs: u64,
249    /// Number of times this account has been locked
250    lockout_count: u32,
251}
252
253/// Brute force protector with account lockout
254pub struct BruteForceProtector {
255    /// Configuration
256    config: BruteForceConfig,
257    /// Failed attempts by username
258    user_attempts: Arc<RwLock<HashMap<String, Vec<FailedAttempt>>>>,
259    /// Failed attempts by IP address
260    ip_attempts: Arc<RwLock<HashMap<IpAddr, Vec<FailedAttempt>>>>,
261    /// Locked accounts
262    locked_accounts: Arc<RwLock<HashMap<String, LockoutInfo>>>,
263    /// Locked IP addresses
264    locked_ips: Arc<RwLock<HashMap<IpAddr, LockoutInfo>>>,
265}
266
267impl BruteForceProtector {
268    /// Create a new brute force protector
269    pub fn new(config: BruteForceConfig) -> Self {
270        Self {
271            config,
272            user_attempts: Arc::new(RwLock::new(HashMap::new())),
273            ip_attempts: Arc::new(RwLock::new(HashMap::new())),
274            locked_accounts: Arc::new(RwLock::new(HashMap::new())),
275            locked_ips: Arc::new(RwLock::new(HashMap::new())),
276        }
277    }
278
279    /// Get current timestamp
280    fn current_timestamp() -> u64 {
281        SystemTime::now()
282            .duration_since(UNIX_EPOCH)
283            .unwrap_or_default()
284            .as_secs()
285    }
286
287    /// Check if a username is currently locked
288    pub async fn is_user_locked(&self, username: &str) -> bool {
289        let locked = self.locked_accounts.read().await;
290        if let Some(lockout) = locked.get(username) {
291            let now = Self::current_timestamp();
292            let unlock_time = lockout.locked_at + lockout.duration_secs;
293            if now < unlock_time {
294                return true;
295            }
296        }
297        false
298    }
299
300    /// Check if an IP address is currently locked
301    pub async fn is_ip_locked(&self, ip: &IpAddr) -> bool {
302        let locked = self.locked_ips.read().await;
303        if let Some(lockout) = locked.get(ip) {
304            let now = Self::current_timestamp();
305            let unlock_time = lockout.locked_at + lockout.duration_secs;
306            if now < unlock_time {
307                return true;
308            }
309        }
310        false
311    }
312
313    /// Record a failed authentication attempt
314    pub async fn record_failed_attempt(&self, username: &str, ip: Option<IpAddr>) {
315        let now = Self::current_timestamp();
316        let cutoff = now - self.config.attempt_window_secs;
317
318        // Record failed attempt for username
319        {
320            let mut attempts = self.user_attempts.write().await;
321            let user_attempts = attempts
322                .entry(username.to_string())
323                .or_insert_with(Vec::new);
324
325            // Remove old attempts outside the window
326            user_attempts.retain(|a| a.timestamp > cutoff);
327
328            // Add new attempt
329            user_attempts.push(FailedAttempt { timestamp: now });
330
331            // Check if we should lock the account
332            if user_attempts.len() as u32 >= self.config.max_attempts {
333                drop(attempts); // Release the lock before acquiring another
334                self.lock_user(username).await;
335            }
336        }
337
338        // Record failed attempt for IP address
339        if let Some(ip_addr) = ip {
340            let mut attempts = self.ip_attempts.write().await;
341            let ip_attempts = attempts.entry(ip_addr).or_insert_with(Vec::new);
342
343            // Remove old attempts outside the window
344            ip_attempts.retain(|a| a.timestamp > cutoff);
345
346            // Add new attempt
347            ip_attempts.push(FailedAttempt { timestamp: now });
348
349            // Check if we should lock the IP
350            if ip_attempts.len() as u32 >= self.config.max_attempts {
351                drop(attempts); // Release the lock before acquiring another
352                self.lock_ip(&ip_addr).await;
353            }
354        }
355    }
356
357    /// Lock a user account
358    async fn lock_user(&self, username: &str) {
359        let now = Self::current_timestamp();
360        let mut locked = self.locked_accounts.write().await;
361
362        let lockout_count = locked
363            .get(username)
364            .map(|l| l.lockout_count + 1)
365            .unwrap_or(1);
366
367        let duration_secs = if self.config.progressive_lockout {
368            // Progressive lockout: double the duration each time
369            self.config.lockout_duration_secs * (2_u64.pow(lockout_count.saturating_sub(1)))
370        } else {
371            self.config.lockout_duration_secs
372        };
373
374        locked.insert(
375            username.to_string(),
376            LockoutInfo {
377                locked_at: now,
378                duration_secs,
379                lockout_count,
380            },
381        );
382
383        // Clear failed attempts for this user
384        let mut attempts = self.user_attempts.write().await;
385        attempts.remove(username);
386    }
387
388    /// Lock an IP address
389    async fn lock_ip(&self, ip: &IpAddr) {
390        let now = Self::current_timestamp();
391        let mut locked = self.locked_ips.write().await;
392
393        let lockout_count = locked.get(ip).map(|l| l.lockout_count + 1).unwrap_or(1);
394
395        let duration_secs = if self.config.progressive_lockout {
396            // Progressive lockout: double the duration each time
397            self.config.lockout_duration_secs * (2_u64.pow(lockout_count.saturating_sub(1)))
398        } else {
399            self.config.lockout_duration_secs
400        };
401
402        locked.insert(
403            *ip,
404            LockoutInfo {
405                locked_at: now,
406                duration_secs,
407                lockout_count,
408            },
409        );
410
411        // Clear failed attempts for this IP
412        let mut attempts = self.ip_attempts.write().await;
413        attempts.remove(ip);
414    }
415
416    /// Clear failed attempts for a user (e.g., after successful login)
417    pub async fn clear_user_attempts(&self, username: &str) {
418        let mut attempts = self.user_attempts.write().await;
419        attempts.remove(username);
420    }
421
422    /// Clear failed attempts for an IP address
423    pub async fn clear_ip_attempts(&self, ip: &IpAddr) {
424        let mut attempts = self.ip_attempts.write().await;
425        attempts.remove(ip);
426    }
427
428    /// Manually unlock a user account (admin function)
429    pub async fn unlock_user(&self, username: &str) {
430        let mut locked = self.locked_accounts.write().await;
431        locked.remove(username);
432
433        let mut attempts = self.user_attempts.write().await;
434        attempts.remove(username);
435    }
436
437    /// Manually unlock an IP address (admin function)
438    pub async fn unlock_ip(&self, ip: &IpAddr) {
439        let mut locked = self.locked_ips.write().await;
440        locked.remove(ip);
441
442        let mut attempts = self.ip_attempts.write().await;
443        attempts.remove(ip);
444    }
445
446    /// Get time remaining until account unlock (in seconds)
447    pub async fn get_unlock_time(&self, username: &str) -> Option<u64> {
448        let locked = self.locked_accounts.read().await;
449        if let Some(lockout) = locked.get(username) {
450            let now = Self::current_timestamp();
451            let unlock_time = lockout.locked_at + lockout.duration_secs;
452            if now < unlock_time {
453                return Some(unlock_time - now);
454            }
455        }
456        None
457    }
458}
459
460// ============================================================================
461// Password Strength Validation
462// ============================================================================
463
464/// Password strength validation result
465#[derive(Debug, Clone)]
466pub struct PasswordStrengthResult {
467    /// Whether the password is valid
468    pub valid: bool,
469    /// List of validation errors
470    pub errors: Vec<String>,
471    /// Calculated entropy in bits
472    pub entropy_bits: f64,
473}
474
475/// Password strength validator
476pub struct PasswordStrengthValidator {
477    /// Configuration
478    config: PasswordStrengthConfig,
479}
480
481impl PasswordStrengthValidator {
482    /// Create a new password strength validator
483    pub fn new(config: PasswordStrengthConfig) -> Self {
484        Self { config }
485    }
486
487    /// Validate password strength
488    pub fn validate(&self, password: &str) -> PasswordStrengthResult {
489        let mut errors = Vec::new();
490
491        // Check minimum length
492        if password.len() < self.config.min_length {
493            errors.push(format!(
494                "Password must be at least {} characters long",
495                self.config.min_length
496            ));
497        }
498
499        // Check uppercase requirement
500        if self.config.require_uppercase && !password.chars().any(|c| c.is_uppercase()) {
501            errors.push("Password must contain at least one uppercase letter".to_string());
502        }
503
504        // Check lowercase requirement
505        if self.config.require_lowercase && !password.chars().any(|c| c.is_lowercase()) {
506            errors.push("Password must contain at least one lowercase letter".to_string());
507        }
508
509        // Check digit requirement
510        if self.config.require_digit && !password.chars().any(|c| c.is_ascii_digit()) {
511            errors.push("Password must contain at least one digit".to_string());
512        }
513
514        // Check special character requirement
515        if self.config.require_special && !password.chars().any(|c| !c.is_alphanumeric()) {
516            errors.push("Password must contain at least one special character".to_string());
517        }
518
519        // Check banned passwords
520        let password_lower = password.to_lowercase();
521        for banned in &self.config.banned_passwords {
522            if password_lower.contains(&banned.to_lowercase()) {
523                errors.push("Password contains a commonly used word or pattern".to_string());
524                break;
525            }
526        }
527
528        // Calculate Shannon entropy
529        let entropy = self.calculate_entropy(password);
530        if entropy < self.config.min_entropy_bits {
531            errors.push(format!(
532                "Password entropy too low ({:.2} bits, minimum {:.2} bits required)",
533                entropy, self.config.min_entropy_bits
534            ));
535        }
536
537        PasswordStrengthResult {
538            valid: errors.is_empty(),
539            errors,
540            entropy_bits: entropy,
541        }
542    }
543
544    /// Calculate Shannon entropy of a password
545    fn calculate_entropy(&self, password: &str) -> f64 {
546        if password.is_empty() {
547            return 0.0;
548        }
549
550        let mut char_counts: HashMap<char, usize> = HashMap::new();
551        for c in password.chars() {
552            *char_counts.entry(c).or_insert(0) += 1;
553        }
554
555        let len = password.len() as f64;
556        let mut entropy = 0.0;
557
558        for count in char_counts.values() {
559            let probability = *count as f64 / len;
560            entropy -= probability * probability.log2();
561        }
562
563        entropy * len
564    }
565}
566
567// ============================================================================
568// Rate Limiting
569// ============================================================================
570
571/// Rate limiter for authentication attempts
572pub struct RateLimiter {
573    /// Configuration
574    config: RateLimitConfig,
575    /// Request counts by IP address
576    request_counts: Arc<RwLock<HashMap<IpAddr, Vec<u64>>>>,
577}
578
579impl RateLimiter {
580    /// Create a new rate limiter
581    pub fn new(config: RateLimitConfig) -> Self {
582        Self {
583            config,
584            request_counts: Arc::new(RwLock::new(HashMap::new())),
585        }
586    }
587
588    /// Check if a request from an IP address should be allowed
589    pub async fn check_rate_limit(&self, ip: &IpAddr) -> bool {
590        let now = SystemTime::now()
591            .duration_since(UNIX_EPOCH)
592            .unwrap_or_default()
593            .as_secs();
594
595        let cutoff = now - self.config.window_secs;
596
597        let mut counts = self.request_counts.write().await;
598        let ip_counts = counts.entry(*ip).or_insert_with(Vec::new);
599
600        // Remove old requests outside the window
601        ip_counts.retain(|&timestamp| timestamp > cutoff);
602
603        // Check if we're over the limit
604        if ip_counts.len() >= self.config.max_requests as usize {
605            return false;
606        }
607
608        // Record this request
609        ip_counts.push(now);
610        true
611    }
612
613    /// Reset rate limit for an IP address (admin function)
614    pub async fn reset_limit(&self, ip: &IpAddr) {
615        let mut counts = self.request_counts.write().await;
616        counts.remove(ip);
617    }
618}
619
620// ============================================================================
621// Main Security Manager
622// ============================================================================
623
624/// Main authentication security manager
625pub struct AuthSecurity {
626    /// Security configuration
627    config: SecurityConfig,
628    /// Brute force protector
629    brute_force: BruteForceProtector,
630    /// Password strength validator
631    password_validator: PasswordStrengthValidator,
632    /// Rate limiter
633    rate_limiter: RateLimiter,
634    /// Audit logger
635    audit_logger: Option<AuditLogger>,
636}
637
638impl AuthSecurity {
639    /// Create a new authentication security manager
640    pub fn new(config: SecurityConfig) -> Self {
641        let audit_logger = if config.enable_audit_log {
642            Some(AuditLogger::new(10000)) // Keep last 10k entries
643        } else {
644            None
645        };
646
647        Self {
648            brute_force: BruteForceProtector::new(config.brute_force.clone()),
649            password_validator: PasswordStrengthValidator::new(config.password_strength.clone()),
650            rate_limiter: RateLimiter::new(config.rate_limit.clone()),
651            audit_logger,
652            config,
653        }
654    }
655
656    /// Get the brute force protector
657    pub fn brute_force(&self) -> &BruteForceProtector {
658        &self.brute_force
659    }
660
661    /// Check if authentication attempt should be allowed
662    ///
663    /// Returns Ok(()) if allowed, Err with reason if not
664    pub async fn check_auth_attempt(&self, username: &str, ip: Option<IpAddr>) -> Result<()> {
665        // Check rate limit
666        if let Some(ip_addr) = ip {
667            if !self.rate_limiter.check_rate_limit(&ip_addr).await {
668                if let Some(logger) = &self.audit_logger {
669                    logger
670                        .log(
671                            AuthEvent::RateLimitExceeded,
672                            username.to_string(),
673                            Some(ip_addr),
674                            None,
675                        )
676                        .await;
677                }
678                return Err(anyhow!("Rate limit exceeded for IP address"));
679            }
680
681            // Check IP lockout
682            if self.brute_force.is_ip_locked(&ip_addr).await {
683                if let Some(logger) = &self.audit_logger {
684                    logger
685                        .log(
686                            AuthEvent::AccountLocked,
687                            username.to_string(),
688                            Some(ip_addr),
689                            Some("IP address locked".to_string()),
690                        )
691                        .await;
692                }
693                return Err(anyhow!("IP address is temporarily locked"));
694            }
695        }
696
697        // Check user account lockout
698        if self.brute_force.is_user_locked(username).await {
699            if let Some(remaining) = self.brute_force.get_unlock_time(username).await {
700                if let Some(logger) = &self.audit_logger {
701                    logger
702                        .log(
703                            AuthEvent::AccountLocked,
704                            username.to_string(),
705                            ip,
706                            Some(format!("Account locked for {} seconds", remaining)),
707                        )
708                        .await;
709                }
710                return Err(anyhow!(
711                    "Account is temporarily locked. Try again in {} seconds",
712                    remaining
713                ));
714            }
715        }
716
717        Ok(())
718    }
719
720    /// Record successful authentication
721    pub async fn record_auth_success(&self, username: &str, ip: Option<IpAddr>) {
722        // Clear failed attempts
723        self.brute_force.clear_user_attempts(username).await;
724        if let Some(ip_addr) = ip {
725            self.brute_force.clear_ip_attempts(&ip_addr).await;
726        }
727
728        // Log success
729        if let Some(logger) = &self.audit_logger {
730            logger
731                .log(AuthEvent::Success, username.to_string(), ip, None)
732                .await;
733        }
734    }
735
736    /// Record failed authentication
737    pub async fn record_auth_failure(&self, username: &str, ip: Option<IpAddr>) {
738        // Record failed attempt
739        self.brute_force.record_failed_attempt(username, ip).await;
740
741        // Log failure
742        if let Some(logger) = &self.audit_logger {
743            logger
744                .log(AuthEvent::Failure, username.to_string(), ip, None)
745                .await;
746        }
747    }
748
749    /// Validate password strength
750    pub fn validate_password(&self, password: &str) -> PasswordStrengthResult {
751        self.password_validator.validate(password)
752    }
753
754    /// Validate password strength and return Result
755    pub fn check_password_strength(&self, password: &str) -> Result<()> {
756        let result = self.password_validator.validate(password);
757        if result.valid {
758            Ok(())
759        } else {
760            Err(anyhow!(
761                "Password strength validation failed: {}",
762                result.errors.join(", ")
763            ))
764        }
765    }
766
767    /// Log a password change event
768    pub async fn log_password_change(&self, username: &str, ip: Option<IpAddr>) {
769        if let Some(logger) = &self.audit_logger {
770            logger
771                .log(AuthEvent::PasswordChanged, username.to_string(), ip, None)
772                .await;
773        }
774    }
775
776    /// Log a user creation event
777    pub async fn log_user_created(&self, username: &str, ip: Option<IpAddr>) {
778        if let Some(logger) = &self.audit_logger {
779            logger
780                .log(AuthEvent::UserCreated, username.to_string(), ip, None)
781                .await;
782        }
783    }
784
785    /// Log a user deletion event
786    pub async fn log_user_deleted(&self, username: &str, ip: Option<IpAddr>) {
787        if let Some(logger) = &self.audit_logger {
788            logger
789                .log(AuthEvent::UserDeleted, username.to_string(), ip, None)
790                .await;
791        }
792    }
793
794    /// Get audit log entries (admin function)
795    pub async fn get_audit_log(&self, count: usize) -> Option<Vec<AuditLogEntry>> {
796        if let Some(logger) = &self.audit_logger {
797            Some(logger.get_recent(count).await)
798        } else {
799            None
800        }
801    }
802
803    /// Get audit log for specific user (admin function)
804    pub async fn get_user_audit_log(&self, username: &str) -> Option<Vec<AuditLogEntry>> {
805        if let Some(logger) = &self.audit_logger {
806            Some(logger.get_for_user(username).await)
807        } else {
808            None
809        }
810    }
811
812    /// Manually unlock a user account (admin function)
813    pub async fn unlock_user(&self, username: &str) {
814        self.brute_force.unlock_user(username).await;
815    }
816
817    /// Manually unlock an IP address (admin function)
818    pub async fn unlock_ip(&self, ip: &IpAddr) {
819        self.brute_force.unlock_ip(ip).await;
820    }
821
822    /// Reset rate limit for an IP address (admin function)
823    pub async fn reset_rate_limit(&self, ip: &IpAddr) {
824        self.rate_limiter.reset_limit(ip).await;
825    }
826
827    /// Get the security configuration
828    pub fn config(&self) -> &SecurityConfig {
829        &self.config
830    }
831}
832
833// ============================================================================
834// Builder Pattern for AuthSecurity
835// ============================================================================
836
837/// Builder for AuthSecurity with fluent API
838pub struct AuthSecurityBuilder {
839    config: SecurityConfig,
840}
841
842impl AuthSecurityBuilder {
843    /// Create a new builder with default configuration
844    pub fn new() -> Self {
845        Self {
846            config: SecurityConfig::default(),
847        }
848    }
849
850    /// Set brute force configuration
851    pub fn brute_force_config(mut self, config: BruteForceConfig) -> Self {
852        self.config.brute_force = config;
853        self
854    }
855
856    /// Set password strength configuration
857    pub fn password_strength_config(mut self, config: PasswordStrengthConfig) -> Self {
858        self.config.password_strength = config;
859        self
860    }
861
862    /// Set rate limit configuration
863    pub fn rate_limit_config(mut self, config: RateLimitConfig) -> Self {
864        self.config.rate_limit = config;
865        self
866    }
867
868    /// Enable or disable audit logging
869    pub fn enable_audit_log(mut self, enable: bool) -> Self {
870        self.config.enable_audit_log = enable;
871        self
872    }
873
874    /// Build the AuthSecurity instance
875    pub fn build(self) -> AuthSecurity {
876        AuthSecurity::new(self.config)
877    }
878}
879
880impl Default for AuthSecurityBuilder {
881    fn default() -> Self {
882        Self::new()
883    }
884}