💾 Archived View for alchemi.dev › en › projects › kochab › files › src › user_management › manager.r… captured on 2023-09-28 at 16:00:55.

View Raw

More Information

⬅️ Previous capture (2022-07-16)

-=-=-=-=-=-=-

use serde::{Serialize, de::DeserializeOwned};

use crate::user_management::{User, Result};
use crate::user_management::user::{RegisteredUser, NotSignedInUser, PartialUser};

#[derive(Debug, Clone)]
/// A struct containing information for managing users.
///
/// Wraps a [`sled::Db`]
pub struct UserManager {
    pub 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(db: sled::Db) -> Result<Self> {
       Ok(Self {
           users: db.open_tree("gay.emii.kochab.users")?,
           certificates: db.open_tree("gay.emii.kochab.certificates")?,
           db,
       })
    }

    /// Lookup the owner of a certificate based on it's fingerprint
    ///
    /// # 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: [u8; 32]) -> Result<Option<String>> {
        if let Some(bytes) = self.certificates.get(cert)? {
            Ok(Some(std::str::from_utf8(bytes.as_ref())?.to_string()))
        } 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<UserData>(
        &self,
        username: impl AsRef<str>
    ) -> Result<Option<RegisteredUser<UserData>>>
    where
        UserData: Serialize + DeserializeOwned
    {
        if let Some(bytes) = self.users.get(username.as_ref())? {
            let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.as_ref())?;
            Ok(Some(RegisteredUser::new(username.as_ref().to_owned(), None, self.clone(), inner)))
        } else {
            Ok(None)
        }
    }

    /// Produce a list of all users in the database
    ///
    /// # Panics
    /// An panics if there is an error reading from the database or if data recieved from
    /// the database is corrupt
    pub fn all_users<UserData>(
        &self,
    ) -> Vec<RegisteredUser<UserData>>
    where
        UserData: Serialize + DeserializeOwned
    {
        self.users.iter()
            .map(|result| {
                let (username, bytes) = result.expect("Failed to connect to database");
                let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.as_ref())
                    .expect("Received malformed data from database");
                let username = String::from_utf8(username.to_vec())
                    .expect("Malformed username in database");
                RegisteredUser::new(username, None, self.clone(), inner)
            })
            .collect()
    }

    /// 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<UserData>(
        &self,
        cert: Option<&[u8; 32]>
    ) -> Result<User<UserData>>
    where
        UserData: Serialize + DeserializeOwned
    {
        if let Some(certificate) = cert {
            if let Some(username) = self.lookup_certificate(*certificate)? {
                let user_inner = self.lookup_user(&username)?
                    .expect("Database corruption: Certificate data refers to non-existant user");
                Ok(User::SignedIn(user_inner.with_cert(*certificate)))
            } else {
                Ok(User::NotSignedIn(NotSignedInUser {
                    certificate: *certificate,
                    manager: self.clone(),
                }))
            }
        } else {
            Ok(User::Unauthenticated)
        }
    }
}