396 lines
16 KiB
Rust
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))
|
|
}
|