use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use serde::Serialize; use serde::de::DeserializeOwned; use crate::error::SessionError; use crate::store::{SessionDiff, SessionState}; pub const SESSION_USER_ID: &str = "user_id"; pub const SESSION_CLIENT_ID: &str = "client_id"; pub const SESSION_CLIENT_IP: &str = "client_ip"; pub const SESSION_USER_AGENT: &str = "user_agent"; pub const SESSION_SECURITY_VERSION: &str = "security_version"; pub const SESSION_AUTH_SESSION_ID: &str = "auth_session_id"; pub const SESSION_MFA_VERIFIED: &str = "mfa_verified"; #[derive(Clone, Default)] pub struct Session { inner: Rc>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum SessionStatus { Changed, Purged, Renewed, #[default] UnChanged, } #[derive(Default)] struct SessionInner { status: SessionStatus, state: SessionState, changes: HashMap>, } impl Session { pub fn new() -> Self { Self::default() } pub fn from_state(state: SessionState) -> Self { Self { inner: Rc::new(RefCell::new(SessionInner { status: SessionStatus::UnChanged, state, changes: HashMap::new(), })), } } pub fn status(&self) -> SessionStatus { self.inner.borrow().status } pub fn snapshot(&self) -> (SessionStatus, SessionState) { let inner = self.inner.borrow(); (inner.status, inner.state.clone()) } pub fn is_empty(&self) -> bool { self.inner.borrow().state.is_empty() } pub fn diff(&self) -> SessionDiff { let inner = self.inner.borrow(); let mut diff = SessionDiff::default(); for (key, value) in &inner.changes { match value { Some(value) => { diff.set.insert(key.clone(), value.clone()); } None => diff.removed.push(key.clone()), } } diff } pub fn get(&self, key: &str) -> Result, SessionError> { let value = self.inner.borrow().state.get(key).cloned(); value .map(|value| deserialize_value(key, &value)) .transpose() } pub fn set(&self, key: impl Into, value: T) -> Result<(), SessionError> { let key = key.into(); self.inner.borrow().ensure_active()?; let value = serialize_value(&key, &value)?; let mut inner = self.inner.borrow_mut(); inner.ensure_active()?; inner.set_value(key, value); Ok(()) } pub fn update(&self, key: &str, update: F) -> Result<(), SessionError> where T: DeserializeOwned + Serialize, F: FnOnce(T) -> T, { let Some(previous) = self.current_value(key)? else { return Ok(()); }; let value = update(deserialize_value(key, &previous)?); let value = serialize_value(key, &value)?; self.commit_update(key, Some(&previous), value) } pub fn remove(&self, key: &str) -> Option { let mut inner = self.inner.borrow_mut(); let value = inner.state.remove(key); if value.is_some() { inner.changes.insert(key.to_owned(), None); inner.mark_changed(); } value } pub fn clear(&self) { let mut inner = self.inner.borrow_mut(); if !inner.state.is_empty() { let removed: Vec = inner.state.drain().map(|(key, _)| key).collect(); for key in removed { inner.changes.insert(key, None); } inner.mark_changed(); } } pub fn purge(&self) { let mut inner = self.inner.borrow_mut(); inner.state.clear(); inner.changes.clear(); inner.status = SessionStatus::Purged; } pub fn renew(&self) { let mut inner = self.inner.borrow_mut(); if inner.status != SessionStatus::Purged { inner.status = SessionStatus::Renewed; } } pub fn user_id(&self) -> Result, SessionError> { self.get(SESSION_USER_ID) } pub fn uid(&self) -> Option { self.user_id().ok().flatten() } pub fn session_id(&self) -> Option { self.get(SESSION_AUTH_SESSION_ID).ok().flatten() } pub fn mfa_verified(&self) -> bool { self.get(SESSION_MFA_VERIFIED) .ok() .flatten() .unwrap_or(false) } pub fn set_user_id(&self, user_id: i64) -> Result<(), SessionError> { self.set(SESSION_USER_ID, user_id) } pub fn client_id(&self) -> Result, SessionError> { self.get(SESSION_CLIENT_ID) } pub fn client_ip(&self) -> Result, SessionError> { self.get(SESSION_CLIENT_IP) } pub fn set_client_ip(&self, client_ip: impl Into) -> Result<(), SessionError> { self.set(SESSION_CLIENT_IP, client_ip.into()) } pub fn user_agent(&self) -> Result, SessionError> { self.get(SESSION_USER_AGENT) } pub fn set_user_agent(&self, user_agent: impl Into) -> Result<(), SessionError> { self.set(SESSION_USER_AGENT, user_agent.into()) } pub fn security_version(&self) -> Result, SessionError> { self.get(SESSION_SECURITY_VERSION) } /// 与 `users.security_version` 一起在认证成功时写入。 pub fn set_security_version(&self, security_version: i32) -> Result<(), SessionError> { self.set(SESSION_SECURITY_VERSION, security_version) } fn current_value(&self, key: &str) -> Result, SessionError> { let inner = self.inner.borrow(); inner.ensure_active()?; Ok(inner.state.get(key).cloned()) } fn commit_update( &self, key: &str, previous: Option<&str>, value: String, ) -> Result<(), SessionError> { let mut inner = self.inner.borrow_mut(); inner.ensure_active()?; if inner.state.get(key).map(String::as_str) != previous { return Err(SessionError::conflict(key)); } inner.set_value(key.to_owned(), value); Ok(()) } } impl SessionInner { fn ensure_active(&self) -> Result<(), SessionError> { match self.status { SessionStatus::Purged => Err(SessionError::Purged), _ => Ok(()), } } fn mark_changed(&mut self) { if self.status == SessionStatus::UnChanged { self.status = SessionStatus::Changed; } } fn set_value(&mut self, key: String, value: String) { if self.state.get(&key) != Some(&value) { self.state.insert(key.clone(), value.clone()); self.changes.insert(key, Some(value)); self.mark_changed(); } } } fn serialize_value(key: &str, value: &T) -> Result { serde_json::to_string(value).map_err(|source| SessionError::serialize(key, source)) } fn deserialize_value(key: &str, value: &str) -> Result { serde_json::from_str(value).map_err(|source| SessionError::deserialize(key, source)) }