|
|
|
@ -48,6 +48,7 @@ lazy_static::lazy_static! {
|
|
|
|
|
pub (crate) struct PartialUser<UserData> {
|
|
|
|
|
pub data: UserData,
|
|
|
|
|
pub certificates: Vec<[u8; 32]>,
|
|
|
|
|
pub username: String,
|
|
|
|
|
#[cfg(feature = "user_management_advanced")]
|
|
|
|
|
pub pass_hash: Option<(Vec<u8>, [u8; 32])>,
|
|
|
|
|
}
|
|
|
|
@ -58,12 +59,12 @@ impl<UserData> PartialUser<UserData> {
|
|
|
|
|
///
|
|
|
|
|
/// This MUST be called if the user data is modified using the AsMut trait, or else
|
|
|
|
|
/// changes will not be written to the database
|
|
|
|
|
fn store(&self, tree: &sled::Tree, username: impl AsRef<[u8]>) -> Result<()>
|
|
|
|
|
fn store(&self, tree: &sled::Tree, uid: u64) -> Result<()>
|
|
|
|
|
where
|
|
|
|
|
UserData: Serialize
|
|
|
|
|
{
|
|
|
|
|
tree.insert(
|
|
|
|
|
&username,
|
|
|
|
|
uid.to_le_bytes(),
|
|
|
|
|
bincode::serialize(&self)?,
|
|
|
|
|
)?;
|
|
|
|
|
Ok(())
|
|
|
|
@ -117,26 +118,48 @@ impl NotSignedInUser {
|
|
|
|
|
if self.manager.users.contains_key(username.as_str())? {
|
|
|
|
|
Err(super::UserManagerError::UsernameNotUnique)
|
|
|
|
|
} else {
|
|
|
|
|
info!("User {} registered!", username);
|
|
|
|
|
|
|
|
|
|
let mut newser = RegisteredUser::new(
|
|
|
|
|
// Create the partial user that will go into the database. We can't create
|
|
|
|
|
// the full user yet, since the ID won't be generated until we perform the
|
|
|
|
|
// insert.
|
|
|
|
|
let partial = PartialUser {
|
|
|
|
|
username,
|
|
|
|
|
data: UserData::default(),
|
|
|
|
|
certificates: vec![self.certificate],
|
|
|
|
|
#[cfg(feature = "user_management_advanced")]
|
|
|
|
|
pass_hash: None,
|
|
|
|
|
};
|
|
|
|
|
let serialized = bincode::serialize(&partial)?;
|
|
|
|
|
|
|
|
|
|
// Insert the user into the three relevant tables, thus finalizing their
|
|
|
|
|
// creation. This also produces the user id.
|
|
|
|
|
let id = (&self.manager.users, &self.manager.certificates, &self.manager.usernames)
|
|
|
|
|
.transaction(|(tx_usr, tx_crt, tx_nam)| {
|
|
|
|
|
let id = tx_usr.generate_id()?;
|
|
|
|
|
let id_bytes = id.to_le_bytes();
|
|
|
|
|
|
|
|
|
|
tx_usr.insert(
|
|
|
|
|
&id_bytes,
|
|
|
|
|
serialized.as_slice(),
|
|
|
|
|
)?;
|
|
|
|
|
tx_crt.insert(
|
|
|
|
|
&partial.certificates[0],
|
|
|
|
|
&id_bytes,
|
|
|
|
|
)?;
|
|
|
|
|
tx_nam.insert(
|
|
|
|
|
partial.username.as_bytes(),
|
|
|
|
|
&id_bytes,
|
|
|
|
|
)?;
|
|
|
|
|
Ok(id)
|
|
|
|
|
})?;
|
|
|
|
|
info!("User {}#{:08x} registered!", partial.username, id);
|
|
|
|
|
|
|
|
|
|
Ok(RegisteredUser::new(
|
|
|
|
|
id,
|
|
|
|
|
Some(self.certificate),
|
|
|
|
|
self.manager,
|
|
|
|
|
PartialUser {
|
|
|
|
|
data: UserData::default(),
|
|
|
|
|
certificates: Vec::with_capacity(1),
|
|
|
|
|
#[cfg(feature = "user_management_advanced")]
|
|
|
|
|
pass_hash: None,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// As a nice bonus, calling add_certificate with a user not yet in the
|
|
|
|
|
// database creates the user and adds the certificate in a single transaction.
|
|
|
|
|
// Because of this, we can delegate here ^^
|
|
|
|
|
newser.add_certificate(self.certificate)?;
|
|
|
|
|
|
|
|
|
|
Ok(newser)
|
|
|
|
|
partial,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -172,7 +195,7 @@ impl NotSignedInUser {
|
|
|
|
|
username: &str,
|
|
|
|
|
password: Option<&[u8]>,
|
|
|
|
|
) -> Result<Option<RegisteredUser<UserData>>> {
|
|
|
|
|
if let Some(mut user) = self.manager.lookup_user(username)? {
|
|
|
|
|
if let Some(mut user) = self.manager.lookup_username(username)? {
|
|
|
|
|
// Perform password check, if caller wants
|
|
|
|
|
if let Some(password) = password {
|
|
|
|
|
if !user.check_password(password)? {
|
|
|
|
@ -195,7 +218,7 @@ impl NotSignedInUser {
|
|
|
|
|
///
|
|
|
|
|
/// For more information about the user lifecycle and sign-in stages, see [`User`]
|
|
|
|
|
pub struct RegisteredUser<UserData: Serialize + DeserializeOwned> {
|
|
|
|
|
username: String,
|
|
|
|
|
uid: u64,
|
|
|
|
|
active_certificate: Option<[u8; 32]>,
|
|
|
|
|
manager: UserManager,
|
|
|
|
|
inner: PartialUser<UserData>,
|
|
|
|
@ -207,13 +230,13 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
|
|
|
|
|
|
|
|
|
/// Create a new user from parts
|
|
|
|
|
pub (crate) fn new(
|
|
|
|
|
username: String,
|
|
|
|
|
uid: u64,
|
|
|
|
|
active_certificate: Option<[u8; 32]>,
|
|
|
|
|
manager: UserManager,
|
|
|
|
|
inner: PartialUser<UserData>
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
username,
|
|
|
|
|
uid,
|
|
|
|
|
active_certificate,
|
|
|
|
|
manager,
|
|
|
|
|
inner,
|
|
|
|
@ -246,9 +269,19 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
|
|
|
|
|
|
|
|
|
/// Get the user's current username.
|
|
|
|
|
///
|
|
|
|
|
/// NOTE: This is not guaranteed not to change.
|
|
|
|
|
/// NOTE: This is not guaranteed not to change. If you need an immutable reference to
|
|
|
|
|
/// this user, prefer their [UID], which is guaranteed static.
|
|
|
|
|
///
|
|
|
|
|
/// [UID]: Self::uid()
|
|
|
|
|
pub fn username(&self) -> &String {
|
|
|
|
|
&self.username
|
|
|
|
|
&self.inner.username
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the user's id.
|
|
|
|
|
///
|
|
|
|
|
/// This is not guaranteed not to change.
|
|
|
|
|
pub fn uid(&self) -> u64 {
|
|
|
|
|
self.uid
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "user_management_advanced")]
|
|
|
|
@ -328,7 +361,7 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
|
|
|
|
where
|
|
|
|
|
UserData: Serialize
|
|
|
|
|
{
|
|
|
|
|
self.inner.store(&self.manager.users, &self.username)?;
|
|
|
|
|
self.inner.store(&self.manager.users, self.uid)?;
|
|
|
|
|
self.has_changed = false;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
@ -346,16 +379,17 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
|
|
|
|
self.inner.certificates.push(certificate);
|
|
|
|
|
|
|
|
|
|
let inner_serialized = bincode::serialize(&self.inner)?;
|
|
|
|
|
let uid_bytes = self.uid.to_le_bytes();
|
|
|
|
|
|
|
|
|
|
(&self.manager.users, &self.manager.certificates)
|
|
|
|
|
.transaction(|(tx_usr, tx_crt)| {
|
|
|
|
|
tx_usr.insert(
|
|
|
|
|
self.username.as_str(),
|
|
|
|
|
&uid_bytes,
|
|
|
|
|
inner_serialized.clone(),
|
|
|
|
|
)?;
|
|
|
|
|
tx_crt.insert(
|
|
|
|
|
&certificate,
|
|
|
|
|
self.username.as_bytes(),
|
|
|
|
|
&uid_bytes,
|
|
|
|
|
)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
})?;
|
|
|
|
@ -384,9 +418,12 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
|
|
|
|
pub fn delete(self) -> Result<()> {
|
|
|
|
|
let certificates = self.all_certificates();
|
|
|
|
|
|
|
|
|
|
(&self.manager.users, &self.manager.certificates).transaction(|(tx_usr, tx_crt)| {
|
|
|
|
|
(&self.manager.users, &self.manager.certificates, &self.manager.usernames).transaction(|(tx_usr, tx_crt, tx_nam)| {
|
|
|
|
|
tx_nam.remove(
|
|
|
|
|
self.inner.username.as_str(),
|
|
|
|
|
)?;
|
|
|
|
|
tx_usr.remove(
|
|
|
|
|
self.username.as_str(),
|
|
|
|
|
&self.uid.to_le_bytes(),
|
|
|
|
|
)?;
|
|
|
|
|
for cert in certificates {
|
|
|
|
|
tx_crt.remove(
|
|
|
|
|