hyaenidae/accounts/src/lib.rs

162 lines
4.1 KiB
Rust

use actix_session::CookieSession;
use actix_web::{http::header::LOCATION, HttpResponse};
use sled::Db;
use std::sync::Arc;
use uuid::Uuid;
mod extractors;
mod forms;
mod store;
pub use {
extractors::{AcceptedCookies, Auth, Authenticated},
forms::{
cookies::{cookies, cookies_page, CookiesArgs, CookiesPageArgs, CookiesState},
delete_user::{
delete_user, delete_user_page, DeleteUserArgs, DeleteUserPageArgs, DeleteUserState,
},
login::{login, login_page, LoginArgs, LoginPageArgs, LoginState},
logout::{logout, logout_page, LogoutArgs, LogoutPageArgs, LogoutState},
register::{register, register_page, RegisterArgs, RegisterPageArgs, RegisterState},
update_password::{
update_password, update_password_page, UpdatePasswordArgs, UpdatePasswordPageArgs,
UpdatePasswordState,
},
update_username::{
update_username, update_username_page, UpdateUsernameArgs, UpdateUsernamePageArgs,
UpdateUsernameState,
},
},
store::User,
};
pub trait Pages {
fn home_path(&self) -> String;
fn not_found_path(&self) -> String;
fn internal_server_error_path(&self) -> String;
fn cookies_path(&self) -> String;
fn login_path(&self) -> String;
fn register_path(&self) -> String;
fn logout_path(&self) -> String;
fn accounts_path(&self) -> String;
fn update_username_path(&self) -> String;
fn update_password_path(&self) -> String;
fn delete_user_path(&self) -> String;
}
#[derive(Clone)]
pub struct Config {
pub key: Vec<u8>,
pub https: bool,
pub pages: Arc<dyn Pages + Send + Sync>,
}
#[derive(Clone)]
pub struct State {
user_store: store::UserStore,
db: Db,
pages: Arc<dyn Pages + Send + Sync>,
}
impl State {
pub async fn by_username(&self, username: String) -> Result<Option<User>, Error> {
Ok(self.user_store.user_by_username(username).await?)
}
pub async fn suspend(&self, user_id: Uuid) -> Result<(), Error> {
Ok(self.user_store.suspend(user_id).await?)
}
}
pub fn state(config: &Config, db: Db) -> Result<State, store::StoreError> {
Ok(State {
user_store: store::UserStore::build(&db)?,
db,
pages: Arc::clone(&config.pages),
})
}
pub fn cookie_middleware(config: &Config) -> CookieSession {
CookieSession::private(&config.key)
.name("accounts_scope")
.secure(config.https)
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{0}")]
Session(#[from] extractors::SessionError),
#[error("{0}")]
Store(#[from] store::StoreError),
#[error("Panic in blocking operation")]
Blocking,
}
impl std::fmt::Debug for Config {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Config")
.field("key", &"Key")
.field("https", &self.https)
.field("pages", &"Box<dyn Pages>")
.finish()
}
}
impl std::fmt::Debug for State {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("State")
.field("user_store", &"UserStore")
.field("db", &"Db")
.finish()
}
}
fn to_cookie_page(state: &State) -> HttpResponse {
HttpResponse::SeeOther()
.header(LOCATION, state.pages.cookies_path())
.finish()
}
fn to_register(state: &State) -> HttpResponse {
HttpResponse::SeeOther()
.header(LOCATION, state.pages.register_path())
.finish()
}
fn to_account_page(state: &State) -> HttpResponse {
HttpResponse::SeeOther()
.header(LOCATION, state.pages.accounts_path())
.finish()
}
fn to_home(state: &State) -> HttpResponse {
HttpResponse::SeeOther()
.header(LOCATION, state.pages.home_path())
.finish()
}
impl<E> From<actix_web::error::BlockingError<E>> for Error
where
Error: From<E>,
E: std::fmt::Debug,
{
fn from(e: actix_web::error::BlockingError<E>) -> Self {
match e {
actix_web::error::BlockingError::Error(e) => e.into(),
_ => Error::Blocking,
}
}
}