//! 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; mod routes; pub use routes::UserManagementRoutes; pub use manager::UserManager; pub use user::User; pub use manager::CertificateData; // 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), DeserializeError(bincode::Error), #[cfg(feature = "user_management_advanced")] Argon2Error(argon2::Error), } impl From for UserManagerError { fn from(error: sled::Error) -> Self { Self::DatabaseError(error) } } impl From for UserManagerError { fn from(error: sled::transaction::TransactionError) -> Self { Self::DatabaseTransactionError(error) } } impl From for UserManagerError { fn from(error: bincode::Error) -> Self { Self::DeserializeError(error) } } #[cfg(feature = "user_management_advanced")] impl From 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::DeserializeError(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::DeserializeError(e) => write!(f, "Recieved messy data 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 = std::result::Result;