use rustls::Certificate; use serde::{Deserialize, Serialize}; use sled::Transactional; use crate::user_management::UserManager; use crate::user_management::Result; use crate::user_management::manager::CertificateData; /// An struct corresponding to the data stored in the user tree #[derive(Clone, Debug, Deserialize, Serialize)] pub struct UserInner { data: UserData, certificates: Vec, pass_hash: Option, } /// Any information about the connecting user #[derive(Clone, Debug)] pub enum User { /// A user who is connected without using a client certificate. /// /// This could be a user who has an account but just isn't presenting a certificate at /// the minute, a user whose client does not support client certificates, or a user /// who has not yet created a certificate for the site Unauthenticated, /// A user who is connecting with a certificate that isn't connected to an account /// /// This is typically a new user who hasn't set up an account yet, or a user /// connecting with a new certificate that needs to be added to an existing account. NotSignedIn(NotSignedInUser), /// A user connecting with an identified account SignedIn(SignedInUser), } #[derive(Clone, Debug)] /// Data about a user with a certificate not associated with an account /// /// For more information about the user lifecycle and sign-in stages, see [`User`] pub struct NotSignedInUser { pub (crate) certificate: Certificate, pub (crate) manager: UserManager, } impl NotSignedInUser { /// Register a new user with this certificate /// /// # Errors /// The provided username must be unique, or else an error will be raised. /// /// Additional errors might occur if there is a problem writing to the database pub fn register( self, username: String, ) -> Result> where UserData: Serialize + Default { if self.manager.users.contains_key(username.as_str())? { Err(super::UserManagerError::UsernameNotUnique) } else { let cert_hash = UserManager::hash_certificate(&self.certificate); let newser = SignedInUser { inner: UserInner { data: UserData::default(), certificates: vec![cert_hash], pass_hash: None, }, username: username.clone(), active_certificate: self.certificate.clone(), manager: self.manager, }; let cert_info = CertificateData { certificate: self.certificate, owner_username: username, }; let newser_serialized = bincode::serialize(&newser.inner)?; let cert_info_serialized = bincode::serialize(&cert_info)?; (&newser.manager.users, &newser.manager.certificates) .transaction(|(tx_usr, tx_crt)| { tx_usr.insert( newser.username.as_str(), newser_serialized.clone(), )?; tx_crt.insert( cert_hash.to_le_bytes().as_ref(), cert_info_serialized.clone(), )?; Ok(()) })?; //TODO Ok(newser) } } /// Attach this certificate to an existing user /// /// Try to add this certificate to another user using a username and password /// /// # Errors /// This will error if the username and password are incorrect, or if the user has yet /// to set a password. /// /// Additional errors might occur if an error occurs during database lookup and /// deserialization pub fn attach<'de, UserData>( username: impl AsRef<[u8]>, password: impl AsRef<[u8]>, ) -> Result> where UserData: Serialize + Deserialize<'de> { todo!() } } #[derive(Clone, Debug)] /// Data about a logged in user /// /// For more information about the user lifecycle and sign-in stages, see [`User`] pub struct SignedInUser { pub (crate) username: String, pub (crate) active_certificate: Certificate, pub (crate) manager: UserManager, pub (crate) inner: UserInner, } impl SignedInUser { /// Get the [`Certificate`] that the user is currently using to connect. pub const fn active_certificate(&self) -> &Certificate { &self.active_certificate } /// Produce a list of all [`Certificate`]s registered to this account pub fn all_certificates(&self) -> Vec { self.inner.certificates .iter() .map( |cid| self.manager.lookup_certificate(*cid) .expect("Database corruption: User refers to non-existant certificate") .expect("Error accessing database") .certificate ) .collect() } /// Get the user's current username. /// /// NOTE: This is not guaranteed not to change. pub const fn username(&self) -> &String { &self.username } /// Check a password against the user's password hash /// /// # Errors /// An error is raised if the user has yet to set a password, or if the user's /// password hash is somehow malformed. pub fn check_password( &self, try_password: impl AsRef<[u8]> ) -> Result { if let Some(hash) = &self.inner.pass_hash { Ok(bcrypt::verify(try_password, hash.as_str())?) } else { Err(super::UserManagerError::PasswordNotSet) } } /// Write any updates to the user to the database. /// /// Updates caused by calling methods directly on the user do not need to be saved. /// This is only for changes made to the UserData. pub fn save(&self) -> Result<()> where UserData: Serialize { self.manager.users.insert( &self.username, bincode::serialize(&self.inner)?, )?; Ok(()) } } impl AsRef for SignedInUser { fn as_ref(&self) -> &UserData { &self.inner.data } } impl AsMut for SignedInUser { /// NOTE: Changes made to the user data won't be persisted until SignedInUser::save /// is called fn as_mut(&mut self) -> &mut UserData { &mut self.inner.data } }