kochab/src/user_management/manager.rs

117 lines
4.0 KiB
Rust

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)
}
}
}