use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
pub cors: Option<CorsConfig>,
pub csrf: Option<CsrfConfig>,
pub rate_limiting: Option<RateLimitConfig>,
pub security_headers: Option<SecurityHeadersConfig>,
}
impl Default for SecurityConfig {
fn default() -> Self {
Self {
cors: Some(CorsConfig::default()),
csrf: Some(CsrfConfig::default()),
rate_limiting: Some(RateLimitConfig::default()),
security_headers: Some(SecurityHeadersConfig::default()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorsConfig {
pub allowed_origins: Option<HashSet<String>>,
pub allowed_methods: HashSet<String>,
pub allowed_headers: HashSet<String>,
pub exposed_headers: HashSet<String>,
pub allow_credentials: bool,
pub max_age: Option<u32>,
}
impl Default for CorsConfig {
fn default() -> Self {
let mut allowed_methods = HashSet::new();
allowed_methods.insert("GET".to_string());
allowed_methods.insert("POST".to_string());
allowed_methods.insert("PUT".to_string());
allowed_methods.insert("DELETE".to_string());
allowed_methods.insert("OPTIONS".to_string());
let mut allowed_headers = HashSet::new();
allowed_headers.insert("content-type".to_string());
allowed_headers.insert("authorization".to_string());
allowed_headers.insert("x-requested-with".to_string());
Self {
allowed_origins: None, allowed_methods,
allowed_headers,
exposed_headers: HashSet::new(),
allow_credentials: false,
max_age: Some(86400), }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CsrfConfig {
pub token_header: String,
pub cookie_name: String,
pub token_lifetime: u64,
pub secure_cookie: bool,
pub exempt_paths: HashSet<String>,
}
impl Default for CsrfConfig {
fn default() -> Self {
Self {
token_header: "X-CSRF-Token".to_string(),
cookie_name: "_csrf_token".to_string(),
token_lifetime: 3600, secure_cookie: true,
exempt_paths: HashSet::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitConfig {
pub max_requests: u32,
pub window_seconds: u32,
pub identifier: RateLimitIdentifier,
pub exempt_paths: HashSet<String>,
}
impl Default for RateLimitConfig {
fn default() -> Self {
Self {
max_requests: 100,
window_seconds: 60, identifier: RateLimitIdentifier::IpAddress,
exempt_paths: HashSet::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RateLimitIdentifier {
IpAddress,
UserId,
ApiKey,
CustomHeader(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityHeadersConfig {
pub x_frame_options: Option<String>,
pub x_content_type_options: bool,
pub x_xss_protection: bool,
pub hsts: Option<HstsConfig>,
pub csp: Option<String>,
pub referrer_policy: Option<String>,
}
impl Default for SecurityHeadersConfig {
fn default() -> Self {
Self {
x_frame_options: Some("DENY".to_string()),
x_content_type_options: true,
x_xss_protection: true,
hsts: Some(HstsConfig::default()),
csp: Some("default-src 'self'".to_string()),
referrer_policy: Some("strict-origin-when-cross-origin".to_string()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HstsConfig {
pub max_age: u32,
pub include_subdomains: bool,
pub preload: bool,
}
impl Default for HstsConfig {
fn default() -> Self {
Self {
max_age: 31536000, include_subdomains: true,
preload: false,
}
}
}