use std::sync::Arc; use axum::{ http::{ StatusCode, Uri, header::HeaderMap, }, Json, routing::{get,post}, Extension, extract::ConnectInfo, response::{IntoResponse,Response,Redirect}, Router, }; use tower_cookies::Cookies; use crate::{ route, defs::{ AppDBs, ReqHandler, ReqHeaderMap, TotpMode, Random, AppConnectInfo, }, users::{ User, UserData, UserItem, OpenidUser, }, login_password::generate_hash, handlers::{ add_session_cookie, get_auth_state, }, }; pub fn users_settings_router_handlers() -> Router { // async fn edit_user_handler( // header: HeaderMap, // uri: Uri, // Extension(app_dbs): Extension>, // Extension(cookies): Extension, // //_auth_state: AuthState, // ) -> Response { // let auth_state = get_auth_state(true, &cookies, &app_dbs).await; // let mut req_handler = ReqHandler::new( // ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string())), // &app_dbs, // &uri, // &auth_state, // "root_handler" // ); // let uri_path = format!("{}",&uri.path().to_string()); // let file = "hello.html"; // let result = app_dbs.tera.render(&file, &app_dbs.context).unwrap_or_else(|e|{ // println!("Error render {}: {}",&file,e); // String::from("") // }); // req_handler.context.insert("with_menu", "1"); // // let mut new_header = header.to_owned(); // //new_header.append("Set-Cookie", "session_token=_; Max-Age=0".parse().unwrap()); // // cookies.remove(Cookie::new(SESSION_COOKIE_NAME, "")); // ( // req_handler.req_header.header, // result.to_owned() // ).into_response() // // "Hello, World!" // } async fn user_settings_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, ) -> Response { let auth_state = get_auth_state(true, &cookies, &app_dbs).await; if auth_state.session.is_none() { return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); } let mut req_handler = ReqHandler::new( ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), &app_dbs, &uri, &auth_state, &random, "user_settings_handler" ); let mut res_headers = HeaderMap::new(); if req_handler.req_header.is_browser() { res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap()); } let user_id = auth_state.user_id(); if user_id.is_empty() { let _ = req_handler.trace_req(format!("user not id found")); // User not exists return Redirect::temporary( &format!("/")).into_response(); } let user_sel = User::select("id", &user_id, true, &app_dbs.user_store).await.unwrap_or_default(); if user_sel.name.is_empty() { let _ = req_handler.trace_req(format!("Edit user id '{}' not found ",&user_id)); // User not exists return Redirect::temporary( &format!("/")).into_response(); } let openid_sel = OpenidUser::list_selection("userid", &user_sel.id.to_string(), &app_dbs.user_store, true,false, "|").await.unwrap_or_else(|e| { println!("Error list selection {}: {}", &user_sel.name, e); Vec::new() }); req_handler.context.insert("openid_sel", &openid_sel); let openid_sel_appkeys = openid_sel.iter().map(|id| id.appkey.to_string()).collect::>().join(","); req_handler.context.insert("openid_sel_appkeys", &openid_sel_appkeys); req_handler.context.insert("with_menu", "1"); req_handler.context.insert("user", &user_sel); req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields); req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode)); // let user_items = User::hash_items(&user_sel.items); // req_handler.context.insert("usr_items", &user_items); req_handler.context.insert("no_edit", "true"); req_handler.context.insert("edit_target", "main"); let result = if let Some(tpl) = app_dbs.config.tpls.get("user_settings") { req_handler.render_template(&tpl,"user setting") } else { String::from("user settings") }; let _ = req_handler.trace_req(format!("User '{}' settings",&user_sel.id)); ( res_headers, result.to_owned() ).into_response() } async fn user_settings_edit_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, axum::extract::Path(data): axum::extract::Path, ) -> Response { let auth_state = get_auth_state(true, &cookies, &app_dbs).await; if auth_state.session.is_none() { return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); } let mut req_handler = ReqHandler::new( ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), &app_dbs, &uri, &auth_state, &random, "user_settings_edit_handler" ); let mut res_headers = HeaderMap::new(); if req_handler.req_header.is_browser() { res_headers.append(axum::http::header::CONTENT_TYPE,"text/html; charset=utf-8".parse().unwrap()); } if data != "main" && data != "password" && data != "totp" { let _ = req_handler.trace_req(format!("Edit user not data section '{}' ",&data)); return Redirect::temporary( &format!("/")).into_response(); } let user_id = auth_state.user_id(); if user_id.is_empty() { // User not exists let _ = req_handler.trace_req(format!("Edit user not id ")); return Redirect::temporary( &format!("/")).into_response(); } let user_sel = User::select("id", &user_id, true, &app_dbs.user_store).await.unwrap_or_default(); if user_sel.name.is_empty() { let _ = req_handler.trace_req(format!("Edit user id '{}' not found ",&user_id)); // User not exists return Redirect::temporary( &format!("/")).into_response(); } let openid_sel = OpenidUser::list_selection("userid", &user_sel.id.to_string(), &app_dbs.user_store, true,false, "|").await.unwrap_or_else(|e| { println!("Error list selection {}: {}", &user_sel.name, e); Vec::new() }); req_handler.context.insert("openid_sel", &openid_sel); let openid_sel_appkeys = openid_sel.iter().map(|id| id.appkey.to_string()).collect::>().join(","); req_handler.context.insert("openid_sel_appkeys", &openid_sel_appkeys); req_handler.context.insert("with_menu", "1"); req_handler.context.insert("user", &user_sel); req_handler.context.insert("edit_target", &data); req_handler.context.insert("admin_fields", &app_dbs.config.admin_fields); req_handler.context.insert("totp_mode", &format!("{}",&app_dbs.config.totp_mode)); if data == "totp" && app_dbs.config.totp_mode != TotpMode::No { if !user_sel.otp_base32.is_empty() { match req_handler.otp_make(&user_sel.otp_base32, &user_sel.otp_defs) { Ok(totp) => { req_handler.context.insert("otp_code", &user_sel.otp_base32); req_handler.context.insert("otp_url", &user_sel.otp_auth_url); req_handler.context.insert("otp_qr", &totp.get_qr_base64().unwrap_or_default()); }, Err(e) => { println!("User settings error totp: {}",e); } } } else { match req_handler.otp_generate() { Ok(totp) => { req_handler.context.insert("otp_code", &totp.get_secret_base32()); req_handler.context.insert("otp_url", &totp.get_url()); req_handler.context.insert("otp_qr", &totp.get_qr_base64().unwrap_or_default()); }, Err(e) => { println!("Error TOTP generartor: {}",e); } } } req_handler.context.insert("totp_digits", &app_dbs.config.totp_digits); req_handler.context.insert("totp_algorithm",&format!("{}",&app_dbs.config.totp_algorithm)); } let _ = req_handler.trace_req(format!("Edit user '{}' settings",&user_id)); // req_handler.context.insert("no_edit", "true"); let result = if let Some(tpl) = app_dbs.config.tpls.get("user_settings") { req_handler.render_template(&tpl,"user setting") } else { String::from("user settings") }; ( res_headers, result.to_owned() ).into_response() } async fn post_user_settings_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, Json(user_data): Json, ) -> Response { let auth_state = get_auth_state(true, &cookies, &app_dbs).await; let req_handler = ReqHandler::new( ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), &app_dbs, &uri, &auth_state, &random, "post_user_settings_handler" ); if auth_state.session.is_none() { let _ = req_handler.trace_req(format!("No session found for user '{}'",&user_data.id)); return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); } let user_id = auth_state.user_id(); if user_id.is_empty() { let _ = req_handler.trace_req(format!("No user found")); return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error" ).into_response(); } let mut user_sel = User::select("id", &user_id, false,&app_dbs.user_store).await.unwrap_or_default(); if user_sel.name.is_empty() { let _ = req_handler.trace_req(format!("User '{}' not found",&user_id)); return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error" ).into_response(); } if !user_data.password.is_empty() { let passwd_score = User::password_score(&user_data.password); if passwd_score < app_dbs.config.password_score { let _ = req_handler.trace_req(format!("User '{}' password '{}' score: {} under {}" ,&user_id,&user_data.password, passwd_score,app_dbs.config.password_score )); return ( StatusCode::UNAUTHORIZED, req_handler.req_header.header, "Error" ).into_response(); } user_sel.password = generate_hash(&user_data.password); } //let openid_sel = OpenidUser::list_selection("userid", &user_sel.id.to_string(), &app_dbs.user_store, true,false, "|").await.unwrap_or_else(|e| { // println!("Error list selection {}: {}", &user_sel.name, e); // Vec::new() //}); //let openid_sel_appkeys = openid_sel.iter().map(|id| id.appkey.to_string()).collect::>().join(","); let otp_enabled: bool; if app_dbs.config.totp_mode != TotpMode::No && !user_data.otp_auth.is_empty() { match req_handler.otp_check(&user_data.otp_code,&user_data.otp_auth, "") { Ok(val) => { if val { otp_enabled = true; } else { // otp_enabled = fasle; let _ = req_handler.trace_req(format!("User '{}' not valid TOTP code",&user_id)); return ( StatusCode::UNAUTHORIZED, req_handler.req_header.header, "Error" ).into_response(); } }, Err(e) => { println!("TOTP check: {}", e); let _ = req_handler.trace_req(format!("User '{}' TOTP check error: {}",&user_id,e)); return ( StatusCode::UNAUTHORIZED, req_handler.req_header.header, "Error" ).into_response(); } } } else { otp_enabled = false; } if otp_enabled { user_sel.otp_enabled= true; user_sel.otp_verified= true; user_sel.otp_base32 = user_data.otp_code.to_owned(); user_sel.otp_auth_url = user_data.otp_url.to_owned(); user_sel.otp_defs = format!("{},{}", &app_dbs.config.totp_digits, format!("{}",&app_dbs.config.totp_algorithm), ); } else { user_sel.otp_enabled= false; user_sel.otp_verified = false; user_sel.otp_base32 = String::from(""); user_sel.otp_auth_url = String::from(""); user_sel.otp_defs = String::from(""); } let user_openids = user_data.opendis.to_owned(); user_sel.from_data(user_data); let new_id= user_sel.id.to_string(); let user_data = user_sel.session_data(); match user_sel.update(&app_dbs.user_store).await { Ok(_) => { let session_token = auth_state.id(); let session_cookie = add_session_cookie(true,&cookies, &session_token, &user_data, 0, &app_dbs, "/").await; if app_dbs.config.verbose > 1 { println!("session cookie: {}", &session_cookie) }; let _ = req_handler.trace_req(format!("User '{}' updated",&user_id)); OpenidUser::sync_ids(&new_id, &user_openids, &app_dbs.user_store).await; let result =String::from("OK"); ( req_handler.req_header.header, result.to_owned() ).into_response() }, Err(e) => { let _ = req_handler.trace_req(format!("User '{}' update error: {}",&user_id,e)); ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error" ).into_response() } } } async fn update_user_item_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, Json(_user_item): Json, //_auth_state: AuthState, //axum::extract::Path(data): axum::extract::Path, ) -> Response { let auth_state = get_auth_state(true, &cookies, &app_dbs).await; let req_handler = ReqHandler::new( ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), &app_dbs, &uri, &auth_state, &random, "update_user_item_handler" ); if ! auth_state.has_auth_role(&app_dbs.config.auth_roles) { let _ = req_handler.trace_req(format!("User '{}' not have role 'dev'",&auth_state.user_id())); return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); // return ( // StatusCode::UNAUTHORIZED, // header, // "Error authorization" // ).into_response(); } let result=""; ( //status, req_handler.req_header.header, result, ).into_response() } route( "/settings", get(user_settings_handler)) .route("/settings/:item", get(user_settings_edit_handler)) // .route("/update", post(update_user_handler)) .route("/settings", post(post_user_settings_handler)) .route("/update_item", post(update_user_item_handler)) }