hyaenidae/accounts/src/extractors.rs

145 lines
3.8 KiB
Rust
Raw Normal View History

2020-12-16 02:40:41 +00:00
use actix_session::Session;
use actix_web::{
dev::Payload,
http::{header::LOCATION, StatusCode},
web::Data,
FromRequest, HttpRequest, HttpResponse, ResponseError,
};
use futures::future::LocalBoxFuture;
use uuid::Uuid;
use crate::{store::User, State};
#[derive(serde::Deserialize, serde::Serialize)]
pub(crate) struct CookieData {
accepted: bool,
}
impl CookieData {
pub(crate) fn set_accepted(session: &Session) -> Result<(), SessionError> {
session
.set("accepted", CookieData { accepted: true })
.map_err(|_| SessionError::Set)
}
fn accepted(session: &Session) -> bool {
session
.get::<CookieData>("accepted")
.map(|opt| opt.is_some())
.unwrap_or(false)
}
}
#[derive(serde::Deserialize, serde::Serialize)]
pub(crate) struct UserData {
id: Uuid,
}
impl UserData {
pub(crate) fn set_data(id: Uuid, session: &Session) -> Result<(), SessionError> {
session
.set("user-data", UserData { id })
.map_err(|_| SessionError::Set)
}
pub(crate) fn remove(session: &Session) {
session.remove("user-data")
}
fn data(session: &Session) -> Result<Option<UserData>, actix_web::Error> {
Ok(session.get::<UserData>("user-data")?)
}
}
pub struct AcceptedCookies(());
pub struct Authenticated {
user: User,
}
impl Authenticated {
pub fn user(self) -> User {
self.user
}
}
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
struct ServerError(String);
impl ResponseError for ServerError {
fn status_code(&self) -> StatusCode {
StatusCode::SEE_OTHER
}
fn error_response(&self) -> HttpResponse {
HttpResponse::build(self.status_code())
.header(LOCATION, self.0.clone())
.finish()
}
}
#[derive(Debug, thiserror::Error)]
pub enum SessionError {
#[error("Failed to set sesion data")]
Set,
}
impl FromRequest for AcceptedCookies {
type Error = actix_web::Error;
type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;
type Config = ();
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let session_fut = Session::extract(req);
let state_fut = Data::<State>::extract(req);
Box::pin(async move {
let session = session_fut.await?;
let state = state_fut.await?;
if CookieData::accepted(&session) {
Ok(AcceptedCookies(()))
} else {
log::debug!("Browser has not accepted cookies");
Err(ServerError(state.pages.not_found_path()).into())
}
})
}
}
impl FromRequest for Authenticated {
type Error = actix_web::Error;
type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;
type Config = ();
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let session_fut = Session::extract(req);
let state_fut = Data::<State>::extract(req);
Box::pin(async move {
let session = session_fut.await?;
let state = state_fut.await?;
if let Some(UserData { id }) = UserData::data(&session)? {
let res = state.user_store.user_by_id(id).await;
let user_opt = match res {
Ok(user_opt) => user_opt,
Err(_) => {
log::debug!("Error fetching user data from store");
return Err(ServerError(state.pages.internal_server_error_path()).into());
}
};
if let Some(user) = user_opt {
return Ok(Authenticated { user });
}
}
log::debug!("No user data found for browser");
Err(ServerError(state.pages.not_found_path()).into())
})
}
}