Skip to main content

auth_framework/auth_modular/
user_manager.rs

1//! User management module
2
3use crate::errors::{AuthError, Result};
4use crate::storage::AuthStorage;
5use std::collections::HashMap;
6use std::sync::Arc;
7use tracing::{debug, info, warn};
8
9/// Canonical user information type shared with [`crate::auth::UserInfo`].
10pub type UserInfo = crate::auth::UserInfo;
11
12/// Result of a successful credential verification via [`UserManager::verify_login_credentials`].
13///
14/// # Example
15/// ```rust,ignore
16/// if let Some(result) = mgr.verify_login_credentials("alice", "pass").await? {
17///     println!("user_id={}, mfa={}", result.user_id, result.mfa_enabled);
18/// }
19/// ```
20pub struct CredentialCheckResult {
21    /// The verified user's ID.
22    pub user_id: String,
23    /// Whether the user has MFA enabled.
24    pub mfa_enabled: bool,
25}
26
27/// User manager for handling user operations.
28///
29/// # Example
30/// ```rust,ignore
31/// use auth_framework::auth_modular::UserManager;
32/// let um = UserManager::new(storage.clone());
33/// let uid = um.register_user("alice", "alice@example.com", "Str0ng!Pass").await?;
34/// ```
35pub struct UserManager {
36    storage: Arc<dyn AuthStorage>,
37}
38
39impl UserManager {
40    /// Create a new user manager.
41    ///
42    /// # Example
43    /// ```rust,ignore
44    /// let um = UserManager::new(storage.clone());
45    /// ```
46    pub fn new(storage: Arc<dyn AuthStorage>) -> Self {
47        Self { storage }
48    }
49
50    /// Create API key for a user.
51    ///
52    /// # Example
53    /// ```rust,ignore
54    /// let key = um.create_api_key("user-1", Some(Duration::from_secs(86400))).await?;
55    /// ```
56    pub async fn create_api_key(
57        &self,
58        user_id: &str,
59        expires_in: Option<std::time::Duration>,
60    ) -> Result<String> {
61        debug!("Creating API key for user '{}'", user_id);
62
63        // Generate a secure API key
64        let api_key = format!("ak_{}", crate::utils::crypto::generate_token(32));
65
66        // Store API key metadata
67        let key_data = serde_json::json!({
68            "user_id": user_id,
69            "created_at": chrono::Utc::now(),
70            "expires_at": expires_in.map(|d| chrono::Utc::now() + chrono::Duration::from_std(d).unwrap_or(chrono::Duration::days(365 * 10)))
71        });
72
73        let storage_key = format!("api_key:{}", api_key);
74        self.storage
75            .store_kv(&storage_key, key_data.to_string().as_bytes(), expires_in)
76            .await?;
77
78        info!("API key created for user '{}'", user_id);
79        Ok(api_key)
80    }
81
82    /// Validate API key and return user information.
83    ///
84    /// # Example
85    /// ```rust,ignore
86    /// let info = um.validate_api_key("ak_abc123").await?;
87    /// println!("user: {}", info.username);
88    /// ```
89    pub async fn validate_api_key(&self, api_key: &str) -> Result<UserInfo> {
90        debug!("Validating API key");
91
92        let storage_key = format!("api_key:{}", api_key);
93        if let Some(key_data) = self.storage.get_kv(&storage_key).await? {
94            let key_info: serde_json::Value = serde_json::from_slice(&key_data)?;
95
96            if let Some(user_id) = key_info["user_id"].as_str() {
97                // Check expiration
98                if let Some(expires_at_str) = key_info["expires_at"].as_str() {
99                    let expires_at: chrono::DateTime<chrono::Utc> = expires_at_str
100                        .parse()
101                        .map_err(|_| AuthError::token("Invalid API key expiration"))?;
102
103                    if chrono::Utc::now() > expires_at {
104                        return Err(AuthError::token("API key expired"));
105                    }
106                }
107
108                // Return user information
109                Ok(UserInfo {
110                    id: user_id.to_string(),
111                    username: format!("api_user_{}", user_id),
112                    email: None,
113                    name: None,
114                    roles: vec!["api_user".to_string()].into(),
115                    active: true,
116                    email_verified: false,
117                    attributes: crate::types::UserAttributes::empty(),
118                })
119            } else {
120                Err(AuthError::token("Invalid API key format"))
121            }
122        } else {
123            Err(AuthError::token("Invalid API key"))
124        }
125    }
126
127    /// Revoke API key.
128    ///
129    /// # Example
130    /// ```rust,ignore
131    /// um.revoke_api_key("ak_abc123").await?;
132    /// ```
133    pub async fn revoke_api_key(&self, api_key: &str) -> Result<()> {
134        debug!("Revoking API key");
135
136        let storage_key = format!("api_key:{}", api_key);
137        if self.storage.get_kv(&storage_key).await?.is_some() {
138            self.storage.delete_kv(&storage_key).await?;
139            info!("API key revoked");
140            Ok(())
141        } else {
142            Err(AuthError::token("API key not found"))
143        }
144    }
145
146    /// Validate username format.
147    ///
148    /// # Example
149    /// ```rust,ignore
150    /// assert!(um.validate_username("alice").await?);
151    /// assert!(!um.validate_username("").await?);
152    /// ```
153    pub async fn validate_username(&self, username: &str) -> Result<bool> {
154        debug!("Validating username format: '{}'", username);
155
156        let is_valid = username.len() >= 3
157            && username.len() <= 32
158            && username
159                .chars()
160                .all(|c| c.is_alphanumeric() || c == '_' || c == '-');
161
162        Ok(is_valid)
163    }
164
165    /// Validate display name format.
166    ///
167    /// # Example
168    /// ```rust,ignore
169    /// assert!(um.validate_display_name("Alice B.").await?);
170    /// ```
171    pub async fn validate_display_name(&self, display_name: &str) -> Result<bool> {
172        debug!("Validating display name format");
173
174        let is_valid = !display_name.is_empty()
175            && display_name.len() <= 100
176            && !display_name.trim().is_empty();
177
178        Ok(is_valid)
179    }
180
181    /// Validate password strength using security policy.
182    ///
183    /// Requires Strong or VeryStrong to protect production deployments.
184    ///
185    /// # Example
186    /// ```rust,ignore
187    /// assert!(um.validate_password_strength("C0mpl3x!Pa$$word").await?);
188    /// assert!(!um.validate_password_strength("weak").await?);
189    /// ```
190    pub async fn validate_password_strength(&self, password: &str) -> Result<bool> {
191        debug!("Validating password strength");
192        let strength = crate::utils::password::check_password_strength(password);
193        let is_valid = crate::utils::password::meets_production_strength(strength.level);
194        if !is_valid {
195            warn!(
196                "Password validation failed - Actual: {:?}, Feedback: {}",
197                strength.level,
198                strength.feedback.join(", ")
199            );
200        }
201        Ok(is_valid)
202    }
203
204    /// Validate user input for security.
205    ///
206    /// Combines a character whitelist with pattern checks for common injection
207    /// vectors. Rejects HTML tags, null bytes, path traversal sequences,
208    /// template injection markers, and dangerous URI schemes.
209    ///
210    /// # Example
211    /// ```rust,ignore
212    /// assert!(um.validate_user_input("hello world").await?);
213    /// assert!(!um.validate_user_input("<script>alert(1)</script>").await?);
214    /// ```
215    pub async fn validate_user_input(&self, input: &str) -> Result<bool> {
216        debug!("Validating user input");
217
218        if input.is_empty() || input.len() > 1000 {
219            return Ok(false);
220        }
221
222        // Character whitelist: reject control characters and angle brackets.
223        if !input.chars().all(|c| {
224            if c.is_control() {
225                matches!(c, ' ' | '\t' | '\n' | '\r')
226            } else {
227                !matches!(c, '<' | '>')
228            }
229        }) {
230            return Ok(false);
231        }
232
233        let lower = input.to_ascii_lowercase();
234        if lower.contains("%3c") || lower.contains("%3e") || lower.contains("%00") {
235            return Ok(false);
236        }
237        if lower.contains("javascript:")
238            || lower.contains("data:")
239            || lower.contains("file:")
240            || lower.contains("jndi:")
241        {
242            return Ok(false);
243        }
244        if input.contains("${") || input.contains("{{") {
245            return Ok(false);
246        }
247        if input.contains("../") || input.contains("..\\") {
248            return Ok(false);
249        }
250        if input.contains('\0') {
251            return Ok(false);
252        }
253        if lower.contains("; drop")
254            || lower.contains(";drop")
255            || lower.contains("' drop")
256            || lower.contains("'; drop")
257            || lower.contains("--")
258        {
259            return Ok(false);
260        }
261
262        Ok(true)
263    }
264
265    /// Get user information by ID.
266    ///
267    /// # Example
268    /// ```rust,ignore
269    /// let info = um.get_user_info("user-1").await?;
270    /// println!("username: {}", info.username);
271    /// ```
272    pub async fn get_user_info(&self, user_id: &str) -> Result<UserInfo> {
273        debug!("Getting user info for '{}'", user_id);
274
275        let key = format!("user:{}", user_id);
276        if let Some(data) = self.storage.get_kv(&key).await? {
277            let user_data: serde_json::Value = serde_json::from_slice(&data)
278                .map_err(|e| AuthError::internal(format!("Failed to parse user data: {e}")))?;
279
280            Ok(UserInfo {
281                id: user_data["user_id"].as_str().unwrap_or(user_id).to_string(),
282                username: user_data["username"].as_str().unwrap_or("").to_string(),
283                email: user_data["email"].as_str().map(String::from),
284                name: user_data["name"].as_str().map(String::from),
285                roles: user_data["roles"]
286                    .as_array()
287                    .map(|arr| {
288                        arr.iter()
289                            .filter_map(|v| v.as_str().map(String::from))
290                            .collect::<Vec<_>>()
291                    })
292                    .unwrap_or_else(|| vec!["user".to_string()])
293                    .into(),
294                active: user_data["active"].as_bool().unwrap_or(true),
295                email_verified: user_data["email_verified"].as_bool().unwrap_or(false),
296                attributes: crate::types::UserAttributes::empty(),
297            })
298        } else {
299            Err(AuthError::validation(format!("User '{user_id}' not found")))
300        }
301    }
302
303    /// List users from the canonical user index.
304    ///
305    /// # Example
306    /// ```rust,ignore
307    /// let users = um.list_users(Some(10), Some(0), true).await?;
308    /// for u in &users { println!("{}", u.username); }
309    /// ```
310    pub async fn list_users(
311        &self,
312        limit: Option<usize>,
313        offset: Option<usize>,
314        active_only: bool,
315    ) -> Result<Vec<UserInfo>> {
316        let bytes = self.storage.get_kv("users:index").await?;
317        let user_ids: Vec<String> = match bytes {
318            Some(bytes) => serde_json::from_slice(&bytes)
319                .map_err(|e| AuthError::internal(format!("Failed to parse user index: {e}")))?,
320            None => return Ok(Vec::new()),
321        };
322
323        let skip = offset.unwrap_or(0);
324        let take = limit.unwrap_or(usize::MAX);
325        let mut users = Vec::new();
326
327        for user_id in user_ids.into_iter().skip(skip) {
328            let user = self.get_user_info(&user_id).await?;
329            if active_only && !user.active {
330                continue;
331            }
332            users.push(user);
333            if users.len() >= take {
334                break;
335            }
336        }
337
338        Ok(users)
339    }
340
341    /// Check if user exists.
342    ///
343    /// # Example
344    /// ```rust,ignore
345    /// if um.user_exists("user-1").await? {
346    ///     println!("user found");
347    /// }
348    /// ```
349    pub async fn user_exists(&self, user_id: &str) -> Result<bool> {
350        debug!("Checking if user '{}' exists", user_id);
351
352        if user_id.is_empty() {
353            return Ok(false);
354        }
355
356        let key = format!("user:{}", user_id);
357        Ok(self.storage.get_kv(&key).await?.is_some())
358    }
359
360    // ────────────────────────────────────────────────────────────────────────
361    // Full user lifecycle management (migrated from auth.rs::AuthFramework)
362    // ────────────────────────────────────────────────────────────────────────
363
364    /// Register a new user, creating all required storage records.
365    ///
366    /// # Example
367    /// ```rust,ignore
368    /// let uid = um.register_user("alice", "alice@example.com", "Str0ng!Pass").await?;
369    /// println!("registered user: {}", uid);
370    /// ```
371    pub async fn register_user(
372        &self,
373        username: &str,
374        email: &str,
375        password: &str,
376    ) -> Result<String> {
377        debug!("Registering new user: {}", username);
378
379        let username_key = format!("user:username:{}", username);
380        if self.storage.get_kv(&username_key).await?.is_some() {
381            return Err(AuthError::validation("Username already exists".to_string()));
382        }
383
384        let email_key = format!("user:email:{}", email);
385        if self.storage.get_kv(&email_key).await?.is_some() {
386            return Err(AuthError::validation("Email already exists".to_string()));
387        }
388
389        let user_id = crate::utils::string::generate_id(Some("user"));
390
391        let password_hash = bcrypt::hash(password, bcrypt::DEFAULT_COST)
392            .map_err(|e| AuthError::crypto(format!("Failed to hash password: {}", e)))?;
393
394        let user_data = serde_json::json!({
395            "user_id": user_id,
396            "username": username,
397            "email": email,
398            "password_hash": password_hash,
399            "roles": ["user"],
400            "active": true,
401            "email_verified": false,
402            "created_at": chrono::Utc::now().to_rfc3339(),
403        });
404
405        let user_key = format!("user:{}", user_id);
406        self.storage
407            .store_kv(&user_key, user_data.to_string().as_bytes(), None)
408            .await?;
409
410        self.storage
411            .store_kv(&username_key, user_id.as_bytes(), None)
412            .await?;
413        self.storage
414            .store_kv(&email_key, user_id.as_bytes(), None)
415            .await?;
416
417        // Maintain global user index for admin listing.
418        let index_key = "users:index";
419        let mut ids: Vec<String> = match self.storage.get_kv(index_key).await? {
420            Some(bytes) => serde_json::from_slice(&bytes).unwrap_or_default(),
421            None => vec![],
422        };
423        ids.push(user_id.clone());
424        if let Ok(idx_json) = serde_json::to_vec(&ids) {
425            if let Err(e) = self.storage.store_kv(index_key, &idx_json, None).await {
426                warn!("Failed to update user index during registration: {}", e);
427            }
428        }
429
430        // Store Argon2 credentials record for authenticate_password_builtin.
431        let creds_key = format!("user:credentials:{}", username);
432        let creds_hash = match crate::utils::password::hash_password(password) {
433            Ok(h) => h,
434            Err(e) => {
435                warn!("Failed to hash credentials for user '{}': {}", username, e);
436                return Ok(user_id);
437            }
438        };
439        let creds_data = serde_json::json!({
440            "user_id": user_id,
441            "username": username,
442            "email": email,
443            "password_hash": creds_hash,
444            "created_at": chrono::Utc::now().to_rfc3339(),
445        });
446        if let Err(e) = self
447            .storage
448            .store_kv(&creds_key, creds_data.to_string().as_bytes(), None)
449            .await
450        {
451            warn!("Failed to store credentials for user '{}': {}", username, e);
452        }
453
454        info!("User '{}' registered successfully", username);
455        Ok(user_id)
456    }
457
458    /// Delete a user and all associated storage records.
459    ///
460    /// # Example
461    /// ```rust,ignore
462    /// um.delete_user("alice").await?;
463    /// ```
464    pub async fn delete_user(&self, username: &str) -> Result<()> {
465        debug!("Deleting user: {}", username);
466
467        let username_key = format!("user:username:{}", username);
468        let user_id_data = self
469            .storage
470            .get_kv(&username_key)
471            .await?
472            .ok_or_else(|| AuthError::validation("User not found".to_string()))?;
473
474        let user_id = String::from_utf8(user_id_data)
475            .map_err(|e| AuthError::crypto(format!("Invalid user ID format: {}", e)))?;
476
477        // Remove email reverse-lookup.
478        let user_key = format!("user:{}", user_id);
479        if let Some(user_data_bytes) = self.storage.get_kv(&user_key).await?
480            && let Ok(user_json_str) = String::from_utf8(user_data_bytes)
481            && let Ok(user_data) = serde_json::from_str::<serde_json::Value>(&user_json_str)
482            && let Some(email) = user_data.get("email").and_then(|v| v.as_str())
483        {
484            if let Err(e) = self
485                .storage
486                .delete_kv(&format!("user:email:{}", email))
487                .await
488            {
489                warn!("Failed to delete email index for user '{}': {}", username, e);
490            }
491        }
492
493        // Remove from global index.
494        let index_key = "users:index";
495        if let Ok(Some(bytes)) = self.storage.get_kv(index_key).await {
496            let mut ids: Vec<String> = serde_json::from_slice(&bytes).unwrap_or_default();
497            ids.retain(|id| id != &user_id);
498            if let Ok(idx_json) = serde_json::to_vec(&ids) {
499                if let Err(e) = self.storage.store_kv(index_key, &idx_json, None).await {
500                    warn!("Failed to update user index during deletion of '{}': {}", username, e);
501                }
502            }
503        }
504
505        if let Err(e) = self.storage.delete_kv(&user_key).await {
506            warn!("Failed to delete user record for '{}': {}", username, e);
507        }
508        if let Err(e) = self.storage.delete_kv(&username_key).await {
509            warn!("Failed to delete username index for '{}': {}", username, e);
510        }
511        if let Err(e) = self
512            .storage
513            .delete_kv(&format!("user:credentials:{}", username))
514            .await
515        {
516            warn!("Failed to delete credentials for '{}': {}", username, e);
517        }
518        if let Err(e) = self
519            .storage
520            .delete_kv(&format!("user:{}:totp_secret", user_id))
521            .await
522        {
523            warn!("Failed to delete TOTP secret for '{}': {}", username, e);
524        }
525        if let Err(e) = self
526            .storage
527            .delete_kv(&format!("user:{}:backup_codes", user_id))
528            .await
529        {
530            warn!("Failed to delete backup codes for '{}': {}", username, e);
531        }
532
533        info!("User '{}' deleted successfully", username);
534        Ok(())
535    }
536
537    /// Delete a user by canonical user ID.
538    ///
539    /// # Example
540    /// ```rust,ignore
541    /// um.delete_user_by_id("user-1").await?;
542    /// ```
543    pub async fn delete_user_by_id(&self, user_id: &str) -> Result<()> {
544        let username = self.get_username_by_id(user_id).await?;
545        self.delete_user(&username).await
546    }
547
548    /// Update the roles assigned to a user.
549    ///
550    /// # Example
551    /// ```rust,ignore
552    /// um.update_user_roles("user-1", &["admin".into(), "editor".into()]).await?;
553    /// ```
554    pub async fn update_user_roles(&self, user_id: &str, roles: &[String]) -> Result<()> {
555        let user_key = format!("user:{}", user_id);
556        let bytes = self
557            .storage
558            .get_kv(&user_key)
559            .await?
560            .ok_or(AuthError::UserNotFound)?;
561        let mut user_data: serde_json::Value =
562            serde_json::from_slice(&bytes).map_err(|e| AuthError::crypto(format!("{e}")))?;
563        user_data["roles"] = serde_json::json!(roles);
564        user_data["updated_at"] = serde_json::json!(chrono::Utc::now().to_rfc3339());
565        self.storage
566            .store_kv(&user_key, user_data.to_string().as_bytes(), None)
567            .await?;
568        info!("Roles updated for user '{}'", user_id);
569        Ok(())
570    }
571
572    /// Enable or disable a user account.
573    ///
574    /// # Example
575    /// ```rust,ignore
576    /// um.set_user_active("user-1", false).await?; // disable
577    /// ```
578    pub async fn set_user_active(&self, user_id: &str, active: bool) -> Result<()> {
579        let user_key = format!("user:{}", user_id);
580        let bytes = self
581            .storage
582            .get_kv(&user_key)
583            .await?
584            .ok_or(AuthError::UserNotFound)?;
585        let mut user_data: serde_json::Value =
586            serde_json::from_slice(&bytes).map_err(|e| AuthError::crypto(format!("{e}")))?;
587        user_data["active"] = serde_json::json!(active);
588        user_data["updated_at"] = serde_json::json!(chrono::Utc::now().to_rfc3339());
589        self.storage
590            .store_kv(&user_key, user_data.to_string().as_bytes(), None)
591            .await?;
592        info!("User '{}' active status set to {}", user_id, active);
593        Ok(())
594    }
595
596    /// Update the email address stored on a user.
597    ///
598    /// # Example
599    /// ```rust,ignore
600    /// um.update_user_email("user-1", "new@example.com").await?;
601    /// ```
602    pub async fn update_user_email(&self, user_id: &str, email: &str) -> Result<()> {
603        let user_key = format!("user:{}", user_id);
604        let bytes = self
605            .storage
606            .get_kv(&user_key)
607            .await?
608            .ok_or(AuthError::UserNotFound)?;
609        let mut user_data: serde_json::Value =
610            serde_json::from_slice(&bytes).map_err(|e| AuthError::crypto(format!("{e}")))?;
611
612        let new_email_key = format!("user:email:{}", email);
613        if let Some(existing_user_id) = self.storage.get_kv(&new_email_key).await? {
614            let existing_user_id = String::from_utf8(existing_user_id)
615                .map_err(|e| AuthError::crypto(format!("Invalid user ID format: {e}")))?;
616            if existing_user_id != user_id {
617                return Err(AuthError::validation("Email already exists".to_string()));
618            }
619        }
620
621        if let Some(old_email) = user_data.get("email").and_then(|value| value.as_str())
622            && old_email != email
623        {
624            self.storage
625                .delete_kv(&format!("user:email:{}", old_email))
626                .await?;
627        }
628
629        user_data["email"] = serde_json::json!(email);
630        user_data["updated_at"] = serde_json::json!(chrono::Utc::now().to_rfc3339());
631
632        self.storage
633            .store_kv(&user_key, user_data.to_string().as_bytes(), None)
634            .await?;
635        self.storage
636            .store_kv(&new_email_key, user_id.as_bytes(), None)
637            .await?;
638
639        Ok(())
640    }
641
642    /// Verify a user's password against the stored bcrypt hash.
643    ///
644    /// # Example
645    /// ```rust,ignore
646    /// let ok = um.verify_user_password("user-1", "secret").await?;
647    /// assert!(ok);
648    /// ```
649    pub async fn verify_user_password(&self, user_id: &str, password: &str) -> Result<bool> {
650        let user_key = format!("user:{}", user_id);
651        let bytes = self
652            .storage
653            .get_kv(&user_key)
654            .await?
655            .ok_or(AuthError::UserNotFound)?;
656        let user_data: serde_json::Value =
657            serde_json::from_slice(&bytes).map_err(|e| AuthError::crypto(format!("{e}")))?;
658        let hash = user_data["password_hash"]
659            .as_str()
660            .ok_or_else(|| AuthError::internal("User has no password hash".to_string()))?;
661        bcrypt::verify(password, hash)
662            .map_err(|e| AuthError::crypto(format!("Password verification failed: {}", e)))
663    }
664
665    /// Resolve a user_id to its username.
666    ///
667    /// # Example
668    /// ```rust,ignore
669    /// let name = um.get_username_by_id("user-1").await?;
670    /// ```
671    pub async fn get_username_by_id(&self, user_id: &str) -> Result<String> {
672        let user_key = format!("user:{}", user_id);
673        let bytes = self
674            .storage
675            .get_kv(&user_key)
676            .await?
677            .ok_or(AuthError::UserNotFound)?;
678        let user_data: serde_json::Value =
679            serde_json::from_slice(&bytes).map_err(|e| AuthError::crypto(format!("{e}")))?;
680        user_data["username"]
681            .as_str()
682            .map(|s| s.to_string())
683            .ok_or_else(|| AuthError::internal("User has no username field".to_string()))
684    }
685
686    /// Check whether a username is already taken.
687    ///
688    /// # Example
689    /// ```rust,ignore
690    /// if um.username_exists("alice").await? {
691    ///     println!("taken");
692    /// }
693    /// ```
694    pub async fn username_exists(&self, username: &str) -> Result<bool> {
695        Ok(self
696            .storage
697            .get_kv(&format!("user:username:{}", username))
698            .await?
699            .is_some())
700    }
701
702    /// Check whether an email address is already registered.
703    ///
704    /// # Example
705    /// ```rust,ignore
706    /// if um.email_exists("a@b.com").await? {
707    ///     println!("already registered");
708    /// }
709    /// ```
710    pub async fn email_exists(&self, email: &str) -> Result<bool> {
711        Ok(self
712            .storage
713            .get_kv(&format!("user:email:{}", email))
714            .await?
715            .is_some())
716    }
717
718    /// Fetch raw user data by username.
719    ///
720    /// # Example
721    /// ```rust,ignore
722    /// let data = um.get_user_by_username("alice").await?;
723    /// println!("email: {:?}", data.get("email"));
724    /// ```
725    pub async fn get_user_by_username(
726        &self,
727        username: &str,
728    ) -> Result<HashMap<String, serde_json::Value>> {
729        let username_key = format!("user:username:{}", username);
730        let user_id_data = self
731            .storage
732            .get_kv(&username_key)
733            .await?
734            .ok_or_else(|| AuthError::validation("User not found".to_string()))?;
735        let user_id = String::from_utf8(user_id_data)
736            .map_err(|e| AuthError::crypto(format!("Invalid user ID format: {}", e)))?;
737        let user_key = format!("user:{}", user_id);
738        let user_data = self
739            .storage
740            .get_kv(&user_key)
741            .await?
742            .ok_or_else(|| AuthError::validation("User not found".to_string()))?;
743        let user_obj: serde_json::Value = serde_json::from_slice(&user_data)
744            .map_err(|e| AuthError::crypto(format!("Failed to parse user data: {}", e)))?;
745        if let Some(obj) = user_obj.as_object() {
746            Ok(obj
747                .into_iter()
748                .map(|(k, v)| (k.clone(), v.clone()))
749                .collect())
750        } else {
751            Err(AuthError::validation(
752                "Invalid user data structure".to_string(),
753            ))
754        }
755    }
756
757    /// Get a sanitised user profile (no password hash) for display/API.
758    ///
759    /// # Example
760    /// ```rust,ignore
761    /// let profile = um.get_user_profile("user-1").await?;
762    /// println!("email: {:?}", profile.email);
763    /// ```
764    pub async fn get_user_profile(
765        &self,
766        user_id: &str,
767    ) -> Result<crate::providers::ProviderProfile> {
768        let user_key = format!("user:{}", user_id);
769        if let Ok(Some(bytes)) = self.storage.get_kv(&user_key).await
770            && let Ok(user_data) = serde_json::from_slice::<serde_json::Value>(&bytes)
771        {
772            let username = user_data["username"].as_str().map(|s| s.to_string());
773            let email = user_data["email"].as_str().map(|s| s.to_string());
774            let name = user_data["name"].as_str().map(|s| s.to_string());
775            let email_verified = user_data["email_verified"].as_bool();
776
777            let mut additional_data = std::collections::HashMap::new();
778            if let Some(obj) = user_data.as_object() {
779                for (k, v) in obj {
780                    match k.as_str() {
781                        "user_id" | "username" | "email" | "name" | "email_verified"
782                        | "password_hash" | "created_at" | "updated_at" => {}
783                        _ => {
784                            additional_data.insert(k.clone(), v.clone());
785                        }
786                    }
787                }
788            }
789
790            return Ok(crate::providers::ProviderProfile {
791                id: Some(user_id.to_string()),
792                provider: Some("local".to_string()),
793                username,
794                name,
795                email,
796                email_verified,
797                picture: None,
798                locale: None,
799                additional_data,
800            });
801        }
802        Err(AuthError::UserNotFound)
803    }
804
805    /// Get a user's roles/scopes from storage, returning `["user"]` as fallback.
806    ///
807    /// # Example
808    /// ```rust,ignore
809    /// let roles = um.get_user_roles("user-1").await?;
810    /// assert!(roles.contains(&"user".to_string()));
811    /// ```
812    pub async fn get_user_roles(&self, user_id: &str) -> Result<Vec<String>> {
813        let user_key = format!("user:{}", user_id);
814        if let Ok(Some(data)) = self.storage.get_kv(&user_key).await
815            && let Ok(v) = serde_json::from_slice::<serde_json::Value>(&data)
816            && let Some(arr) = v.get("roles").and_then(|r| r.as_array())
817        {
818            let roles: Vec<String> = arr
819                .iter()
820                .filter_map(|v| v.as_str().map(String::from))
821                .collect();
822            if !roles.is_empty() {
823                return Ok(roles);
824            }
825        }
826        Ok(vec!["user".to_string()])
827    }
828
829    /// Verify username/password credentials in a timing-safe manner.
830    ///
831    /// Returns `Ok(None)` when the credentials are invalid — a dummy Argon2
832    /// verification is always executed when the username is not found, so
833    /// callers cannot distinguish missing users from wrong passwords via timing.
834    /// Returns `Ok(Some(result))` on success.
835    ///
836    /// # Example
837    /// ```rust,ignore
838    /// match um.verify_login_credentials("alice", "password123").await? {
839    ///     Some(cred) => println!("user_id={}, mfa={}", cred.user_id, cred.mfa_enabled),
840    ///     None => println!("invalid credentials"),
841    /// }
842    /// ```
843    pub async fn verify_login_credentials(
844        &self,
845        username: &str,
846        password: &str,
847    ) -> Result<Option<CredentialCheckResult>> {
848        use crate::utils::password::verify_password;
849
850        if username.is_empty() || password.is_empty() {
851            return Ok(None);
852        }
853
854        let user_key = format!("user:credentials:{username}");
855        let stored_bytes = match self.storage.get_kv(&user_key).await? {
856            Some(bytes) => bytes,
857            None => {
858                // Constant-time: always do real work even for missing users.
859                let _ = verify_password(
860                    password,
861                    "$argon2id$v=19$m=19456,t=2,p=1$dGVzdHNhbHRmb3J0aW1pbmc$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
862                );
863                return Ok(None);
864            }
865        };
866
867        let user_data_str = String::from_utf8(stored_bytes)
868            .map_err(|e| AuthError::internal(format!("Failed to parse user data: {e}")))?;
869        let user_data: serde_json::Value = serde_json::from_str(&user_data_str)
870            .map_err(|e| AuthError::internal(format!("Failed to parse user JSON: {e}")))?;
871
872        let password_hash = user_data["password_hash"].as_str().ok_or_else(|| {
873            AuthError::internal("Missing password hash in user record".to_string())
874        })?;
875
876        if !verify_password(password, password_hash).unwrap_or(false) {
877            return Ok(None);
878        }
879
880        let user_id = user_data["user_id"]
881            .as_str()
882            .ok_or_else(|| AuthError::internal("Missing user_id in user record".to_string()))?
883            .to_string();
884
885        // Check whether the account has been deactivated.
886        let canonical_key = format!("user:{}", user_id);
887        if let Ok(Some(canonical_bytes)) = self.storage.get_kv(&canonical_key).await
888            && let Ok(canonical_str) = String::from_utf8(canonical_bytes)
889            && let Ok(canonical_data) = serde_json::from_str::<serde_json::Value>(&canonical_str)
890            && !canonical_data["active"].as_bool().unwrap_or(true)
891        {
892            return Ok(None);
893        }
894
895        let mfa_enabled = matches!(
896            self.storage
897                .get_kv(&format!("mfa_enabled:{}", user_id))
898                .await,
899            Ok(Some(_))
900        );
901
902        Ok(Some(CredentialCheckResult {
903            user_id,
904            mfa_enabled,
905        }))
906    }
907
908    /// Update a user's password by username.
909    ///
910    /// # Example
911    /// ```rust,ignore
912    /// um.update_user_password("alice", "N3wStr0ng!Pass").await?;
913    /// ```
914    pub async fn update_user_password(&self, username: &str, new_password: &str) -> Result<()> {
915        debug!("Updating password for user: {}", username);
916
917        crate::utils::validation::validate_password(new_password)
918            .map_err(|e| AuthError::validation(format!("Password validation failed: {e}")))?;
919
920        let username_key = format!("user:username:{}", username);
921        let user_id_data = self
922            .storage
923            .get_kv(&username_key)
924            .await?
925            .ok_or_else(|| AuthError::validation("User not found".to_string()))?;
926        let user_id = String::from_utf8(user_id_data)
927            .map_err(|e| AuthError::crypto(format!("Invalid user ID format: {}", e)))?;
928
929        let user_key = format!("user:{}", user_id);
930        let user_bytes = self
931            .storage
932            .get_kv(&user_key)
933            .await?
934            .ok_or_else(|| AuthError::validation("User not found".to_string()))?;
935        let mut user_data: serde_json::Value = serde_json::from_slice(&user_bytes)
936            .map_err(|e| AuthError::crypto(format!("Failed to parse user data: {}", e)))?;
937
938        let password_hash = bcrypt::hash(new_password, bcrypt::DEFAULT_COST)
939            .map_err(|e| AuthError::crypto(format!("Failed to hash password: {}", e)))?;
940        user_data["password_hash"] = serde_json::json!(password_hash);
941        user_data["updated_at"] = serde_json::json!(chrono::Utc::now().to_rfc3339());
942        self.storage
943            .store_kv(&user_key, user_data.to_string().as_bytes(), None)
944            .await?;
945
946        // Update Argon2 credentials record used by the login endpoint.
947        let creds_key = format!("user:credentials:{}", username);
948        let creds_hash = crate::utils::password::hash_password(new_password)
949            .map_err(|e| AuthError::crypto(format!("Failed to hash login credentials: {e}")))?;
950        let creds_bytes =
951            self.storage.get_kv(&creds_key).await?.ok_or_else(|| {
952                AuthError::internal("Login credentials record not found".to_string())
953            })?;
954        let mut creds: serde_json::Value = serde_json::from_slice(&creds_bytes)
955            .map_err(|e| AuthError::internal(format!("Failed to parse credentials record: {e}")))?;
956        creds["password_hash"] = serde_json::json!(creds_hash);
957        creds["updated_at"] = serde_json::json!(chrono::Utc::now().to_rfc3339());
958        self.storage
959            .store_kv(&creds_key, creds.to_string().as_bytes(), None)
960            .await?;
961
962        info!("Password updated for user: {}", username);
963        Ok(())
964    }
965
966    /// Update a user password by canonical user ID.
967    ///
968    /// # Example
969    /// ```rust,ignore
970    /// um.update_user_password_by_id("user-1", "N3wStr0ng!Pass").await?;
971    /// ```
972    pub async fn update_user_password_by_id(
973        &self,
974        user_id: &str,
975        new_password: &str,
976    ) -> Result<()> {
977        let username = self.get_username_by_id(user_id).await?;
978        self.update_user_password(&username, new_password).await
979    }
980}
981
982#[cfg(test)]
983mod tests {
984    use super::*;
985    use crate::storage::MemoryStorage;
986
987    fn make_manager() -> UserManager {
988        UserManager::new(Arc::new(MemoryStorage::new()))
989    }
990
991    // ── register_user ───────────────────────────────────────────────────
992
993    #[tokio::test]
994    async fn test_register_user_success() {
995        let mgr = make_manager();
996        let id = mgr
997            .register_user("alice", "alice@example.com", "StrongP@ss1!")
998            .await
999            .unwrap();
1000        assert!(id.starts_with("user"));
1001    }
1002
1003    #[tokio::test]
1004    async fn test_register_user_duplicate_username() {
1005        let mgr = make_manager();
1006        mgr.register_user("bob", "bob@example.com", "StrongP@ss1!")
1007            .await
1008            .unwrap();
1009        let err = mgr
1010            .register_user("bob", "bob2@example.com", "StrongP@ss1!")
1011            .await;
1012        assert!(err.is_err());
1013    }
1014
1015    #[tokio::test]
1016    async fn test_register_user_duplicate_email() {
1017        let mgr = make_manager();
1018        mgr.register_user("carol", "dup@example.com", "StrongP@ss1!")
1019            .await
1020            .unwrap();
1021        let err = mgr
1022            .register_user("carol2", "dup@example.com", "StrongP@ss1!")
1023            .await;
1024        assert!(err.is_err());
1025    }
1026
1027    // ── get_user_info ───────────────────────────────────────────────────
1028
1029    #[tokio::test]
1030    async fn test_get_user_info_success() {
1031        let mgr = make_manager();
1032        let id = mgr
1033            .register_user("dave", "dave@example.com", "StrongP@ss1!")
1034            .await
1035            .unwrap();
1036        let info = mgr.get_user_info(&id).await.unwrap();
1037        assert_eq!(info.username, "dave");
1038        assert!(info.active);
1039    }
1040
1041    #[tokio::test]
1042    async fn test_get_user_info_not_found() {
1043        let mgr = make_manager();
1044        assert!(mgr.get_user_info("nonexistent").await.is_err());
1045    }
1046
1047    // ── user_exists ─────────────────────────────────────────────────────
1048
1049    #[tokio::test]
1050    async fn test_user_exists_true() {
1051        let mgr = make_manager();
1052        let id = mgr
1053            .register_user("eve", "eve@example.com", "StrongP@ss1!")
1054            .await
1055            .unwrap();
1056        assert!(mgr.user_exists(&id).await.unwrap());
1057    }
1058
1059    #[tokio::test]
1060    async fn test_user_exists_false() {
1061        let mgr = make_manager();
1062        assert!(!mgr.user_exists("nobody").await.unwrap());
1063    }
1064
1065    #[tokio::test]
1066    async fn test_user_exists_empty_id() {
1067        let mgr = make_manager();
1068        assert!(!mgr.user_exists("").await.unwrap());
1069    }
1070
1071    // ── delete_user ─────────────────────────────────────────────────────
1072
1073    #[tokio::test]
1074    async fn test_delete_user_success() {
1075        let mgr = make_manager();
1076        let id = mgr
1077            .register_user("frank", "frank@example.com", "StrongP@ss1!")
1078            .await
1079            .unwrap();
1080        mgr.delete_user("frank").await.unwrap();
1081        assert!(!mgr.user_exists(&id).await.unwrap());
1082    }
1083
1084    #[tokio::test]
1085    async fn test_delete_user_not_found() {
1086        let mgr = make_manager();
1087        assert!(mgr.delete_user("ghost").await.is_err());
1088    }
1089
1090    #[tokio::test]
1091    async fn test_delete_user_by_id() {
1092        let mgr = make_manager();
1093        let id = mgr
1094            .register_user("gina", "gina@example.com", "StrongP@ss1!")
1095            .await
1096            .unwrap();
1097        mgr.delete_user_by_id(&id).await.unwrap();
1098        assert!(!mgr.user_exists(&id).await.unwrap());
1099    }
1100
1101    // ── list_users ──────────────────────────────────────────────────────
1102
1103    #[tokio::test]
1104    async fn test_list_users_empty() {
1105        let mgr = make_manager();
1106        let users = mgr.list_users(None, None, false).await.unwrap();
1107        assert!(users.is_empty());
1108    }
1109
1110    #[tokio::test]
1111    async fn test_list_users_with_limit_and_offset() {
1112        let mgr = make_manager();
1113        mgr.register_user("u1", "u1@example.com", "StrongP@ss1!")
1114            .await
1115            .unwrap();
1116        mgr.register_user("u2", "u2@example.com", "StrongP@ss1!")
1117            .await
1118            .unwrap();
1119        mgr.register_user("u3", "u3@example.com", "StrongP@ss1!")
1120            .await
1121            .unwrap();
1122        let page = mgr.list_users(Some(1), Some(1), false).await.unwrap();
1123        assert_eq!(page.len(), 1);
1124    }
1125
1126    // ── verify_user_password ────────────────────────────────────────────
1127
1128    #[tokio::test]
1129    async fn test_verify_password_correct() {
1130        let mgr = make_manager();
1131        let id = mgr
1132            .register_user("hank", "hank@example.com", "StrongP@ss1!")
1133            .await
1134            .unwrap();
1135        assert!(mgr.verify_user_password(&id, "StrongP@ss1!").await.unwrap());
1136    }
1137
1138    #[tokio::test]
1139    async fn test_verify_password_incorrect() {
1140        let mgr = make_manager();
1141        let id = mgr
1142            .register_user("ivan", "ivan@example.com", "StrongP@ss1!")
1143            .await
1144            .unwrap();
1145        assert!(!mgr.verify_user_password(&id, "wrong").await.unwrap());
1146    }
1147
1148    #[tokio::test]
1149    async fn test_verify_password_user_not_found() {
1150        let mgr = make_manager();
1151        assert!(mgr.verify_user_password("ghost", "x").await.is_err());
1152    }
1153
1154    // ── update_user_password ────────────────────────────────────────────
1155
1156    #[tokio::test]
1157    async fn test_update_password() {
1158        let mgr = make_manager();
1159        let id = mgr
1160            .register_user("jack", "jack@example.com", "StrongP@ss1!")
1161            .await
1162            .unwrap();
1163        mgr.update_user_password("jack", "NewStr0ng!Pass")
1164            .await
1165            .unwrap();
1166        assert!(
1167            mgr.verify_user_password(&id, "NewStr0ng!Pass")
1168                .await
1169                .unwrap()
1170        );
1171        assert!(!mgr.verify_user_password(&id, "StrongP@ss1!").await.unwrap());
1172    }
1173
1174    #[tokio::test]
1175    async fn test_update_password_user_not_found() {
1176        let mgr = make_manager();
1177        assert!(
1178            mgr.update_user_password("ghost", "NewStr0ng!Pass")
1179                .await
1180                .is_err()
1181        );
1182    }
1183
1184    // ── update_user_roles ───────────────────────────────────────────────
1185
1186    #[tokio::test]
1187    async fn test_update_user_roles() {
1188        let mgr = make_manager();
1189        let id = mgr
1190            .register_user("kate", "kate@example.com", "StrongP@ss1!")
1191            .await
1192            .unwrap();
1193        mgr.update_user_roles(&id, &["admin".into(), "user".into()])
1194            .await
1195            .unwrap();
1196        let info = mgr.get_user_info(&id).await.unwrap();
1197        assert!(info.roles.contains(&"admin".to_string()));
1198    }
1199
1200    #[tokio::test]
1201    async fn test_update_user_roles_not_found() {
1202        let mgr = make_manager();
1203        assert!(
1204            mgr.update_user_roles("ghost", &["admin".into()])
1205                .await
1206                .is_err()
1207        );
1208    }
1209
1210    // ── set_user_active ─────────────────────────────────────────────────
1211
1212    #[tokio::test]
1213    async fn test_set_user_active_disable() {
1214        let mgr = make_manager();
1215        let id = mgr
1216            .register_user("leon", "leon@example.com", "StrongP@ss1!")
1217            .await
1218            .unwrap();
1219        mgr.set_user_active(&id, false).await.unwrap();
1220        let info = mgr.get_user_info(&id).await.unwrap();
1221        assert!(!info.active);
1222    }
1223
1224    #[tokio::test]
1225    async fn test_set_user_active_not_found() {
1226        let mgr = make_manager();
1227        assert!(mgr.set_user_active("ghost", false).await.is_err());
1228    }
1229
1230    // ── update_user_email ───────────────────────────────────────────────
1231
1232    #[tokio::test]
1233    async fn test_update_user_email() {
1234        let mgr = make_manager();
1235        let id = mgr
1236            .register_user("mary", "mary@example.com", "StrongP@ss1!")
1237            .await
1238            .unwrap();
1239        mgr.update_user_email(&id, "mary_new@example.com")
1240            .await
1241            .unwrap();
1242        let info = mgr.get_user_info(&id).await.unwrap();
1243        assert_eq!(info.email.as_deref(), Some("mary_new@example.com"));
1244    }
1245
1246    #[tokio::test]
1247    async fn test_update_user_email_already_taken() {
1248        let mgr = make_manager();
1249        mgr.register_user("n1", "taken@example.com", "StrongP@ss1!")
1250            .await
1251            .unwrap();
1252        let id2 = mgr
1253            .register_user("n2", "n2@example.com", "StrongP@ss1!")
1254            .await
1255            .unwrap();
1256        assert!(
1257            mgr.update_user_email(&id2, "taken@example.com")
1258                .await
1259                .is_err()
1260        );
1261    }
1262
1263    // ── API key operations ──────────────────────────────────────────────
1264
1265    #[tokio::test]
1266    async fn test_create_and_validate_api_key() {
1267        let mgr = make_manager();
1268        let id = mgr
1269            .register_user("oscar", "oscar@example.com", "StrongP@ss1!")
1270            .await
1271            .unwrap();
1272        let key = mgr.create_api_key(&id, None).await.unwrap();
1273        assert!(key.starts_with("ak_"));
1274        let info = mgr.validate_api_key(&key).await.unwrap();
1275        assert_eq!(info.id, id);
1276    }
1277
1278    #[tokio::test]
1279    async fn test_validate_api_key_invalid() {
1280        let mgr = make_manager();
1281        assert!(mgr.validate_api_key("bad_key").await.is_err());
1282    }
1283
1284    #[tokio::test]
1285    async fn test_revoke_api_key() {
1286        let mgr = make_manager();
1287        let id = mgr
1288            .register_user("pat", "pat@example.com", "StrongP@ss1!")
1289            .await
1290            .unwrap();
1291        let key = mgr.create_api_key(&id, None).await.unwrap();
1292        mgr.revoke_api_key(&key).await.unwrap();
1293        assert!(mgr.validate_api_key(&key).await.is_err());
1294    }
1295
1296    #[tokio::test]
1297    async fn test_revoke_api_key_not_found() {
1298        let mgr = make_manager();
1299        assert!(mgr.revoke_api_key("nonexistent").await.is_err());
1300    }
1301
1302    // ── validate_username ───────────────────────────────────────────────
1303
1304    #[tokio::test]
1305    async fn test_validate_username_valid() {
1306        let mgr = make_manager();
1307        assert!(mgr.validate_username("good_user-1").await.unwrap());
1308    }
1309
1310    #[tokio::test]
1311    async fn test_validate_username_too_short() {
1312        let mgr = make_manager();
1313        assert!(!mgr.validate_username("ab").await.unwrap());
1314    }
1315
1316    #[tokio::test]
1317    async fn test_validate_username_special_chars() {
1318        let mgr = make_manager();
1319        assert!(!mgr.validate_username("user@name").await.unwrap());
1320    }
1321
1322    // ── validate_display_name ───────────────────────────────────────────
1323
1324    #[tokio::test]
1325    async fn test_validate_display_name_valid() {
1326        let mgr = make_manager();
1327        assert!(mgr.validate_display_name("Alice Bob").await.unwrap());
1328    }
1329
1330    #[tokio::test]
1331    async fn test_validate_display_name_empty() {
1332        let mgr = make_manager();
1333        assert!(!mgr.validate_display_name("").await.unwrap());
1334    }
1335
1336    #[tokio::test]
1337    async fn test_validate_display_name_only_whitespace() {
1338        let mgr = make_manager();
1339        assert!(!mgr.validate_display_name("   ").await.unwrap());
1340    }
1341
1342    // ── validate_password_strength ──────────────────────────────────────
1343
1344    #[tokio::test]
1345    async fn test_validate_password_strong() {
1346        let mgr = make_manager();
1347        assert!(
1348            mgr.validate_password_strength("C0mpl3x!P@ssw0rd")
1349                .await
1350                .unwrap()
1351        );
1352    }
1353
1354    #[tokio::test]
1355    async fn test_validate_password_weak() {
1356        let mgr = make_manager();
1357        assert!(!mgr.validate_password_strength("123").await.unwrap());
1358    }
1359
1360    // ── validate_user_input ─────────────────────────────────────────────
1361
1362    #[tokio::test]
1363    async fn test_validate_user_input_clean() {
1364        let mgr = make_manager();
1365        assert!(mgr.validate_user_input("Hello World 123").await.unwrap());
1366    }
1367
1368    #[tokio::test]
1369    async fn test_validate_user_input_html_tags() {
1370        let mgr = make_manager();
1371        assert!(
1372            !mgr.validate_user_input("<script>alert(1)</script>")
1373                .await
1374                .unwrap()
1375        );
1376    }
1377
1378    #[tokio::test]
1379    async fn test_validate_user_input_sql_injection() {
1380        let mgr = make_manager();
1381        assert!(
1382            !mgr.validate_user_input("'; drop table users--")
1383                .await
1384                .unwrap()
1385        );
1386    }
1387
1388    #[tokio::test]
1389    async fn test_validate_user_input_template_injection() {
1390        let mgr = make_manager();
1391        assert!(!mgr.validate_user_input("${evil}").await.unwrap());
1392    }
1393
1394    #[tokio::test]
1395    async fn test_validate_user_input_path_traversal() {
1396        let mgr = make_manager();
1397        assert!(!mgr.validate_user_input("../../etc/passwd").await.unwrap());
1398    }
1399
1400    #[tokio::test]
1401    async fn test_validate_user_input_javascript_uri() {
1402        let mgr = make_manager();
1403        assert!(
1404            !mgr.validate_user_input("javascript:alert(1)")
1405                .await
1406                .unwrap()
1407        );
1408    }
1409
1410    #[tokio::test]
1411    async fn test_validate_user_input_empty() {
1412        let mgr = make_manager();
1413        assert!(!mgr.validate_user_input("").await.unwrap());
1414    }
1415
1416    #[tokio::test]
1417    async fn test_validate_user_input_encoded_tags() {
1418        let mgr = make_manager();
1419        assert!(!mgr.validate_user_input("%3cscript%3e").await.unwrap());
1420    }
1421
1422    // ── username_exists / email_exists ───────────────────────────────────
1423
1424    #[tokio::test]
1425    async fn test_username_exists() {
1426        let mgr = make_manager();
1427        mgr.register_user("quinn", "quinn@example.com", "StrongP@ss1!")
1428            .await
1429            .unwrap();
1430        assert!(mgr.username_exists("quinn").await.unwrap());
1431        assert!(!mgr.username_exists("noone").await.unwrap());
1432    }
1433
1434    #[tokio::test]
1435    async fn test_email_exists() {
1436        let mgr = make_manager();
1437        mgr.register_user("ross", "ross@example.com", "StrongP@ss1!")
1438            .await
1439            .unwrap();
1440        assert!(mgr.email_exists("ross@example.com").await.unwrap());
1441        assert!(!mgr.email_exists("nobody@example.com").await.unwrap());
1442    }
1443
1444    // ── get_user_by_username ────────────────────────────────────────────
1445
1446    #[tokio::test]
1447    async fn test_get_user_by_username() {
1448        let mgr = make_manager();
1449        mgr.register_user("sam", "sam@example.com", "StrongP@ss1!")
1450            .await
1451            .unwrap();
1452        let data = mgr.get_user_by_username("sam").await.unwrap();
1453        assert_eq!(data["username"].as_str(), Some("sam"));
1454    }
1455
1456    #[tokio::test]
1457    async fn test_get_user_by_username_not_found() {
1458        let mgr = make_manager();
1459        assert!(mgr.get_user_by_username("ghost").await.is_err());
1460    }
1461
1462    // ── get_user_profile ────────────────────────────────────────────────
1463
1464    #[tokio::test]
1465    async fn test_get_user_profile() {
1466        let mgr = make_manager();
1467        let id = mgr
1468            .register_user("tina", "tina@example.com", "StrongP@ss1!")
1469            .await
1470            .unwrap();
1471        let profile = mgr.get_user_profile(&id).await.unwrap();
1472        assert_eq!(profile.username.as_deref(), Some("tina"));
1473        assert_eq!(profile.email.as_deref(), Some("tina@example.com"));
1474        // password_hash should NOT be in additional_data
1475        assert!(!profile.additional_data.contains_key("password_hash"));
1476    }
1477
1478    #[tokio::test]
1479    async fn test_get_user_profile_not_found() {
1480        let mgr = make_manager();
1481        assert!(mgr.get_user_profile("ghost").await.is_err());
1482    }
1483
1484    // ── get_user_roles ──────────────────────────────────────────────────
1485
1486    #[tokio::test]
1487    async fn test_get_user_roles_default() {
1488        let mgr = make_manager();
1489        let id = mgr
1490            .register_user("uma", "uma@example.com", "StrongP@ss1!")
1491            .await
1492            .unwrap();
1493        let roles = mgr.get_user_roles(&id).await.unwrap();
1494        assert!(roles.contains(&"user".to_string()));
1495    }
1496
1497    #[tokio::test]
1498    async fn test_get_user_roles_not_found_returns_default() {
1499        let mgr = make_manager();
1500        let roles = mgr.get_user_roles("ghost").await.unwrap();
1501        assert_eq!(roles, vec!["user".to_string()]);
1502    }
1503
1504    // ── verify_login_credentials ────────────────────────────────────────
1505
1506    #[tokio::test]
1507    async fn test_verify_login_credentials_success() {
1508        let mgr = make_manager();
1509        mgr.register_user("vera", "vera@example.com", "StrongP@ss1!")
1510            .await
1511            .unwrap();
1512        let result = mgr
1513            .verify_login_credentials("vera", "StrongP@ss1!")
1514            .await
1515            .unwrap();
1516        assert!(result.is_some());
1517        assert!(!result.unwrap().mfa_enabled);
1518    }
1519
1520    #[tokio::test]
1521    async fn test_verify_login_credentials_wrong_password() {
1522        let mgr = make_manager();
1523        mgr.register_user("wanda", "wanda@example.com", "StrongP@ss1!")
1524            .await
1525            .unwrap();
1526        let result = mgr
1527            .verify_login_credentials("wanda", "wrong")
1528            .await
1529            .unwrap();
1530        assert!(result.is_none());
1531    }
1532
1533    #[tokio::test]
1534    async fn test_verify_login_credentials_unknown_user() {
1535        let mgr = make_manager();
1536        let result = mgr
1537            .verify_login_credentials("ghost", "StrongP@ss1!")
1538            .await
1539            .unwrap();
1540        assert!(result.is_none());
1541    }
1542
1543    #[tokio::test]
1544    async fn test_verify_login_credentials_empty_inputs() {
1545        let mgr = make_manager();
1546        assert!(
1547            mgr.verify_login_credentials("", "pass")
1548                .await
1549                .unwrap()
1550                .is_none()
1551        );
1552        assert!(
1553            mgr.verify_login_credentials("user", "")
1554                .await
1555                .unwrap()
1556                .is_none()
1557        );
1558    }
1559
1560    #[tokio::test]
1561    async fn test_verify_login_credentials_deactivated() {
1562        let mgr = make_manager();
1563        let id = mgr
1564            .register_user("xena", "xena@example.com", "StrongP@ss1!")
1565            .await
1566            .unwrap();
1567        mgr.set_user_active(&id, false).await.unwrap();
1568        let result = mgr
1569            .verify_login_credentials("xena", "StrongP@ss1!")
1570            .await
1571            .unwrap();
1572        assert!(result.is_none());
1573    }
1574
1575    // ── get_username_by_id ──────────────────────────────────────────────
1576
1577    #[tokio::test]
1578    async fn test_get_username_by_id() {
1579        let mgr = make_manager();
1580        let id = mgr
1581            .register_user("yara", "yara@example.com", "StrongP@ss1!")
1582            .await
1583            .unwrap();
1584        let username = mgr.get_username_by_id(&id).await.unwrap();
1585        assert_eq!(username, "yara");
1586    }
1587
1588    #[tokio::test]
1589    async fn test_get_username_by_id_not_found() {
1590        let mgr = make_manager();
1591        assert!(mgr.get_username_by_id("ghost").await.is_err());
1592    }
1593
1594    // ── list_users active_only filter ───────────────────────────────────
1595
1596    #[tokio::test]
1597    async fn test_list_users_active_only() {
1598        let mgr = make_manager();
1599        let id1 = mgr
1600            .register_user("active1", "active1@example.com", "StrongP@ss1!")
1601            .await
1602            .unwrap();
1603        let id2 = mgr
1604            .register_user("inactive1", "inactive1@example.com", "StrongP@ss1!")
1605            .await
1606            .unwrap();
1607        mgr.set_user_active(&id2, false).await.unwrap();
1608
1609        let all = mgr.list_users(None, None, false).await.unwrap();
1610        let active = mgr.list_users(None, None, true).await.unwrap();
1611        assert!(all.len() > active.len());
1612        assert!(active.iter().all(|u| u.active));
1613        // The inactive user should still be in the "all" list
1614        assert!(all.iter().any(|u| u.id == id1));
1615        assert!(all.iter().any(|u| u.id == id2));
1616    }
1617}