Skip to main content

modo_session/
config.rs

1use serde::Deserialize;
2
3/// Configuration for the session subsystem.
4///
5/// All fields have sane defaults (see [`Default`]). Deserialises from YAML/TOML
6/// with `#[serde(default)]`, so you only need to specify the fields you want to
7/// override.
8///
9/// # Example (YAML)
10///
11/// ```yaml
12/// session_ttl_secs: 86400
13/// cookie_name: "_sess"
14/// validate_fingerprint: true
15/// touch_interval_secs: 600
16/// max_sessions_per_user: 5
17/// trusted_proxies:
18///   - "10.0.0.0/8"
19/// ```
20#[derive(Debug, Clone, Deserialize)]
21#[serde(default)]
22pub struct SessionConfig {
23    /// Session lifetime in seconds (default: 2 592 000 = 30 days).
24    pub session_ttl_secs: u64,
25    /// Name of the HTTP cookie that carries the session token (default: `"_session"`).
26    pub cookie_name: String,
27    /// Whether to reject sessions whose request fingerprint changed since creation
28    /// (default: `true`).  Disabling this reduces hijack protection but allows users
29    /// behind rotating IPs or proxies to keep their session.
30    pub validate_fingerprint: bool,
31    /// Minimum number of seconds between consecutive `touch` (expiry renewal) DB
32    /// writes (default: 300 = 5 minutes).
33    pub touch_interval_secs: u64,
34    /// Maximum number of concurrent active sessions per user before the
35    /// least-recently-used session is evicted (default: 10).
36    pub max_sessions_per_user: usize,
37    /// CIDR ranges of trusted reverse-proxy addresses.  When non-empty, the
38    /// `X-Forwarded-For` / `X-Real-IP` headers are only trusted when the TCP
39    /// connection originates from one of these ranges (default: empty = trust
40    /// proxy headers unconditionally).
41    pub trusted_proxies: Vec<String>,
42}
43
44impl Default for SessionConfig {
45    fn default() -> Self {
46        Self {
47            session_ttl_secs: 2_592_000, // 30 days
48            cookie_name: "_session".to_string(),
49            validate_fingerprint: true,
50            touch_interval_secs: 300, // 5 minutes
51            max_sessions_per_user: 10,
52            trusted_proxies: Vec::new(),
53        }
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn default_values() {
63        let config = SessionConfig::default();
64        assert_eq!(config.session_ttl_secs, 2_592_000);
65        assert_eq!(config.cookie_name, "_session");
66        assert!(config.validate_fingerprint);
67        assert_eq!(config.touch_interval_secs, 300);
68        assert_eq!(config.max_sessions_per_user, 10);
69        assert!(config.trusted_proxies.is_empty());
70    }
71
72    #[test]
73    fn partial_yaml_deserialization() {
74        let yaml = r#"
75session_ttl_secs: 3600
76cookie_name: "my_sess"
77"#;
78        let config: SessionConfig = serde_yaml_ng::from_str(yaml).unwrap();
79        assert_eq!(config.session_ttl_secs, 3600);
80        assert_eq!(config.cookie_name, "my_sess");
81        // defaults for omitted fields
82        assert!(config.validate_fingerprint);
83        assert_eq!(config.touch_interval_secs, 300);
84        assert_eq!(config.max_sessions_per_user, 10);
85    }
86}