1use serde::Deserialize;
2
3fn deserialize_nonzero_usize<'de, D>(deserializer: D) -> Result<usize, D::Error>
4where
5 D: serde::Deserializer<'de>,
6{
7 let value = usize::deserialize(deserializer)?;
8 if value == 0 {
9 return Err(serde::de::Error::custom(
10 "max_sessions_per_user must be > 0; setting it to 0 would lock out all users",
11 ));
12 }
13 Ok(value)
14}
15
16#[derive(Debug, Clone, Deserialize)]
34#[serde(default)]
35pub struct SessionConfig {
36 pub session_ttl_secs: u64,
38 pub cookie_name: String,
40 pub validate_fingerprint: bool,
44 pub touch_interval_secs: u64,
47 #[serde(deserialize_with = "deserialize_nonzero_usize")]
53 pub max_sessions_per_user: usize,
54 pub trusted_proxies: Vec<String>,
65}
66
67impl Default for SessionConfig {
68 fn default() -> Self {
69 Self {
70 session_ttl_secs: 2_592_000, cookie_name: "_session".to_string(),
72 validate_fingerprint: true,
73 touch_interval_secs: 300, max_sessions_per_user: 10,
75 trusted_proxies: Vec::new(),
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn default_values() {
86 let config = SessionConfig::default();
87 assert_eq!(config.session_ttl_secs, 2_592_000);
88 assert_eq!(config.cookie_name, "_session");
89 assert!(config.validate_fingerprint);
90 assert_eq!(config.touch_interval_secs, 300);
91 assert_eq!(config.max_sessions_per_user, 10);
92 assert!(config.trusted_proxies.is_empty());
93 }
94
95 #[test]
96 fn partial_yaml_deserialization() {
97 let yaml = r#"
98session_ttl_secs: 3600
99cookie_name: "my_sess"
100"#;
101 let config: SessionConfig = serde_yaml_ng::from_str(yaml).unwrap();
102 assert_eq!(config.session_ttl_secs, 3600);
103 assert_eq!(config.cookie_name, "my_sess");
104 assert!(config.validate_fingerprint);
106 assert_eq!(config.touch_interval_secs, 300);
107 assert_eq!(config.max_sessions_per_user, 10);
108 }
109
110 #[test]
111 fn zero_max_sessions_returns_error() {
112 let yaml = r#"
113max_sessions_per_user: 0
114"#;
115 let err = serde_yaml_ng::from_str::<SessionConfig>(yaml).unwrap_err();
116 assert!(
117 err.to_string()
118 .contains("max_sessions_per_user must be > 0"),
119 "unexpected error: {err}",
120 );
121 }
122
123 #[test]
124 fn nonzero_max_sessions_accepted() {
125 let yaml = r#"
126max_sessions_per_user: 1
127"#;
128 let config: SessionConfig = serde_yaml_ng::from_str(yaml).unwrap();
129 assert_eq!(config.max_sessions_per_user, 1);
130 }
131}