💾 Archived View for alchemi.dev › en › projects › kochab › files › src › user_management › mod.rs captured on 2022-07-16 at 16:43:52.

View Raw

More Information

-=-=-=-=-=-=-

//! 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>;