Guest User

Untitled

a guest
Dec 15th, 2025
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.95 KB | None | 0 0
  1. use actix_web::{
  2. dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
  3. Error, HttpMessage,
  4. };
  5. use futures_util::future::LocalBoxFuture;
  6. use std::{
  7. future::{ready, Ready},
  8. rc::Rc,
  9. };
  10.  
  11. use crate::errors::AppError;
  12. use crate::models::Account;
  13.  
  14. pub struct Auth;
  15.  
  16. impl<S, B> Transform<S, ServiceRequest> for Auth
  17. where
  18. S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
  19. S::Future: 'static,
  20. B: 'static,
  21. {
  22. type Response = ServiceResponse<B>;
  23. type Error = Error;
  24. type Transform = AuthMiddleware<S>;
  25. type InitError = ();
  26. type Future = Ready<Result<Self::Transform, Self::InitError>>;
  27.  
  28. fn new_transform(&self, service: S) -> Self::Future {
  29. ready(Ok(AuthMiddleware {
  30. service: Rc::new(service),
  31. }))
  32. }
  33. }
  34.  
  35. pub struct AuthMiddleware<S> {
  36. service: Rc<S>,
  37. }
  38.  
  39. impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
  40. where
  41. S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
  42. S::Future: 'static,
  43. B: 'static,
  44. {
  45. type Response = ServiceResponse<B>;
  46. type Error = Error;
  47. type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
  48.  
  49. forward_ready!(service);
  50.  
  51. fn call(&self, req: ServiceRequest) -> Self::Future {
  52. let auth_header = req.headers().get("Authorization");
  53. let auth_header = if let Some(auth_header) = auth_header {
  54. auth_header.to_str().unwrap_or("")
  55. } else {
  56. return Box::pin(async move {
  57. Err(Error::from(AppError::Unauthorized(
  58. "Missing Authorization header".to_string(),
  59. )))
  60. });
  61. };
  62.  
  63. let parts: Vec<&str> = auth_header.split_whitespace().collect();
  64. if parts.len() != 2 || parts[0] != "Bearer" {
  65. return Box::pin(async move {
  66. Err(Error::from(AppError::Unauthorized(
  67. "Invalid Authorization header format".to_string(),
  68. )))
  69. });
  70. }
  71.  
  72. let token = parts[1];
  73. let account_id = match token.parse::<i32>() {
  74. Ok(id) => id,
  75. Err(_) => {
  76. return Box::pin(async move {
  77. Err(Error::from(AppError::Unauthorized(
  78. "Invalid token format".to_string(),
  79. )))
  80. });
  81. }
  82. };
  83.  
  84. let pool = req
  85. .app_data::<actix_web::web::Data<deadpool_postgres::Pool>>()
  86. .expect("Pool not found")
  87. .get_ref()
  88. .clone();
  89.  
  90. let (req, payload) = req.into_parts();
  91.  
  92. let service = Rc::clone(&self.service);
  93.  
  94. Box::pin(async move {
  95. let account = Account::find_by_id(&pool, account_id).await?;
  96. let account = match account {
  97. Some(account) => account,
  98. None => {
  99. return Err(Error::from(AppError::Unauthorized(
  100. "Account not found".to_string(),
  101. )));
  102. }
  103. };
  104.  
  105. if account.expires_at < chrono::Utc::now() {
  106. return Err(Error::from(AppError::Unauthorized(
  107. "Account expired".to_string(),
  108. )));
  109. }
  110.  
  111. let req = ServiceRequest::from_parts(req, payload);
  112. req.extensions_mut().insert(account);
  113.  
  114. service.call(req).await
  115. })
  116. }
  117. }
  118.  
  119. #[cfg(test)]
  120. mod tests {
  121. use super::*;
  122. use actix_web::{test, web, App, Responder};
  123.  
  124. async fn test_handler(req: ServiceRequest) -> impl Responder {
  125. let account = req.extensions().get::<Account>().unwrap();
  126. HttpResponse::Ok().json(account)
  127. }
  128.  
  129. #[actix_web::test]
  130. async fn test_auth_middleware() {
  131. let pool = deadpool_postgres::Pool::new(
  132. deadpool_postgres::Config {
  133. host: Some("localhost".to_string()),
  134. port: Some(5432),
  135. user: Some("postgres".to_string()),
  136. password: Some("postgres".to_string()),
  137. dbname: Some("test".to_string()),
  138. ..Default::default()
  139. },
  140. tokio_postgres::NoTls,
  141. )
  142. .unwrap();
  143.  
  144. let app = test::init_service(
  145. App::new()
  146. .app_data(web::Data::new(pool.clone()))
  147. .wrap(Auth)
  148. .service(test_handler),
  149. )
  150. .await;
  151.  
  152. let req = test::TestRequest::get()
  153. .header("Authorization", "Bearer 1")
  154. .to_request();
  155. let resp = test::call_service(&app, req).await;
  156. assert_eq!(resp.status(), 200);
  157.  
  158. let req = test::TestRequest::get()
  159. .header("Authorization", "Invalid")
  160. .to_request();
  161. let resp = test::call_service(&app, req).await;
  162. assert_eq!(resp.status(), 401);
  163. }
  164. }
Advertisement
Add Comment
Please, Sign In to add comment