Added the add_certificate and attach methods

This commit is contained in:
Emi Tatsuo 2020-11-19 17:41:32 -05:00
parent cdc3921a83
commit 766c472cbf
Signed by: Emi
GPG key ID: 68FAB2E2E6DFC98B

View file

@ -113,39 +113,21 @@ impl NotSignedInUser {
if self.manager.users.contains_key(username.as_str())? { if self.manager.users.contains_key(username.as_str())? {
Err(super::UserManagerError::UsernameNotUnique) Err(super::UserManagerError::UsernameNotUnique)
} else { } else {
let cert_hash = UserManager::hash_certificate(&self.certificate); let mut newser = RegisteredUser::new(
username,
let newser = RegisteredUser::new(
username.clone(),
Some(self.certificate.clone()), Some(self.certificate.clone()),
self.manager, self.manager,
PartialUser { PartialUser {
data: UserData::default(), data: UserData::default(),
certificates: vec![cert_hash], certificates: Vec::with_capacity(1),
pass_hash: None, pass_hash: None,
}, },
); );
let cert_info = CertificateData { // As a nice bonus, calling add_certificate with a user not yet in the
certificate: self.certificate, // database creates the user and adds the certificate in a single transaction.
owner_username: username, // Because of this, we can delegate here ^^
}; newser.add_certificate(self.certificate)?;
let newser_serialized = bincode::serialize(&newser.inner)?;
let cert_info_serialized = bincode::serialize(&cert_info)?;
(&newser.manager.users, &newser.manager.certificates)
.transaction(|(tx_usr, tx_crt)| {
tx_usr.insert(
newser.username.as_str(),
newser_serialized.clone(),
)?;
tx_crt.insert(
cert_hash.to_le_bytes().as_ref(),
cert_info_serialized.clone(),
)?;
Ok(())
})?; //TODO
Ok(newser) Ok(newser)
} }
@ -158,8 +140,19 @@ impl NotSignedInUser {
/// log in with either this certificate or any of the certificates they already had /// log in with either this certificate or any of the certificates they already had
/// registered. /// registered.
/// ///
/// This method can check the user's password to ensure that they match before
/// registering. If you want to skip this verification, perhaps because you've
/// already verified that this user owns this account, then you can pass [`None`] as
/// the password to skip the password check.
///
/// This method returns the new RegisteredUser instance representing the now-attached /// This method returns the new RegisteredUser instance representing the now-attached
/// user. /// user, or [`None`] if the username and password didn't match.
///
/// Because this method both performs a bcrypt verification and a database access, it
/// should be considered expensive.
///
/// If you already have a [`RegisteredUser`] that you would like to attach a
/// certificate to, consider using [`RegisteredUser::add_certificate()`]
/// ///
/// # Errors /// # Errors
/// This will error if the username and password are incorrect, or if the user has yet /// This will error if the username and password are incorrect, or if the user has yet
@ -168,10 +161,23 @@ impl NotSignedInUser {
/// Additional errors might occur if an error occurs during database lookup and /// Additional errors might occur if an error occurs during database lookup and
/// deserialization /// deserialization
pub fn attach<UserData: Serialize + DeserializeOwned>( pub fn attach<UserData: Serialize + DeserializeOwned>(
username: impl AsRef<[u8]>, self,
password: impl AsRef<[u8]>, username: impl AsRef<str>,
) -> Result<RegisteredUser<UserData>> { password: Option<impl AsRef<[u8]>>,
todo!() ) -> Result<Option<RegisteredUser<UserData>>> {
if let Some(mut user) = self.manager.lookup_user(username)? {
// Perform password check, if caller wants
if let Some(password) = password {
if !user.check_password(password)? {
return Ok(None);
}
}
user.add_certificate(self.certificate)?;
Ok(Some(user))
} else {
Ok(None)
}
} }
} }
@ -207,6 +213,10 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
} }
/// Update the active certificate /// Update the active certificate
///
/// This is not to be confused with [`RegisteredUser::add_certificate`], which
/// performs the database operations needed to register a new certificate to a user.
/// This literally just marks the active certificate.
pub (crate) fn with_cert(mut self, cert: Certificate) -> Self { pub (crate) fn with_cert(mut self, cert: Certificate) -> Self {
self.active_certificate = Some(cert); self.active_certificate = Some(cert);
self self
@ -303,6 +313,44 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
self.has_changed = false; self.has_changed = false;
Ok(()) Ok(())
} }
/// Register a new certificate to this user
///
/// This adds a new certificate to this user for use in logins. This requires a
/// couple database accesses, one in order to link the user to the certificate, and
/// one in order to link the certificate to the user.
///
/// If you have a [`NotSignedInUser`] and are looking for a way to link them to an
/// existing user, consider [`NotSignedInUser::attach()`], which contains facilities for
/// password checking and automatically performs the user lookup.
pub fn add_certificate(&mut self, certificate: Certificate) -> Result<()> {
let cert_hash = UserManager::hash_certificate(&certificate);
self.inner.certificates.push(cert_hash);
let cert_info = CertificateData {
certificate,
owner_username: self.username.clone(),
};
let inner_serialized = bincode::serialize(&self.inner)?;
let cert_info_serialized = bincode::serialize(&cert_info)?;
(&self.manager.users, &self.manager.certificates)
.transaction(|(tx_usr, tx_crt)| {
tx_usr.insert(
self.username.as_str(),
inner_serialized.clone(),
)?;
tx_crt.insert(
cert_hash.to_le_bytes().as_ref(),
cert_info_serialized.clone(),
)?;
Ok(())
})?;
Ok(())
}
} }
impl<UserData: Serialize + DeserializeOwned> std::ops::Drop for RegisteredUser<UserData> { impl<UserData: Serialize + DeserializeOwned> std::ops::Drop for RegisteredUser<UserData> {