EinsatzOnline/src/database/controller/password_resets.rs

117 lines
4.0 KiB
Rust

use std::iter;
use argon2::Config;
use chrono::{NaiveDateTime, Utc};
use diesel::{ExpressionMethods, RunQueryDsl};
use diesel::query_dsl::filter_dsl::FilterDsl;
use diesel::query_dsl::select_dsl::SelectDsl;
use rand::{Rng, thread_rng};
use rand::distributions::Alphanumeric;
use rocket::State;
use crate::database::controller::connector::establish_connection;
use crate::helper::settings::Settings;
use crate::logger::{add_entry, LogActions};
use crate::logger::entries::InsertableLogEntry;
use crate::schema::password_resets::dsl::password_resets;
use crate::schema::users::dsl::users;
/// Adds password reset token to database and returns it
pub fn add_token(settings: &State<Settings>, user_id: uuid::Uuid) -> Result<String, diesel::result::Error>{
let connection = establish_connection(settings);
let mut rng = thread_rng();
let token: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.map(char::from)
.take(60)
.collect();
match diesel::insert_into(password_resets).values((crate::schema::password_resets::token.eq(token.clone()), crate::schema::password_resets::user_id.eq(user_id))).execute(&connection){
Ok(_) => Ok(token),
Err(e) => {
error!("Couldn't create password reset token: {}", e);
Err(e)
}
}
}
pub fn remove_token(settings: &State<Settings>, token2: String) -> Result<(), diesel::result::Error>{
let connection = establish_connection(settings);
match diesel::delete(password_resets).filter(crate::schema::password_resets::dsl::token.eq(token2)).execute(&connection){
Ok(_) => Ok(()),
Err(e) => {
error!("Couldn't delete token: {}", e);
Err(e)
}
}
}
pub fn validate_token(settings: &State<Settings>, token2: String) -> Result<uuid::Uuid, diesel::result::Error>{
let connection = establish_connection(settings);
match password_resets.select((crate::schema::password_resets::dsl::user_id, crate::schema::password_resets::issued)).filter(crate::schema::password_resets::dsl::token.eq(token2.clone())).get_result::<(uuid::Uuid, NaiveDateTime)>(&connection){
Ok(data) => {
let issued = data.1;
let now = Utc::now().naive_local();
let duration = now.signed_duration_since(issued).num_seconds();
if duration > settings.application.reset_password_token_valid_duration{
remove_token(settings, token2)?;
return Err(diesel::result::Error::NotFound)
}
Ok(data.0)
},
Err(e) => match e{
diesel::result::Error::NotFound => {
return Err(e)
},
_ => {
error!("Couldn't validate token: {}", e);
return Err(e)
}
}
}
}
pub fn change_password_with_token(settings: &State<Settings>, token2: String, password: String) -> Result<(), ()>{
let user_id = match validate_token(settings, token2.clone()){
Ok(user_id) => user_id,
Err(_) => return Err(())
};
let connection =establish_connection(settings);
let salt = rand::thread_rng().gen::<[u8; 32]>();
let hashed_password = match argon2::hash_encoded(password.as_bytes(), &salt, &Config::default()){
Ok(pw) => pw,
Err(e) => {
error!("Couldn't hash password: {}", e);
return Err(())
}
};
match diesel::update(users).filter(crate::schema::users::dsl::id.eq(user_id)).set(crate::schema::users::dsl::password.eq(hashed_password)).execute(&connection){
Ok(_) => (),
Err(e) => {
error!("Couldn't set new password: {}", e);
return Err(())
}
};
add_entry(InsertableLogEntry::new(LogActions::PasswordReset, Some(user_id), None, None), settings);
match remove_token(settings, token2){
Ok(_) => Ok(()),
Err(_) => Err(())
}
}
pub fn validate_password(password: String) -> bool{
if password.len() < 10{
return false
}
true
}