// Session management for SeckelAPI use crate::config::Config; use crate::models::Session; use chrono::{Duration, Utc}; use std::collections::HashMap; use std::sync::{Arc, RwLock}; use uuid::Uuid; #[derive(Clone)] pub struct SessionManager { sessions: Arc>>, config: Arc, } impl SessionManager { pub fn new(config: Arc) -> Self { Self { sessions: Arc::new(RwLock::new(HashMap::new())), config, } } fn get_timeout_for_power(&self, power: i32) -> u64 { self.config.get_session_timeout(power) } pub fn create_session( &self, user_id: i32, username: String, role_id: i32, role_name: String, power: i32, ) -> String { let token = Uuid::new_v4().to_string(); let now = Utc::now(); let session = Session { user_id, username, role_id, role_name, power, created_at: now, last_accessed: now, }; if let Ok(mut sessions) = self.sessions.write() { // Check concurrent session limit for this user let max_sessions = self.config.get_max_concurrent_sessions(power); let user_sessions: Vec<(String, chrono::DateTime)> = sessions .iter() .filter(|(_, s)| s.user_id == user_id) .map(|(token, s)| (token.clone(), s.created_at)) .collect(); // If at limit, remove the oldest session if user_sessions.len() >= max_sessions as usize { if let Some(oldest_token) = user_sessions .iter() .min_by_key(|(_, created)| created) .map(|(token, _)| token.clone()) { sessions.remove(&oldest_token); } } sessions.insert(token.clone(), session); } token } pub fn get_session(&self, token: &str) -> Option { let mut session_to_update = None; { if let Ok(sessions) = self.sessions.read() { if let Some(session) = sessions.get(token) { let now = Utc::now(); let timeout_for_user = self.get_timeout_for_power(session.power); let timeout_duration = Duration::minutes(timeout_for_user as i64); // Check if session has expired if now - session.last_accessed > timeout_duration { return None; // Session expired, will be cleaned up later } else { session_to_update = Some(session.clone()); } } } } if let Some(mut session) = session_to_update { // Update last accessed time only if refresh_on_activity is enabled if self.config.security.refresh_session_on_activity { session.last_accessed = Utc::now(); if let Ok(mut sessions) = self.sessions.write() { sessions.insert(token.to_string(), session.clone()); } } Some(session) } else { None } } pub fn remove_session(&self, token: &str) -> bool { if let Ok(mut sessions) = self.sessions.write() { sessions.remove(token).is_some() } else { false } } pub fn cleanup_expired_sessions(&self) { let now = Utc::now(); if let Ok(mut sessions) = self.sessions.write() { sessions.retain(|_, session| { let timeout_for_user = self.get_timeout_for_power(session.power); let timeout_duration = Duration::minutes(timeout_for_user as i64); now - session.last_accessed <= timeout_duration }); } } }