Switch to using UIDs
This commit is contained in:
parent
12a7a24694
commit
00584c2f27
|
@ -296,7 +296,7 @@ impl Request {
|
||||||
where
|
where
|
||||||
UserData: Serialize + DeserializeOwned
|
UserData: Serialize + DeserializeOwned
|
||||||
{
|
{
|
||||||
Ok(self.manager.get_user(self.certificate())?)
|
Ok(self.manager.get_user_by_cert(self.certificate())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature="user_management")]
|
#[cfg(feature="user_management")]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use crate::user_management::{User, Result};
|
use crate::user_management::{User, Result};
|
||||||
use crate::user_management::user::{RegisteredUser, NotSignedInUser, PartialUser};
|
use crate::user_management::user::{RegisteredUser, NotSignedInUser, PartialUser};
|
||||||
|
|
||||||
|
@ -21,8 +23,9 @@ pub struct UserManager {
|
||||||
/// [`lookup_user()`]: Self::lookup_user
|
/// [`lookup_user()`]: Self::lookup_user
|
||||||
/// [`lookup_certificate()`]: Self::lookup_certificate
|
/// [`lookup_certificate()`]: Self::lookup_certificate
|
||||||
pub db: sled::Db,
|
pub db: sled::Db,
|
||||||
pub (crate) users: sled::Tree, // user_id:String maps to data:UserData
|
pub (crate) users: sled::Tree, // user_id:u64 maps to data:PartialUser
|
||||||
pub (crate) certificates: sled::Tree, // certificate:u64 maps to data:CertificateData
|
pub (crate) certificates: sled::Tree, // fingerprint:[u8; 32] maps to uid:u64
|
||||||
|
pub (crate) usernames: sled::Tree, // username:String maps to uid:u64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserManager {
|
impl UserManager {
|
||||||
|
@ -35,6 +38,7 @@ impl UserManager {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
users: db.open_tree("gay.emii.kochab.users")?,
|
users: db.open_tree("gay.emii.kochab.users")?,
|
||||||
certificates: db.open_tree("gay.emii.kochab.certificates")?,
|
certificates: db.open_tree("gay.emii.kochab.certificates")?,
|
||||||
|
usernames: db.open_tree("gay.emii.kochab.usernames")?,
|
||||||
db,
|
db,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -44,29 +48,60 @@ impl UserManager {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// An error is thrown if there is an error reading from the database or if data
|
/// An error is thrown if there is an error reading from the database or if data
|
||||||
/// recieved from the database is corrupt
|
/// recieved from the database is corrupt
|
||||||
pub fn lookup_certificate(&self, cert: [u8; 32]) -> Result<Option<String>> {
|
///
|
||||||
|
/// [`None`] can be returned if their is no user with this certificate.
|
||||||
|
pub fn lookup_certificate<UserData: Serialize + DeserializeOwned>(
|
||||||
|
&self,
|
||||||
|
cert: [u8; 32]
|
||||||
|
) -> Result<Option<RegisteredUser<UserData>>> {
|
||||||
if let Some(bytes) = self.certificates.get(cert)? {
|
if let Some(bytes) = self.certificates.get(cert)? {
|
||||||
Ok(Some(std::str::from_utf8(bytes.as_ref())?.to_string()))
|
let id = u64::from_le_bytes(bytes.as_ref().try_into()?);
|
||||||
|
Ok(Some(
|
||||||
|
self.lookup_user(id)?
|
||||||
|
.ok_or(super::DeserializeError::InvalidReference(id))?
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lookup information about a user by username
|
/// Get the user with the specified username
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// An error is thrown if there is an error reading from the database or if data
|
||||||
|
/// recieved from the database is corrupt
|
||||||
|
///
|
||||||
|
/// [`None`] can be returned if their is no user with this username.
|
||||||
|
pub fn lookup_username<UserData: Serialize + DeserializeOwned>(
|
||||||
|
&self,
|
||||||
|
username: impl AsRef<str>
|
||||||
|
) -> Result<Option<RegisteredUser<UserData>>> {
|
||||||
|
if let Some(bytes) = self.usernames.get(username.as_ref())? {
|
||||||
|
let id = u64::from_le_bytes(bytes.as_ref().try_into()?);
|
||||||
|
Ok(Some(
|
||||||
|
self.lookup_user(id)?
|
||||||
|
.ok_or(super::DeserializeError::InvalidReference(id))?
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup a user by uid
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// An error is thrown if there is an error reading from the database or if data
|
/// An error is thrown if there is an error reading from the database or if data
|
||||||
/// recieved from the database is corrupt
|
/// recieved from the database is corrupt
|
||||||
pub fn lookup_user<UserData>(
|
pub fn lookup_user<UserData>(
|
||||||
&self,
|
&self,
|
||||||
username: impl AsRef<str>
|
uid: u64,
|
||||||
) -> Result<Option<RegisteredUser<UserData>>>
|
) -> Result<Option<RegisteredUser<UserData>>>
|
||||||
where
|
where
|
||||||
UserData: Serialize + DeserializeOwned
|
UserData: Serialize + DeserializeOwned
|
||||||
{
|
{
|
||||||
if let Some(bytes) = self.users.get(username.as_ref())? {
|
if let Some(bytes) = self.users.get(uid.to_le_bytes())? {
|
||||||
let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.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)))
|
Ok(Some(RegisteredUser::new(uid, None, self.clone(), inner)))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -79,20 +114,18 @@ impl UserManager {
|
||||||
/// the database is corrupt
|
/// the database is corrupt
|
||||||
pub fn all_users<UserData>(
|
pub fn all_users<UserData>(
|
||||||
&self,
|
&self,
|
||||||
) -> Vec<RegisteredUser<UserData>>
|
) -> impl Iterator<Item = Result<RegisteredUser<UserData>>>
|
||||||
where
|
where
|
||||||
UserData: Serialize + DeserializeOwned
|
UserData: Serialize + DeserializeOwned
|
||||||
{
|
{
|
||||||
|
let this = self.clone();
|
||||||
self.users.iter()
|
self.users.iter()
|
||||||
.map(|result| {
|
.map(move|result| {
|
||||||
let (username, bytes) = result.expect("Failed to connect to database");
|
let (uid, bytes) = result?;
|
||||||
let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.as_ref())
|
let inner: PartialUser<UserData> = bincode::deserialize_from(bytes.as_ref())?;
|
||||||
.expect("Received malformed data from database");
|
let uid = u64::from_le_bytes(uid.as_ref().try_into()?);
|
||||||
let username = String::from_utf8(username.to_vec())
|
Ok(RegisteredUser::new(uid, None, this.clone(), inner))
|
||||||
.expect("Malformed username in database");
|
|
||||||
RegisteredUser::new(username, None, self.clone(), inner)
|
|
||||||
})
|
})
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to determine the user who sent a request based on the certificate.
|
/// Attempt to determine the user who sent a request based on the certificate.
|
||||||
|
@ -103,7 +136,7 @@ impl UserManager {
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// Pancis if the database is corrupt
|
/// Pancis if the database is corrupt
|
||||||
pub fn get_user<UserData>(
|
pub fn get_user_by_cert<UserData>(
|
||||||
&self,
|
&self,
|
||||||
cert: Option<&[u8; 32]>
|
cert: Option<&[u8; 32]>
|
||||||
) -> Result<User<UserData>>
|
) -> Result<User<UserData>>
|
||||||
|
@ -111,9 +144,7 @@ impl UserManager {
|
||||||
UserData: Serialize + DeserializeOwned
|
UserData: Serialize + DeserializeOwned
|
||||||
{
|
{
|
||||||
if let Some(certificate) = cert {
|
if let Some(certificate) = cert {
|
||||||
if let Some(username) = self.lookup_certificate(*certificate)? {
|
if let Some(user_inner) = self.lookup_certificate(*certificate)? {
|
||||||
let user_inner = self.lookup_user(&username)?
|
|
||||||
.expect("Database corruption: Certificate data refers to non-existant user");
|
|
||||||
Ok(User::SignedIn(user_inner.with_cert(*certificate)))
|
Ok(User::SignedIn(user_inner.with_cert(*certificate)))
|
||||||
} else {
|
} else {
|
||||||
Ok(User::NotSignedIn(NotSignedInUser {
|
Ok(User::NotSignedIn(NotSignedInUser {
|
||||||
|
|
|
@ -93,12 +93,24 @@ impl From<bincode::Error> for UserManagerError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<std::array::TryFromSliceError> for UserManagerError {
|
||||||
|
fn from(error: std::array::TryFromSliceError) -> Self {
|
||||||
|
Self::DeserializeError(error.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<std::str::Utf8Error> for UserManagerError {
|
impl From<std::str::Utf8Error> for UserManagerError {
|
||||||
fn from(error: std::str::Utf8Error) -> Self {
|
fn from(error: std::str::Utf8Error) -> Self {
|
||||||
Self::DeserializeError(error.into())
|
Self::DeserializeError(error.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<DeserializeError> for UserManagerError {
|
||||||
|
fn from(error: DeserializeError) -> Self {
|
||||||
|
Self::DeserializeError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "user_management_advanced")]
|
#[cfg(feature = "user_management_advanced")]
|
||||||
impl From<argon2::Error> for UserManagerError {
|
impl From<argon2::Error> for UserManagerError {
|
||||||
fn from(error: argon2::Error) -> Self {
|
fn from(error: argon2::Error) -> Self {
|
||||||
|
@ -159,12 +171,18 @@ pub enum DeserializeError {
|
||||||
/// version, or you're using a database that was created by a version of kochab that
|
/// version, or you're using a database that was created by a version of kochab that
|
||||||
/// was released after the one you're currently using. However, as of right now, no
|
/// was released after the one you're currently using. However, as of right now, no
|
||||||
/// such release exists or is planned.
|
/// such release exists or is planned.
|
||||||
UidError(std::str::Utf8Error),
|
UidError(std::array::TryFromSliceError),
|
||||||
|
|
||||||
/// There was an error deserializing a username
|
/// There was an error deserializing a username
|
||||||
///
|
///
|
||||||
/// Indicates data corruption or a misaligned database version
|
/// Indicates data corruption or a misaligned database version
|
||||||
UsernameError(std::str::Utf8Error),
|
UsernameError(std::str::Utf8Error),
|
||||||
|
|
||||||
|
/// A certificate or username was linked to a non-existant user id
|
||||||
|
///
|
||||||
|
/// This error should not occur, except in very unlikely scenarios, or if there's a
|
||||||
|
/// bug with kochab
|
||||||
|
InvalidReference(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bincode::Error> for DeserializeError {
|
impl From<bincode::Error> for DeserializeError {
|
||||||
|
@ -173,6 +191,12 @@ impl From<bincode::Error> for DeserializeError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<std::array::TryFromSliceError> for DeserializeError {
|
||||||
|
fn from(error: std::array::TryFromSliceError) -> Self {
|
||||||
|
Self::UidError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<std::str::Utf8Error> for DeserializeError {
|
impl From<std::str::Utf8Error> for DeserializeError {
|
||||||
fn from(error: std::str::Utf8Error) -> Self {
|
fn from(error: std::str::Utf8Error) -> Self {
|
||||||
Self::UsernameError(error)
|
Self::UsernameError(error)
|
||||||
|
@ -185,6 +209,7 @@ impl std::error::Error for DeserializeError {
|
||||||
Self::StructError(e) => Some(e),
|
Self::StructError(e) => Some(e),
|
||||||
Self::UidError(e) => Some(e),
|
Self::UidError(e) => Some(e),
|
||||||
Self::UsernameError(e) => Some(e),
|
Self::UsernameError(e) => Some(e),
|
||||||
|
Self::InvalidReference(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,6 +223,8 @@ impl std::fmt::Display for DeserializeError {
|
||||||
write!(f, "Got wrong number of bytes while deserializing user ID: {}", e),
|
write!(f, "Got wrong number of bytes while deserializing user ID: {}", e),
|
||||||
Self::UsernameError(e) =>
|
Self::UsernameError(e) =>
|
||||||
write!(f, "Got invalid UTF-8 instead of username: {}", e),
|
write!(f, "Got invalid UTF-8 instead of username: {}", e),
|
||||||
|
Self::InvalidReference(uid) =>
|
||||||
|
write!(f, "Database refers to nonexistant user with userid {}", uid),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ lazy_static::lazy_static! {
|
||||||
pub (crate) struct PartialUser<UserData> {
|
pub (crate) struct PartialUser<UserData> {
|
||||||
pub data: UserData,
|
pub data: UserData,
|
||||||
pub certificates: Vec<[u8; 32]>,
|
pub certificates: Vec<[u8; 32]>,
|
||||||
|
pub username: String,
|
||||||
#[cfg(feature = "user_management_advanced")]
|
#[cfg(feature = "user_management_advanced")]
|
||||||
pub pass_hash: Option<(Vec<u8>, [u8; 32])>,
|
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
|
/// This MUST be called if the user data is modified using the AsMut trait, or else
|
||||||
/// changes will not be written to the database
|
/// 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
|
where
|
||||||
UserData: Serialize
|
UserData: Serialize
|
||||||
{
|
{
|
||||||
tree.insert(
|
tree.insert(
|
||||||
&username,
|
uid.to_le_bytes(),
|
||||||
bincode::serialize(&self)?,
|
bincode::serialize(&self)?,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -117,26 +118,48 @@ 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 {
|
||||||
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,
|
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),
|
Some(self.certificate),
|
||||||
self.manager,
|
self.manager,
|
||||||
PartialUser {
|
partial,
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +195,7 @@ impl NotSignedInUser {
|
||||||
username: &str,
|
username: &str,
|
||||||
password: Option<&[u8]>,
|
password: Option<&[u8]>,
|
||||||
) -> Result<Option<RegisteredUser<UserData>>> {
|
) -> 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
|
// Perform password check, if caller wants
|
||||||
if let Some(password) = password {
|
if let Some(password) = password {
|
||||||
if !user.check_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`]
|
/// For more information about the user lifecycle and sign-in stages, see [`User`]
|
||||||
pub struct RegisteredUser<UserData: Serialize + DeserializeOwned> {
|
pub struct RegisteredUser<UserData: Serialize + DeserializeOwned> {
|
||||||
username: String,
|
uid: u64,
|
||||||
active_certificate: Option<[u8; 32]>,
|
active_certificate: Option<[u8; 32]>,
|
||||||
manager: UserManager,
|
manager: UserManager,
|
||||||
inner: PartialUser<UserData>,
|
inner: PartialUser<UserData>,
|
||||||
|
@ -207,13 +230,13 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
||||||
|
|
||||||
/// Create a new user from parts
|
/// Create a new user from parts
|
||||||
pub (crate) fn new(
|
pub (crate) fn new(
|
||||||
username: String,
|
uid: u64,
|
||||||
active_certificate: Option<[u8; 32]>,
|
active_certificate: Option<[u8; 32]>,
|
||||||
manager: UserManager,
|
manager: UserManager,
|
||||||
inner: PartialUser<UserData>
|
inner: PartialUser<UserData>
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
username,
|
uid,
|
||||||
active_certificate,
|
active_certificate,
|
||||||
manager,
|
manager,
|
||||||
inner,
|
inner,
|
||||||
|
@ -246,9 +269,19 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
||||||
|
|
||||||
/// Get the user's current username.
|
/// 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 {
|
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")]
|
#[cfg(feature = "user_management_advanced")]
|
||||||
|
@ -328,7 +361,7 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
||||||
where
|
where
|
||||||
UserData: Serialize
|
UserData: Serialize
|
||||||
{
|
{
|
||||||
self.inner.store(&self.manager.users, &self.username)?;
|
self.inner.store(&self.manager.users, self.uid)?;
|
||||||
self.has_changed = false;
|
self.has_changed = false;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -346,16 +379,17 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
||||||
self.inner.certificates.push(certificate);
|
self.inner.certificates.push(certificate);
|
||||||
|
|
||||||
let inner_serialized = bincode::serialize(&self.inner)?;
|
let inner_serialized = bincode::serialize(&self.inner)?;
|
||||||
|
let uid_bytes = self.uid.to_le_bytes();
|
||||||
|
|
||||||
(&self.manager.users, &self.manager.certificates)
|
(&self.manager.users, &self.manager.certificates)
|
||||||
.transaction(|(tx_usr, tx_crt)| {
|
.transaction(|(tx_usr, tx_crt)| {
|
||||||
tx_usr.insert(
|
tx_usr.insert(
|
||||||
self.username.as_str(),
|
&uid_bytes,
|
||||||
inner_serialized.clone(),
|
inner_serialized.clone(),
|
||||||
)?;
|
)?;
|
||||||
tx_crt.insert(
|
tx_crt.insert(
|
||||||
&certificate,
|
&certificate,
|
||||||
self.username.as_bytes(),
|
&uid_bytes,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
@ -384,9 +418,12 @@ impl<UserData: Serialize + DeserializeOwned> RegisteredUser<UserData> {
|
||||||
pub fn delete(self) -> Result<()> {
|
pub fn delete(self) -> Result<()> {
|
||||||
let certificates = self.all_certificates();
|
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(
|
tx_usr.remove(
|
||||||
self.username.as_str(),
|
&self.uid.to_le_bytes(),
|
||||||
)?;
|
)?;
|
||||||
for cert in certificates {
|
for cert in certificates {
|
||||||
tx_crt.remove(
|
tx_crt.remove(
|
||||||
|
|
Loading…
Reference in a new issue