💾 Archived View for alchemi.dev › en › projects › kochab › files › src › user_management › manager.r… captured on 2023-09-28 at 16:00:55.
⬅️ Previous capture (2022-07-16)
-=-=-=-=-=-=-
use serde::{Serialize, de::DeserializeOwned}; use crate::user_management::{User, Result}; use crate::user_management::user::{RegisteredUser, NotSignedInUser, PartialUser}; #[derive(Debug, Clone)] /// A struct containing information for managing users. /// /// Wraps a [`sled::Db`] pub struct UserManager { pub db: sled::Db, pub (crate) users: sled::Tree, // user_id:String maps to data:UserData pub (crate) certificates: sled::Tree, // certificate:u64 maps to data:CertificateData } impl UserManager { /// Create or open a new UserManager /// /// The `dir` argument is the path to a data directory, to be populated using sled. /// This will be created if it does not exist. pub fn new(db: sled::Db) -> Result<Self> { Ok(Self { users: db.open_tree("gay.emii.kochab.users")?, certificates: db.open_tree("gay.emii.kochab.certificates")?, db, }) } /// Lookup the owner of a certificate based on it's fingerprint /// /// # Errors /// An error is thrown if there is an error reading from the database or if data /// recieved from the database is corrupt pub fn lookup_certificate(&self, cert: [u8; 32]) -> Result<Option<String>> { if let Some(bytes) = self.certificates.get(cert)? { Ok(Some(std::str::from_utf8(bytes.as_ref())?.to_string())) } else { Ok(None) } } /// Lookup information about a user by username /// /// # Errors /// An error is thrown if there is an error reading from the database or if data /// recieved from the database is corrupt pub fn lookup_user<UserData>( &self, username: impl AsRef<str> ) -> Result<Option<RegisteredUser<UserData>>> where UserData: Serialize + DeserializeOwned { if let Some(bytes) = self.users.get(username.as_ref())? { let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.as_ref())?; Ok(Some(RegisteredUser::new(username.as_ref().to_owned(), None, self.clone(), inner))) } else { Ok(None) } } /// Produce a list of all users in the database /// /// # Panics /// An panics if there is an error reading from the database or if data recieved from /// the database is corrupt pub fn all_users<UserData>( &self, ) -> Vec<RegisteredUser<UserData>> where UserData: Serialize + DeserializeOwned { self.users.iter() .map(|result| { let (username, bytes) = result.expect("Failed to connect to database"); let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.as_ref()) .expect("Received malformed data from database"); let username = String::from_utf8(username.to_vec()) .expect("Malformed username in database"); RegisteredUser::new(username, None, self.clone(), inner) }) .collect() } /// Attempt to determine the user who sent a request based on the certificate. /// /// # Errors /// An error is thrown if there is an error reading from the database or if data /// recieved from the database is corrupt /// /// # Panics /// Pancis if the database is corrupt pub fn get_user<UserData>( &self, cert: Option<&[u8; 32]> ) -> Result<User<UserData>> where UserData: Serialize + DeserializeOwned { if let Some(certificate) = cert { if let Some(username) = self.lookup_certificate(*certificate)? { let user_inner = self.lookup_user(&username)? .expect("Database corruption: Certificate data refers to non-existant user"); Ok(User::SignedIn(user_inner.with_cert(*certificate))) } else { Ok(User::NotSignedIn(NotSignedInUser { certificate: *certificate, manager: self.clone(), })) } } else { Ok(User::Unauthenticated) } } }