tallyweb_frontend/
middleware.rs1use std::future::{ready, Ready};
2
3use actix_web::{
4 dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
5 http::header,
6 Error, HttpResponse,
7};
8use futures_util::future::LocalBoxFuture;
9
10use super::UserSession;
11
12pub struct CheckSession;
17
18impl<S, B> Transform<S, ServiceRequest> for CheckSession
22where
23 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
24 S::Future: 'static,
25 B: 'static,
26{
27 type Response = ServiceResponse<B>;
28 type Error = Error;
29 type InitError = ();
30 type Transform = CheckSessionMW<S>;
31 type Future = Ready<Result<Self::Transform, Self::InitError>>;
32
33 fn new_transform(&self, service: S) -> Self::Future {
34 ready(Ok(CheckSessionMW { service }))
35 }
36}
37
38pub struct CheckSessionMW<S> {
39 service: S,
40}
41
42impl<S, B> CheckSessionMW<S>
43where
44 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
45 S::Future: 'static,
46 B: 'static,
47{
48}
49
50impl<S, B> Service<ServiceRequest> for CheckSessionMW<S>
51where
52 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
53 S::Future: 'static,
54 B: 'static,
55{
56 type Response = ServiceResponse<B>;
57 type Error = Error;
58 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
59
60 forward_ready!(service);
61
62 fn call(&self, req: ServiceRequest) -> Self::Future {
63 let get_session = |req: &ServiceRequest| {
64 let cookie = req
65 .cookie("session")
66 .ok_or(actix_web::error::ErrorUnauthorized(
67 "Missing `session` cookie",
68 ))?
69 .value()
70 .to_string();
71
72 let pool = req
73 .app_data::<actix_web::web::Data<backend::PgPool>>()
74 .ok_or(actix_web::error::ErrorInternalServerError(
75 "Missing DB pool",
76 ))?;
77
78 let session: UserSession = serde_json::from_str(&cookie)
79 .map_err(|err| actix_web::error::ErrorBadRequest(err))?;
80
81 Ok((session.clone(), pool.clone().into_inner().clone()))
82 };
83
84 let (session, pool) = match get_session(&req) {
85 Ok(res) => res,
86 Err(err) => return Box::pin(async { Err(err) }),
87 };
88
89 let fut = self.service.call(req);
90
91 Box::pin(async move {
92 match backend::auth::check_user(&pool, &session.username, session.token).await {
93 Ok(backend::auth::SessionState::Valid) => fut.await,
94 Ok(backend::auth::SessionState::Expired) => {
95 let (req, resp) = fut.await?.into_parts();
96 let resp = HttpResponse::Ok()
97 .insert_header(("serverfnredirect", "/login"))
98 .insert_header((header::LOCATION, "/login"))
99 .message_body(resp.into_body())?;
100 Ok(ServiceResponse::new(req, resp))
101 }
102 Err(err) => Err(actix_web::error::ErrorUnauthorized(err)),
103 }
104 })
105 }
106}