elif_security/
config.rs

1//! Security configuration types and utilities
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashSet;
5use std::time::Duration;
6
7/// Global security configuration
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SecurityConfig {
10    /// CORS configuration
11    pub cors: Option<CorsConfig>,
12    
13    /// CSRF protection configuration  
14    pub csrf: Option<CsrfConfig>,
15    
16    /// Rate limiting configuration
17    pub rate_limiting: Option<RateLimitConfig>,
18    
19    /// Security headers configuration
20    pub security_headers: Option<SecurityHeadersConfig>,
21}
22
23impl Default for SecurityConfig {
24    fn default() -> Self {
25        Self {
26            cors: Some(CorsConfig::default()),
27            csrf: Some(CsrfConfig::default()),
28            rate_limiting: Some(RateLimitConfig::default()),
29            security_headers: Some(SecurityHeadersConfig::default()),
30        }
31    }
32}
33
34/// CORS (Cross-Origin Resource Sharing) configuration
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct CorsConfig {
37    /// Allowed origins - None means allow all origins (*)
38    pub allowed_origins: Option<HashSet<String>>,
39    
40    /// Allowed HTTP methods
41    pub allowed_methods: HashSet<String>,
42    
43    /// Allowed request headers
44    pub allowed_headers: HashSet<String>,
45    
46    /// Headers exposed to the client
47    pub exposed_headers: HashSet<String>,
48    
49    /// Whether to allow credentials (cookies, authorization headers)
50    pub allow_credentials: bool,
51    
52    /// Maximum age for preflight cache (seconds)
53    pub max_age: Option<u32>,
54}
55
56impl Default for CorsConfig {
57    fn default() -> Self {
58        let mut allowed_methods = HashSet::new();
59        allowed_methods.insert("GET".to_string());
60        allowed_methods.insert("POST".to_string());
61        allowed_methods.insert("PUT".to_string());
62        allowed_methods.insert("DELETE".to_string());
63        allowed_methods.insert("OPTIONS".to_string());
64        
65        let mut allowed_headers = HashSet::new();
66        allowed_headers.insert("content-type".to_string());
67        allowed_headers.insert("authorization".to_string());
68        allowed_headers.insert("x-requested-with".to_string());
69        
70        Self {
71            allowed_origins: None, // Allow all by default (not recommended for production)
72            allowed_methods,
73            allowed_headers,
74            exposed_headers: HashSet::new(),
75            allow_credentials: false,
76            max_age: Some(86400), // 24 hours
77        }
78    }
79}
80
81/// CSRF (Cross-Site Request Forgery) protection configuration
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct CsrfConfig {
84    /// Token header name
85    pub token_header: String,
86    
87    /// Cookie name for CSRF token
88    pub cookie_name: String,
89    
90    /// Token lifetime in seconds
91    pub token_lifetime: u64,
92    
93    /// Whether to use secure cookies (HTTPS only)
94    pub secure_cookie: bool,
95    
96    /// Paths that are exempt from CSRF protection
97    pub exempt_paths: HashSet<String>,
98}
99
100impl Default for CsrfConfig {
101    fn default() -> Self {
102        Self {
103            token_header: "X-CSRF-Token".to_string(),
104            cookie_name: "_csrf_token".to_string(),
105            token_lifetime: 3600, // 1 hour
106            secure_cookie: true,
107            exempt_paths: HashSet::new(),
108        }
109    }
110}
111
112/// Rate limiting configuration
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct RateLimitConfig {
115    /// Maximum requests per window
116    pub max_requests: u32,
117    
118    /// Time window duration
119    pub window_seconds: u32,
120    
121    /// Identifier strategy (IP, user ID, etc.)
122    pub identifier: RateLimitIdentifier,
123    
124    /// Paths exempt from rate limiting
125    pub exempt_paths: HashSet<String>,
126}
127
128impl Default for RateLimitConfig {
129    fn default() -> Self {
130        Self {
131            max_requests: 100,
132            window_seconds: 60, // 1 minute
133            identifier: RateLimitIdentifier::IpAddress,
134            exempt_paths: HashSet::new(),
135        }
136    }
137}
138
139/// Rate limit identifier strategy
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub enum RateLimitIdentifier {
142    /// Use client IP address
143    IpAddress,
144    /// Use authenticated user ID
145    UserId,
146    /// Use API key
147    ApiKey,
148    /// Custom identifier from header
149    CustomHeader(String),
150}
151
152/// Security headers configuration
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct SecurityHeadersConfig {
155    /// Enable X-Frame-Options header
156    pub x_frame_options: Option<String>,
157    
158    /// Enable X-Content-Type-Options header
159    pub x_content_type_options: bool,
160    
161    /// Enable X-XSS-Protection header
162    pub x_xss_protection: bool,
163    
164    /// Strict-Transport-Security header (HSTS)
165    pub hsts: Option<HstsConfig>,
166    
167    /// Content Security Policy header
168    pub csp: Option<String>,
169    
170    /// Referrer Policy header
171    pub referrer_policy: Option<String>,
172}
173
174impl Default for SecurityHeadersConfig {
175    fn default() -> Self {
176        Self {
177            x_frame_options: Some("DENY".to_string()),
178            x_content_type_options: true,
179            x_xss_protection: true,
180            hsts: Some(HstsConfig::default()),
181            csp: Some("default-src 'self'".to_string()),
182            referrer_policy: Some("strict-origin-when-cross-origin".to_string()),
183        }
184    }
185}
186
187/// HTTP Strict Transport Security (HSTS) configuration
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct HstsConfig {
190    /// Maximum age in seconds
191    pub max_age: u32,
192    
193    /// Include subdomains
194    pub include_subdomains: bool,
195    
196    /// Preload directive
197    pub preload: bool,
198}
199
200impl Default for HstsConfig {
201    fn default() -> Self {
202        Self {
203            max_age: 31536000, // 1 year
204            include_subdomains: true,
205            preload: false,
206        }
207    }
208}