use std::sync::Arc; use casbin::CoreApi; use axum::{ extract::{Request,ConnectInfo}, http::{ StatusCode, }, Extension, response::{IntoResponse,Response,Redirect}, middleware::Next, }; use tower_cookies::{Cookie, Cookies}; use crate::{ USER_AGENT, SESSION_COOKIE_NAME, defs::{ AppDBs, ServPath, AuthState, SessionStoreDB, TraceData, TraceContent, ReqHeaderMap, AppConnectInfo, }, }; /* // OLD get_cookie from Request pub fn get_cookie(req: &Request) -> Option { req .headers() .get_all("Cookie") .iter() .filter_map(|cookie| { cookie .to_str() .ok() .and_then(|cookie| cookie.parse::().ok()) }) .find_map(|cookie| { (cookie.name() == SESSION_COOKIE_NAME).then(move || cookie.value().to_owned()) }) .and_then(|cookie_value| cookie_value.parse::().ok()) } */ pub async fn add_session_cookie(make: bool, cookies: &Cookies, session_token: &str, user_data: &str, expire: u64, app_dbs: &AppDBs, cookie_path: &str) -> String { if make { cookies.remove(Cookie::new(SESSION_COOKIE_NAME, "")); } let result_store = SessionStoreDB::store_session_data(&session_token,&user_data, expire, &app_dbs).await; if result_store.is_empty() { eprintln!("Unable to store session {}", &app_dbs.config.session_store_uri); } else { let cookie = Cookie::build(SESSION_COOKIE_NAME, result_store.to_owned()) // .domain(domain) .path(format!("{}",cookie_path)) .secure(true) .http_only(true) .finish(); if make { cookies.add(cookie); } } result_store } pub async fn get_auth_state(update: bool, cookies: &Cookies, app_dbs: &AppDBs) -> AuthState { if let Some(s_cookie) = cookies.get(SESSION_COOKIE_NAME) { let session_cookie = s_cookie.to_string().replace(&format!("{}=",SESSION_COOKIE_NAME),""); let mut auth_state = AuthState::from_cookie(session_cookie.to_string(), app_dbs).await; if update { let _ = auth_state.expire_in(app_dbs.config.session_expire, &app_dbs).await; } auth_state } else { // eprintln!("get_auth_state: No SESSION COOKIE found "); AuthState::default() } } pub fn trace_req(uri_path: &str, user_id: String, sid: String, info: String, context: String, role: String, req_header: ReqHeaderMap, app_dbs: &AppDBs) -> std::io::Result<()> { let timestamp = chrono::Utc::now().timestamp().to_string(); let trace_content = TraceContent{ when: timestamp.to_owned(), sid, origin: uri_path.to_owned(), trigger: String::from("req_handler"), id: user_id.to_owned(), info: info.to_owned(), context, role, req: req_header.req_info(), }; let trace_data = TraceData{ user_id, timestamp, contents: vec![trace_content] }; trace_data.save(&app_dbs.config,false) } pub async fn rewrite_request_uri( Extension(app_dbs): Extension>, Extension(cookies): Extension, ConnectInfo(app_connect_info): ConnectInfo, req: Request, next: Next, ) -> Result { // TODO Trace acccess to log or user session file !!! let auth_state = get_auth_state(true, &cookies, &app_dbs).await; let uri_path = req.uri().path().to_owned(); if uri_path == "/" { return Ok(next.run(req).await); } // For long path is better than: // let arr_root_path: Vec = uri_path.split("/").map(|s| s.to_string()).collect(); // let root_path = arr_root_path[1].to_owned(); let mut root_path = String::from("/"); for it in uri_path.split("/") { if ! it.is_empty() { root_path = format!("/{}",it.to_owned()); break; } } let serv_paths: Vec = app_dbs.config.serv_paths.clone().into_iter().filter( |it| it.is_restricted && it.url_path == root_path ).collect(); // Only on First one if serv_paths.len() > 0 { let serv_path = serv_paths[0].to_owned(); let name = auth_state.user_name(); if name.is_empty() { let uri_path = req.uri().path().to_string(); if uri_path.ends_with(".html") { eprintln!("rewrite_request_uri: No user found in session for {}", &uri_path); let new_uri = format!("{}?o={}",&serv_path.not_auth.as_str(),req.uri().path().to_string()); let _ = trace_req(&uri_path, auth_state.user_id(),auth_state.id(), format!("user no name found in session"), String::from("rewrite_request_uri"), auth_state.user_roles(), ReqHeaderMap::new(req.headers().to_owned(), &uri_path, &app_connect_info), &app_dbs); return Err( Redirect::temporary( &new_uri).into_response() ); } else { return Ok(next.run(req).await); } } let arr_roles: Vec = auth_state.user_roles().split(",").map(|s| s.replace(" ", "").to_string()).collect(); let req_method = req.method().to_string(); let target_path = serv_path.url_path.to_owned(); let enforcer = app_dbs.enforcer.clone(); for role in arr_roles { let mut lock = enforcer.write().await; let result = lock.enforce_mut( vec![role.to_owned(),target_path.to_owned(), req_method.to_owned()] ).unwrap_or_else(|e|{ println!("Error enforce: {}",e); false }); drop(lock); if result { if uri_path.ends_with(".html") || app_dbs.config.trace_level > 1 { let _ = trace_req(&uri_path,auth_state.user_id(),auth_state.id(), format!("user in session with role {}",role), String::from("rewrite_request_uri"), auth_state.user_roles(), ReqHeaderMap::new(req.headers().to_owned(), &uri_path, &app_connect_info), &app_dbs); } return Ok(next.run(req).await); } } // try with email let mut lock = enforcer.write().await; let result = lock.enforce_mut( vec![ name, target_path.to_owned(), req_method.to_owned() ] ).unwrap_or_else(|e|{ println!("Error enforce: {}",e); false }); drop(lock); if result { return Ok(next.run(req).await); } let new_uri = format!("{}",serv_path.not_auth); let agent = if let Some(user_agent) = req.headers().get(USER_AGENT) { user_agent.to_str().unwrap_or("").to_owned() } else { String::from("") }; if uri_path.ends_with(".html") || app_dbs.config.trace_level > 1 { let _ = trace_req(&uri_path,auth_state.user_id(),auth_state.id(), format!("user found in session"), String::from("rewrite_request_uri"), auth_state.user_roles(), ReqHeaderMap::new(req.headers().to_owned(), &uri_path, &app_connect_info), &app_dbs); } if agent.contains("curl") { return Ok( format!("Got to {}",&new_uri).into_response() ); } else { return Err( Redirect::temporary(&new_uri).into_response() ); } } if uri_path.ends_with(".html") || app_dbs.config.trace_level > 1 { let _ = trace_req(&uri_path,auth_state.user_id(),auth_state.id(), format!("user in session"), String::from("rewrite_request_uri"), auth_state.user_roles(), ReqHeaderMap::new(req.headers().to_owned(), &uri_path, &app_connect_info), &app_dbs); } Ok(next.run(req).await) } pub async fn handle_404(_req: Request) -> (StatusCode, &'static str) { (StatusCode::NOT_FOUND, "Not found") }