Skip to main content

authplane_sdk/
fetch_settings.rs

1#[derive(Debug, Clone, PartialEq)]
2pub struct FetchSettings {
3    pub ssrf_protection: bool,
4    pub allow_http: bool,
5    pub allow_localhost: bool,
6    pub allow_private_networks: bool,
7    pub timeout_seconds: f64,
8}
9
10impl Default for FetchSettings {
11    fn default() -> Self {
12        Self {
13            ssrf_protection: true,
14            allow_http: false,
15            allow_localhost: false,
16            allow_private_networks: false,
17            timeout_seconds: 10.0,
18        }
19    }
20}
21
22impl FetchSettings {
23    pub fn from_dev_mode(dev_mode: bool) -> Self {
24        if dev_mode {
25            Self {
26                ssrf_protection: true,
27                allow_http: true,
28                allow_localhost: true,
29                allow_private_networks: true,
30                timeout_seconds: 10.0,
31            }
32        } else {
33            Self::default()
34        }
35    }
36
37    /// Resolve dev-mode from the environment variable `AUTHPLANE_DEV_MODE`.
38    ///
39    /// Truthy values: `"true"`, `"1"`, `"yes"` (case-insensitive).
40    /// Falls back to `from_dev_mode(false)` when the variable is absent or
41    /// not truthy.
42    pub fn from_dev_mode_env() -> Self {
43        let dev_mode = std::env::var("AUTHPLANE_DEV_MODE")
44            .ok()
45            .map(|v| matches!(v.to_ascii_lowercase().as_str(), "true" | "1" | "yes"))
46            .unwrap_or(false);
47        Self::from_dev_mode(dev_mode)
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::FetchSettings;
54
55    #[test]
56    fn dev_mode_relaxes_fetch_restrictions() {
57        let settings = FetchSettings::from_dev_mode(true);
58        assert!(settings.allow_http);
59        assert!(settings.allow_localhost);
60        assert!(settings.allow_private_networks);
61        assert!(settings.ssrf_protection);
62    }
63
64    #[test]
65    fn prod_mode_uses_secure_defaults() {
66        let settings = FetchSettings::from_dev_mode(false);
67        assert!(!settings.allow_http);
68        assert!(!settings.allow_localhost);
69        assert!(!settings.allow_private_networks);
70        assert!(settings.ssrf_protection);
71        assert_eq!(settings.timeout_seconds, 10.0);
72    }
73
74    #[test]
75    fn default_matches_prod_mode() {
76        // Callers who construct FetchSettings::default() should not end up
77        // with dev-mode permissions — this guards against a refactor that
78        // silently flips the default to `allow_http = true`.
79        assert_eq!(
80            FetchSettings::default(),
81            FetchSettings::from_dev_mode(false)
82        );
83    }
84
85    #[test]
86    fn timeout_can_be_overridden_without_touching_ssrf() {
87        let settings = FetchSettings {
88            timeout_seconds: 45.0,
89            ..FetchSettings::default()
90        };
91        assert_eq!(settings.timeout_seconds, 45.0);
92        assert!(settings.ssrf_protection);
93        assert!(!settings.allow_http);
94    }
95
96    #[test]
97    fn fractional_timeout_is_preserved() {
98        // Reqwest's Duration::from_secs_f64 handles sub-second timeouts —
99        // make sure the struct carries them verbatim.
100        let settings = FetchSettings {
101            timeout_seconds: 0.25,
102            ..FetchSettings::default()
103        };
104        assert_eq!(settings.timeout_seconds, 0.25);
105    }
106
107    #[test]
108    fn clone_and_eq_are_reflexive() {
109        let a = FetchSettings::from_dev_mode(true);
110        let b = a.clone();
111        assert_eq!(a, b);
112    }
113}