use std::path::Path; use super::{AppError, UserData}; use axum::{body::Body, extract::State, http::Request, middleware::Next, response::IntoResponse}; use axum_extra::TypedHeader; use chrono::Utc; use headers::Cookie; use sqlx::PgPool; pub async fn inject_user_data( State(db_pool): State, cookie: Option>, mut request: Request, next: Next, ) -> Result { if let Some(cookie) = cookie { if let Some(session_token) = cookie.get("session_token") { let session_token: Vec<&str> = session_token.split('_').collect(); let query: Result<(uuid::Uuid, chrono::NaiveDateTime, String), _> = sqlx::query_as( r#"SELECT user_id,expires_at,session_token_p2 FROM user_sessions WHERE session_token_p1=$1"#, ) .bind(session_token[0]) .fetch_one(&db_pool) .await; if let Ok(query) = query { if let Ok(session_token_p2_db) = query.2.as_bytes().try_into() { if let Ok(session_token_p2_cookie) = session_token .get(1) .copied() .unwrap_or_default() .as_bytes() .try_into() { if constant_time_eq::constant_time_eq_n::<36>( session_token_p2_cookie, session_token_p2_db, ) { let id = query.0; let expires_at = query.1; if expires_at > Utc::now().naive_local() { let row: UserData = sqlx::query_as( r#"SELECT id, created_at, created_by, updated_at, updated_by, email, name, family_name, given_name FROM users WHERE id = $1"#, ) .bind(id) .fetch_one(&db_pool) .await .unwrap(); request.extensions_mut().insert(Some(UserData { id: row.id, created_at: row.created_at, created_by: row.created_by, updated_at: row.updated_at, updated_by: row.updated_by, email: row.email, name: row.name, family_name: row.family_name, given_name: row.given_name, })); } } } } } } } Ok(next.run(request).await) } pub async fn is_authorized(path: &str, user_data: Option, db_pool: PgPool) -> bool { if let Some(user_data) = user_data { let query: Result<(uuid::Uuid,), _> = match path { "/profile" => { return true; } _ => { // loop through path to find a permission let mut remaining_path = Path::new(path); loop { let query: Result<(String,), _> = sqlx::query_as(r#"select r.item from role_permissions r where item = $1"#) .bind(remaining_path.to_str().unwrap()) .fetch_one(&db_pool) .await; if let Ok(query) = query { if query.0 != "" { break; } } if remaining_path.parent().is_none() { break; } remaining_path = remaining_path.parent().unwrap(); } sqlx::query_as(r#"select u.id from role_permissions r join user_roles ur on ur.role_id = r.role_id join users u on u.id = ur.user_id where item = $1 and email = $2"#) .bind(remaining_path.to_str().unwrap()) .bind(user_data.email.as_str()) .fetch_one(&db_pool) .await } }; if let Ok(query) = query { if query.0 == user_data.id { return true; } } } return false; }