hyaenidae/accounts/src/lib.rs
2021-04-02 12:07:19 -05:00

155 lines
4 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()
.insert_header((LOCATION, state.pages.cookies_path()))
.finish()
}
fn to_register(state: &State) -> HttpResponse {
HttpResponse::SeeOther()
.insert_header((LOCATION, state.pages.register_path()))
.finish()
}
fn to_account_page(state: &State) -> HttpResponse {
HttpResponse::SeeOther()
.insert_header((LOCATION, state.pages.accounts_path()))
.finish()
}
fn to_home(state: &State) -> HttpResponse {
HttpResponse::SeeOther()
.insert_header((LOCATION, state.pages.home_path()))
.finish()
}
impl From<actix_web::error::BlockingError> for Error {
fn from(_: actix_web::error::BlockingError) -> Self {
Self::Blocking
}
}