1use axum::{extract::State, http::StatusCode, response::Json};
16use bcrypt::{hash, verify, DEFAULT_COST};
17use chrono::{Duration, Utc};
18use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
19use serde::{Deserialize, Serialize};
20use std::collections::HashMap;
21use std::sync::Arc;
22use std::time::{Duration as StdDuration, Instant};
23use tokio::sync::RwLock;
24
25use crate::handlers::AdminState;
26use crate::models::ApiResponse;
27use crate::rbac::UserContext;
28use mockforge_collab::models::UserRole;
29
30mod password_policy;
31pub use password_policy::{PasswordPolicy, PasswordValidationError};
32
33static JWT_SECRET: &[u8] = b"mockforge-secret-key-change-in-production";
35
36#[derive(Debug, Serialize, Deserialize)]
38pub struct Claims {
39 pub sub: String,
41 pub username: String,
43 pub role: String,
45 pub email: Option<String>,
47 pub iat: i64,
49 pub exp: i64,
51}
52
53#[derive(Debug, Deserialize)]
55pub struct LoginRequest {
56 pub username: String,
57 pub password: String,
58}
59
60#[derive(Debug, Serialize)]
62pub struct LoginResponse {
63 pub token: String,
64 pub refresh_token: String,
65 pub user: UserInfo,
66 pub expires_in: i64,
67}
68
69#[derive(Debug, Serialize, Clone)]
71pub struct UserInfo {
72 pub id: String,
73 pub username: String,
74 pub role: String,
75 pub email: Option<String>,
76}
77
78#[derive(Debug, Deserialize)]
80pub struct RefreshTokenRequest {
81 pub refresh_token: String,
82}
83
84#[derive(Debug, Clone)]
86pub struct UserStore {
87 users: Arc<RwLock<HashMap<String, User>>>,
88 rate_limiter: RateLimiter,
89 account_lockout: AccountLockout,
90 password_policy: PasswordPolicy,
91}
92
93#[derive(Debug, Clone)]
94struct User {
95 id: String,
96 username: String,
97 password_hash: String, role: UserRole,
99 email: Option<String>,
100}
101
102#[derive(Debug, Clone)]
104struct RateLimiter {
105 attempts: Arc<RwLock<HashMap<String, Vec<Instant>>>>,
106 max_attempts: usize,
107 window_seconds: u64,
108}
109
110#[derive(Debug, Clone)]
112struct AccountLockout {
113 failed_attempts: Arc<RwLock<HashMap<String, (usize, Option<Instant>)>>>,
115 max_failed_attempts: usize,
117 lockout_duration_seconds: u64,
119}
120
121impl AccountLockout {
122 fn new(max_failed_attempts: usize, lockout_duration_seconds: u64) -> Self {
123 Self {
124 failed_attempts: Arc::new(RwLock::new(HashMap::new())),
125 max_failed_attempts,
126 lockout_duration_seconds,
127 }
128 }
129
130 async fn is_locked(&self, username: &str) -> bool {
132 let attempts = self.failed_attempts.read().await;
133 if let Some((count, locked_until)) = attempts.get(username) {
134 if *count >= self.max_failed_attempts {
135 if let Some(until) = locked_until {
136 return until > &Instant::now();
137 }
138 }
139 }
140 false
141 }
142
143 async fn record_failure(&self, username: &str) {
145 let mut attempts = self.failed_attempts.write().await;
146 let entry = attempts.entry(username.to_string()).or_insert((0, None));
147 entry.0 += 1;
148
149 if entry.0 >= self.max_failed_attempts {
150 entry.1 = Some(Instant::now() + StdDuration::from_secs(self.lockout_duration_seconds));
151 tracing::warn!("Account locked: {} ({} failed attempts)", username, entry.0);
152 }
153 }
154
155 async fn reset(&self, username: &str) {
157 let mut attempts = self.failed_attempts.write().await;
158 attempts.remove(username);
159 }
160
161 async fn remaining_lockout_time(&self, username: &str) -> Option<u64> {
163 let attempts = self.failed_attempts.read().await;
164 if let Some((_, locked_until)) = attempts.get(username) {
165 if let Some(until) = locked_until {
166 let now = Instant::now();
167 if until > &now {
168 return Some(until.duration_since(now).as_secs());
169 }
170 }
171 }
172 None
173 }
174}
175
176impl RateLimiter {
177 fn new(max_attempts: usize, window_seconds: u64) -> Self {
178 Self {
179 attempts: Arc::new(RwLock::new(HashMap::new())),
180 max_attempts,
181 window_seconds,
182 }
183 }
184
185 async fn check_rate_limit(&self, key: &str) -> Result<(), String> {
186 let mut attempts = self.attempts.write().await;
187 let now = Instant::now();
188 let window = StdDuration::from_secs(self.window_seconds);
189
190 if let Some(attempt_times) = attempts.get_mut(key) {
192 attempt_times.retain(|&time| now.duration_since(time) < window);
193
194 if attempt_times.len() >= self.max_attempts {
195 return Err(format!(
196 "Too many login attempts. Please try again in {} seconds.",
197 self.window_seconds
198 ));
199 }
200 }
201
202 attempts.entry(key.to_string()).or_insert_with(Vec::new).push(now);
204
205 Ok(())
206 }
207
208 async fn reset_rate_limit(&self, key: &str) {
209 let mut attempts = self.attempts.write().await;
210 attempts.remove(key);
211 }
212}
213
214impl Default for UserStore {
215 fn default() -> Self {
216 Self::new()
217 }
218}
219
220impl UserStore {
221 pub fn new() -> Self {
222 let users = Arc::new(RwLock::new(HashMap::new()));
223 let rate_limiter = RateLimiter::new(5, 300); let account_lockout = AccountLockout::new(5, 900); let password_policy = PasswordPolicy::default(); let default_users = vec![
230 ("admin", "admin123", UserRole::Admin, "admin@mockforge.dev"),
232 ("viewer", "viewer123", UserRole::Viewer, "viewer@mockforge.dev"),
234 ("editor", "editor123", UserRole::Editor, "editor@mockforge.dev"),
236 ];
237
238 let store = Self {
239 users,
240 rate_limiter,
241 account_lockout,
242 password_policy,
243 };
244
245 let store_clone = store.clone();
247 tokio::spawn(async move {
248 let mut users = store_clone.users.write().await;
249 for (username, password, role, email) in default_users {
250 if let Ok(password_hash) = hash(password, DEFAULT_COST) {
252 let user = User {
253 id: format!("{}-001", username),
254 username: username.to_string(),
255 password_hash,
256 role,
257 email: Some(email.to_string()),
258 };
259 users.insert(username.to_string(), user);
260 } else {
261 tracing::error!("Failed to hash password for user: {}", username);
262 }
263 }
264 });
265
266 store
267 }
268
269 pub async fn authenticate(&self, username: &str, password: &str) -> Result<User, String> {
270 if self.account_lockout.is_locked(username).await {
272 if let Some(remaining) = self.account_lockout.remaining_lockout_time(username).await {
273 return Err(format!(
274 "Account is locked due to too many failed login attempts. Please try again in {} seconds.",
275 remaining
276 ));
277 }
278 }
279
280 self.rate_limiter.check_rate_limit(username).await?;
282
283 let users = self.users.read().await;
284 if let Some(user) = users.get(username) {
285 match verify(password, &user.password_hash) {
287 Ok(true) => {
288 self.rate_limiter.reset_rate_limit(username).await;
290 self.account_lockout.reset(username).await;
291 Ok(user.clone())
292 }
293 Ok(false) => {
294 self.account_lockout.record_failure(username).await;
296 Err("Invalid username or password".to_string())
297 }
298 Err(e) => {
299 tracing::error!("Password verification error: {}", e);
300 Err("Authentication error".to_string())
301 }
302 }
303 } else {
304 Err("Invalid username or password".to_string())
306 }
307 }
308
309 pub async fn create_user(
311 &self,
312 username: String,
313 password: String,
314 role: UserRole,
315 email: Option<String>,
316 ) -> Result<User, String> {
317 #[cfg(feature = "password-policy")]
319 {
320 self.password_policy
321 .validate(&password, Some(&username))
322 .map_err(|e| e.to_string())?;
323 }
324
325 let mut users = self.users.write().await;
327 if users.contains_key(&username) {
328 return Err("Username already exists".to_string());
329 }
330
331 let password_hash =
333 hash(&password, DEFAULT_COST).map_err(|e| format!("Failed to hash password: {}", e))?;
334
335 let user = User {
337 id: format!("{}-{}", username, uuid::Uuid::new_v4()),
338 username: username.clone(),
339 password_hash,
340 role,
341 email,
342 };
343
344 users.insert(username, user.clone());
345 Ok(user)
346 }
347
348 pub async fn get_user_by_id(&self, user_id: &str) -> Option<User> {
349 let users = self.users.read().await;
350 users.values().find(|u| u.id == user_id).cloned()
351 }
352}
353
354static GLOBAL_USER_STORE: std::sync::OnceLock<Arc<UserStore>> = std::sync::OnceLock::new();
356
357pub fn init_global_user_store() -> Arc<UserStore> {
359 GLOBAL_USER_STORE.get_or_init(|| Arc::new(UserStore::new())).clone()
360}
361
362pub fn get_global_user_store() -> Option<Arc<UserStore>> {
364 GLOBAL_USER_STORE.get().cloned()
365}
366
367pub fn generate_token(
369 user: &User,
370 expires_in_seconds: i64,
371) -> Result<String, jsonwebtoken::errors::Error> {
372 let now = Utc::now();
373 let exp = now + Duration::seconds(expires_in_seconds);
374 let secret = JWT_SECRET;
375
376 let claims = Claims {
377 sub: user.id.clone(),
378 username: user.username.clone(),
379 role: format!("{:?}", user.role).to_lowercase(),
380 email: user.email.clone(),
381 iat: now.timestamp(),
382 exp: exp.timestamp(),
383 };
384
385 encode(&Header::default(), &claims, &EncodingKey::from_secret(secret))
386}
387
388pub fn generate_refresh_token(user: &User) -> Result<String, jsonwebtoken::errors::Error> {
390 generate_token(user, 7 * 24 * 60 * 60)
392}
393
394pub fn validate_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
396 let secret = JWT_SECRET;
397 let token_data =
398 decode::<Claims>(token, &DecodingKey::from_secret(secret), &Validation::default())?;
399
400 Ok(token_data.claims)
401}
402
403pub async fn login(
405 State(_state): State<AdminState>,
406 Json(request): Json<LoginRequest>,
407) -> Result<Json<ApiResponse<LoginResponse>>, StatusCode> {
408 let user_store = get_global_user_store().ok_or_else(|| {
409 tracing::error!("User store not initialized");
410 StatusCode::INTERNAL_SERVER_ERROR
411 })?;
412
413 let user =
415 user_store
416 .authenticate(&request.username, &request.password)
417 .await
418 .map_err(|e| {
419 tracing::warn!("Authentication failed for user {}: {}", request.username, e);
420 if e.contains("Too many") {
422 StatusCode::TOO_MANY_REQUESTS
423 } else {
424 StatusCode::UNAUTHORIZED
425 }
426 })?;
427
428 let access_token = generate_token(&user, 24 * 60 * 60) .map_err(|e| {
431 tracing::error!("Failed to generate access token: {}", e);
432 StatusCode::INTERNAL_SERVER_ERROR
433 })?;
434
435 let refresh_token = generate_refresh_token(&user).map_err(|e| {
436 tracing::error!("Failed to generate refresh token: {}", e);
437 StatusCode::INTERNAL_SERVER_ERROR
438 })?;
439
440 let user_info = UserInfo {
441 id: user.id,
442 username: user.username,
443 role: format!("{:?}", user.role).to_lowercase(),
444 email: user.email,
445 };
446
447 Ok(Json(ApiResponse::success(LoginResponse {
448 token: access_token,
449 refresh_token,
450 user: user_info,
451 expires_in: 24 * 60 * 60,
452 })))
453}
454
455pub async fn refresh_token(
457 State(_state): State<AdminState>,
458 Json(request): Json<RefreshTokenRequest>,
459) -> Result<Json<ApiResponse<LoginResponse>>, StatusCode> {
460 let claims = validate_token(&request.refresh_token).map_err(|_| {
462 tracing::warn!("Invalid refresh token");
463 StatusCode::UNAUTHORIZED
464 })?;
465
466 let user_store = get_global_user_store().ok_or_else(|| {
467 tracing::error!("User store not initialized");
468 StatusCode::INTERNAL_SERVER_ERROR
469 })?;
470
471 let user = user_store.get_user_by_id(&claims.sub).await.ok_or_else(|| {
473 tracing::warn!("User not found: {}", claims.sub);
474 StatusCode::UNAUTHORIZED
475 })?;
476
477 let access_token = generate_token(&user, 24 * 60 * 60) .map_err(|e| {
480 tracing::error!("Failed to generate access token: {}", e);
481 StatusCode::INTERNAL_SERVER_ERROR
482 })?;
483
484 let refresh_token = generate_refresh_token(&user).map_err(|e| {
485 tracing::error!("Failed to generate refresh token: {}", e);
486 StatusCode::INTERNAL_SERVER_ERROR
487 })?;
488
489 let user_info = UserInfo {
490 id: user.id,
491 username: user.username,
492 role: format!("{:?}", user.role).to_lowercase(),
493 email: user.email,
494 };
495
496 Ok(Json(ApiResponse::success(LoginResponse {
497 token: access_token,
498 refresh_token,
499 user: user_info,
500 expires_in: 24 * 60 * 60,
501 })))
502}
503
504pub async fn get_current_user(
506 State(_state): State<AdminState>,
507) -> Result<Json<ApiResponse<UserInfo>>, StatusCode> {
508 Err(StatusCode::NOT_IMPLEMENTED)
511}
512
513pub async fn logout(State(_state): State<AdminState>) -> Json<ApiResponse<String>> {
515 Json(ApiResponse::success("Logged out successfully".to_string()))
518}
519
520pub fn claims_to_user_context(claims: &Claims) -> UserContext {
522 let role = match claims.role.as_str() {
523 "admin" => UserRole::Admin,
524 "editor" => UserRole::Editor,
525 "viewer" => UserRole::Viewer,
526 _ => UserRole::Viewer,
527 };
528
529 UserContext {
530 user_id: claims.sub.clone(),
531 username: claims.username.clone(),
532 role,
533 email: claims.email.clone(),
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 #[tokio::test]
542 async fn test_user_store_creation() {
543 let store = UserStore::new();
544 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
546
547 let result = store.authenticate("admin", "admin123").await;
549 assert!(result.is_ok(), "Admin user should exist");
550 }
551
552 #[tokio::test]
553 async fn test_user_store_default() {
554 let store = UserStore::default();
555 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
556
557 let result = store.authenticate("admin", "admin123").await;
558 assert!(result.is_ok());
559 }
560
561 #[tokio::test]
562 async fn test_authenticate_success() {
563 let store = UserStore::new();
564 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
565
566 let result = store.authenticate("admin", "admin123").await;
567 assert!(result.is_ok());
568
569 let user = result.unwrap();
570 assert_eq!(user.username, "admin");
571 assert!(matches!(user.role, UserRole::Admin));
572 }
573
574 #[tokio::test]
575 async fn test_authenticate_wrong_password() {
576 let store = UserStore::new();
577 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
578
579 let result = store.authenticate("admin", "wrongpassword").await;
580 assert!(result.is_err());
581 assert_eq!(result.unwrap_err(), "Invalid username or password");
582 }
583
584 #[tokio::test]
585 async fn test_authenticate_nonexistent_user() {
586 let store = UserStore::new();
587 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
588
589 let result = store.authenticate("nonexistent", "password").await;
590 assert!(result.is_err());
591 assert_eq!(result.unwrap_err(), "Invalid username or password");
592 }
593
594 #[tokio::test]
595 async fn test_rate_limiting() {
596 let store = UserStore::new();
597 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
598
599 for _ in 0..5 {
601 let _ = store.authenticate("admin", "wrongpassword").await;
602 }
603
604 let result = store.authenticate("admin", "wrongpassword").await;
606 assert!(result.is_err());
607 let error = result.unwrap_err();
608 assert!(
609 error.contains("Too many") || error.contains("locked"),
610 "Expected rate limit or lockout error, got: {}",
611 error
612 );
613 }
614
615 #[tokio::test]
616 async fn test_account_lockout_after_failures() {
617 let store = UserStore::new();
618 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
619
620 for _ in 0..5 {
622 let _ = store.authenticate("editor", "wrongpassword").await;
623 }
624
625 let result = store.authenticate("editor", "editor123").await;
627 assert!(result.is_err());
628 let error = result.unwrap_err();
629 assert!(
630 error.contains("locked") || error.contains("Too many"),
631 "Expected lockout error, got: {}",
632 error
633 );
634 }
635
636 #[tokio::test]
637 async fn test_account_lockout_reset_on_success() {
638 let store = UserStore::new();
639 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
640
641 for _ in 0..2 {
643 let _ = store.authenticate("viewer", "wrongpassword").await;
644 }
645
646 let result = store.authenticate("viewer", "viewer123").await;
648 assert!(result.is_ok());
649
650 for _ in 0..2 {
652 let _ = store.authenticate("viewer", "wrongpassword").await;
653 }
654
655 let result = store.authenticate("viewer", "wrongpassword").await;
657 assert!(result.is_err());
658 assert!(!result.unwrap_err().contains("locked"));
659 }
660
661 #[tokio::test]
662 async fn test_create_user_success() {
663 let store = UserStore::new();
664 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
665
666 let result = store
667 .create_user(
668 "newuser".to_string(),
669 "NewP@ssw0rd123".to_string(),
670 UserRole::Editor,
671 Some("newuser@example.com".to_string()),
672 )
673 .await;
674
675 assert!(result.is_ok());
676 let user = result.unwrap();
677 assert_eq!(user.username, "newuser");
678 assert!(matches!(user.role, UserRole::Editor));
679 assert_eq!(user.email, Some("newuser@example.com".to_string()));
680
681 let auth_result = store.authenticate("newuser", "NewP@ssw0rd123").await;
683 assert!(auth_result.is_ok());
684 }
685
686 #[tokio::test]
687 async fn test_create_user_duplicate_username() {
688 let store = UserStore::new();
689 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
690
691 let result = store
692 .create_user("admin".to_string(), "NewP@ssw0rd123".to_string(), UserRole::Editor, None)
693 .await;
694
695 assert!(result.is_err());
696 assert_eq!(result.unwrap_err(), "Username already exists");
697 }
698
699 #[tokio::test]
700 async fn test_get_user_by_id() {
701 let store = UserStore::new();
702 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
703
704 let auth_result = store.authenticate("admin", "admin123").await.unwrap();
706 let user_id = auth_result.id.clone();
707
708 let result = store.get_user_by_id(&user_id).await;
710 assert!(result.is_some());
711
712 let user = result.unwrap();
713 assert_eq!(user.id, user_id);
714 assert_eq!(user.username, "admin");
715 }
716
717 #[tokio::test]
718 async fn test_get_user_by_id_not_found() {
719 let store = UserStore::new();
720 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
721
722 let result = store.get_user_by_id("nonexistent-id").await;
723 assert!(result.is_none());
724 }
725
726 #[test]
727 fn test_generate_token_success() {
728 let user = User {
729 id: "test-id".to_string(),
730 username: "testuser".to_string(),
731 password_hash: "hash".to_string(),
732 role: UserRole::Editor,
733 email: Some("test@example.com".to_string()),
734 };
735
736 let result = generate_token(&user, 3600);
737 assert!(result.is_ok());
738
739 let token = result.unwrap();
740 assert!(!token.is_empty());
741 }
742
743 #[test]
744 fn test_generate_refresh_token() {
745 let user = User {
746 id: "test-id".to_string(),
747 username: "testuser".to_string(),
748 password_hash: "hash".to_string(),
749 role: UserRole::Editor,
750 email: None,
751 };
752
753 let result = generate_refresh_token(&user);
754 assert!(result.is_ok());
755
756 let token = result.unwrap();
757 assert!(!token.is_empty());
758 }
759
760 #[test]
761 fn test_validate_token_success() {
762 let user = User {
763 id: "test-id".to_string(),
764 username: "testuser".to_string(),
765 password_hash: "hash".to_string(),
766 role: UserRole::Viewer,
767 email: Some("test@example.com".to_string()),
768 };
769
770 let token = generate_token(&user, 3600).unwrap();
771 let result = validate_token(&token);
772
773 assert!(result.is_ok());
774 let claims = result.unwrap();
775 assert_eq!(claims.sub, "test-id");
776 assert_eq!(claims.username, "testuser");
777 assert_eq!(claims.role, "viewer");
778 assert_eq!(claims.email, Some("test@example.com".to_string()));
779 }
780
781 #[test]
782 fn test_validate_token_invalid() {
783 let result = validate_token("invalid.token.here");
784 assert!(result.is_err());
785 }
786
787 #[test]
788 fn test_validate_token_expired() {
789 let user = User {
790 id: "test-id".to_string(),
791 username: "testuser".to_string(),
792 password_hash: "hash".to_string(),
793 role: UserRole::Editor,
794 email: None,
795 };
796
797 let token = generate_token(&user, -120).unwrap();
799 let result = validate_token(&token);
800
801 assert!(result.is_err());
803 }
804
805 #[test]
806 fn test_claims_serialization() {
807 let claims = Claims {
808 sub: "user123".to_string(),
809 username: "testuser".to_string(),
810 role: "admin".to_string(),
811 email: Some("test@example.com".to_string()),
812 iat: 1234567890,
813 exp: 1234567890 + 3600,
814 };
815
816 let serialized = serde_json::to_string(&claims).unwrap();
817 let deserialized: Claims = serde_json::from_str(&serialized).unwrap();
818
819 assert_eq!(deserialized.sub, claims.sub);
820 assert_eq!(deserialized.username, claims.username);
821 assert_eq!(deserialized.role, claims.role);
822 assert_eq!(deserialized.email, claims.email);
823 }
824
825 #[test]
826 fn test_claims_to_user_context() {
827 let claims = Claims {
828 sub: "user123".to_string(),
829 username: "testuser".to_string(),
830 role: "editor".to_string(),
831 email: Some("test@example.com".to_string()),
832 iat: 1234567890,
833 exp: 1234567890 + 3600,
834 };
835
836 let context = claims_to_user_context(&claims);
837 assert_eq!(context.user_id, "user123");
838 assert_eq!(context.username, "testuser");
839 assert_eq!(context.role, UserRole::Editor);
840 assert_eq!(context.email, Some("test@example.com".to_string()));
841 }
842
843 #[test]
844 fn test_claims_to_user_context_unknown_role_defaults_to_viewer() {
845 let claims = Claims {
846 sub: "user123".to_string(),
847 username: "testuser".to_string(),
848 role: "unknown".to_string(),
849 email: None,
850 iat: 1234567890,
851 exp: 1234567890 + 3600,
852 };
853
854 let context = claims_to_user_context(&claims);
855 assert_eq!(context.role, UserRole::Viewer);
856 }
857
858 #[test]
859 fn test_login_request_deserialization() {
860 let json = r#"{"username": "testuser", "password": "testpass"}"#;
861 let request: LoginRequest = serde_json::from_str(json).unwrap();
862 assert_eq!(request.username, "testuser");
863 assert_eq!(request.password, "testpass");
864 }
865
866 #[test]
867 fn test_refresh_token_request_deserialization() {
868 let json = r#"{"refresh_token": "token123"}"#;
869 let request: RefreshTokenRequest = serde_json::from_str(json).unwrap();
870 assert_eq!(request.refresh_token, "token123");
871 }
872
873 #[test]
874 fn test_user_info_serialization() {
875 let user_info = UserInfo {
876 id: "user123".to_string(),
877 username: "testuser".to_string(),
878 role: "admin".to_string(),
879 email: Some("test@example.com".to_string()),
880 };
881
882 let serialized = serde_json::to_string(&user_info).unwrap();
883 assert!(serialized.contains("user123"));
884 assert!(serialized.contains("testuser"));
885 assert!(serialized.contains("admin"));
886 }
887
888 #[test]
889 fn test_login_response_serialization() {
890 let user_info = UserInfo {
891 id: "user123".to_string(),
892 username: "testuser".to_string(),
893 role: "editor".to_string(),
894 email: None,
895 };
896
897 let response = LoginResponse {
898 token: "access.token.here".to_string(),
899 refresh_token: "refresh.token.here".to_string(),
900 user: user_info,
901 expires_in: 3600,
902 };
903
904 let serialized = serde_json::to_string(&response).unwrap();
905 assert!(serialized.contains("access.token.here"));
906 assert!(serialized.contains("refresh.token.here"));
907 assert!(serialized.contains("3600"));
908 }
909
910 #[tokio::test]
911 async fn test_rate_limiter_creation() {
912 let limiter = RateLimiter::new(5, 60);
913 let result = limiter.check_rate_limit("test-key").await;
914 assert!(result.is_ok());
915 }
916
917 #[tokio::test]
918 async fn test_rate_limiter_exceeds_limit() {
919 let limiter = RateLimiter::new(3, 60);
920
921 for _ in 0..3 {
923 assert!(limiter.check_rate_limit("test-key").await.is_ok());
924 }
925
926 let result = limiter.check_rate_limit("test-key").await;
928 assert!(result.is_err());
929 assert!(result.unwrap_err().contains("Too many"));
930 }
931
932 #[tokio::test]
933 async fn test_rate_limiter_reset() {
934 let limiter = RateLimiter::new(3, 60);
935
936 for _ in 0..3 {
938 limiter.check_rate_limit("test-key").await.ok();
939 }
940
941 limiter.reset_rate_limit("test-key").await;
943
944 let result = limiter.check_rate_limit("test-key").await;
946 assert!(result.is_ok());
947 }
948
949 #[tokio::test]
950 async fn test_rate_limiter_different_keys() {
951 let limiter = RateLimiter::new(2, 60);
952
953 for _ in 0..2 {
955 limiter.check_rate_limit("key1").await.ok();
956 }
957
958 let result = limiter.check_rate_limit("key2").await;
960 assert!(result.is_ok());
961
962 let result = limiter.check_rate_limit("key1").await;
964 assert!(result.is_err());
965 }
966
967 #[tokio::test]
968 async fn test_account_lockout_creation() {
969 let lockout = AccountLockout::new(3, 900);
970 let is_locked = lockout.is_locked("test-user").await;
971 assert!(!is_locked);
972 }
973
974 #[tokio::test]
975 async fn test_account_lockout_record_failure() {
976 let lockout = AccountLockout::new(3, 900);
977
978 for _ in 0..2 {
979 lockout.record_failure("test-user").await;
980 }
981
982 let is_locked = lockout.is_locked("test-user").await;
983 assert!(!is_locked, "Should not be locked after 2 failures");
984
985 lockout.record_failure("test-user").await;
986 let is_locked = lockout.is_locked("test-user").await;
987 assert!(is_locked, "Should be locked after 3 failures");
988 }
989
990 #[tokio::test]
991 async fn test_account_lockout_reset() {
992 let lockout = AccountLockout::new(2, 900);
993
994 for _ in 0..2 {
996 lockout.record_failure("test-user").await;
997 }
998
999 assert!(lockout.is_locked("test-user").await);
1000
1001 lockout.reset("test-user").await;
1003
1004 assert!(!lockout.is_locked("test-user").await);
1005 }
1006
1007 #[tokio::test]
1008 async fn test_account_lockout_remaining_time() {
1009 let lockout = AccountLockout::new(2, 5); for _ in 0..2 {
1013 lockout.record_failure("test-user").await;
1014 }
1015
1016 let remaining = lockout.remaining_lockout_time("test-user").await;
1017 assert!(remaining.is_some());
1018 let time = remaining.unwrap();
1019 assert!(time > 0 && time <= 5);
1020 }
1021
1022 #[tokio::test]
1023 async fn test_global_user_store_initialization() {
1024 let store1 = init_global_user_store();
1025 let store2 = get_global_user_store();
1026
1027 assert!(store2.is_some());
1028
1029 let store2 = store2.unwrap();
1031 assert!(Arc::ptr_eq(&store1, &store2));
1032 }
1033
1034 #[tokio::test]
1035 async fn test_all_default_users_exist() {
1036 let store = UserStore::new();
1037 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
1038
1039 let result = store.authenticate("admin", "admin123").await;
1041 assert!(result.is_ok());
1042 assert!(matches!(result.unwrap().role, UserRole::Admin));
1043
1044 let result = store.authenticate("viewer", "viewer123").await;
1046 assert!(result.is_ok());
1047 assert!(matches!(result.unwrap().role, UserRole::Viewer));
1048
1049 let result = store.authenticate("editor", "editor123").await;
1051 assert!(result.is_ok());
1052 assert!(matches!(result.unwrap().role, UserRole::Editor));
1053 }
1054
1055 #[tokio::test]
1056 async fn test_concurrent_authentication_attempts() {
1057 let store = Arc::new(UserStore::new());
1058 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
1059
1060 let mut handles = vec![];
1061
1062 for i in 0..10 {
1064 let store_clone = store.clone();
1065 let handle = tokio::spawn(async move {
1066 if i % 2 == 0 {
1067 store_clone.authenticate("admin", "admin123").await
1068 } else {
1069 store_clone.authenticate("viewer", "viewer123").await
1070 }
1071 });
1072 handles.push(handle);
1073 }
1074
1075 for handle in handles {
1077 let result = handle.await.unwrap();
1078 assert!(result.is_ok());
1079 }
1080 }
1081}