leptos_sync_core/security/authentication/
manager.rs

1//! Authentication manager implementation
2
3use super::{
4    config::AuthConfig,
5    crypto::{
6        generate_mfa_secret, generate_reset_token, generate_session_token, generate_totp_code,
7        generate_user_id, hash_password, verify_password,
8    },
9    types::{PasswordResetToken, User, UserSession},
10    validation::validate_password,
11};
12use crate::SyncError;
13use chrono::{DateTime, Utc};
14use std::collections::HashMap;
15use tokio::sync::RwLock;
16
17/// Authentication manager for handling user authentication and sessions
18pub struct AuthenticationManager {
19    config: AuthConfig,
20    users: RwLock<HashMap<String, User>>,
21    sessions: RwLock<HashMap<String, UserSession>>,
22    reset_tokens: RwLock<HashMap<String, PasswordResetToken>>,
23}
24
25impl AuthenticationManager {
26    /// Create a new authentication manager
27    pub fn new() -> Self {
28        Self {
29            config: AuthConfig::default(),
30            users: RwLock::new(HashMap::new()),
31            sessions: RwLock::new(HashMap::new()),
32            reset_tokens: RwLock::new(HashMap::new()),
33        }
34    }
35
36    /// Create a new authentication manager with custom configuration
37    pub fn with_config(config: AuthConfig) -> Self {
38        Self {
39            config,
40            users: RwLock::new(HashMap::new()),
41            sessions: RwLock::new(HashMap::new()),
42            reset_tokens: RwLock::new(HashMap::new()),
43        }
44    }
45
46    /// Register a new user
47    pub async fn register_user(
48        &self,
49        username: &str,
50        password: &str,
51        email: &str,
52    ) -> Result<String, SyncError> {
53        // Validate password strength
54        validate_password(password, &self.config)?;
55
56        // Check if user already exists
57        let users = self.users.read().await;
58        if users
59            .values()
60            .any(|u| u.username == username || u.email == email)
61        {
62            return Err(SyncError::AuthenticationError(
63                "User already exists".to_string(),
64            ));
65        }
66        drop(users);
67
68        // Generate user ID
69        let user_id = generate_user_id();
70
71        // Hash password
72        let (password_hash, salt) = hash_password(password)?;
73
74        // Create user
75        let user = User {
76            id: user_id.clone(),
77            username: username.to_string(),
78            email: email.to_string(),
79            password_hash,
80            salt,
81            created_at: Utc::now(),
82            last_login: None,
83            is_active: true,
84            is_verified: false,
85            mfa_enabled: false,
86            mfa_secret: None,
87            failed_login_attempts: 0,
88            locked_until: None,
89        };
90
91        // Store user
92        let mut users = self.users.write().await;
93        users.insert(user_id.clone(), user);
94
95        Ok(user_id)
96    }
97
98    /// Authenticate user with username and password
99    pub async fn login(&self, username: &str, password: &str) -> Result<UserSession, SyncError> {
100        // Find user
101        let user = self.find_user_by_username(username).await?;
102
103        // Check if user is locked
104        if let Some(locked_until) = user.locked_until {
105            if Utc::now() < locked_until {
106                return Err(SyncError::AuthenticationError(
107                    "Account is locked".to_string(),
108                ));
109            }
110        }
111
112        // Check if user is active
113        if !user.is_active {
114            return Err(SyncError::AuthenticationError(
115                "Account is inactive".to_string(),
116            ));
117        }
118
119        // Verify password
120        if !verify_password(password, &user.password_hash, &user.salt)? {
121            // Increment failed login attempts
122            self.increment_failed_attempts(&user.id).await?;
123            return Err(SyncError::AuthenticationError(
124                "Invalid credentials".to_string(),
125            ));
126        }
127
128        // Check if MFA is enabled for user
129        if user.mfa_enabled {
130            return Err(SyncError::AuthenticationError(
131                "MFA required - use login_with_mfa method".to_string(),
132            ));
133        }
134
135        // Reset failed login attempts on successful login
136        self.reset_failed_attempts(&user.id).await?;
137
138        // Update last login
139        self.update_last_login(&user.id).await?;
140
141        // Create session
142        let session = self.create_session(&user.id).await?;
143
144        Ok(session)
145    }
146
147    /// Authenticate user with MFA
148    pub async fn login_with_mfa(
149        &self,
150        username: &str,
151        password: &str,
152        mfa_code: &str,
153    ) -> Result<UserSession, SyncError> {
154        // First authenticate with password
155        let user = self.find_user_by_username(username).await?;
156
157        // Check if user is locked
158        if let Some(locked_until) = user.locked_until {
159            if Utc::now() < locked_until {
160                return Err(SyncError::AuthenticationError(
161                    "Account is locked".to_string(),
162                ));
163            }
164        }
165
166        // Check if user is active
167        if !user.is_active {
168            return Err(SyncError::AuthenticationError(
169                "Account is inactive".to_string(),
170            ));
171        }
172
173        // Verify password
174        if !verify_password(password, &user.password_hash, &user.salt)? {
175            self.increment_failed_attempts(&user.id).await?;
176            return Err(SyncError::AuthenticationError(
177                "Invalid credentials".to_string(),
178            ));
179        }
180
181        // Check if MFA is enabled
182        if !user.mfa_enabled {
183            return Err(SyncError::AuthenticationError(
184                "MFA not enabled for user".to_string(),
185            ));
186        }
187
188        // Verify MFA code
189        if !self.verify_mfa_code(&user.id, mfa_code).await? {
190            self.increment_failed_attempts(&user.id).await?;
191            return Err(SyncError::AuthenticationError(
192                "Invalid MFA code".to_string(),
193            ));
194        }
195
196        // Reset failed login attempts
197        self.reset_failed_attempts(&user.id).await?;
198
199        // Update last login
200        self.update_last_login(&user.id).await?;
201
202        // Create session
203        let session = self.create_session(&user.id).await?;
204
205        Ok(session)
206    }
207
208    /// Validate user session
209    pub async fn validate_session(&self, token: &str) -> Result<bool, SyncError> {
210        let sessions = self.sessions.read().await;
211        if let Some(session) = sessions.get(token) {
212            // Check if session is expired
213            if Utc::now() > session.expires_at {
214                drop(sessions);
215                self.logout(token).await?;
216                return Ok(false);
217            }
218
219            // Update last activity
220            drop(sessions);
221            self.update_session_activity(token).await?;
222            Ok(true)
223        } else {
224            Ok(false)
225        }
226    }
227
228    /// Logout user
229    pub async fn logout(&self, token: &str) -> Result<(), SyncError> {
230        let mut sessions = self.sessions.write().await;
231        sessions.remove(token);
232        Ok(())
233    }
234
235    /// Initiate password reset
236    pub async fn initiate_password_reset(&self, username: &str) -> Result<String, SyncError> {
237        let user = self.find_user_by_username(username).await?;
238
239        // Generate reset token
240        let token = generate_reset_token();
241        let expires_at = Utc::now() + chrono::Duration::hours(1);
242
243        let reset_token = PasswordResetToken {
244            token: token.clone(),
245            user_id: user.id.clone(),
246            expires_at,
247            created_at: Utc::now(),
248            used: false,
249        };
250
251        // Store reset token
252        let mut reset_tokens = self.reset_tokens.write().await;
253        reset_tokens.insert(token.clone(), reset_token);
254
255        Ok(token)
256    }
257
258    /// Complete password reset
259    pub async fn complete_password_reset(
260        &self,
261        token: &str,
262        new_password: &str,
263    ) -> Result<(), SyncError> {
264        // Validate new password
265        validate_password(new_password, &self.config)?;
266
267        // Find reset token
268        let mut reset_tokens = self.reset_tokens.write().await;
269        if let Some(reset_token) = reset_tokens.get_mut(token) {
270            // Check if token is expired
271            if Utc::now() > reset_token.expires_at {
272                return Err(SyncError::AuthenticationError(
273                    "Reset token has expired".to_string(),
274                ));
275            }
276
277            // Check if token is already used
278            if reset_token.used {
279                return Err(SyncError::AuthenticationError(
280                    "Reset token has already been used".to_string(),
281                ));
282            }
283
284            // Mark token as used
285            reset_token.used = true;
286
287            // Update user password
288            let (password_hash, salt) = hash_password(new_password)?;
289            let mut users = self.users.write().await;
290            if let Some(user) = users.get_mut(&reset_token.user_id) {
291                user.password_hash = password_hash;
292                user.salt = salt;
293                user.failed_login_attempts = 0;
294                user.locked_until = None;
295            }
296
297            Ok(())
298        } else {
299            Err(SyncError::AuthenticationError(
300                "Invalid reset token".to_string(),
301            ))
302        }
303    }
304
305    /// Enable MFA for user
306    pub async fn enable_mfa(&self, user_id: &str) -> Result<(), SyncError> {
307        let mut users = self.users.write().await;
308        if let Some(user) = users.get_mut(user_id) {
309            user.mfa_enabled = true;
310            user.mfa_secret = Some(generate_mfa_secret());
311        } else {
312            return Err(SyncError::AuthenticationError("User not found".to_string()));
313        }
314        Ok(())
315    }
316
317    /// Generate MFA code for user
318    pub async fn generate_mfa_code(&self, user_id: &str) -> Result<String, SyncError> {
319        let users = self.users.read().await;
320        if let Some(user) = users.get(user_id) {
321            if let Some(secret) = &user.mfa_secret {
322                // Generate TOTP code (simplified implementation)
323                let code = generate_totp_code(secret);
324                Ok(code)
325            } else {
326                Err(SyncError::AuthenticationError(
327                    "MFA secret not found".to_string(),
328                ))
329            }
330        } else {
331            Err(SyncError::AuthenticationError("User not found".to_string()))
332        }
333    }
334
335    /// Get user by ID
336    pub async fn get_user(&self, user_id: &str) -> Result<User, SyncError> {
337        let users = self.users.read().await;
338        users
339            .get(user_id)
340            .cloned()
341            .ok_or_else(|| SyncError::AuthenticationError("User not found".to_string()))
342    }
343
344    /// List all users
345    pub async fn list_users(&self) -> Vec<User> {
346        let users = self.users.read().await;
347        users.values().cloned().collect()
348    }
349
350    /// Clean up expired sessions
351    pub async fn cleanup_expired_sessions(&self) -> usize {
352        let mut sessions = self.sessions.write().await;
353        let now = Utc::now();
354        let expired_tokens: Vec<String> = sessions
355            .iter()
356            .filter(|(_, session)| session.expires_at < now)
357            .map(|(token, _)| token.clone())
358            .collect();
359
360        for token in &expired_tokens {
361            sessions.remove(token);
362        }
363
364        expired_tokens.len()
365    }
366
367    /// Clean up expired reset tokens
368    pub async fn cleanup_expired_reset_tokens(&self) -> usize {
369        let mut reset_tokens = self.reset_tokens.write().await;
370        let now = Utc::now();
371        let expired_tokens: Vec<String> = reset_tokens
372            .iter()
373            .filter(|(_, token)| token.expires_at < now)
374            .map(|(token, _)| token.clone())
375            .collect();
376
377        for token in &expired_tokens {
378            reset_tokens.remove(token);
379        }
380
381        expired_tokens.len()
382    }
383
384    // Private helper methods
385
386    /// Find user by username
387    async fn find_user_by_username(&self, username: &str) -> Result<User, SyncError> {
388        let users = self.users.read().await;
389        users
390            .values()
391            .find(|u| u.username == username)
392            .cloned()
393            .ok_or_else(|| SyncError::AuthenticationError("User not found".to_string()))
394    }
395
396    /// Create user session
397    async fn create_session(&self, user_id: &str) -> Result<UserSession, SyncError> {
398        let token = generate_session_token();
399        let now = Utc::now();
400        let expires_at = now + self.config.session_timeout;
401
402        let session = UserSession {
403            user_id: user_id.to_string(),
404            token: token.clone(),
405            expires_at,
406            created_at: now,
407            last_activity: now,
408            ip_address: None,
409            user_agent: None,
410        };
411
412        let mut sessions = self.sessions.write().await;
413        sessions.insert(token, session.clone());
414
415        Ok(session)
416    }
417
418    /// Update session activity
419    async fn update_session_activity(&self, token: &str) -> Result<(), SyncError> {
420        let mut sessions = self.sessions.write().await;
421        if let Some(session) = sessions.get_mut(token) {
422            session.last_activity = Utc::now();
423        }
424        Ok(())
425    }
426
427    /// Increment failed login attempts
428    async fn increment_failed_attempts(&self, user_id: &str) -> Result<(), SyncError> {
429        let mut users = self.users.write().await;
430        if let Some(user) = users.get_mut(user_id) {
431            user.failed_login_attempts += 1;
432            if user.failed_login_attempts >= self.config.max_failed_attempts {
433                user.locked_until = Some(Utc::now() + self.config.lockout_duration);
434            }
435        }
436        Ok(())
437    }
438
439    /// Reset failed login attempts
440    async fn reset_failed_attempts(&self, user_id: &str) -> Result<(), SyncError> {
441        let mut users = self.users.write().await;
442        if let Some(user) = users.get_mut(user_id) {
443            user.failed_login_attempts = 0;
444            user.locked_until = None;
445        }
446        Ok(())
447    }
448
449    /// Update last login
450    async fn update_last_login(&self, user_id: &str) -> Result<(), SyncError> {
451        let mut users = self.users.write().await;
452        if let Some(user) = users.get_mut(user_id) {
453            user.last_login = Some(Utc::now());
454        }
455        Ok(())
456    }
457
458    /// Verify MFA code
459    async fn verify_mfa_code(&self, user_id: &str, code: &str) -> Result<bool, SyncError> {
460        let users = self.users.read().await;
461        if let Some(user) = users.get(user_id) {
462            if let Some(secret) = &user.mfa_secret {
463                let expected_code = generate_totp_code(secret);
464                Ok(expected_code == code)
465            } else {
466                Ok(false)
467            }
468        } else {
469            Ok(false)
470        }
471    }
472}