121 lines
4.1 KiB
Rust
121 lines
4.1 KiB
Rust
use rustls::Certificate;
|
|
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
|
|
|
use std::path::Path;
|
|
|
|
use crate::user_management::{User, Result};
|
|
use crate::user_management::user::{RegisteredUser, NotSignedInUser, PartialUser};
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
/// Data stored in the certificate tree about a certain certificate
|
|
pub struct CertificateData {
|
|
#[serde(with = "CertificateDef")]
|
|
/// The certificate in question
|
|
pub certificate: Certificate,
|
|
|
|
/// The username of the user to which this certificate is registered
|
|
pub owner_username: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(remote = "Certificate")]
|
|
struct CertificateDef(Vec<u8>);
|
|
|
|
#[derive(Debug, Clone)]
|
|
/// A struct containing information for managing users.
|
|
///
|
|
/// Wraps a [`sled::Db`]
|
|
pub struct UserManager {
|
|
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(dir: impl AsRef<Path>) -> Result<Self> {
|
|
let db = sled::open(dir)?;
|
|
Ok(Self {
|
|
users: db.open_tree("users")?,
|
|
certificates: db.open_tree("certificates")?,
|
|
db,
|
|
})
|
|
}
|
|
|
|
/// Produce a u32 hash from a certificate, used for [`lookup_certificate()`](Self::lookup_certificate())
|
|
pub fn hash_certificate(cert: &Certificate) -> u32 {
|
|
let mut hasher = crc32fast::Hasher::new();
|
|
hasher.update(cert.0.as_ref());
|
|
hasher.finalize()
|
|
}
|
|
|
|
/// Lookup information about a certificate based on it's u32 hash
|
|
///
|
|
/// # 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: u32) -> Result<Option<CertificateData>> {
|
|
if let Some(bytes) = self.certificates.get(cert.to_le_bytes())? {
|
|
Ok(Some(bincode::deserialize(&bytes)?))
|
|
} 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)
|
|
}
|
|
}
|
|
|
|
/// 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<&Certificate>
|
|
) -> Result<User<UserData>>
|
|
where
|
|
UserData: Serialize + DeserializeOwned
|
|
{
|
|
if let Some(certificate) = cert {
|
|
let cert_hash = Self::hash_certificate(certificate);
|
|
if let Some(certificate_data) = self.lookup_certificate(cert_hash)? {
|
|
let user_inner = self.lookup_user(&certificate_data.owner_username)?
|
|
.expect("Database corruption: Certificate data refers to non-existant user");
|
|
Ok(User::SignedIn(user_inner.with_cert(certificate_data.certificate)))
|
|
} else {
|
|
Ok(User::NotSignedIn(NotSignedInUser {
|
|
certificate: certificate.clone(),
|
|
manager: self.clone(),
|
|
}))
|
|
}
|
|
} else {
|
|
Ok(User::Unauthenticated)
|
|
}
|
|
}
|
|
}
|