1use crate::{Error, Result};
7use chrono::Duration;
8use mockforge_core::time_travel_now;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use uuid::Uuid;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct VirtualUser {
16 pub id: Uuid,
18 pub username: String,
20 pub email: String,
22 #[serde(skip_serializing)]
24 pub password_hash: Option<String>,
25 #[serde(default)]
27 pub roles: Vec<String>,
28}
29
30#[derive(Debug, Serialize, Deserialize)]
32struct JwtClaims {
33 sub: String,
35 username: String,
37 email: String,
39 exp: usize,
41 iat: usize,
43 #[serde(default)]
45 roles: Vec<String>,
46}
47
48impl JwtClaims {
49 fn is_expired(&self) -> bool {
54 let now = time_travel_now().timestamp() as usize;
55 now >= self.exp
56 }
57}
58
59pub struct VbrAuthService {
61 jwt_secret: String,
63 token_expiration: u64,
65 users: std::sync::Arc<tokio::sync::RwLock<HashMap<String, VirtualUser>>>,
68}
69
70impl VbrAuthService {
71 pub fn new(jwt_secret: String, token_expiration_secs: u64) -> Self {
73 Self {
74 jwt_secret,
75 token_expiration: token_expiration_secs,
76 users: std::sync::Arc::new(tokio::sync::RwLock::new(HashMap::new())),
77 }
78 }
79
80 pub async fn create_default_user(
82 &self,
83 username: String,
84 password: String,
85 email: String,
86 ) -> Result<VirtualUser> {
87 let password_hash = self.hash_password(&password)?;
89
90 let user = VirtualUser {
91 id: Uuid::new_v4(),
92 username: username.clone(),
93 email,
94 password_hash: Some(password_hash),
95 roles: Vec::new(),
96 };
97
98 let mut users = self.users.write().await;
99 users.insert(username, user.clone());
100 Ok(user)
101 }
102
103 fn hash_password(&self, password: &str) -> Result<String> {
105 use sha2::{Digest, Sha256};
107 let mut hasher = Sha256::new();
108 hasher.update(password.as_bytes());
109 hasher.update(self.jwt_secret.as_bytes()); let hash = hasher.finalize();
111 Ok(format!("{:x}", hash))
112 }
113
114 fn verify_password(&self, password: &str, hash: &str) -> bool {
116 match self.hash_password(password) {
117 Ok(new_hash) => new_hash == hash,
118 Err(_) => false,
119 }
120 }
121
122 pub async fn authenticate(&self, username: &str, password: &str) -> Result<VirtualUser> {
124 let users = self.users.read().await;
125 let user = users
126 .get(username)
127 .ok_or_else(|| Error::generic("User not found".to_string()))?;
128
129 if let Some(ref hash) = user.password_hash {
131 if !self.verify_password(password, hash) {
132 return Err(Error::generic("Invalid password".to_string()));
133 }
134 }
135
136 Ok(user.clone())
137 }
138
139 pub fn generate_token(&self, user: &VirtualUser) -> Result<String> {
144 let now = time_travel_now();
145 let exp = now
146 .checked_add_signed(Duration::seconds(self.token_expiration as i64))
147 .ok_or_else(|| Error::generic("Invalid expiration time".to_string()))?
148 .timestamp() as usize;
149
150 let claims = JwtClaims {
151 sub: user.id.to_string(),
152 username: user.username.clone(),
153 email: user.email.clone(),
154 exp,
155 iat: now.timestamp() as usize,
156 roles: user.roles.clone(),
157 };
158
159 #[cfg(feature = "jwt")]
161 {
162 use jsonwebtoken::{encode, EncodingKey, Header};
163 let token = encode(
164 &Header::default(),
165 &claims,
166 &EncodingKey::from_secret(self.jwt_secret.as_bytes()),
167 )
168 .map_err(|e| Error::generic(format!("Token generation failed: {}", e)))?;
169 Ok(token)
170 }
171
172 #[cfg(not(feature = "jwt"))]
173 {
174 let token_data = serde_json::to_string(&claims)
176 .map_err(|e| Error::generic(format!("Serialization failed: {}", e)))?;
177 Ok(format!("vbr.{}", base64::encode(&token_data)))
178 }
179 }
180
181 pub fn validate_token(&self, token: &str) -> Result<VirtualUser> {
183 #[cfg(feature = "jwt")]
184 {
185 use jsonwebtoken::{decode, DecodingKey, Validation};
186 let validation = Validation::default();
187 let token_data = decode::<JwtClaims>(
188 token,
189 &DecodingKey::from_secret(self.jwt_secret.as_bytes()),
190 &validation,
191 )
192 .map_err(|e| Error::generic(format!("Token validation failed: {}", e)))?;
193
194 if token_data.claims.is_expired() {
195 return Err(Error::generic("Token expired".to_string()));
196 }
197
198 Ok(VirtualUser {
199 id: Uuid::parse_str(&token_data.claims.sub)
200 .map_err(|e| Error::generic(format!("Invalid user ID: {}", e)))?,
201 username: token_data.claims.username,
202 email: token_data.claims.email,
203 password_hash: None,
204 roles: token_data.claims.roles,
205 })
206 }
207
208 #[cfg(not(feature = "jwt"))]
209 {
210 if let Some(token_data) = token.strip_prefix("vbr.") {
212 let decoded = base64::decode(token_data)
213 .map_err(|e| Error::generic(format!("Token decode failed: {}", e)))?;
214 let claims: JwtClaims = serde_json::from_slice(&decoded)
215 .map_err(|e| Error::generic(format!("Token parse failed: {}", e)))?;
216
217 if claims.is_expired() {
218 return Err(Error::generic("Token expired".to_string()));
219 }
220
221 Ok(VirtualUser {
222 id: Uuid::parse_str(&claims.sub)
223 .map_err(|e| Error::generic(format!("Invalid user ID: {}", e)))?,
224 username: claims.username,
225 email: claims.email,
226 password_hash: None,
227 roles: claims.roles,
228 })
229 } else {
230 Err(Error::generic("Invalid token format".to_string()))
231 }
232 }
233 }
234
235 pub async fn get_user(&self, username: &str) -> Option<VirtualUser> {
237 let users = self.users.read().await;
238 users.get(username).cloned()
239 }
240
241 pub async fn list_users(&self) -> Vec<VirtualUser> {
243 let users = self.users.read().await;
244 users.values().cloned().collect()
245 }
246}
247
248#[cfg(not(feature = "jwt"))]
250mod base64 {
251 use base64::{engine::general_purpose, Engine as _};
252 pub fn encode(data: &str) -> String {
253 general_purpose::STANDARD.encode(data.as_bytes())
254 }
255 pub fn decode(data: &str) -> Result<Vec<u8>, String> {
256 general_purpose::STANDARD
257 .decode(data)
258 .map_err(|e| format!("Decode error: {}", e))
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
268 fn test_virtual_user_clone() {
269 let user = VirtualUser {
270 id: Uuid::new_v4(),
271 username: "testuser".to_string(),
272 email: "test@example.com".to_string(),
273 password_hash: Some("hash123".to_string()),
274 roles: vec!["admin".to_string()],
275 };
276
277 let cloned = user.clone();
278 assert_eq!(user.username, cloned.username);
279 assert_eq!(user.email, cloned.email);
280 assert_eq!(user.roles, cloned.roles);
281 }
282
283 #[test]
284 fn test_virtual_user_debug() {
285 let user = VirtualUser {
286 id: Uuid::new_v4(),
287 username: "debuguser".to_string(),
288 email: "debug@test.com".to_string(),
289 password_hash: None,
290 roles: Vec::new(),
291 };
292
293 let debug = format!("{:?}", user);
294 assert!(debug.contains("VirtualUser"));
295 assert!(debug.contains("debuguser"));
296 }
297
298 #[test]
299 fn test_virtual_user_serialize() {
300 let user = VirtualUser {
301 id: Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap(),
302 username: "serialuser".to_string(),
303 email: "serial@test.com".to_string(),
304 password_hash: Some("secret".to_string()),
305 roles: vec!["user".to_string()],
306 };
307
308 let json = serde_json::to_string(&user).unwrap();
309 assert!(json.contains("serialuser"));
310 assert!(json.contains("serial@test.com"));
311 assert!(!json.contains("secret"));
313 }
314
315 #[test]
316 fn test_virtual_user_deserialize() {
317 let json = r#"{
318 "id": "550e8400-e29b-41d4-a716-446655440000",
319 "username": "deserialuser",
320 "email": "deserial@test.com",
321 "roles": ["admin", "user"]
322 }"#;
323
324 let user: VirtualUser = serde_json::from_str(json).unwrap();
325 assert_eq!(user.username, "deserialuser");
326 assert_eq!(user.email, "deserial@test.com");
327 assert_eq!(user.roles, vec!["admin", "user"]);
328 }
329
330 #[test]
331 fn test_virtual_user_default_roles() {
332 let json = r#"{
333 "id": "550e8400-e29b-41d4-a716-446655440000",
334 "username": "norolesuser",
335 "email": "noroles@test.com"
336 }"#;
337
338 let user: VirtualUser = serde_json::from_str(json).unwrap();
339 assert!(user.roles.is_empty());
340 }
341
342 #[test]
343 fn test_virtual_user_with_multiple_roles() {
344 let user = VirtualUser {
345 id: Uuid::new_v4(),
346 username: "multirole".to_string(),
347 email: "multi@test.com".to_string(),
348 password_hash: None,
349 roles: vec![
350 "admin".to_string(),
351 "moderator".to_string(),
352 "user".to_string(),
353 ],
354 };
355
356 assert_eq!(user.roles.len(), 3);
357 assert!(user.roles.contains(&"admin".to_string()));
358 assert!(user.roles.contains(&"moderator".to_string()));
359 }
360
361 #[test]
363 fn test_vbr_auth_service_new() {
364 let service = VbrAuthService::new("test_secret".to_string(), 3600);
365 assert_eq!(service.jwt_secret, "test_secret");
366 assert_eq!(service.token_expiration, 3600);
367 }
368
369 #[test]
370 fn test_vbr_auth_service_hash_password() {
371 let service = VbrAuthService::new("secret".to_string(), 3600);
372 let hash1 = service.hash_password("password123").unwrap();
373 let hash2 = service.hash_password("password123").unwrap();
374
375 assert_eq!(hash1, hash2);
377
378 let hash3 = service.hash_password("different").unwrap();
380 assert_ne!(hash1, hash3);
381 }
382
383 #[test]
384 fn test_vbr_auth_service_verify_password() {
385 let service = VbrAuthService::new("secret".to_string(), 3600);
386 let hash = service.hash_password("mypassword").unwrap();
387
388 assert!(service.verify_password("mypassword", &hash));
389 assert!(!service.verify_password("wrongpassword", &hash));
390 }
391
392 #[tokio::test]
393 async fn test_vbr_auth_service_create_default_user() {
394 let service = VbrAuthService::new("secret".to_string(), 3600);
395
396 let user = service
397 .create_default_user(
398 "newuser".to_string(),
399 "password123".to_string(),
400 "new@test.com".to_string(),
401 )
402 .await
403 .unwrap();
404
405 assert_eq!(user.username, "newuser");
406 assert_eq!(user.email, "new@test.com");
407 assert!(user.password_hash.is_some());
408 }
409
410 #[tokio::test]
411 async fn test_vbr_auth_service_authenticate_success() {
412 let service = VbrAuthService::new("secret".to_string(), 3600);
413
414 service
415 .create_default_user(
416 "authuser".to_string(),
417 "correctpass".to_string(),
418 "auth@test.com".to_string(),
419 )
420 .await
421 .unwrap();
422
423 let result = service.authenticate("authuser", "correctpass").await;
424 assert!(result.is_ok());
425 let user = result.unwrap();
426 assert_eq!(user.username, "authuser");
427 }
428
429 #[tokio::test]
430 async fn test_vbr_auth_service_authenticate_wrong_password() {
431 let service = VbrAuthService::new("secret".to_string(), 3600);
432
433 service
434 .create_default_user(
435 "wrongpassuser".to_string(),
436 "correctpass".to_string(),
437 "wrong@test.com".to_string(),
438 )
439 .await
440 .unwrap();
441
442 let result = service.authenticate("wrongpassuser", "wrongpass").await;
443 assert!(result.is_err());
444 }
445
446 #[tokio::test]
447 async fn test_vbr_auth_service_authenticate_user_not_found() {
448 let service = VbrAuthService::new("secret".to_string(), 3600);
449
450 let result = service.authenticate("nonexistent", "anypass").await;
451 assert!(result.is_err());
452 }
453
454 #[tokio::test]
455 async fn test_vbr_auth_service_get_user() {
456 let service = VbrAuthService::new("secret".to_string(), 3600);
457
458 service
459 .create_default_user(
460 "getuser".to_string(),
461 "pass".to_string(),
462 "get@test.com".to_string(),
463 )
464 .await
465 .unwrap();
466
467 let user = service.get_user("getuser").await;
468 assert!(user.is_some());
469 assert_eq!(user.unwrap().email, "get@test.com");
470
471 let nonexistent = service.get_user("nonexistent").await;
472 assert!(nonexistent.is_none());
473 }
474
475 #[tokio::test]
476 async fn test_vbr_auth_service_list_users() {
477 let service = VbrAuthService::new("secret".to_string(), 3600);
478
479 let users = service.list_users().await;
481 assert!(users.is_empty());
482
483 service
485 .create_default_user(
486 "user1".to_string(),
487 "pass1".to_string(),
488 "u1@test.com".to_string(),
489 )
490 .await
491 .unwrap();
492
493 service
494 .create_default_user(
495 "user2".to_string(),
496 "pass2".to_string(),
497 "u2@test.com".to_string(),
498 )
499 .await
500 .unwrap();
501
502 let users = service.list_users().await;
503 assert_eq!(users.len(), 2);
504 }
505
506 #[tokio::test]
507 async fn test_vbr_auth_service_generate_token() {
508 let service = VbrAuthService::new("my_jwt_secret".to_string(), 3600);
509
510 let user = service
511 .create_default_user(
512 "tokenuser".to_string(),
513 "pass".to_string(),
514 "token@test.com".to_string(),
515 )
516 .await
517 .unwrap();
518
519 let token = service.generate_token(&user);
520 assert!(token.is_ok());
521 let token_str = token.unwrap();
522 assert!(!token_str.is_empty());
523 }
524
525 #[tokio::test]
526 async fn test_vbr_auth_service_validate_token() {
527 let service = VbrAuthService::new("jwt_secret_for_test".to_string(), 3600);
528
529 let user = service
530 .create_default_user(
531 "validateuser".to_string(),
532 "pass".to_string(),
533 "validate@test.com".to_string(),
534 )
535 .await
536 .unwrap();
537
538 let token = service.generate_token(&user).unwrap();
539 let validated_user = service.validate_token(&token);
540
541 assert!(validated_user.is_ok());
542 let validated = validated_user.unwrap();
543 assert_eq!(validated.username, "validateuser");
544 assert_eq!(validated.email, "validate@test.com");
545 }
546
547 #[test]
548 fn test_vbr_auth_service_validate_invalid_token() {
549 let service = VbrAuthService::new("secret".to_string(), 3600);
550
551 let result = service.validate_token("invalid_token");
552 assert!(result.is_err());
553 }
554
555 #[test]
556 fn test_vbr_auth_service_validate_malformed_token() {
557 let service = VbrAuthService::new("secret".to_string(), 3600);
558
559 let result = service.validate_token("not.vbr.token");
561 assert!(result.is_err());
562 }
563
564 #[test]
565 fn test_hash_different_secrets_produce_different_hashes() {
566 let service1 = VbrAuthService::new("secret1".to_string(), 3600);
567 let service2 = VbrAuthService::new("secret2".to_string(), 3600);
568
569 let hash1 = service1.hash_password("samepassword").unwrap();
570 let hash2 = service2.hash_password("samepassword").unwrap();
571
572 assert_ne!(hash1, hash2);
574 }
575
576 #[tokio::test]
577 async fn test_vbr_auth_service_user_with_roles() {
578 let service = VbrAuthService::new("secret".to_string(), 3600);
579
580 let user = service
581 .create_default_user(
582 "roleuser".to_string(),
583 "pass".to_string(),
584 "role@test.com".to_string(),
585 )
586 .await
587 .unwrap();
588
589 assert!(user.roles.is_empty());
591 }
592}