1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct BruteForceConfig {
25 pub max_attempts: u32,
27 pub lockout_duration_secs: u64,
29 pub attempt_window_secs: u64,
31 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, attempt_window_secs: 300, progressive_lockout: true,
42 }
43 }
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct PasswordStrengthConfig {
49 pub min_length: usize,
51 pub require_uppercase: bool,
53 pub require_lowercase: bool,
55 pub require_digit: bool,
57 pub require_special: bool,
59 pub min_entropy_bits: f64,
61 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#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct RateLimitConfig {
88 pub max_requests: u32,
90 pub window_secs: u64,
92}
93
94impl Default for RateLimitConfig {
95 fn default() -> Self {
96 Self {
97 max_requests: 10,
98 window_secs: 60, }
100 }
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct SecurityConfig {
106 pub brute_force: BruteForceConfig,
108 pub password_strength: PasswordStrengthConfig,
110 pub rate_limit: RateLimitConfig,
112 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#[derive(Debug, Clone, Serialize, Deserialize)]
133pub enum AuthEvent {
134 Success,
136 Failure,
138 AccountLocked,
140 RateLimitExceeded,
142 PasswordChanged,
144 UserCreated,
146 UserDeleted,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct AuditLogEntry {
153 pub timestamp: u64,
155 pub event: AuthEvent,
157 pub username: String,
159 pub ip_address: Option<IpAddr>,
161 pub details: Option<String>,
163}
164
165pub struct AuditLogger {
167 entries: Arc<RwLock<Vec<AuditLogEntry>>>,
169 max_entries: usize,
171}
172
173impl AuditLogger {
174 pub fn new(max_entries: usize) -> Self {
176 Self {
177 entries: Arc::new(RwLock::new(Vec::new())),
178 max_entries,
179 }
180 }
181
182 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 if entries.len() > self.max_entries {
208 let start = entries.len() - self.max_entries;
209 *entries = entries[start..].to_vec();
210 }
211 }
212
213 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 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#[derive(Debug, Clone)]
237struct FailedAttempt {
238 timestamp: u64,
240}
241
242#[derive(Debug, Clone)]
244struct LockoutInfo {
245 locked_at: u64,
247 duration_secs: u64,
249 lockout_count: u32,
251}
252
253pub struct BruteForceProtector {
255 config: BruteForceConfig,
257 user_attempts: Arc<RwLock<HashMap<String, Vec<FailedAttempt>>>>,
259 ip_attempts: Arc<RwLock<HashMap<IpAddr, Vec<FailedAttempt>>>>,
261 locked_accounts: Arc<RwLock<HashMap<String, LockoutInfo>>>,
263 locked_ips: Arc<RwLock<HashMap<IpAddr, LockoutInfo>>>,
265}
266
267impl BruteForceProtector {
268 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 fn current_timestamp() -> u64 {
281 SystemTime::now()
282 .duration_since(UNIX_EPOCH)
283 .unwrap_or_default()
284 .as_secs()
285 }
286
287 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 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 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 {
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 user_attempts.retain(|a| a.timestamp > cutoff);
327
328 user_attempts.push(FailedAttempt { timestamp: now });
330
331 if user_attempts.len() as u32 >= self.config.max_attempts {
333 drop(attempts); self.lock_user(username).await;
335 }
336 }
337
338 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 ip_attempts.retain(|a| a.timestamp > cutoff);
345
346 ip_attempts.push(FailedAttempt { timestamp: now });
348
349 if ip_attempts.len() as u32 >= self.config.max_attempts {
351 drop(attempts); self.lock_ip(&ip_addr).await;
353 }
354 }
355 }
356
357 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 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 let mut attempts = self.user_attempts.write().await;
385 attempts.remove(username);
386 }
387
388 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 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 let mut attempts = self.ip_attempts.write().await;
413 attempts.remove(ip);
414 }
415
416 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 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 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 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 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#[derive(Debug, Clone)]
466pub struct PasswordStrengthResult {
467 pub valid: bool,
469 pub errors: Vec<String>,
471 pub entropy_bits: f64,
473}
474
475pub struct PasswordStrengthValidator {
477 config: PasswordStrengthConfig,
479}
480
481impl PasswordStrengthValidator {
482 pub fn new(config: PasswordStrengthConfig) -> Self {
484 Self { config }
485 }
486
487 pub fn validate(&self, password: &str) -> PasswordStrengthResult {
489 let mut errors = Vec::new();
490
491 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 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 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 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 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 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 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 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
567pub struct RateLimiter {
573 config: RateLimitConfig,
575 request_counts: Arc<RwLock<HashMap<IpAddr, Vec<u64>>>>,
577}
578
579impl RateLimiter {
580 pub fn new(config: RateLimitConfig) -> Self {
582 Self {
583 config,
584 request_counts: Arc::new(RwLock::new(HashMap::new())),
585 }
586 }
587
588 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 ip_counts.retain(|×tamp| timestamp > cutoff);
602
603 if ip_counts.len() >= self.config.max_requests as usize {
605 return false;
606 }
607
608 ip_counts.push(now);
610 true
611 }
612
613 pub async fn reset_limit(&self, ip: &IpAddr) {
615 let mut counts = self.request_counts.write().await;
616 counts.remove(ip);
617 }
618}
619
620pub struct AuthSecurity {
626 config: SecurityConfig,
628 brute_force: BruteForceProtector,
630 password_validator: PasswordStrengthValidator,
632 rate_limiter: RateLimiter,
634 audit_logger: Option<AuditLogger>,
636}
637
638impl AuthSecurity {
639 pub fn new(config: SecurityConfig) -> Self {
641 let audit_logger = if config.enable_audit_log {
642 Some(AuditLogger::new(10000)) } 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 pub fn brute_force(&self) -> &BruteForceProtector {
658 &self.brute_force
659 }
660
661 pub async fn check_auth_attempt(&self, username: &str, ip: Option<IpAddr>) -> Result<()> {
665 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 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 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 pub async fn record_auth_success(&self, username: &str, ip: Option<IpAddr>) {
722 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 if let Some(logger) = &self.audit_logger {
730 logger
731 .log(AuthEvent::Success, username.to_string(), ip, None)
732 .await;
733 }
734 }
735
736 pub async fn record_auth_failure(&self, username: &str, ip: Option<IpAddr>) {
738 self.brute_force.record_failed_attempt(username, ip).await;
740
741 if let Some(logger) = &self.audit_logger {
743 logger
744 .log(AuthEvent::Failure, username.to_string(), ip, None)
745 .await;
746 }
747 }
748
749 pub fn validate_password(&self, password: &str) -> PasswordStrengthResult {
751 self.password_validator.validate(password)
752 }
753
754 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 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 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 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 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 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 pub async fn unlock_user(&self, username: &str) {
814 self.brute_force.unlock_user(username).await;
815 }
816
817 pub async fn unlock_ip(&self, ip: &IpAddr) {
819 self.brute_force.unlock_ip(ip).await;
820 }
821
822 pub async fn reset_rate_limit(&self, ip: &IpAddr) {
824 self.rate_limiter.reset_limit(ip).await;
825 }
826
827 pub fn config(&self) -> &SecurityConfig {
829 &self.config
830 }
831}
832
833pub struct AuthSecurityBuilder {
839 config: SecurityConfig,
840}
841
842impl AuthSecurityBuilder {
843 pub fn new() -> Self {
845 Self {
846 config: SecurityConfig::default(),
847 }
848 }
849
850 pub fn brute_force_config(mut self, config: BruteForceConfig) -> Self {
852 self.config.brute_force = config;
853 self
854 }
855
856 pub fn password_strength_config(mut self, config: PasswordStrengthConfig) -> Self {
858 self.config.password_strength = config;
859 self
860 }
861
862 pub fn rate_limit_config(mut self, config: RateLimitConfig) -> Self {
864 self.config.rate_limit = config;
865 self
866 }
867
868 pub fn enable_audit_log(mut self, enable: bool) -> Self {
870 self.config.enable_audit_log = enable;
871 self
872 }
873
874 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}