1use serde::Deserialize;
16
17#[derive(Debug, Clone, Deserialize)]
19#[serde(deny_unknown_fields)]
20pub struct LockoutConfig {
21 #[serde(default = "default_max_failures")]
26 pub max_failures: u32,
27
28 #[serde(default = "default_failure_window_secs")]
31 pub failure_window_secs: u64,
32
33 #[serde(default = "default_lockout_duration_secs")]
36 pub lockout_duration_secs: u64,
37}
38
39fn default_max_failures() -> u32 {
40 5
41}
42
43fn default_failure_window_secs() -> u64 {
44 300
45}
46
47fn default_lockout_duration_secs() -> u64 {
48 900
49}
50
51impl Default for LockoutConfig {
52 fn default() -> Self {
53 Self {
54 max_failures: default_max_failures(),
55 failure_window_secs: default_failure_window_secs(),
56 lockout_duration_secs: default_lockout_duration_secs(),
57 }
58 }
59}
60
61impl LockoutConfig {
62 pub fn validate(&self) -> Result<(), String> {
64 if self.max_failures == 0 {
66 return Ok(());
67 }
68 if self.failure_window_secs == 0 {
69 return Err(
70 "[auth_lockout].failure_window_secs must be at least 1 (or set max_failures = 0 to disable)".into(),
71 );
72 }
73 if self.lockout_duration_secs == 0 {
74 return Err(
75 "[auth_lockout].lockout_duration_secs must be at least 1 (or set max_failures = 0 to disable)".into(),
76 );
77 }
78 Ok(())
79 }
80
81 pub fn to_policy(&self) -> crate::auth::failure_tracker::LockoutPolicy {
83 crate::auth::failure_tracker::LockoutPolicy {
84 max_failures: self.max_failures,
85 failure_window: std::time::Duration::from_secs(self.failure_window_secs),
86 lockout_duration: std::time::Duration::from_secs(self.lockout_duration_secs),
87 }
88 }
89}