Return a full SignedInUser from lookup_user(), restrict access to PartialUser

Okay this is a much nicer API tbh.  Why didn't I do this from the start?  'cause I'm bad
at planning that's why.  We got there eventually though!
This commit is contained in:
Emi Tatsuo 2020-11-19 16:54:29 -05:00
parent 1908d0a0d7
commit 2f1196228f
Signed by: Emi
GPG Key ID: 68FAB2E2E6DFC98B
2 changed files with 23 additions and 47 deletions

View File

@ -73,13 +73,14 @@ impl UserManager {
/// recieved from the database is corrupt /// recieved from the database is corrupt
pub fn lookup_user<UserData>( pub fn lookup_user<UserData>(
&self, &self,
username: impl AsRef<[u8]> username: impl AsRef<str>
) -> Result<Option<PartialUser<UserData>>> ) -> Result<Option<SignedInUser<UserData>>>
where where
UserData: Serialize + DeserializeOwned UserData: Serialize + DeserializeOwned
{ {
if let Some(bytes) = self.users.get(username)? { if let Some(bytes) = self.users.get(username.as_ref())? {
Ok(Some(bincode::deserialize_from(bytes.as_ref())?)) let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.as_ref())?;
Ok(Some(SignedInUser::new(username.as_ref().to_owned(), None, self.clone(), inner)))
} else { } else {
Ok(None) Ok(None)
} }
@ -105,12 +106,7 @@ 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::new( Ok(User::SignedIn(user_inner.with_cert(certificate_data.certificate)))
certificate_data.owner_username,
certificate_data.certificate,
self.clone(),
user_inner,
)))
} else { } else {
Ok(User::NotSignedIn(NotSignedInUser { Ok(User::NotSignedIn(NotSignedInUser {
certificate: certificate.clone(), certificate: certificate.clone(),

View File

@ -10,14 +10,9 @@
//! information, like the user's username and active certificate. //! information, like the user's username and active certificate.
//! //!
//! [`SignedInUser`] is particularly signifigant in that this is the struct used to modify //! [`SignedInUser`] is particularly signifigant in that this is the struct used to modify
//! the data stored for the current user. This is accomplished through the //! the data stored for almost all users. This is accomplished through the
//! [`as_mut()`](SignedInUser::as_mut) method. Changes made this way must be persisted //! [`as_mut()`](SignedInUser::as_mut) method. Changes made this way must be persisted
//! using [`save()`](SignedInUser::save()) or by dropping the user. //! using [`save()`](SignedInUser::save()) or by dropping the user.
//!
//! [`PartialUser`] is the main way of modifying data stored for users who aren't
//! currently connecting. These are mainly obtained through the
//! [`UserManager::lookup_user()`] method. Unlinke with [`SignedInUser`], these are not
//! committed on drop, and [`PartialUser::store()`] must be manually called
use rustls::Certificate; use rustls::Certificate;
use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde::{Deserialize, Serialize, de::DeserializeOwned};
use sled::Transactional; use sled::Transactional;
@ -47,7 +42,7 @@ lazy_static::lazy_static! {
/// In order to generate a full user obj, you need to perform a lookup with a specific /// In order to generate a full user obj, you need to perform a lookup with a specific
/// certificate. /// certificate.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PartialUser<UserData> { pub (crate) struct PartialUser<UserData> {
pub data: UserData, pub data: UserData,
pub certificates: Vec<u32>, pub certificates: Vec<u32>,
pub pass_hash: Option<(Vec<u8>, [u8; 32])>, pub pass_hash: Option<(Vec<u8>, [u8; 32])>,
@ -55,18 +50,11 @@ pub struct PartialUser<UserData> {
impl<UserData> PartialUser<UserData> { impl<UserData> PartialUser<UserData> {
/// A list of certificate hashes registered to this user
///
/// Can be looked up using [`UserManager::lookup_certificate()`] to get full information
pub const fn certificates(&self) -> &Vec<u32> {
&self.certificates
}
/// Write user data to the database /// Write user data to the database
/// ///
/// This MUST be called if the user data is modified using the AsMut trait, or else /// This MUST be called if the user data is modified using the AsMut trait, or else
/// changes will not be written to the database /// changes will not be written to the database
pub fn store(&self, tree: &sled::Tree, username: impl AsRef<[u8]>) -> Result<()> fn store(&self, tree: &sled::Tree, username: impl AsRef<[u8]>) -> Result<()>
where where
UserData: Serialize UserData: Serialize
{ {
@ -78,23 +66,6 @@ impl<UserData> PartialUser<UserData> {
} }
} }
impl<UserData> AsRef<UserData> for PartialUser<UserData> {
/// Access any data the application has stored for the user.
fn as_ref(&self) -> &UserData {
&self.data
}
}
impl<UserData> AsMut<UserData> for PartialUser<UserData> {
/// Modify the data stored for a user
///
/// IMPORTANT: Changes will not be written to the database until
/// [`PartialUser::store()`] is called
fn as_mut(&mut self) -> &mut UserData {
&mut self.data
}
}
/// Any information about the connecting user /// Any information about the connecting user
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum User<UserData: Serialize + DeserializeOwned> { pub enum User<UserData: Serialize + DeserializeOwned> {
@ -146,7 +117,7 @@ impl NotSignedInUser {
let newser = SignedInUser::new( let newser = SignedInUser::new(
username.clone(), username.clone(),
self.certificate.clone(), Some(self.certificate.clone()),
self.manager, self.manager,
PartialUser { PartialUser {
data: UserData::default(), data: UserData::default(),
@ -210,7 +181,7 @@ impl NotSignedInUser {
/// 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: Serialize + DeserializeOwned> { pub struct SignedInUser<UserData: Serialize + DeserializeOwned> {
username: String, username: String,
active_certificate: Certificate, active_certificate: Option<Certificate>,
manager: UserManager, manager: UserManager,
inner: PartialUser<UserData>, inner: PartialUser<UserData>,
/// Indicates that [`SignedInUser::as_mut()`] has been called, but [`SignedInUser::save()`] has not /// Indicates that [`SignedInUser::as_mut()`] has been called, but [`SignedInUser::save()`] has not
@ -222,7 +193,7 @@ impl<UserData: Serialize + DeserializeOwned> SignedInUser<UserData> {
/// Create a new user from parts /// Create a new user from parts
pub (crate) fn new( pub (crate) fn new(
username: String, username: String,
active_certificate: Certificate, active_certificate: Option<Certificate>,
manager: UserManager, manager: UserManager,
inner: PartialUser<UserData> inner: PartialUser<UserData>
) -> Self { ) -> Self {
@ -235,9 +206,18 @@ impl<UserData: Serialize + DeserializeOwned> SignedInUser<UserData> {
} }
} }
/// Update the active certificate
pub (crate) fn with_cert(mut self, cert: Certificate) -> Self {
self.active_certificate = Some(cert);
self
}
/// Get the [`Certificate`] that the user is currently using to connect. /// Get the [`Certificate`] that the user is currently using to connect.
pub fn active_certificate(&self) -> &Certificate { ///
&self.active_certificate /// If this user was retrieved by a [`UserManager::lookup_user()`], this will be
/// [`None`]. In all other cases, this will be [`Some`].
pub fn active_certificate(&self) -> Option<&Certificate> {
self.active_certificate.as_ref()
} }
/// Produce a list of all [`Certificate`]s registered to this account /// Produce a list of all [`Certificate`]s registered to this account