Save SignedInUser on drop, and make changes necessary to that end

Which was a surprisingly large number of changes /shrug
This commit is contained in:
Emi Tatsuo 2020-11-19 14:25:36 -05:00
parent e7cf782a60
commit 18228bb1c5
Signed by: Emi
GPG Key ID: 68FAB2E2E6DFC98B
2 changed files with 58 additions and 32 deletions

View File

@ -105,12 +105,12 @@ impl UserManager {
if let Some(certificate_data) = self.lookup_certificate(cert_hash)? { if let Some(certificate_data) = self.lookup_certificate(cert_hash)? {
let user_inner = self.lookup_user(&certificate_data.owner_username)? let user_inner = self.lookup_user(&certificate_data.owner_username)?
.expect("Database corruption: Certificate data refers to non-existant user"); .expect("Database corruption: Certificate data refers to non-existant user");
Ok(User::SignedIn(SignedInUser { Ok(User::SignedIn(SignedInUser::new(
username: certificate_data.owner_username, certificate_data.owner_username,
active_certificate: certificate_data.certificate, certificate_data.certificate,
manager: self.clone(), self.clone(),
inner: user_inner, user_inner,
})) )))
} else { } else {
Ok(User::NotSignedIn(NotSignedInUser { Ok(User::NotSignedIn(NotSignedInUser {
certificate: certificate.clone(), certificate: certificate.clone(),

View File

@ -19,7 +19,7 @@
//! [`UserManager::lookup_user()`] method. Unlinke with [`SignedInUser`], these are not //! [`UserManager::lookup_user()`] method. Unlinke with [`SignedInUser`], these are not
//! committed on drop, and [`PartialUser::store()`] must be manually called //! committed on drop, and [`PartialUser::store()`] must be manually called
use rustls::Certificate; use rustls::Certificate;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize, de::DeserializeOwned};
use sled::Transactional; use sled::Transactional;
use crate::user_management::UserManager; use crate::user_management::UserManager;
@ -86,7 +86,7 @@ impl<UserData> AsMut<UserData> for PartialUser<UserData> {
/// Any information about the connecting user /// Any information about the connecting user
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum User<UserData> { pub enum User<UserData: Serialize + DeserializeOwned> {
/// A user who is connected without using a client certificate. /// 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 /// 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. /// 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 /// Additional errors might occur if there is a problem writing to the database
pub fn register<UserData>( pub fn register<UserData: Serialize + DeserializeOwned + Default>(
self, self,
username: String, username: String,
) -> Result<SignedInUser<UserData>> ) -> Result<SignedInUser<UserData>> {
where
UserData: Serialize + Default
{
if self.manager.users.contains_key(username.as_str())? { if self.manager.users.contains_key(username.as_str())? {
Err(super::UserManagerError::UsernameNotUnique) Err(super::UserManagerError::UsernameNotUnique)
} else { } else {
let cert_hash = UserManager::hash_certificate(&self.certificate); let cert_hash = UserManager::hash_certificate(&self.certificate);
let newser = SignedInUser { let newser = SignedInUser::new(
inner: PartialUser { username.clone(),
self.certificate.clone(),
self.manager,
PartialUser {
data: UserData::default(), data: UserData::default(),
certificates: vec![cert_hash], certificates: vec![cert_hash],
pass_hash: None, pass_hash: None,
}, },
username: username.clone(), );
active_certificate: self.certificate.clone(),
manager: self.manager,
};
let cert_info = CertificateData { let cert_info = CertificateData {
certificate: self.certificate, certificate: self.certificate,
@ -188,13 +185,10 @@ impl NotSignedInUser {
/// ///
/// Additional errors might occur if an error occurs during database lookup and /// Additional errors might occur if an error occurs during database lookup and
/// deserialization /// deserialization
pub fn attach<'de, UserData>( pub fn attach<UserData: Serialize + DeserializeOwned>(
username: impl AsRef<[u8]>, username: impl AsRef<[u8]>,
password: impl AsRef<[u8]>, password: impl AsRef<[u8]>,
) -> Result<SignedInUser<UserData>> ) -> Result<SignedInUser<UserData>> {
where
UserData: Serialize + Deserialize<'de>
{
todo!() todo!()
} }
} }
@ -203,16 +197,35 @@ impl NotSignedInUser {
/// Data about a logged in user /// Data about a logged in user
/// ///
/// For more information about the user lifecycle and sign-in stages, see [`User`] /// For more information about the user lifecycle and sign-in stages, see [`User`]
pub struct SignedInUser<UserData> { pub struct SignedInUser<UserData: Serialize + DeserializeOwned> {
pub (crate) username: String, pub (crate) username: String,
pub (crate) active_certificate: Certificate, pub (crate) active_certificate: Certificate,
pub (crate) manager: UserManager, pub (crate) manager: UserManager,
pub (crate) inner: PartialUser<UserData>, pub (crate) inner: PartialUser<UserData>,
/// Indicates that [`SignedInUser::as_mut()`] has been called, but [`SignedInUser::save()`] has not
has_changed: bool,
} }
impl<UserData> SignedInUser<UserData> { impl<UserData: Serialize + DeserializeOwned> SignedInUser<UserData> {
/// Create a new user from parts
pub (crate) fn new(
username: String,
active_certificate: Certificate,
manager: UserManager,
inner: PartialUser<UserData>
) -> Self {
Self {
username,
active_certificate,
manager,
inner,
has_changed: false,
}
}
/// Get the [`Certificate`] that the user is currently using to connect. /// 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 &self.active_certificate
} }
@ -232,7 +245,7 @@ impl<UserData> SignedInUser<UserData> {
/// Get the user's current username. /// Get the user's current username.
/// ///
/// NOTE: This is not guaranteed not to change. /// NOTE: This is not guaranteed not to change.
pub const fn username(&self) -> &String { pub fn username(&self) -> &String {
&self.username &self.username
} }
@ -256,24 +269,37 @@ impl<UserData> SignedInUser<UserData> {
/// ///
/// Updates caused by calling methods directly on the user do not need to be saved. /// Updates caused by calling methods directly on the user do not need to be saved.
/// This is only for changes made to the UserData. /// This is only for changes made to the UserData.
pub fn save(&self) -> Result<()> pub fn save(&mut self) -> Result<()>
where where
UserData: Serialize UserData: Serialize
{ {
self.inner.store(&self.manager.users, &self.username) self.inner.store(&self.manager.users, &self.username)?;
self.has_changed = false;
Ok(())
} }
} }
impl<UserData> AsRef<UserData> for SignedInUser<UserData> { impl<UserData: Serialize + DeserializeOwned> std::ops::Drop for SignedInUser<UserData> {
fn drop(&mut self) {
if self.has_changed {
if let Err(e) = self.save() {
error!("Failed to save user data to database: {:?}", e);
}
}
}
}
impl<UserData: Serialize + DeserializeOwned> AsRef<UserData> for SignedInUser<UserData> {
fn as_ref(&self) -> &UserData { fn as_ref(&self) -> &UserData {
&self.inner.data &self.inner.data
} }
} }
impl<UserData> AsMut<UserData> for SignedInUser<UserData> { impl<UserData: Serialize + DeserializeOwned> AsMut<UserData> for SignedInUser<UserData> {
/// NOTE: Changes made to the user data won't be persisted until SignedInUser::save /// NOTE: Changes made to the user data won't be persisted until SignedInUser::save
/// is called /// is called
fn as_mut(&mut self) -> &mut UserData { fn as_mut(&mut self) -> &mut UserData {
self.has_changed = true;
&mut self.inner.data &mut self.inner.data
} }
} }