Skip to main content

reinhardt_conf/settings/
cors.rs

1//! CORS settings fragment
2//!
3//! Provides composable CORS configuration as a [`SettingsFragment`](crate::settings::fragment::SettingsFragment).
4
5use reinhardt_core::macros::settings;
6use serde::{Deserialize, Serialize};
7
8fn default_allow_origins() -> Vec<String> {
9	vec!["*".to_string()]
10}
11
12fn default_allow_methods() -> Vec<String> {
13	vec![
14		"GET".to_string(),
15		"POST".to_string(),
16		"PUT".to_string(),
17		"PATCH".to_string(),
18		"DELETE".to_string(),
19	]
20}
21
22fn default_allow_headers() -> Vec<String> {
23	vec!["*".to_string()]
24}
25
26fn default_max_age() -> u64 {
27	3600
28}
29
30/// CORS configuration fragment.
31///
32/// Controls cross-origin resource sharing policy.
33#[settings(fragment = true, section = "cors")]
34#[non_exhaustive]
35#[derive(Clone, Debug, Serialize, Deserialize)]
36pub struct CorsSettings {
37	/// Allowed origin domains (use `"*"` for any origin).
38	#[serde(default = "default_allow_origins")]
39	pub allow_origins: Vec<String>,
40	/// Allowed HTTP methods.
41	#[serde(default = "default_allow_methods")]
42	pub allow_methods: Vec<String>,
43	/// Allowed HTTP request headers.
44	#[serde(default = "default_allow_headers")]
45	pub allow_headers: Vec<String>,
46	/// Whether to allow credentials (cookies, authorization headers).
47	pub allow_credentials: bool,
48	/// Maximum age (in seconds) for preflight response caching.
49	#[serde(default = "default_max_age")]
50	pub max_age: u64,
51}
52
53impl Default for CorsSettings {
54	fn default() -> Self {
55		Self {
56			allow_origins: vec!["*".to_string()],
57			allow_methods: vec![
58				"GET".to_string(),
59				"POST".to_string(),
60				"PUT".to_string(),
61				"PATCH".to_string(),
62				"DELETE".to_string(),
63			],
64			allow_headers: vec!["*".to_string()],
65			allow_credentials: false,
66			max_age: 3600,
67		}
68	}
69}
70
71#[cfg(test)]
72mod tests {
73	use super::*;
74	use crate::settings::fragment::SettingsFragment;
75	use rstest::rstest;
76
77	#[rstest]
78	fn test_cors_section_name() {
79		// Arrange / Act
80		let section = CorsSettings::section();
81
82		// Assert
83		assert_eq!(section, "cors");
84	}
85
86	#[rstest]
87	fn test_cors_default_values() {
88		// Arrange / Act
89		let settings = CorsSettings::default();
90
91		// Assert
92		assert_eq!(settings.allow_origins, vec!["*"]);
93		assert_eq!(settings.allow_methods.len(), 5);
94		assert!(settings.allow_methods.contains(&"GET".to_string()));
95		assert!(settings.allow_methods.contains(&"POST".to_string()));
96		assert!(settings.allow_methods.contains(&"PUT".to_string()));
97		assert!(settings.allow_methods.contains(&"PATCH".to_string()));
98		assert!(settings.allow_methods.contains(&"DELETE".to_string()));
99		assert_eq!(settings.allow_headers, vec!["*"]);
100		assert!(!settings.allow_credentials);
101		assert_eq!(settings.max_age, 3600);
102	}
103}