use std::fmt::Debug; use serde::{Deserialize, Serialize}; use session::{DatabasePool, ReadOnlySession as AxumReadOnlySession, Session as AxumSession}; use uuid::Uuid; pub const UID_KEY: &str = "auth:uid"; pub const USERS_KEY: &str = "auth:users"; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AuthUser { pub uid: Uuid, pub username: String, pub display_name: Option, pub avatar_url: Option, pub email: String, } pub trait SessionUser { fn uid(&self) -> Option; fn user(&self) -> Option { let uid = self.uid()?; self.users().into_iter().find(|user| user.uid == uid) } fn users(&self) -> Vec; fn is_authenticated(&self) -> bool { self.uid().is_some() } } pub trait SessionUserMut: SessionUser { fn login_user(&self, user: AuthUser); fn switch_uid(&self, uid: Uuid) -> bool; fn clear_uid(&self); fn logout_user(&self, uid: Uuid) -> bool; } macro_rules! impl_session_user { ($ty:ident) => { impl SessionUser for $ty where D: DatabasePool + Clone + Debug + Sync + Send + 'static, { fn uid(&self) -> Option { self.get::(UID_KEY) } fn users(&self) -> Vec { self.get::>(USERS_KEY).unwrap_or_default() } } }; } impl_session_user!(AxumSession); impl_session_user!(AxumReadOnlySession); impl SessionUserMut for AxumSession where D: DatabasePool + Clone + Debug + Sync + Send + 'static, { fn login_user(&self, user: AuthUser) { let uid = user.uid; let mut users = self.users(); users.retain(|existing| existing.uid != uid); users.push(user); self.set(USERS_KEY, users); self.set(UID_KEY, uid); } fn switch_uid(&self, uid: Uuid) -> bool { if self.users().iter().any(|user| user.uid == uid) { self.set(UID_KEY, uid); true } else { false } } fn clear_uid(&self) { self.remove(UID_KEY); } fn logout_user(&self, uid: Uuid) -> bool { let mut users = self.users(); let was_active = self.uid() == Some(uid); let before = users.len(); users.retain(|user| user.uid != uid); if users.len() == before { return false; } if was_active { match users.last() { Some(remainder) => self.set(UID_KEY, remainder.uid), None => self.remove(UID_KEY), } } self.set(USERS_KEY, users); true } }