use actix_identity::Identity; use serde::{Deserialize, Serialize}; use crate::constants::{MAX_INACTIVITY_DURATION, MAX_SESSION_DURATION, MIN_ACTIVITY_RECORD_TIME}; use crate::data::user::{User, UserID}; use crate::utils::time::time; #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] pub enum SessionStatus { Invalid, SignedIn, NeedNewPassword, NeedMFA, } impl Default for SessionStatus { fn default() -> Self { Self::Invalid } } #[derive(Debug, Serialize, Deserialize, Default)] struct SessionIdentityData { pub id: UserID, pub is_admin: bool, login_time: u64, last_access: u64, pub status: SessionStatus, } pub struct SessionIdentity<'a>(pub &'a Identity); impl<'a> SessionIdentity<'a> { pub fn set_user(&self, user: &User) { self.set_session_data(&SessionIdentityData { id: user.uid.clone(), is_admin: user.admin, login_time: time(), last_access: time(), status: SessionStatus::SignedIn, }); } fn get_session_data(&self) -> Option { let mut res: Option = self.0.identity() .as_deref() .map(serde_json::from_str) .map(|f| f.expect("Failed to deserialize session data!")); // Check if session is valid if let Some(sess) = &res { if sess.id.is_empty() { return None; } } if let Some(session) = res.as_mut() { if session.login_time + MAX_SESSION_DURATION < time() { log::info!("Session for {} reached max duration timeout", session.id); self.0.forget(); return None; } if session.last_access + MAX_INACTIVITY_DURATION < time() { log::info!("Session is expired for {}", session.id); self.0.forget(); return None; } if session.last_access + MIN_ACTIVITY_RECORD_TIME < time() { log::debug!("Refresh last access for session"); session.last_access = time(); self.set_session_data(session); } } res } fn set_session_data(&self, data: &SessionIdentityData) { let s = serde_json::to_string(data) .expect("Failed to serialize session data!"); self.0.remember(s); } pub fn set_status(&self, status: SessionStatus) { let mut sess = self.get_session_data().unwrap_or_default(); sess.status = status; self.set_session_data(&sess); } pub fn is_authenticated(&self) -> bool { self.get_session_data() .map(|s| s.status == SessionStatus::SignedIn) .unwrap_or(false) } pub fn need_new_password(&self) -> bool { self.get_session_data() .map(|s| s.status == SessionStatus::NeedNewPassword) .unwrap_or(false) } pub fn user_id(&self) -> UserID { self.get_session_data() .unwrap_or_default() .id } }