use std::future::Future; use std::pin::Pin; use std::sync::Arc; use super::{SESSION_COOKIE_NAME, Session}; use crate::account::user::User; use crate::error::Error; use actix_web::{Error as ActixError, FromRequest, HttpRequest, dev::Payload, http, web}; use serde::{Deserialize, Serialize}; use crate::state::AppState; #[derive(Debug, Serialize, Deserialize)] pub struct Auth { pub session_id: Option, pub api_key: Option, pub user: User, } impl FromRequest for Auth { type Error = ActixError; type Future = Pin>>>; fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { let state = match req.app_data::>() { Some(state) => state, None => return Box::pin( async { Err(Error::new(500, "Internal server error".to_string()).into()) }, ) }; // Check for an API key match req .headers() .get(http::header::AUTHORIZATION) .map(|h| h.to_str().unwrap().split_at(7).1.to_string()) { Some(key_id) => { let state = Arc::clone(&state); let fut = async move { // Check if the Session API key exists let api_key = match Session::get(&state, &key_id).await { Ok(session) => session, Err(err) => { log::error!("Invalid session auth attempt: {}", err); return Err(Error::new(401, "API Key does not exist".to_string()).into()); } }; match User::select(&state.pool, &api_key.username).await { Some(user) => Ok(Auth { session_id: None, api_key: Some(key_id), user, }), None => Err(Error::new(404, format!("User {} not found", api_key.username)).into()), } }; return Box::pin(fut); } None => {} }; // Check for session let session_id = match req .cookie(SESSION_COOKIE_NAME) .map(|c| c.value().to_string()) .or_else(|| { req .headers() .get(http::header::AUTHORIZATION) .map(|h| h.to_str().unwrap().split_at(7).1.to_string()) }) { Some(id) => id, None => { let fut = async { Err( Error { status: 401, details: "No session ID found in the request".to_string(), } .into(), ) }; return Box::pin(fut); } }; // Get IP address from request let ip_address = req.peer_addr().unwrap().ip().to_string(); // Verify the session let state = Arc::clone(&state); // state: Arc let fut = async move { match Session::verify(&state, &session_id, &ip_address).await { Ok(session) => match User::select(&state.pool, &session.username).await { Some(user) => Ok(Auth { session_id: Some(session_id), api_key: None, user, }), None => Err(Error::new(404, format!("User {} not found", session.username)).into()), }, Err(err) => Err(err.into()), } }; Box::pin(fut) } }