2020-12-01 19:43:15 +00:00
|
|
|
use serde::{Serialize, de::DeserializeOwned};
|
2020-11-16 06:13:16 +00:00
|
|
|
|
|
|
|
use crate::user_management::{User, Result};
|
2020-11-19 22:00:32 +00:00
|
|
|
use crate::user_management::user::{RegisteredUser, NotSignedInUser, PartialUser};
|
2020-11-16 06:13:16 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
/// A struct containing information for managing users.
|
|
|
|
///
|
|
|
|
/// Wraps a [`sled::Db`]
|
|
|
|
pub struct UserManager {
|
2020-12-08 16:01:07 +00:00
|
|
|
/// Allows access to the [`sled::Db`] used by the UserManager to store user data.
|
|
|
|
///
|
|
|
|
/// Do not try to use this database to access user information, and instead prefer
|
|
|
|
/// methods such as [`lookup_user()`] and [`lookup_certificate()`].
|
|
|
|
///
|
|
|
|
/// However, you're welcome to use the database to store you own data without needing
|
|
|
|
/// to run parallel sled databases. It's recommended that any trees you open be
|
|
|
|
/// namespaced like `tld.yourdomain.projectname.treename` in order to prevent
|
|
|
|
/// conflict.
|
|
|
|
///
|
|
|
|
/// [`lookup_user()`]: Self::lookup_user
|
|
|
|
/// [`lookup_certificate()`]: Self::lookup_certificate
|
2020-11-27 02:04:02 +00:00
|
|
|
pub db: sled::Db,
|
2020-11-16 06:13:16 +00:00
|
|
|
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.
|
2020-11-27 22:24:16 +00:00
|
|
|
pub fn new(db: sled::Db) -> Result<Self> {
|
2020-11-16 06:13:16 +00:00
|
|
|
Ok(Self {
|
2020-11-27 02:04:02 +00:00
|
|
|
users: db.open_tree("gay.emii.kochab.users")?,
|
|
|
|
certificates: db.open_tree("gay.emii.kochab.certificates")?,
|
2020-11-16 06:13:16 +00:00
|
|
|
db,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-01 19:43:15 +00:00
|
|
|
/// Lookup the owner of a certificate based on it's fingerprint
|
2020-11-16 06:13:16 +00:00
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
/// An error is thrown if there is an error reading from the database or if data
|
|
|
|
/// recieved from the database is corrupt
|
2020-12-01 19:43:15 +00:00
|
|
|
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()))
|
2020-11-16 06:13:16 +00:00
|
|
|
} 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
|
2020-11-19 19:27:36 +00:00
|
|
|
pub fn lookup_user<UserData>(
|
2020-11-16 06:13:16 +00:00
|
|
|
&self,
|
2020-11-19 21:54:29 +00:00
|
|
|
username: impl AsRef<str>
|
2020-11-19 22:00:32 +00:00
|
|
|
) -> Result<Option<RegisteredUser<UserData>>>
|
2020-11-16 06:13:16 +00:00
|
|
|
where
|
|
|
|
UserData: Serialize + DeserializeOwned
|
|
|
|
{
|
2020-11-19 21:54:29 +00:00
|
|
|
if let Some(bytes) = self.users.get(username.as_ref())? {
|
|
|
|
let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.as_ref())?;
|
2020-11-19 22:00:32 +00:00
|
|
|
Ok(Some(RegisteredUser::new(username.as_ref().to_owned(), None, self.clone(), inner)))
|
2020-11-16 06:13:16 +00:00
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-27 23:01:49 +00:00
|
|
|
/// 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()
|
|
|
|
}
|
|
|
|
|
2020-11-16 06:13:16 +00:00
|
|
|
/// 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
|
2020-11-19 19:27:36 +00:00
|
|
|
pub fn get_user<UserData>(
|
2020-11-16 06:13:16 +00:00
|
|
|
&self,
|
2020-12-01 19:43:15 +00:00
|
|
|
cert: Option<&[u8; 32]>
|
2020-11-16 06:13:16 +00:00
|
|
|
) -> Result<User<UserData>>
|
|
|
|
where
|
|
|
|
UserData: Serialize + DeserializeOwned
|
|
|
|
{
|
|
|
|
if let Some(certificate) = cert {
|
2020-12-01 19:43:15 +00:00
|
|
|
if let Some(username) = self.lookup_certificate(*certificate)? {
|
|
|
|
let user_inner = self.lookup_user(&username)?
|
2020-11-16 06:13:16 +00:00
|
|
|
.expect("Database corruption: Certificate data refers to non-existant user");
|
2020-12-01 19:43:15 +00:00
|
|
|
Ok(User::SignedIn(user_inner.with_cert(*certificate)))
|
2020-11-16 06:13:16 +00:00
|
|
|
} else {
|
|
|
|
Ok(User::NotSignedIn(NotSignedInUser {
|
2020-12-01 19:43:15 +00:00
|
|
|
certificate: *certificate,
|
2020-11-16 06:13:16 +00:00
|
|
|
manager: self.clone(),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Ok(User::Unauthenticated)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|