leptos_sync_core/security/authentication/
manager.rs1use 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
17pub 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 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 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 pub async fn register_user(
48 &self,
49 username: &str,
50 password: &str,
51 email: &str,
52 ) -> Result<String, SyncError> {
53 validate_password(password, &self.config)?;
55
56 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 let user_id = generate_user_id();
70
71 let (password_hash, salt) = hash_password(password)?;
73
74 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 let mut users = self.users.write().await;
93 users.insert(user_id.clone(), user);
94
95 Ok(user_id)
96 }
97
98 pub async fn login(&self, username: &str, password: &str) -> Result<UserSession, SyncError> {
100 let user = self.find_user_by_username(username).await?;
102
103 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 if !user.is_active {
114 return Err(SyncError::AuthenticationError(
115 "Account is inactive".to_string(),
116 ));
117 }
118
119 if !verify_password(password, &user.password_hash, &user.salt)? {
121 self.increment_failed_attempts(&user.id).await?;
123 return Err(SyncError::AuthenticationError(
124 "Invalid credentials".to_string(),
125 ));
126 }
127
128 if user.mfa_enabled {
130 return Err(SyncError::AuthenticationError(
131 "MFA required - use login_with_mfa method".to_string(),
132 ));
133 }
134
135 self.reset_failed_attempts(&user.id).await?;
137
138 self.update_last_login(&user.id).await?;
140
141 let session = self.create_session(&user.id).await?;
143
144 Ok(session)
145 }
146
147 pub async fn login_with_mfa(
149 &self,
150 username: &str,
151 password: &str,
152 mfa_code: &str,
153 ) -> Result<UserSession, SyncError> {
154 let user = self.find_user_by_username(username).await?;
156
157 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 if !user.is_active {
168 return Err(SyncError::AuthenticationError(
169 "Account is inactive".to_string(),
170 ));
171 }
172
173 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 if !user.mfa_enabled {
183 return Err(SyncError::AuthenticationError(
184 "MFA not enabled for user".to_string(),
185 ));
186 }
187
188 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 self.reset_failed_attempts(&user.id).await?;
198
199 self.update_last_login(&user.id).await?;
201
202 let session = self.create_session(&user.id).await?;
204
205 Ok(session)
206 }
207
208 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 if Utc::now() > session.expires_at {
214 drop(sessions);
215 self.logout(token).await?;
216 return Ok(false);
217 }
218
219 drop(sessions);
221 self.update_session_activity(token).await?;
222 Ok(true)
223 } else {
224 Ok(false)
225 }
226 }
227
228 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 pub async fn initiate_password_reset(&self, username: &str) -> Result<String, SyncError> {
237 let user = self.find_user_by_username(username).await?;
238
239 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 let mut reset_tokens = self.reset_tokens.write().await;
253 reset_tokens.insert(token.clone(), reset_token);
254
255 Ok(token)
256 }
257
258 pub async fn complete_password_reset(
260 &self,
261 token: &str,
262 new_password: &str,
263 ) -> Result<(), SyncError> {
264 validate_password(new_password, &self.config)?;
266
267 let mut reset_tokens = self.reset_tokens.write().await;
269 if let Some(reset_token) = reset_tokens.get_mut(token) {
270 if Utc::now() > reset_token.expires_at {
272 return Err(SyncError::AuthenticationError(
273 "Reset token has expired".to_string(),
274 ));
275 }
276
277 if reset_token.used {
279 return Err(SyncError::AuthenticationError(
280 "Reset token has already been used".to_string(),
281 ));
282 }
283
284 reset_token.used = true;
286
287 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 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 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 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 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 pub async fn list_users(&self) -> Vec<User> {
346 let users = self.users.read().await;
347 users.values().cloned().collect()
348 }
349
350 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 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 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 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 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 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 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 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 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}