1#[derive(Debug, Clone)]
2pub struct MockAuthMethod {
3 pub should_succeed: bool,
5 pub user_profiles: HashMap<String, UserProfile>,
7 pub delay: Option<Duration>,
9}
10
11impl MockAuthMethod {
12 pub fn new_success() -> Self {
14 MockAuthMethod {
15 should_succeed: true,
16 user_profiles: HashMap::new(),
17 delay: None,
18 }
19 }
20
21 pub fn with_user(mut self, user_id: impl Into<String>, profile: UserProfile) -> Self {
23 self.user_profiles.insert(user_id.into(), profile);
24 self
25 }
26
27 pub fn with_delay(mut self, delay: Duration) -> Self {
29 self.delay = Some(delay);
30 self
31 }
32}
33use crate::authentication::credentials::{Credential, CredentialMetadata};
34use crate::errors::{AuthError, Result};
35use crate::methods::{AuthMethod, MethodResult};
36use crate::providers::UserProfile;
37use crate::storage::AuthStorage;
38use crate::storage::core::SessionData;
39use crate::tokens::AuthToken;
40use dashmap::DashMap;
41use std::collections::HashMap;
42use std::sync::Arc;
43use std::time::Duration;
44use uuid::Uuid;
45#[cfg(test)]
47#[tokio::test]
49async fn test_mock_storage() {
50 use crate::testing::test_infrastructure::TestEnvironmentGuard;
51 let _env = TestEnvironmentGuard::new().with_jwt_secret("test-secret");
52
53 let storage = MockStorage::new();
54 let token = helpers::create_test_token("testuser");
55 storage.store_token(&token).await.unwrap();
56 let retrieved = storage.get_token(&token.token_id).await.unwrap();
57 assert!(retrieved.is_some());
58 assert_eq!(retrieved.unwrap().token_id, token.token_id);
59}
60
61#[tokio::test]
62async fn test_failing_mock_storage() {
63 use crate::testing::test_infrastructure::TestEnvironmentGuard;
64 let _env = TestEnvironmentGuard::new().with_jwt_secret("test-secret");
65
66 let storage = MockStorage::new_failing();
67 let token = helpers::create_test_token("testuser");
68 let result = storage.store_token(&token).await;
69 assert!(result.is_err());
70}
71
72#[test]
73fn test_secret_loading_from_env() {
74 use crate::auth::AuthFramework;
75 use crate::config::AuthConfig;
76 use crate::testing::test_infrastructure::TestEnvironmentGuard;
77
78 let _env = TestEnvironmentGuard::new().with_jwt_secret("env_secret_value");
79
80 let config = AuthConfig::default().secret("config_secret_value");
81 let framework = AuthFramework::new(config.clone());
82 let token = framework
83 .token_manager()
84 .create_jwt_token("user", vec!["read".to_string()], None);
85 assert!(token.is_ok());
86}
87
88#[test]
89fn test_secret_loading_from_config() {
90 use crate::auth::AuthFramework;
91 use crate::config::AuthConfig;
92 use crate::testing::test_infrastructure::TestEnvironmentGuard;
93
94 let _env = TestEnvironmentGuard::new();
96
97 let config = AuthConfig::default().secret("config_secret_value");
98 let framework = AuthFramework::new(config.clone());
99 let token = framework
100 .token_manager()
101 .create_jwt_token("user", vec!["read".to_string()], None);
102 assert!(token.is_ok());
103}
104
105#[test]
106fn test_secret_missing_returns_error() {
107 use crate::auth::AuthFramework;
108 use crate::config::AuthConfig;
109
110 unsafe {
112 std::env::remove_var("JWT_SECRET");
113 }
114
115 unsafe {
117 std::env::set_var("ENVIRONMENT", "production");
118
119 let config = AuthConfig::default();
120 match AuthFramework::new_validated(config) {
121 Err(e) => {
122 assert!(e.to_string().contains("JWT secret"));
124 }
125 Ok(_) => panic!("Expected error when JWT_SECRET is missing in production"),
126 }
127
128 std::env::remove_var("ENVIRONMENT");
130 }
131}
132
133impl AuthMethod for MockAuthMethod {
134 type MethodResult = MethodResult;
135 type AuthToken = AuthToken;
136
137 fn name(&self) -> &str {
138 "mock"
139 }
140
141 fn validate_config(&self) -> Result<()> {
142 Ok(())
143 }
144
145 async fn authenticate(
146 &self,
147 credential: Credential,
148 _metadata: CredentialMetadata,
149 ) -> Result<Self::MethodResult> {
150 if let Some(delay) = self.delay {
152 tokio::time::sleep(delay).await;
153 }
154
155 if !self.should_succeed {
156 return Ok(MethodResult::Failure {
157 reason: "Mock authentication failed".to_string(),
158 });
159 }
160
161 let user_id = match credential {
163 Credential::Password { username, .. } => username.clone(),
164 Credential::ApiKey { key } => format!("api_user_{}", &key[..8.min(key.len())]),
165 Credential::OAuth { .. } => "oauth_user".to_string(),
166 Credential::DeviceCode { .. } => "device_user".to_string(),
167 _ => "test_user".to_string(),
168 };
169
170 let token = AuthToken {
172 token_id: Uuid::new_v4().to_string(),
173 user_id: user_id.clone(),
174 access_token: format!("mock_token_{}", Uuid::new_v4()),
175 refresh_token: Some(format!("refresh_{}", Uuid::new_v4())),
176 token_type: Some("Bearer".to_string()),
177 expires_at: chrono::Utc::now() + chrono::Duration::seconds(3600),
178 scopes: vec!["read".to_string(), "write".to_string()],
179 issued_at: chrono::Utc::now(),
180 auth_method: "mock".to_string(),
181 subject: Some(user_id.clone()),
182 issuer: Some("mock".to_string()),
183 user_profile: None,
184 client_id: Some("test_client".to_string()),
185 permissions: vec!["read:all".to_string(), "write:all".to_string()],
186 roles: vec!["mock_user".to_string()],
187 metadata: crate::tokens::TokenMetadata::default(),
188 };
189
190 Ok(MethodResult::Success(Box::new(token)))
191 }
192
193 async fn refresh_token(&self, _refresh_token: String) -> Result<Self::AuthToken> {
194 if !self.should_succeed {
195 return Err(AuthError::auth_method("mock", "Refresh failed"));
196 }
197
198 Ok(AuthToken {
199 token_id: Uuid::new_v4().to_string(),
200 user_id: "refreshed_user".to_string(),
201 access_token: "mock_refreshed_token".to_string(),
202 refresh_token: Some("mock_new_refresh_token".to_string()),
203 token_type: Some("Bearer".to_string()),
204 expires_at: chrono::Utc::now() + chrono::Duration::seconds(3600),
205 scopes: vec!["read".to_string(), "write".to_string()],
206 issued_at: chrono::Utc::now(),
207 auth_method: "mock".to_string(),
208 client_id: Some("test_client".to_string()),
209 metadata: crate::tokens::TokenMetadata::default(),
210 subject: Some("refreshed_user".to_string()),
211 issuer: Some("mock".to_string()),
212 user_profile: None,
213 permissions: vec!["read:all".to_string(), "write:all".to_string()],
214 roles: vec!["refreshed_user".to_string()],
215 })
216 }
217}
218
219#[derive(Debug, Clone)]
221pub struct MockStorage {
222 tokens: Arc<DashMap<String, AuthToken>>,
223 sessions: Arc<DashMap<String, SessionData>>,
224 kv_store: Arc<DashMap<String, Vec<u8>>>,
225 should_fail: bool,
226}
227
228impl MockStorage {
229 pub fn new() -> Self {
231 Self {
232 tokens: Arc::new(DashMap::new()),
233 sessions: Arc::new(DashMap::new()),
234 kv_store: Arc::new(DashMap::new()),
235 should_fail: false,
236 }
237 }
238
239 pub fn new_failing() -> Self {
241 let mut storage = Self::new();
242 storage.should_fail = true;
243 storage
244 }
245
246 pub fn with_token(&self, token: AuthToken) -> Result<()> {
248 if self.should_fail {
249 return Err(AuthError::internal("Mock storage configured to fail"));
250 }
251
252 self.tokens.insert(token.access_token.clone(), token);
254 Ok(())
255 }
256
257 pub fn clear(&self) {
259 self.tokens.clear();
260 self.sessions.clear();
261 self.kv_store.clear();
262 }
263}
264
265impl Default for MockStorage {
266 fn default() -> Self {
267 Self::new()
268 }
269}
270
271#[async_trait::async_trait]
272impl AuthStorage for MockStorage {
273 async fn store_token(&self, token: &AuthToken) -> Result<()> {
274 if self.should_fail {
275 return Err(AuthError::internal("Mock storage configured to fail"));
276 }
277
278 self.tokens
280 .insert(token.access_token.clone(), token.clone());
281 Ok(())
282 }
283
284 async fn get_token(&self, token_id: &str) -> Result<Option<AuthToken>> {
285 if self.should_fail {
286 return Err(AuthError::internal("Mock storage configured to fail"));
287 }
288
289 for entry in self.tokens.iter() {
291 if entry.value().token_id == token_id {
292 return Ok(Some(entry.value().clone()));
293 }
294 }
295 Ok(None)
296 }
297
298 async fn get_token_by_access_token(&self, access_token: &str) -> Result<Option<AuthToken>> {
299 if self.should_fail {
300 return Err(AuthError::internal("Mock storage configured to fail"));
301 }
302
303 Ok(self
305 .tokens
306 .get(access_token)
307 .map(|entry| entry.value().clone()))
308 }
309
310 async fn update_token(&self, token: &AuthToken) -> Result<()> {
311 if self.should_fail {
312 return Err(AuthError::internal("Mock storage configured to fail"));
313 }
314
315 self.tokens
317 .insert(token.access_token.clone(), token.clone());
318 Ok(())
319 }
320
321 async fn delete_token(&self, token_id: &str) -> Result<()> {
322 if self.should_fail {
323 return Err(AuthError::internal("Mock storage configured to fail"));
324 }
325
326 self.tokens.retain(|_, token| token.token_id != token_id);
328 Ok(())
329 }
330
331 async fn list_user_tokens(&self, user_id: &str) -> Result<Vec<AuthToken>> {
332 if self.should_fail {
333 return Err(AuthError::internal("Mock storage configured to fail"));
334 }
335
336 let mut tokens = Vec::new();
338 for entry in self.tokens.iter() {
339 if entry.value().user_id == user_id {
340 tokens.push(entry.value().clone());
341 }
342 }
343 Ok(tokens)
344 }
345
346 async fn store_session(&self, session_id: &str, data: &SessionData) -> Result<()> {
347 if self.should_fail {
348 return Err(AuthError::internal("Mock storage configured to fail"));
349 }
350
351 self.sessions.insert(session_id.to_string(), data.clone());
353 Ok(())
354 }
355
356 async fn get_session(&self, session_id: &str) -> Result<Option<SessionData>> {
357 if self.should_fail {
358 return Err(AuthError::internal("Mock storage configured to fail"));
359 }
360
361 Ok(self
363 .sessions
364 .get(session_id)
365 .map(|entry| entry.value().clone()))
366 }
367
368 async fn delete_session(&self, session_id: &str) -> Result<()> {
369 if self.should_fail {
370 return Err(AuthError::internal("Mock storage configured to fail"));
371 }
372
373 self.sessions.remove(session_id);
375 Ok(())
376 }
377
378 async fn list_user_sessions(&self, user_id: &str) -> Result<Vec<SessionData>> {
379 if self.should_fail {
380 return Err(AuthError::internal("Mock storage configured to fail"));
381 }
382
383 let mut sessions = Vec::new();
385 for entry in self.sessions.iter() {
386 if entry.value().user_id == user_id && !entry.value().is_expired() {
387 sessions.push(entry.value().clone());
388 }
389 }
390 Ok(sessions)
391 }
392
393 async fn store_kv(&self, key: &str, value: &[u8], _ttl: Option<Duration>) -> Result<()> {
394 if self.should_fail {
395 return Err(AuthError::internal("Mock storage configured to fail"));
396 }
397
398 self.kv_store.insert(key.to_string(), value.to_vec());
400 Ok(())
401 }
402
403 async fn get_kv(&self, key: &str) -> Result<Option<Vec<u8>>> {
404 if self.should_fail {
405 return Err(AuthError::internal("Mock storage configured to fail"));
406 }
407
408 Ok(self.kv_store.get(key).map(|entry| entry.value().clone()))
410 }
411
412 async fn delete_kv(&self, key: &str) -> Result<()> {
413 if self.should_fail {
414 return Err(AuthError::internal("Mock storage configured to fail"));
415 }
416
417 self.kv_store.remove(key);
419 Ok(())
420 }
421
422 async fn cleanup_expired(&self) -> Result<()> {
423 if self.should_fail {
424 return Err(AuthError::internal("Mock storage configured to fail"));
425 }
426
427 let now = chrono::Utc::now();
428
429 self.tokens.retain(|_, token| token.expires_at > now);
431
432 Ok(())
433 }
434
435 async fn count_active_sessions(&self) -> Result<u64> {
436 if self.should_fail {
437 return Err(AuthError::internal("Mock storage configured to fail"));
438 }
439
440 let mut count = 0u64;
442 for entry in self.sessions.iter() {
443 if !entry.value().is_expired() {
444 count += 1;
445 }
446 }
447 Ok(count)
448 }
449}
450
451pub mod helpers {
453 use super::*;
454 pub fn create_test_user_profile(user_id: &str) -> UserProfile {
458 UserProfile::new()
459 .with_id(user_id)
460 .with_provider("test")
461 .with_name(Some(format!("Test User {}", user_id)))
462 .with_email(Some(format!("{}@test.com", user_id)))
463 .with_email_verified(true)
464 }
465
466 pub fn create_test_token(user_id: &str) -> AuthToken {
468 let now = chrono::Utc::now();
469 AuthToken {
470 token_id: Uuid::new_v4().to_string(),
471 user_id: user_id.to_string(),
472 access_token: format!("test_token_{}", Uuid::new_v4()),
473 refresh_token: Some(format!("refresh_token_{}", Uuid::new_v4())),
474 token_type: Some("Bearer".to_string()),
475 expires_at: now + chrono::Duration::seconds(3600),
476 scopes: vec!["read".to_string(), "write".to_string()],
477 issued_at: now,
478 auth_method: "test".to_string(),
479 client_id: Some("test_client".to_string()),
480 metadata: crate::tokens::TokenMetadata::default(),
481 subject: Some(user_id.to_string()),
482 issuer: Some("test".to_string()),
483 user_profile: None,
484 permissions: vec!["read:all".to_string(), "write:all".to_string()],
485 roles: vec!["test_user".to_string()],
486 }
487 }
488
489 pub fn create_test_credentials() -> Vec<Credential> {
491 vec![
492 Credential::password("testuser", "testpass"),
493 Credential::api_key("test_api_key"),
494 Credential::oauth_code("test_auth_code"),
495 Credential::device_code("test_device_code", "test_client_id"),
496 Credential::jwt("test.jwt.token"),
497 ]
498 }
499}
500
501