Newer
Older
use actix_web::{
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
Error,
};
use futures::prelude::future::LocalBoxFuture;
use std::future::{ready, Ready};
pub struct AuthData;
impl<S, B> Transform<S, ServiceRequest> for AuthData
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = AuthenticatedMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(AuthenticatedMiddleware { service }))
}
}
pub struct AuthenticatedMiddleware<S> {
service: S,
}
#[derive(serde::Deserialize)]
struct Credentials {
id: String,
password: String,
}
async fn auth(req: &mut ServiceRequest) -> Result<(), Box<dyn actix_web::ResponseError>> {
let cookie = req.cookie("auth");
match cookie {
Some(cookie) => Ok(()),
None => match req.extract::<actix_web::web::Form<Credentials>>().await {
Ok(credentials) => Ok(()),
Err(_) => Err(Box::new(actix_web::ResponseError::status_code(
actix_web::http::StatusCode::UNAUTHORIZED,
))),
},
}
}
impl<S, B> Service<ServiceRequest> for AuthenticatedMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<actix_web::body::EitherBody<B>>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
Box::pin(async move {
let credentials = req.extract::<actix_web::web::Form<Credentials>>().await;
let authenticated = auth(&mut req).await;
if let Err(msg) = authenticated {
return Ok(req.error_response(Error::from(msg)).map_into_right_body());
}
self.service
.call(req)
.await
.map(|res| res.map_into_left_body())