1use serde::{Deserialize, Serialize};
4use std::collections::HashSet;
5use std::time::Duration;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SecurityConfig {
10 pub cors: Option<CorsConfig>,
12
13 pub csrf: Option<CsrfConfig>,
15
16 pub rate_limiting: Option<RateLimitConfig>,
18
19 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#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct CorsConfig {
37 pub allowed_origins: Option<HashSet<String>>,
39
40 pub allowed_methods: HashSet<String>,
42
43 pub allowed_headers: HashSet<String>,
45
46 pub exposed_headers: HashSet<String>,
48
49 pub allow_credentials: bool,
51
52 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, allowed_methods,
73 allowed_headers,
74 exposed_headers: HashSet::new(),
75 allow_credentials: false,
76 max_age: Some(86400), }
78 }
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct CsrfConfig {
84 pub token_header: String,
86
87 pub cookie_name: String,
89
90 pub token_lifetime: u64,
92
93 pub secure_cookie: bool,
95
96 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, secure_cookie: true,
107 exempt_paths: HashSet::new(),
108 }
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct RateLimitConfig {
115 pub max_requests: u32,
117
118 pub window_seconds: u32,
120
121 pub identifier: RateLimitIdentifier,
123
124 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, identifier: RateLimitIdentifier::IpAddress,
134 exempt_paths: HashSet::new(),
135 }
136 }
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub enum RateLimitIdentifier {
142 IpAddress,
144 UserId,
146 ApiKey,
148 CustomHeader(String),
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct SecurityHeadersConfig {
155 pub x_frame_options: Option<String>,
157
158 pub x_content_type_options: bool,
160
161 pub x_xss_protection: bool,
163
164 pub hsts: Option<HstsConfig>,
166
167 pub csp: Option<String>,
169
170 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#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct HstsConfig {
190 pub max_age: u32,
192
193 pub include_subdomains: bool,
195
196 pub preload: bool,
198}
199
200impl Default for HstsConfig {
201 fn default() -> Self {
202 Self {
203 max_age: 31536000, include_subdomains: true,
205 preload: false,
206 }
207 }
208}