ec0bdb8
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<RefCell<SessionInner>>,
}
#[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<String, Option<String>>,
}
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<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, SessionError> {
let value = self.inner.borrow().state.get(key).cloned();
value
.map(|value| deserialize_value(key, &value))
.transpose()
}
pub fn set<T: Serialize>(&self, key: impl Into<String>, 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<T, F>(&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<String> {
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<String> = 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<Option<i64>, SessionError> {
self.get(SESSION_USER_ID)
}
pub fn uid(&self) -> Option<i64> {
self.user_id().ok().flatten()
}
pub fn session_id(&self) -> Option<String> {
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<Option<String>, SessionError> {
self.get(SESSION_CLIENT_ID)
}
pub fn client_ip(&self) -> Result<Option<String>, SessionError> {
self.get(SESSION_CLIENT_IP)
}
pub fn set_client_ip(&self, client_ip: impl Into<String>) -> Result<(), SessionError> {
self.set(SESSION_CLIENT_IP, client_ip.into())
}
pub fn user_agent(&self) -> Result<Option<String>, SessionError> {
self.get(SESSION_USER_AGENT)
}
pub fn set_user_agent(&self, user_agent: impl Into<String>) -> Result<(), SessionError> {
self.set(SESSION_USER_AGENT, user_agent.into())
}
pub fn security_version(&self) -> Result<Option<i32>, 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<Option<String>, 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<T: Serialize + ?Sized>(key: &str, value: &T) -> Result<String, SessionError> {
serde_json::to_string(value).map_err(|source| SessionError::serialize(key, source))
}
fn deserialize_value<T: DeserializeOwned>(key: &str, value: &str) -> Result<T, SessionError> {
serde_json::from_str(value).map_err(|source| SessionError::deserialize(key, source))
}