feat(settings): add CorsSettings struct for CORS configuration

Implements CorsSettings struct with validation and deserialization support
for configuring Cross-Origin Resource Sharing in the application settings.

Ref: T010 (specs/001-modbus-relay-control)
This commit is contained in:
2026-01-01 23:29:31 +01:00
parent b1fd30af67
commit 9a55aa433c
8 changed files with 982 additions and 14 deletions

View File

@@ -24,6 +24,7 @@ poem = { version = "3.1.12", default-features = false, features = ["csrf", "rust
poem-openapi = { version = "5.1.16", features = ["chrono", "swagger-ui"] }
serde = "1.0.228"
serde_json = "1.0.148"
serde_yaml = "0.9.34"
sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite", "derive", "migrate"] }
thiserror = "2.0.17"
tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread"] }

View File

@@ -25,6 +25,9 @@ pub struct Settings {
pub modbus: ModbusSettings,
/// Relay configuration
pub relay: RelaySettings,
/// CORS configuration
#[serde(default)]
pub cors: CorsSettings,
}
impl Settings {
@@ -211,6 +214,54 @@ impl Default for RelaySettings {
}
}
/// Default value for CORS max age in seconds (1 hour).
const fn default_max_age_secs() -> i32 {
3600
}
/// CORS (Cross-Origin Resource Sharing) configuration for the HTTP API.
///
/// Controls which origins can access the API from browsers. In development,
/// use permissive settings (`allowed_origins: ["*"]`). In production, use
/// restrictive settings with specific origins.
///
/// # Security Constraint
///
/// When `allow_credentials` is `true`, `allowed_origins` MUST NOT contain
/// wildcard `"*"`. This is enforced by browser security policy and will be
/// validated by the `build_cors()` function.
#[derive(Debug, serde::Deserialize, Clone)]
pub struct CorsSettings {
/// List of allowed origin URLs (e.g., `["https://sta.example.com"]`).
///
/// Use `["*"]` for development to allow all origins.
/// In production, specify exact origins to prevent unauthorized access.
#[serde(default)]
pub allowed_origins: Vec<String>,
/// Whether to allow credentials (cookies, authorization headers) in CORS requests.
///
/// Set to `true` in production when using Authelia authentication.
/// MUST be `false` when using wildcard `"*"` in `allowed_origins`.
#[serde(default)]
pub allow_credentials: bool,
/// Duration in seconds that browsers can cache CORS preflight responses.
///
/// Typical value: `3600` (1 hour). Higher values reduce preflight requests
/// but delay policy changes from taking effect.
#[serde(default = "default_max_age_secs")]
pub max_age_secs: i32,
}
impl Default for CorsSettings {
fn default() -> Self {
Self {
allowed_origins: vec![],
allow_credentials: false,
max_age_secs: 3600,
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -181,8 +181,7 @@ mod tests {
burst_size: 100,
per_seconds: 60,
},
modbus: crate::settings::ModbusSettings::default(),
relay: crate::settings::RelaySettings::default(),
..Default::default()
}
}