use rustls::Certificate; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use std::path::Path; use crate::user_management::{User, Result}; use crate::user_management::user::{SignedInUser, NotSignedInUser, UserInner}; #[derive(Debug, Clone, Deserialize, Serialize)] /// Data stored in the certificate tree about a certain certificate pub struct CertificateData { #[serde(with = "CertificateDef")] pub certificate: Certificate, pub owner_username: String, } #[derive(Serialize, Deserialize)] #[serde(remote = "Certificate")] struct CertificateDef(Vec); #[derive(Debug, Clone)] /// A struct containing information for managing users. /// /// Wraps a [`sled::Db`] pub struct UserManager { 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(dir: impl AsRef) -> Result { let db = sled::open(dir)?; Ok(Self { users: db.open_tree("users")?, certificates: db.open_tree("certificates")?, db, }) } /// Produce a u32 hash from a certificate, used for [`lookup_certficate()`] pub fn hash_certificate(cert: &Certificate) -> u32 { let mut hasher = crc32fast::Hasher::new(); hasher.update(cert.0.as_ref()); hasher.finalize() } /// Lookup information about a certificate based on it's u32 hash /// /// # 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: u32) -> Result> { if let Some(bytes) = self.certificates.get(cert.to_le_bytes())? { Ok(Some(bincode::deserialize(&bytes)?)) } 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<'de, UserData>( &self, username: impl AsRef<[u8]> ) -> Result>> where UserData: Serialize + DeserializeOwned { if let Some(bytes) = self.users.get(username)? { Ok(Some(bincode::deserialize_from(bytes.as_ref())?)) } else { Ok(None) } } /// 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<'de, UserData>( &self, cert: Option<&Certificate> ) -> Result> where UserData: Serialize + DeserializeOwned { if let Some(certificate) = cert { let cert_hash = Self::hash_certificate(certificate); if let Some(certificate_data) = self.lookup_certificate(cert_hash)? { let user_inner = self.lookup_user(&certificate_data.owner_username)? .expect("Database corruption: Certificate data refers to non-existant user"); Ok(User::SignedIn(SignedInUser { username: certificate_data.owner_username, active_certificate: certificate_data.certificate, manager: self.clone(), inner: user_inner, })) } else { Ok(User::NotSignedIn(NotSignedInUser { certificate: certificate.clone(), manager: self.clone(), })) } } else { Ok(User::Unauthenticated) } } }