use std::sync::Arc; use urlencoding::{encode,decode}; 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, AuthState, ReqHandler, ReqHeaderMap, MailMessage, TotpMode, Random, AppConnectInfo, }, users::{ User, UserData, UserItem, UserStatus, }, login_password::generate_hash, handlers::{ add_session_cookie, get_auth_state, }, }; pub fn users_password_router_handlers() -> Router { async fn post_reset_password_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, Json(user_item): Json, ) -> 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_connect_info), &app_dbs, &uri, &auth_state, &random, "post_reset_password_handler" ); if ! app_dbs.config.use_mail { let _ = req_handler.trace_req(format!("Mail disabled in config, user '{}' password can not be reset",&user_item.name)); return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error no service" ).into_response(); } if user_item.name.is_empty() { let _ = req_handler.trace_req(format!("No user name")); return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error" ).into_response(); } let field = if user_item.name.contains("@") { "email" } else { "name" }; let mut user_sel = User::select(&field, &user_item.name, false, &app_dbs.user_store).await.unwrap_or_else(|e|{ println!("Error select: {}", e); User::default() }); if user_sel.name.is_empty() { let _ = req_handler.trace_req(format!("User '{}' = '{}' not found",&field,&user_item.name)); // User not exists return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error data" ).into_response(); } if user_sel.status != UserStatus::Active && user_sel.status != UserStatus::Created { let _ = req_handler.trace_req(format!("user '{}' in not valid status: {}",&user_item.name, &user_sel.status)); return ( StatusCode::UNAUTHORIZED, req_handler.req_header.header, "Error status" ).into_response(); } if app_dbs.config.totp_mode != TotpMode::No { if user_sel.otp_base32.is_empty() && (app_dbs.config.totp_mode == TotpMode::Mandatory || user_sel.otp_enabled) { let _ = req_handler.trace_req(format!("user '{}' not valid Totp: {}",&user_sel.name, &user_sel.otp_enabled)); return ( StatusCode::UNAUTHORIZED, req_handler.req_header.header, "Error data" ).into_response(); } else if user_sel.otp_enabled && user_sel.otp_verified && !user_sel.otp_base32.is_empty() && !user_sel.otp_defs.is_empty() { match req_handler.otp_check(&user_sel.otp_base32,&user_item.value, &user_sel.otp_defs) { Ok(val) => { if !val { let _ = req_handler.trace_req(format!("User '{}' not valid TOTP code",&user_item.name)); 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_item.name,e)); return ( StatusCode::UNAUTHORIZED, req_handler.req_header.header, "Error" ).into_response(); } } } } let session_token = req_handler.new_token(); user_sel.status = UserStatus::Pending; let user_data = user_sel.session_data(); let session_cookie = add_session_cookie(false,&cookies, &session_token, &user_data, app_dbs.config.session_expire, &app_dbs, "invitation").await; let session_encoded_key = encode(session_cookie.as_str()); let body=format!("This is a user password reset request for docserver service"); let subject = format!("DocServer password reset"); let reset_url= format!( "{}://{}/reset", &app_dbs.config.protocol, &app_dbs.config.hostport, ); let reset_expiration = format!("{} minutes",(&app_dbs.config.session_expire/60)); req_handler.context.insert("reset_url", &reset_url); req_handler.context.insert("reset_expiration", &reset_expiration); req_handler.context.insert("email_body",&body); req_handler.context.insert("reset_key",&session_encoded_key); req_handler.context.insert("email_subject",&subject); let mail_content= if let Some(tpl) = app_dbs.config.tpls.get("reset_password_mail_txt") { req_handler.render_template(tpl, "reset password") } else { format!("{}\n{}\n{}/{}\n",&subject,&body,&reset_url,&session_cookie) }; let mail_html_content = if let Some(tpl) = app_dbs.config.tpls.get("reset_password_mail_html") { req_handler.render_template(tpl, "invite") } else { format!("{}\n{}\n{}/{}\n",&subject,&body,&reset_url,&session_cookie) }; let mail_check = MailMessage::check(&app_dbs); if ! mail_check.is_empty() { let _ = req_handler.trace_req(format!("Mail service check error: {}",&mail_check)); ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error service" ).into_response() } else { match MailMessage::new( &app_dbs.config.mail_from, &user_sel.email, &app_dbs.config.mail_reply_to, ) { Ok(mail_message) => { match mail_message.send_html_message( &subject, &mail_content, &mail_html_content, &app_dbs ).await { Ok(_) => { let _ = req_handler.trace_req(format!("Mail sent to: '{}' reset url: {} cookie: {}", &user_sel.name, &reset_url,&session_cookie )); ( StatusCode::OK, format!("Mail sent to {}",&user_sel.name) ).into_response() }, Err(e) => { let _ = req_handler.trace_req(format!("Mail message send to: '{}' Error: {} ",&user_sel.name,e)); println!("Error mail message send: {}",e); ( StatusCode::INTERNAL_SERVER_ERROR, req_handler.req_header.header, "Error service" ).into_response() } } }, Err(e) => { let _ = req_handler.trace_req(format!("Mail message send to: '{}' Creation error: {} ",&user_sel.name,e)); println!("Error mail message creation: {}",e); ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error service" ).into_response() } } } //let result=format!("{}","OK"); // if ! auth_state.has_auth_role(&app_dbs.config.auth_roles) { // return Redirect::temporary( &format!("/login?o={}",uri.path().to_string())).into_response(); // // return ( // // StatusCode::UNAUTHORIZED, // // header, // // "Error authorization" // // ).into_response(); // } // ( // //status, // req_handler.req_header.header, // result, // ).into_response() } async fn reset_password_handler( header: HeaderMap, uri: Uri, Extension(app_dbs): Extension>, Extension(cookies): Extension, Extension(random): Extension, ConnectInfo(app_connect_info): ConnectInfo, // Query(req_params): Query, axum::extract::Path(data): axum::extract::Path, ) -> Response { let session_cookie = decode(&data).unwrap_or_default().to_string(); let auth_state = AuthState::from_cookie(session_cookie.to_owned(), &app_dbs).await; let mut req_handler = ReqHandler::new( ReqHeaderMap::new(header, &format!("{}",&uri.path().to_string()), &app_connect_info), &app_dbs, &uri, &auth_state, &random, "reset_password_handler" ); 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 user_sel = User::select("id", &user_id, false, &app_dbs.user_store).await.unwrap_or_else(|e|{ println!("Error select: {}", e); User::default() }); if user_sel.name.is_empty() { let _ = req_handler.trace_req(format!("User 'id' = '{}' not found",&user_id)); // User not exists return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error data" ).into_response(); } if user_sel.status != UserStatus::Active { let _ = req_handler.trace_req(format!("user '{}' in not valid status: {}",&user_id, &user_sel.status)); return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error status" ).into_response(); } 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()); } req_handler.context.insert("with_menu", "1"); req_handler.context.insert("user", &user_sel); req_handler.context.insert("edit_target", "password"); req_handler.context.insert("edit_reset", "password"); req_handler.context.remove("web_menu_items"); 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 user_data = user_sel.session_data(); let session_token = req_handler.new_token(); 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 '{}' reset password",&user_id)); ( res_headers, result.to_owned() ).into_response() } async fn post_user_password_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_reset_password_handler" ); if user_data.password.is_empty() { let _ = req_handler.trace_req(format!("No passwordfound")); return ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error" ).into_response(); } 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(); } 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 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 '{}' reset password OK",&user_id)); let result =String::from("OK"); ( req_handler.req_header.header, result.to_owned() ).into_response() }, Err(e) => { let _ = req_handler.trace_req(format!("Error user '{}' reset password: {}",&user_id,e)); ( StatusCode::BAD_REQUEST, req_handler.req_header.header, "Error" ).into_response() }, } } route( "/reset/:data", get(reset_password_handler)) .route("/reset", post(post_reset_password_handler)) .route("/resetup", post(post_user_password_handler)) }