auth_framework/server/core/
common_config.rs1use crate::errors::Result;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::time::Duration;
10
11pub trait ServerConfig {
13 fn validate(&self) -> Result<()> {
19 Ok(())
20 }
21
22 fn config_name(&self) -> &'static str;
24
25 fn is_enabled(&self) -> bool {
27 true
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct TimeoutConfig {
34 pub connect_timeout: Duration,
36 pub read_timeout: Duration,
38 pub write_timeout: Duration,
40}
41
42impl Default for TimeoutConfig {
43 fn default() -> Self {
44 Self {
45 connect_timeout: Duration::from_secs(30),
46 read_timeout: Duration::from_secs(30),
47 write_timeout: Duration::from_secs(30),
48 }
49 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct SecurityConfig {
55 pub enable_tls: bool,
57 pub min_tls_version: String,
59 pub cipher_suites: Vec<String>,
61 pub cert_validation: CertificateValidation,
63 pub verify_certificates: bool,
65 #[serde(default)]
68 pub accept_invalid_certs: bool,
69}
70
71impl Default for SecurityConfig {
72 fn default() -> Self {
73 Self {
74 enable_tls: true,
75 min_tls_version: "1.2".to_string(),
76 cipher_suites: vec![
77 "TLS_AES_256_GCM_SHA384".to_string(),
78 "TLS_CHACHA20_POLY1305_SHA256".to_string(),
79 "TLS_AES_128_GCM_SHA256".to_string(),
80 ],
81 cert_validation: CertificateValidation::Full,
82 verify_certificates: true,
83 accept_invalid_certs: false,
84 }
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub enum CertificateValidation {
91 Full,
93 SkipHostname,
95 None,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct EndpointConfig {
102 pub base_url: String,
104 pub api_version: Option<String>,
106 pub headers: HashMap<String, String>,
108 pub timeout: TimeoutConfig,
110 pub security: SecurityConfig,
112}
113
114impl EndpointConfig {
115 pub fn new(base_url: impl Into<String>) -> Self {
117 Self {
118 base_url: base_url.into(),
119 api_version: None,
120 headers: HashMap::new(),
121 timeout: TimeoutConfig::default(),
122 security: SecurityConfig::default(),
123 }
124 }
125
126 pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
128 self.headers.insert(key.into(), value.into());
129 self
130 }
131
132 pub fn with_api_version(mut self, version: impl Into<String>) -> Self {
134 self.api_version = Some(version.into());
135 self
136 }
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct RetryConfig {
142 pub max_attempts: u32,
144 pub initial_delay: Duration,
146 pub max_delay: Duration,
148 pub backoff_multiplier: f64,
150 pub jitter_factor: f64,
152}
153
154impl Default for RetryConfig {
155 fn default() -> Self {
156 Self {
157 max_attempts: 3,
158 initial_delay: Duration::from_millis(100),
159 max_delay: Duration::from_secs(30),
160 backoff_multiplier: 2.0,
161 jitter_factor: 0.1,
162 }
163 }
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct LoggingConfig {
169 pub debug: bool,
171 pub log_bodies: bool,
173 pub log_sensitive: bool,
175 pub max_log_size: usize,
177}
178
179impl Default for LoggingConfig {
180 fn default() -> Self {
181 Self {
182 debug: false,
183 log_bodies: false,
184 log_sensitive: false,
185 max_log_size: 4096,
186 }
187 }
188}
189
190pub mod validation {
192 use super::*;
193
194 pub fn validate_url(url: &str) -> Result<()> {
196 if url.is_empty() {
197 return Err(crate::errors::AuthError::config(
198 "URL cannot be empty".to_string(),
199 ));
200 }
201
202 if !url.starts_with("http://") && !url.starts_with("https://") {
203 return Err(crate::errors::AuthError::config(format!(
204 "Invalid URL format: {}",
205 url
206 )));
207 }
208
209 Ok(())
210 }
211
212 pub fn validate_positive_duration(duration: &Duration, field_name: &str) -> Result<()> {
214 if duration.is_zero() {
215 return Err(crate::errors::AuthError::config(format!(
216 "{} must be greater than zero",
217 field_name
218 )));
219 }
220 Ok(())
221 }
222
223 pub fn validate_port(port: u16) -> Result<()> {
225 if port == 0 {
226 return Err(crate::errors::AuthError::config(
227 "Port cannot be zero".to_string(),
228 ));
229 }
230 Ok(())
231 }
232
233 pub fn validate_required_field(value: &str, field_name: &str) -> Result<()> {
235 if value.trim().is_empty() {
236 return Err(crate::errors::AuthError::config(format!(
237 "{} is required and cannot be empty",
238 field_name
239 )));
240 }
241 Ok(())
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_timeout_config_default() {
251 let tc = TimeoutConfig::default();
252 assert_eq!(tc.connect_timeout, Duration::from_secs(30));
253 assert_eq!(tc.read_timeout, Duration::from_secs(30));
254 }
255
256 #[test]
257 fn test_security_config_default() {
258 let sc = SecurityConfig::default();
259 assert!(sc.enable_tls);
260 assert!(sc.verify_certificates);
261 }
262
263 #[test]
264 fn test_retry_config_default() {
265 let rc = RetryConfig::default();
266 assert!(rc.max_attempts > 0);
267 }
268
269 #[test]
270 fn test_logging_config_default() {
271 let lc = LoggingConfig::default();
272 assert!(!lc.debug);
273 assert!(!lc.log_bodies);
274 assert!(!lc.log_sensitive);
275 assert!(lc.max_log_size > 0);
276 }
277
278 #[test]
279 fn test_endpoint_config_new() {
280 let ec = EndpointConfig::new("https://api.example.com");
281 assert_eq!(ec.base_url, "https://api.example.com");
282 assert!(ec.api_version.is_none());
283 }
284
285 #[test]
286 fn test_endpoint_config_with_header() {
287 let ec = EndpointConfig::new("https://api.example.com")
288 .with_header("Authorization", "Bearer xxx");
289 assert_eq!(ec.headers.get("Authorization").unwrap(), "Bearer xxx");
290 }
291
292 #[test]
293 fn test_endpoint_config_with_api_version() {
294 let ec = EndpointConfig::new("https://api.example.com").with_api_version("2024-01-01");
295 assert_eq!(ec.api_version.as_deref(), Some("2024-01-01"));
296 }
297
298 #[test]
299 fn test_validate_url_valid() {
300 assert!(validation::validate_url("https://example.com").is_ok());
301 }
302
303 #[test]
304 fn test_validate_url_empty() {
305 assert!(validation::validate_url("").is_err());
306 }
307
308 #[test]
309 fn test_validate_url_no_scheme() {
310 assert!(validation::validate_url("example.com").is_err());
311 }
312
313 #[test]
314 fn test_validate_positive_duration() {
315 assert!(validation::validate_positive_duration(&Duration::from_secs(1), "timeout").is_ok());
316 }
317
318 #[test]
319 fn test_validate_zero_duration() {
320 assert!(validation::validate_positive_duration(&Duration::ZERO, "timeout").is_err());
321 }
322
323 #[test]
324 fn test_validate_port() {
325 assert!(validation::validate_port(8080).is_ok());
326 assert!(validation::validate_port(0).is_err());
327 }
328
329 #[test]
330 fn test_validate_required_field() {
331 assert!(validation::validate_required_field("value", "name").is_ok());
332 assert!(validation::validate_required_field("", "name").is_err());
333 assert!(validation::validate_required_field(" ", "name").is_err());
334 }
335
336 #[test]
337 fn test_security_config_accept_invalid_certs_defaults_false() {
338 let sc = SecurityConfig::default();
339 assert!(
340 !sc.accept_invalid_certs,
341 "accept_invalid_certs must default to false"
342 );
343 }
344
345 #[test]
346 fn test_security_config_accept_invalid_certs_deserialization_default() {
347 let json = r#"{"enable_tls":true,"min_tls_version":"1.2","cipher_suites":[],"cert_validation":"Full","verify_certificates":true}"#;
349 let sc: SecurityConfig = serde_json::from_str(json).unwrap();
350 assert!(!sc.accept_invalid_certs);
351 }
352}