reinhardt_conf/settings/
security.rs1use super::fragment::SettingsValidation;
6use super::profile::Profile;
7use super::validation::{ValidationError, ValidationResult};
8use reinhardt_core::macros::settings;
9use serde::{Deserialize, Serialize};
10
11#[settings(fragment = true, section = "security", validate = false)]
19#[derive(Clone, Debug, Serialize, Deserialize)]
20pub struct SecuritySettings {
21 #[serde(default)]
23 pub secure_proxy_ssl_header: Option<(String, String)>,
24 #[serde(default)]
26 pub secure_ssl_redirect: bool,
27 #[serde(default)]
29 pub secure_hsts_seconds: Option<u64>,
30 #[serde(default)]
32 pub secure_hsts_include_subdomains: bool,
33 #[serde(default)]
35 pub secure_hsts_preload: bool,
36 #[serde(default)]
38 pub session_cookie_secure: bool,
39 #[serde(default)]
41 pub csrf_cookie_secure: bool,
42 #[serde(default = "default_append_slash")]
44 pub append_slash: bool,
45}
46
47fn default_append_slash() -> bool {
48 true
49}
50
51impl Default for SecuritySettings {
52 fn default() -> Self {
53 Self {
54 secure_proxy_ssl_header: None,
55 secure_ssl_redirect: false,
56 secure_hsts_seconds: None,
57 secure_hsts_include_subdomains: false,
58 secure_hsts_preload: false,
59 session_cookie_secure: false,
60 csrf_cookie_secure: false,
61 append_slash: true,
62 }
63 }
64}
65
66impl SettingsValidation for SecuritySettings {
67 fn validate(&self, profile: &Profile) -> ValidationResult {
68 if profile.is_production() {
69 if !self.secure_ssl_redirect {
70 return Err(ValidationError::Security(
71 "secure_ssl_redirect should be true in production".to_string(),
72 ));
73 }
74 if !self.session_cookie_secure {
75 return Err(ValidationError::Security(
76 "session_cookie_secure should be true in production".to_string(),
77 ));
78 }
79 if !self.csrf_cookie_secure {
80 return Err(ValidationError::Security(
81 "csrf_cookie_secure should be true in production".to_string(),
82 ));
83 }
84 }
85 Ok(())
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::SecuritySettings;
92 use crate::settings::fragment::SettingsFragment;
93 use crate::settings::profile::Profile;
94 use rstest::rstest;
95
96 #[rstest]
97 fn test_security_settings_section() {
98 let section = SecuritySettings::section();
100
101 assert_eq!(section, "security");
103 }
104
105 #[rstest]
106 fn test_security_settings_default() {
107 let settings = SecuritySettings::default();
109
110 assert!(!settings.secure_ssl_redirect);
112 assert!(!settings.session_cookie_secure);
113 assert!(!settings.csrf_cookie_secure);
114 assert!(settings.append_slash);
115 assert!(settings.secure_proxy_ssl_header.is_none());
116 }
117
118 #[rstest]
119 fn test_security_settings_development_validation_ok() {
120 let settings = SecuritySettings::default();
122
123 let result = settings.validate(&Profile::Development);
125
126 assert!(result.is_ok());
128 }
129
130 #[rstest]
131 fn test_security_settings_production_validation_fails_without_ssl() {
132 let settings = SecuritySettings::default();
134
135 let result = settings.validate(&Profile::Production);
137
138 assert!(result.is_err());
140 }
141
142 #[rstest]
143 fn test_security_settings_production_validation_ok() {
144 let settings = SecuritySettings {
146 secure_ssl_redirect: true,
147 session_cookie_secure: true,
148 csrf_cookie_secure: true,
149 ..Default::default()
150 };
151
152 let result = settings.validate(&Profile::Production);
154
155 assert!(result.is_ok());
157 }
158}