Files
aviation/api/src/account/auth.rs
2025-09-19 19:33:53 -04:00

107 lines
3.1 KiB
Rust

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<String>,
pub api_key: Option<String>,
pub user: User,
}
impl FromRequest for Auth {
type Error = ActixError;
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let state = match req.app_data::<web::Data<AppState>>() {
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<State>
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)
}
}