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
1 changed files with 78 additions and 30 deletions

View File

@ -113,39 +113,21 @@ impl NotSignedInUser {
if self.manager.users.contains_key(username.as_str())? {
Err(super::UserManagerError::UsernameNotUnique)
} else {
let cert_hash = UserManager::hash_certificate(&self.certificate);
let newser = RegisteredUser::new(
username.clone(),
let mut newser = RegisteredUser::new(
username,
Some(self.certificate.clone()),
self.manager,
PartialUser {
data: UserData::default(),
certificates: vec![cert_hash],
certificates: Vec::with_capacity(1),
pass_hash: None,
},
);
let cert_info = CertificateData {
certificate: self.certificate,
owner_username: username,
};
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
// 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)
}
@ -158,8 +140,19 @@ impl NotSignedInUser {
/// log in with either this certificate or any of the certificates they already had
/// 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
/// 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
/// 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
/// deserialization
pub fn attach<UserData: Serialize + DeserializeOwned>(
username: impl AsRef<[u8]>,
password: impl AsRef<[u8]>,
) -> Result<RegisteredUser<UserData>> {
todo!()
self,
username: impl AsRef<str>,
password: Option<impl AsRef<[u8]>>,
) -> 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
///
/// 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 {
self.active_certificate = Some(cert);
self
@ -303,6 +313,44 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
self.has_changed = false;
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> {