2020-12-16 04:14:01 +00:00
|
|
|
use crate::{store::User, State};
|
|
|
|
use actix_session::{Session, UserSession};
|
2020-12-16 02:40:41 +00:00
|
|
|
use actix_web::{
|
2020-12-16 04:14:01 +00:00
|
|
|
dev::{Payload, Service, ServiceRequest, ServiceResponse, Transform},
|
2020-12-16 02:40:41 +00:00
|
|
|
http::{header::LOCATION, StatusCode},
|
|
|
|
web::Data,
|
2020-12-16 04:14:01 +00:00
|
|
|
FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
|
|
|
|
};
|
|
|
|
use event_listener::Event;
|
|
|
|
use futures::future::{ok, LocalBoxFuture, Ready};
|
|
|
|
use std::{
|
2021-01-08 04:42:22 +00:00
|
|
|
cell::{Cell, RefCell},
|
2020-12-16 04:14:01 +00:00
|
|
|
rc::Rc,
|
|
|
|
task::{Context, Poll},
|
2020-12-16 02:40:41 +00:00
|
|
|
};
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
#[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(());
|
|
|
|
|
2020-12-16 04:14:01 +00:00
|
|
|
#[derive(Clone)]
|
2020-12-16 02:40:41 +00:00
|
|
|
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 {
|
2020-12-16 04:14:01 +00:00
|
|
|
let session = req.get_session();
|
2020-12-16 02:40:41 +00:00
|
|
|
let state_fut = Data::<State>::extract(req);
|
|
|
|
|
|
|
|
Box::pin(async move {
|
|
|
|
let state = state_fut.await?;
|
|
|
|
|
|
|
|
if CookieData::accepted(&session) {
|
|
|
|
Ok(AcceptedCookies(()))
|
|
|
|
} else {
|
|
|
|
Err(ServerError(state.pages.not_found_path()).into())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-08 04:42:22 +00:00
|
|
|
impl FromRequest for User {
|
|
|
|
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 fut = Authenticated::extract(req);
|
|
|
|
|
|
|
|
Box::pin(async move { fut.await.map(|auth| auth.user()) })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 02:40:41 +00:00
|
|
|
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 {
|
2020-12-16 04:14:01 +00:00
|
|
|
let opt = req
|
|
|
|
.extensions()
|
|
|
|
.get::<AuthState>()
|
|
|
|
.map(|auth_state| (auth_state.clone(), auth_state.2.listen()));
|
|
|
|
|
2020-12-16 02:40:41 +00:00
|
|
|
let state_fut = Data::<State>::extract(req);
|
|
|
|
|
|
|
|
Box::pin(async move {
|
2020-12-16 04:14:01 +00:00
|
|
|
let (auth_state, listen_fut) = match opt {
|
|
|
|
Some(auth_state) => auth_state,
|
|
|
|
None => {
|
|
|
|
let state = state_fut.await?;
|
|
|
|
return Err(ServerError(state.pages.not_found_path()).into());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(auth) = auth_state.0.borrow().as_ref() {
|
|
|
|
return Ok(auth.clone());
|
|
|
|
}
|
|
|
|
|
2021-01-08 04:42:22 +00:00
|
|
|
if !auth_state.1.get() {
|
2020-12-16 04:14:01 +00:00
|
|
|
listen_fut.await;
|
|
|
|
|
|
|
|
if let Some(auth) = auth_state.0.borrow().as_ref() {
|
|
|
|
return Ok(auth.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 02:40:41 +00:00
|
|
|
let state = state_fut.await?;
|
2020-12-16 04:14:01 +00:00
|
|
|
Err(ServerError(state.pages.not_found_path()).into())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2020-12-16 02:40:41 +00:00
|
|
|
|
2020-12-16 04:14:01 +00:00
|
|
|
pub struct Auth(pub State);
|
|
|
|
pub struct AuthMiddleware<S>(S, State);
|
|
|
|
#[derive(Clone)]
|
|
|
|
struct AuthState(
|
|
|
|
Rc<RefCell<Option<Authenticated>>>,
|
2021-01-08 04:42:22 +00:00
|
|
|
Rc<Cell<bool>>,
|
2020-12-16 04:14:01 +00:00
|
|
|
Rc<Event>,
|
|
|
|
);
|
2021-01-08 04:42:22 +00:00
|
|
|
struct DropGuard(Rc<Event>, Rc<Cell<bool>>);
|
2020-12-16 04:14:01 +00:00
|
|
|
|
|
|
|
fn auth_state() -> (AuthState, DropGuard) {
|
|
|
|
let event = Rc::new(Event::new());
|
|
|
|
let state = Rc::new(RefCell::new(None));
|
2021-01-08 04:42:22 +00:00
|
|
|
let flag = Rc::new(Cell::new(false));
|
2020-12-16 04:14:01 +00:00
|
|
|
|
2021-01-08 04:42:22 +00:00
|
|
|
(
|
|
|
|
AuthState(state, Rc::clone(&flag), Rc::clone(&event)),
|
|
|
|
DropGuard(event, flag),
|
|
|
|
)
|
2020-12-16 04:14:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for DropGuard {
|
|
|
|
fn drop(&mut self) {
|
2021-01-08 04:42:22 +00:00
|
|
|
self.1.set(true);
|
2020-12-16 04:14:01 +00:00
|
|
|
self.0.notify(usize::MAX)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<S, B> Transform<S> for Auth
|
|
|
|
where
|
|
|
|
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
|
|
|
|
S::Future: 'static,
|
|
|
|
{
|
|
|
|
type Request = S::Request;
|
|
|
|
type Response = S::Response;
|
|
|
|
type Error = S::Error;
|
|
|
|
type InitError = ();
|
|
|
|
type Transform = AuthMiddleware<S>;
|
|
|
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
|
|
|
|
|
|
|
fn new_transform(&self, service: S) -> Self::Future {
|
|
|
|
ok(AuthMiddleware(service, self.0.clone()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<S, B> Service for AuthMiddleware<S>
|
|
|
|
where
|
|
|
|
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
|
|
|
|
S::Future: 'static,
|
|
|
|
{
|
|
|
|
type Request = S::Request;
|
|
|
|
type Response = S::Response;
|
|
|
|
type Error = S::Error;
|
|
|
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
|
|
|
|
|
|
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
|
|
self.0.poll_ready(cx)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn call(&mut self, req: Self::Request) -> Self::Future {
|
|
|
|
let session = req.get_session();
|
|
|
|
let state = self.1.clone();
|
|
|
|
|
|
|
|
let (auth_state, drop_guard) = auth_state();
|
|
|
|
req.extensions_mut().insert(auth_state.clone());
|
|
|
|
|
|
|
|
let fut = self.0.call(req);
|
|
|
|
|
|
|
|
Box::pin(async move {
|
2020-12-16 02:40:41 +00:00
|
|
|
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(_) => {
|
2020-12-16 04:14:01 +00:00
|
|
|
log::warn!("Error fetching user data from store");
|
2020-12-16 02:40:41 +00:00
|
|
|
return Err(ServerError(state.pages.internal_server_error_path()).into());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(user) = user_opt {
|
2021-01-14 04:41:20 +00:00
|
|
|
if !user.suspended() {
|
|
|
|
*auth_state.0.borrow_mut() = Some(Authenticated { user });
|
|
|
|
}
|
2020-12-16 02:40:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 04:14:01 +00:00
|
|
|
drop(drop_guard);
|
|
|
|
|
|
|
|
fut.await
|
2020-12-16 02:40:41 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|