From e4b361ab120b82716f9e323338d20935e1ca1de1 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Mon, 13 Jul 2020 14:20:28 +0200 Subject: [PATCH] Can generate a reset token if password is valid --- src/api_data/mod.rs | 3 ++- src/api_data/res_check_security_answers.rs | 17 ++++++++++++ src/controllers/account_controller.rs | 30 ++++++++++++++++++++++ src/controllers/routes.rs | 1 + src/helpers/account_helper.rs | 13 ++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/api_data/res_check_security_answers.rs diff --git a/src/api_data/mod.rs b/src/api_data/mod.rs index 0ac2cb9..209644d 100644 --- a/src/api_data/mod.rs +++ b/src/api_data/mod.rs @@ -45,4 +45,5 @@ pub mod user_membership_api; mod type_container_api; pub mod res_check_email_exists; pub mod res_check_security_questions_exists; -pub mod res_get_security_questions; \ No newline at end of file +pub mod res_get_security_questions; +pub mod res_check_security_answers; \ No newline at end of file diff --git a/src/api_data/res_check_security_answers.rs b/src/api_data/res_check_security_answers.rs new file mode 100644 index 0000000..a630b26 --- /dev/null +++ b/src/api_data/res_check_security_answers.rs @@ -0,0 +1,17 @@ +//! # Check security answsers result +//! +//! If the user gave valid security answers, we give him a password reset token. +//! +//! @author Pierre Hubert +use serde::Serialize; + +#[derive(Serialize)] +pub struct ResCheckSecurityAnswers { + reset_token: String +} + +impl ResCheckSecurityAnswers { + pub fn new(reset_token: String) -> ResCheckSecurityAnswers { + ResCheckSecurityAnswers { reset_token } + } +} \ No newline at end of file diff --git a/src/controllers/account_controller.rs b/src/controllers/account_controller.rs index facfe67..cfff084 100644 --- a/src/controllers/account_controller.rs +++ b/src/controllers/account_controller.rs @@ -1,6 +1,9 @@ +use percent_encoding::percent_decode_str; + use crate::api_data::current_user_id::CurrentUserID; use crate::api_data::login_success::LoginSuccess; use crate::api_data::res_check_email_exists::ResCheckEmailExists; +use crate::api_data::res_check_security_answers::ResCheckSecurityAnswers; use crate::api_data::res_check_security_questions_exists::ResCheckSecurityQuestionsExists; use crate::api_data::res_get_security_questions::ResGetSecurityQuestions; use crate::controllers::routes::RequestResult; @@ -118,4 +121,31 @@ pub fn get_security_questions(r: &mut HttpRequestHandler) -> RequestResult { } r.set_response(ResGetSecurityQuestions::new(&user)) +} + +/// Check the security answers given by a user to reset a password +pub fn check_security_answers(r: &mut HttpRequestHandler) -> RequestResult { + let user = r.post_user_info_from_email("email")?; + + if !user.has_security_questions() { + r.forbidden("Specified user has not setup security questions !".to_string())?; + } + + let answers: Vec = r.post_string_opt("answers", 3, true)? + .split("&") + .map(|s| percent_decode_str(s).decode_utf8_lossy().to_lowercase().trim().to_string()) + .collect::>(); + + if answers.len() != 2 { + r.forbidden("Please specify two answers!".to_string())?; + } + + if answers[0] != user.security_answer_1.unwrap().to_lowercase().trim() || + answers[1] != user.security_answer_2.unwrap().to_lowercase().trim() { + r.forbidden("Invalid security answers!".to_string())?; + } + + let token = account_helper::generate_password_reset_token(&user.id)?; + + r.set_response(ResCheckSecurityAnswers::new(token)) } \ No newline at end of file diff --git a/src/controllers/routes.rs b/src/controllers/routes.rs index ece0d33..8c61d73 100644 --- a/src/controllers/routes.rs +++ b/src/controllers/routes.rs @@ -78,6 +78,7 @@ pub fn get_routes() -> Vec { Route::post_without_login("/account/exists_email", Box::new(account_controller::exists_mail)), Route::post_without_login("/account/has_security_questions", Box::new(account_controller::has_security_questions)), Route::post_without_login("/account/get_security_questions", Box::new(account_controller::get_security_questions)), + Route::post_without_login("/account/check_security_answers", Box::new(account_controller::check_security_answers)), // User controller Route::post_without_login("/user/getInfo", Box::new(user_controller::get_single)), diff --git a/src/helpers/account_helper.rs b/src/helpers/account_helper.rs index d37e71d..af214bf 100644 --- a/src/helpers/account_helper.rs +++ b/src/helpers/account_helper.rs @@ -114,6 +114,19 @@ pub fn destroy_all_user_tokens(id: &UserID) -> ResultBoxError { .exec() } +/// Generate a new password reset token +pub fn generate_password_reset_token(user_id: &UserID) -> ResultBoxError { + let token = rand_str(255); + + database::UpdateInfo::new(USERS_TABLE) + .cond_user_id("ID", user_id) + .set_str("password_reset_token", &token) + .set_u64("password_reset_token_time_create", time()) + .exec()?; + + Ok(token) +} + /// Check out whether a virtual directory is taken by a user or not pub fn check_user_directory_availability(dir: &str, user_id: Option) -> ResultBoxError { let found_user = user_helper::find_user_by_virtual_directory(dir);