diff --git a/src/user_management/manager.rs b/src/user_management/manager.rs index 4f93e5e..426096a 100644 --- a/src/user_management/manager.rs +++ b/src/user_management/manager.rs @@ -105,12 +105,12 @@ impl UserManager { 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, - })) + Ok(User::SignedIn(SignedInUser::new( + certificate_data.owner_username, + certificate_data.certificate, + self.clone(), + user_inner, + ))) } else { Ok(User::NotSignedIn(NotSignedInUser { certificate: certificate.clone(), diff --git a/src/user_management/user.rs b/src/user_management/user.rs index ca61472..13ee056 100644 --- a/src/user_management/user.rs +++ b/src/user_management/user.rs @@ -19,7 +19,7 @@ //! [`UserManager::lookup_user()`] method. Unlinke with [`SignedInUser`], these are not //! committed on drop, and [`PartialUser::store()`] must be manually called use rustls::Certificate; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use sled::Transactional; use crate::user_management::UserManager; @@ -86,7 +86,7 @@ impl AsMut for PartialUser { /// Any information about the connecting user #[derive(Clone, Debug)] -pub enum User { +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 @@ -124,28 +124,25 @@ impl NotSignedInUser { /// 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( + pub fn register( self, username: String, - ) -> Result> - where - UserData: Serialize + Default - { + ) -> Result> { 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: PartialUser { + let newser = SignedInUser::new( + username.clone(), + self.certificate.clone(), + self.manager, + PartialUser { 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, @@ -188,13 +185,10 @@ impl NotSignedInUser { /// /// Additional errors might occur if an error occurs during database lookup and /// deserialization - pub fn attach<'de, UserData>( + pub fn attach( username: impl AsRef<[u8]>, password: impl AsRef<[u8]>, - ) -> Result> - where - UserData: Serialize + Deserialize<'de> - { + ) -> Result> { todo!() } } @@ -203,16 +197,35 @@ impl NotSignedInUser { /// Data about a logged in user /// /// For more information about the user lifecycle and sign-in stages, see [`User`] -pub struct SignedInUser { +pub struct SignedInUser { pub (crate) username: String, pub (crate) active_certificate: Certificate, pub (crate) manager: UserManager, pub (crate) inner: PartialUser, + /// Indicates that [`SignedInUser::as_mut()`] has been called, but [`SignedInUser::save()`] has not + has_changed: bool, } -impl SignedInUser { +impl SignedInUser { + + /// Create a new user from parts + pub (crate) fn new( + username: String, + active_certificate: Certificate, + manager: UserManager, + inner: PartialUser + ) -> Self { + Self { + username, + active_certificate, + manager, + inner, + has_changed: false, + } + } + /// Get the [`Certificate`] that the user is currently using to connect. - pub const fn active_certificate(&self) -> &Certificate { + pub fn active_certificate(&self) -> &Certificate { &self.active_certificate } @@ -232,7 +245,7 @@ impl SignedInUser { /// Get the user's current username. /// /// NOTE: This is not guaranteed not to change. - pub const fn username(&self) -> &String { + pub fn username(&self) -> &String { &self.username } @@ -256,24 +269,37 @@ impl SignedInUser { /// /// 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<()> + pub fn save(&mut self) -> Result<()> where UserData: Serialize { - self.inner.store(&self.manager.users, &self.username) + self.inner.store(&self.manager.users, &self.username)?; + self.has_changed = false; + Ok(()) } } -impl AsRef for SignedInUser { +impl std::ops::Drop for SignedInUser { + fn drop(&mut self) { + if self.has_changed { + if let Err(e) = self.save() { + error!("Failed to save user data to database: {:?}", e); + } + } + } +} + +impl AsRef for SignedInUser { fn as_ref(&self) -> &UserData { &self.inner.data } } -impl AsMut for SignedInUser { +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 { + self.has_changed = true; &mut self.inner.data } }