2020-11-19 18:20:14 +00:00
|
|
|
//! 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()`].
|
2020-11-19 22:00:32 +00:00
|
|
|
//! * You should now have a [`RegisteredUser`] either from registering/attaching a
|
2020-11-19 18:20:14 +00:00
|
|
|
//! [`NotSignedInUser`] or because the user was already registered
|
2020-11-19 22:00:32 +00:00
|
|
|
//! * Access and modify user data using [`RegisteredUser::as_mut()`], changes are
|
2020-11-19 18:20:14 +00:00
|
|
|
//! automatically persisted to the database (on user drop).
|
|
|
|
//!
|
|
|
|
//! Use of this module requires the `user_management` feature to be enabled
|
2020-11-16 06:13:16 +00:00
|
|
|
pub mod user;
|
|
|
|
mod manager;
|
2020-11-23 15:01:53 +00:00
|
|
|
#[cfg(feature = "user_management_routes")]
|
2020-11-22 04:03:56 +00:00
|
|
|
mod routes;
|
2020-11-23 15:01:53 +00:00
|
|
|
#[cfg(feature = "user_management_routes")]
|
2020-11-22 04:03:56 +00:00
|
|
|
pub use routes::UserManagementRoutes;
|
2020-11-16 06:13:16 +00:00
|
|
|
pub use manager::UserManager;
|
|
|
|
pub use user::User;
|
2020-11-19 22:07:33 +00:00
|
|
|
// Imports for docs
|
|
|
|
#[allow(unused_imports)]
|
|
|
|
use user::{NotSignedInUser, RegisteredUser};
|
|
|
|
#[allow(unused_imports)]
|
|
|
|
use crate::types::Request;
|
2020-11-16 06:13:16 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2020-12-08 16:01:07 +00:00
|
|
|
/// An error that occured in the user manager
|
2020-11-16 06:13:16 +00:00
|
|
|
pub enum UserManagerError {
|
2020-12-08 16:01:07 +00:00
|
|
|
/// Tried to set a user's username to a username that already exists
|
|
|
|
///
|
|
|
|
/// Recommended handling: Explicitly catch the error, and display a custom warning to
|
|
|
|
/// the user before asking them to try another username
|
2020-11-16 06:13:16 +00:00
|
|
|
UsernameNotUnique,
|
2020-12-08 16:01:07 +00:00
|
|
|
|
|
|
|
/// Attempted to validate the user's password, but they haven't set one yet
|
|
|
|
///
|
|
|
|
/// Recommended handling: Inform the user that either their username or password was
|
|
|
|
/// incorrect.
|
2020-11-16 06:13:16 +00:00
|
|
|
PasswordNotSet,
|
2020-12-08 16:01:07 +00:00
|
|
|
|
|
|
|
/// There was an error connecting to sled
|
|
|
|
///
|
|
|
|
/// Recommended handling: Log a visible error for the sysadmin to see, and exit
|
2020-11-16 06:13:16 +00:00
|
|
|
DatabaseError(sled::Error),
|
2020-12-08 16:01:07 +00:00
|
|
|
|
|
|
|
/// There was an error running a database transaction
|
|
|
|
///
|
|
|
|
/// Recommended handling: Same as [`UserManagerError::DatabaseError`]
|
2020-11-16 06:13:16 +00:00
|
|
|
DatabaseTransactionError(sled::transaction::TransactionError),
|
2020-12-08 16:01:07 +00:00
|
|
|
|
|
|
|
/// There was an error deserializing from the database
|
|
|
|
///
|
2020-12-16 21:45:35 +00:00
|
|
|
/// This likely indicates that the database was generated with a different version of
|
|
|
|
/// the software than is curretly being used, either because the UserData struct has
|
|
|
|
/// changed, or because kochab itself has updated it's schema.
|
2020-12-08 16:01:07 +00:00
|
|
|
///
|
|
|
|
/// Recommended handling: Log a visible error and exit. Recommend seeking a database
|
|
|
|
/// migration script or deleting the database
|
2020-12-16 21:45:35 +00:00
|
|
|
DeserializeError(DeserializeError),
|
2020-12-08 16:01:07 +00:00
|
|
|
|
2020-11-23 03:24:36 +00:00
|
|
|
#[cfg(feature = "user_management_advanced")]
|
2020-12-08 16:01:07 +00:00
|
|
|
/// There was an error hashing or checking the user's password.
|
|
|
|
///
|
|
|
|
/// This likely indicates database corruption, and should be handled in the same way
|
|
|
|
/// as a [`UserManagerError::DeserializeBincodeError`]
|
2020-11-19 21:07:52 +00:00
|
|
|
Argon2Error(argon2::Error),
|
2020-11-16 06:13:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2020-12-16 21:45:35 +00:00
|
|
|
Self::DeserializeError(error.into())
|
2020-12-01 19:43:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::str::Utf8Error> for UserManagerError {
|
|
|
|
fn from(error: std::str::Utf8Error) -> Self {
|
2020-12-16 21:45:35 +00:00
|
|
|
Self::DeserializeError(error.into())
|
2020-11-16 06:13:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-23 03:24:36 +00:00
|
|
|
#[cfg(feature = "user_management_advanced")]
|
2020-11-19 21:07:52 +00:00
|
|
|
impl From<argon2::Error> for UserManagerError {
|
|
|
|
fn from(error: argon2::Error) -> Self {
|
|
|
|
Self::Argon2Error(error)
|
2020-11-16 06:13:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
2020-12-16 21:45:35 +00:00
|
|
|
Self::DeserializeError(e) => Some(e),
|
2020-11-23 03:24:36 +00:00
|
|
|
#[cfg(feature = "user_management_advanced")]
|
2020-11-19 21:07:52 +00:00
|
|
|
Self::Argon2Error(e) => Some(e),
|
2020-11-16 06:13:16 +00:00
|
|
|
_ => 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),
|
2020-12-16 21:45:35 +00:00
|
|
|
Self::DeserializeError(e) =>
|
2020-11-16 06:13:16 +00:00
|
|
|
write!(f, "Recieved messy data from database, possible corruption: {}", e),
|
2020-11-23 03:24:36 +00:00
|
|
|
#[cfg(feature = "user_management_advanced")]
|
2020-11-19 21:07:52 +00:00
|
|
|
Self::Argon2Error(e) =>
|
|
|
|
write!(f, "Argon2 Error, likely malformed password hash, possible database corruption: {}", e),
|
2020-11-16 06:13:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:45:35 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
/// Indicates an error deserializing from the database
|
|
|
|
///
|
|
|
|
/// This likely indicates that the database was generated with
|
|
|
|
/// a different version of the software than is curretly being used, either because
|
|
|
|
/// the UserData struct has changed, or because kochab itself has updated it's schema.
|
|
|
|
pub enum DeserializeError {
|
|
|
|
|
|
|
|
/// There was an error deserializing an entire struct
|
|
|
|
///
|
|
|
|
/// Most likely indicates that the struct itself changed, i.e. between versions.
|
|
|
|
StructError(bincode::Error),
|
|
|
|
|
|
|
|
/// The was an error deserializing a userid
|
|
|
|
///
|
|
|
|
/// Likely because too many bytes were received. If you are getting this, you are
|
|
|
|
/// likely using a pre-0.1.0 version of kochab, and should update to the latest
|
|
|
|
/// 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
|
|
|
|
/// such release exists or is planned.
|
|
|
|
UidError(std::str::Utf8Error),
|
|
|
|
|
|
|
|
/// There was an error deserializing a username
|
|
|
|
///
|
|
|
|
/// Indicates data corruption or a misaligned database version
|
|
|
|
UsernameError(std::str::Utf8Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<bincode::Error> for DeserializeError {
|
|
|
|
fn from(error: bincode::Error) -> Self {
|
|
|
|
Self::StructError(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::str::Utf8Error> for DeserializeError {
|
|
|
|
fn from(error: std::str::Utf8Error) -> Self {
|
|
|
|
Self::UsernameError(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for DeserializeError {
|
|
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
|
|
match self {
|
|
|
|
Self::StructError(e) => Some(e),
|
|
|
|
Self::UidError(e) => Some(e),
|
|
|
|
Self::UsernameError(e) => Some(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Display for DeserializeError {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
|
|
|
|
match self {
|
|
|
|
Self::StructError(e) =>
|
|
|
|
write!(f, "Failed to deserialize struct: {}", e),
|
|
|
|
Self::UidError(e) =>
|
|
|
|
write!(f, "Got wrong number of bytes while deserializing user ID: {}", e),
|
|
|
|
Self::UsernameError(e) =>
|
|
|
|
write!(f, "Got invalid UTF-8 instead of username: {}", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-08 16:01:07 +00:00
|
|
|
/// A result type returned by many methods within the [`user_management`] module
|
2020-11-16 06:13:16 +00:00
|
|
|
pub type Result<T> = std::result::Result<T, UserManagerError>;
|