Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use actix_web::{
- dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
- Error, HttpMessage,
- };
- use futures_util::future::LocalBoxFuture;
- use std::{
- future::{ready, Ready},
- rc::Rc,
- };
- use crate::errors::AppError;
- use crate::models::Account;
- pub struct Auth;
- impl<S, B> Transform<S, ServiceRequest> for Auth
- where
- S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
- S::Future: 'static,
- B: 'static,
- {
- type Response = ServiceResponse<B>;
- type Error = Error;
- type Transform = AuthMiddleware<S>;
- type InitError = ();
- type Future = Ready<Result<Self::Transform, Self::InitError>>;
- fn new_transform(&self, service: S) -> Self::Future {
- ready(Ok(AuthMiddleware {
- service: Rc::new(service),
- }))
- }
- }
- pub struct AuthMiddleware<S> {
- service: Rc<S>,
- }
- impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
- where
- S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
- S::Future: 'static,
- B: 'static,
- {
- type Response = ServiceResponse<B>;
- type Error = Error;
- type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
- forward_ready!(service);
- fn call(&self, req: ServiceRequest) -> Self::Future {
- let auth_header = req.headers().get("Authorization");
- let auth_header = if let Some(auth_header) = auth_header {
- auth_header.to_str().unwrap_or("")
- } else {
- return Box::pin(async move {
- Err(Error::from(AppError::Unauthorized(
- "Missing Authorization header".to_string(),
- )))
- });
- };
- let parts: Vec<&str> = auth_header.split_whitespace().collect();
- if parts.len() != 2 || parts[0] != "Bearer" {
- return Box::pin(async move {
- Err(Error::from(AppError::Unauthorized(
- "Invalid Authorization header format".to_string(),
- )))
- });
- }
- let token = parts[1];
- let account_id = match token.parse::<i32>() {
- Ok(id) => id,
- Err(_) => {
- return Box::pin(async move {
- Err(Error::from(AppError::Unauthorized(
- "Invalid token format".to_string(),
- )))
- });
- }
- };
- let pool = req
- .app_data::<actix_web::web::Data<deadpool_postgres::Pool>>()
- .expect("Pool not found")
- .get_ref()
- .clone();
- let (req, payload) = req.into_parts();
- let service = Rc::clone(&self.service);
- Box::pin(async move {
- let account = Account::find_by_id(&pool, account_id).await?;
- let account = match account {
- Some(account) => account,
- None => {
- return Err(Error::from(AppError::Unauthorized(
- "Account not found".to_string(),
- )));
- }
- };
- if account.expires_at < chrono::Utc::now() {
- return Err(Error::from(AppError::Unauthorized(
- "Account expired".to_string(),
- )));
- }
- let req = ServiceRequest::from_parts(req, payload);
- req.extensions_mut().insert(account);
- service.call(req).await
- })
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- use actix_web::{test, web, App, Responder};
- async fn test_handler(req: ServiceRequest) -> impl Responder {
- let account = req.extensions().get::<Account>().unwrap();
- HttpResponse::Ok().json(account)
- }
- #[actix_web::test]
- async fn test_auth_middleware() {
- let pool = deadpool_postgres::Pool::new(
- deadpool_postgres::Config {
- host: Some("localhost".to_string()),
- port: Some(5432),
- user: Some("postgres".to_string()),
- password: Some("postgres".to_string()),
- dbname: Some("test".to_string()),
- ..Default::default()
- },
- tokio_postgres::NoTls,
- )
- .unwrap();
- let app = test::init_service(
- App::new()
- .app_data(web::Data::new(pool.clone()))
- .wrap(Auth)
- .service(test_handler),
- )
- .await;
- let req = test::TestRequest::get()
- .header("Authorization", "Bearer 1")
- .to_request();
- let resp = test::call_service(&app, req).await;
- assert_eq!(resp.status(), 200);
- let req = test::TestRequest::get()
- .header("Authorization", "Invalid")
- .to_request();
- let resp = test::call_service(&app, req).await;
- assert_eq!(resp.status(), 401);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment