docserver/src/handlers/users/password_handlers.rs

396 lines
16 KiB
Rust

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<Arc<AppDBs>>,
Extension(cookies): Extension<Cookies>,
Extension(random): Extension<Random>,
ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
Json(user_item): Json<UserItem>,
) -> 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<Arc<AppDBs>>,
Extension(cookies): Extension<Cookies>,
Extension(random): Extension<Random>,
ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
// Query(req_params): Query<ReqParams>,
axum::extract::Path(data): axum::extract::Path<String>,
) -> 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<Arc<AppDBs>>,
Extension(cookies): Extension<Cookies>,
Extension(random): Extension<Random>,
ConnectInfo(app_connect_info): ConnectInfo<AppConnectInfo>,
Json(user_data): Json<UserData>,
) -> 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))
}