use super::fragment::SettingsValidation;
use super::profile::Profile;
use super::validation::{ValidationError, ValidationResult};
use reinhardt_core::macros::settings;
use serde::{Deserialize, Serialize};
#[settings(fragment = true, section = "security", validate = false)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SecuritySettings {
#[serde(default)]
pub secure_proxy_ssl_header: Option<(String, String)>,
#[serde(default)]
pub secure_ssl_redirect: bool,
#[serde(default)]
pub secure_hsts_seconds: Option<u64>,
#[serde(default)]
pub secure_hsts_include_subdomains: bool,
#[serde(default)]
pub secure_hsts_preload: bool,
#[serde(default)]
pub session_cookie_secure: bool,
#[serde(default)]
pub csrf_cookie_secure: bool,
#[serde(default = "default_append_slash")]
pub append_slash: bool,
}
fn default_append_slash() -> bool {
true
}
impl Default for SecuritySettings {
fn default() -> Self {
Self {
secure_proxy_ssl_header: None,
secure_ssl_redirect: false,
secure_hsts_seconds: None,
secure_hsts_include_subdomains: false,
secure_hsts_preload: false,
session_cookie_secure: false,
csrf_cookie_secure: false,
append_slash: true,
}
}
}
impl SettingsValidation for SecuritySettings {
fn validate(&self, profile: &Profile) -> ValidationResult {
if profile.is_production() {
if !self.secure_ssl_redirect {
return Err(ValidationError::Security(
"secure_ssl_redirect should be true in production".to_string(),
));
}
if !self.session_cookie_secure {
return Err(ValidationError::Security(
"session_cookie_secure should be true in production".to_string(),
));
}
if !self.csrf_cookie_secure {
return Err(ValidationError::Security(
"csrf_cookie_secure should be true in production".to_string(),
));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::SecuritySettings;
use crate::settings::fragment::SettingsFragment;
use crate::settings::profile::Profile;
use rstest::rstest;
#[rstest]
fn test_security_settings_section() {
let section = SecuritySettings::section();
assert_eq!(section, "security");
}
#[rstest]
fn test_security_settings_default() {
let settings = SecuritySettings::default();
assert!(!settings.secure_ssl_redirect);
assert!(!settings.session_cookie_secure);
assert!(!settings.csrf_cookie_secure);
assert!(settings.append_slash);
assert!(settings.secure_proxy_ssl_header.is_none());
}
#[rstest]
fn test_security_settings_development_validation_ok() {
let settings = SecuritySettings::default();
let result = settings.validate(&Profile::Development);
assert!(result.is_ok());
}
#[rstest]
fn test_security_settings_production_validation_fails_without_ssl() {
let settings = SecuritySettings::default();
let result = settings.validate(&Profile::Production);
assert!(result.is_err());
}
#[rstest]
fn test_security_settings_production_validation_ok() {
let settings = SecuritySettings {
secure_ssl_redirect: true,
session_cookie_secure: true,
csrf_cookie_secure: true,
..Default::default()
};
let result = settings.validate(&Profile::Production);
assert!(result.is_ok());
}
}