kochab/src/user_management/mod.rs

115 lines
4.3 KiB
Rust

//! Tools for registering users & persisting arbitrary user data
//!
//! Many Gemini applications use some form of a login method in order to allow users to
//! persist personal data, authenticate themselves, and login from multiple devices using
//! multiple certificates.
//!
//! This module contains tools to help you build a system like this without stress. A
//! typical workflow looks something like this:
//!
//! * Call [`Request::user()`] to retrieve information about a user
//! * Direct any users without a certificate to create a certificate
//! * Ask users with a certificate not yet linked to an account to create an account using
//! [`NotSignedInUser::register()`] or link their certificate to an existing account
//! with a password using [`NotSignedInUser::attach()`].
//! * You should now have a [`RegisteredUser`] either from registering/attaching a
//! [`NotSignedInUser`] or because the user was already registered
//! * Access and modify user data using [`RegisteredUser::as_mut()`], changes are
//! automatically persisted to the database (on user drop).
//!
//! Use of this module requires the `user_management` feature to be enabled
pub mod user;
mod manager;
#[cfg(feature = "user_management_routes")]
mod routes;
#[cfg(feature = "user_management_routes")]
pub use routes::UserManagementRoutes;
pub use manager::UserManager;
pub use user::User;
// Imports for docs
#[allow(unused_imports)]
use user::{NotSignedInUser, RegisteredUser};
#[allow(unused_imports)]
use crate::types::Request;
#[derive(Debug)]
pub enum UserManagerError {
UsernameNotUnique,
PasswordNotSet,
DatabaseError(sled::Error),
DatabaseTransactionError(sled::transaction::TransactionError),
DeserializeBincodeError(bincode::Error),
DeserializeUtf8Error(std::str::Utf8Error),
#[cfg(feature = "user_management_advanced")]
Argon2Error(argon2::Error),
}
impl From<sled::Error> for UserManagerError {
fn from(error: sled::Error) -> Self {
Self::DatabaseError(error)
}
}
impl From<sled::transaction::TransactionError> for UserManagerError {
fn from(error: sled::transaction::TransactionError) -> Self {
Self::DatabaseTransactionError(error)
}
}
impl From<bincode::Error> for UserManagerError {
fn from(error: bincode::Error) -> Self {
Self::DeserializeBincodeError(error)
}
}
impl From<std::str::Utf8Error> for UserManagerError {
fn from(error: std::str::Utf8Error) -> Self {
Self::DeserializeUtf8Error(error)
}
}
#[cfg(feature = "user_management_advanced")]
impl From<argon2::Error> for UserManagerError {
fn from(error: argon2::Error) -> Self {
Self::Argon2Error(error)
}
}
impl std::error::Error for UserManagerError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::DatabaseError(e) => Some(e),
Self::DatabaseTransactionError(e) => Some(e),
Self::DeserializeBincodeError(e) => Some(e),
Self::DeserializeUtf8Error(e) => Some(e),
#[cfg(feature = "user_management_advanced")]
Self::Argon2Error(e) => Some(e),
_ => None
}
}
}
impl std::fmt::Display for UserManagerError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
match self {
Self::UsernameNotUnique =>
write!(f, "Attempted to create a user using a username that's already been taken"),
Self::PasswordNotSet =>
write!(f, "Attempted to check the password of a user who has not set one yet"),
Self::DatabaseError(e) =>
write!(f, "Error accessing the user database: {}", e),
Self::DatabaseTransactionError(e) =>
write!(f, "Error accessing the user database: {}", e),
Self::DeserializeBincodeError(e) =>
write!(f, "Recieved messy data from database, possible corruption: {}", e),
Self::DeserializeUtf8Error(e) =>
write!(f, "Recieved invalid UTF-8 from database, possible corruption: {}", e),
#[cfg(feature = "user_management_advanced")]
Self::Argon2Error(e) =>
write!(f, "Argon2 Error, likely malformed password hash, possible database corruption: {}", e),
}
}
}
pub type Result<T> = std::result::Result<T, UserManagerError>;