refactor: reset password
This commit is contained in:
parent
ab648dd4f2
commit
ee4481225e
@ -107,7 +107,7 @@ impl User {
|
|||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_for_login(pool: &PgPool, email: &str) -> Result<User> {
|
pub async fn read_for_login(pool: &PgPool, email: &str) -> Result<Option<User>> {
|
||||||
let record = sqlx::query!(
|
let record = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
SELECT id,
|
SELECT id,
|
||||||
@ -126,25 +126,25 @@ impl User {
|
|||||||
"#,
|
"#,
|
||||||
email,
|
email,
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_optional(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let result = User {
|
let user = record.map(|r| User {
|
||||||
id: record.id,
|
id: r.id,
|
||||||
name: record.name,
|
name: r.name,
|
||||||
email: record.email,
|
email: r.email,
|
||||||
password: record.password,
|
password: r.password,
|
||||||
salt: record.salt,
|
salt: r.salt,
|
||||||
role: record.role,
|
role: r.role,
|
||||||
function: record.function,
|
function: r.function,
|
||||||
area_id: record.areaid,
|
area_id: r.areaid,
|
||||||
area: None,
|
area: None,
|
||||||
locked: record.locked,
|
locked: r.locked,
|
||||||
last_login: record.lastlogin,
|
last_login: r.lastlogin,
|
||||||
receive_notifications: record.receivenotifications,
|
receive_notifications: r.receivenotifications,
|
||||||
};
|
});
|
||||||
|
|
||||||
Ok(result)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn exists(pool: &PgPool, email: &str) -> Result<Option<i32>> {
|
pub async fn exists(pool: &PgPool, email: &str) -> Result<Option<i32>> {
|
||||||
|
@ -31,7 +31,9 @@ pub async fn get(
|
|||||||
return Ok(HttpResponse::Found()
|
return Ok(HttpResponse::Found()
|
||||||
.insert_header((LOCATION, "/"))
|
.insert_header((LOCATION, "/"))
|
||||||
.finish());
|
.finish());
|
||||||
} else if let Some(token) = &query.token {
|
}
|
||||||
|
|
||||||
|
if let Some(token) = &query.token {
|
||||||
if let Some(_) = PasswordReset::does_token_exist(pool.get_ref(), token).await? {
|
if let Some(_) = PasswordReset::does_token_exist(pool.get_ref(), token).await? {
|
||||||
let template = ResetPasswordTemplate {
|
let template = ResetPasswordTemplate {
|
||||||
token,
|
token,
|
||||||
|
@ -4,7 +4,7 @@ use brass_db::models::User;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::utils::auth::hash_plain_password_with_salt;
|
use crate::utils::{auth::hash_plain_password_with_salt, ApplicationError};
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct LoginForm {
|
pub struct LoginForm {
|
||||||
@ -18,26 +18,33 @@ async fn post(
|
|||||||
web::Form(form): web::Form<LoginForm>,
|
web::Form(form): web::Form<LoginForm>,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> impl Responder {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if let Ok(user) = User::read_for_login(pool.get_ref(), &form.email.to_lowercase()).await {
|
let not_found_response = HttpResponse::BadRequest().body("E-Mail oder Passwort falsch.");
|
||||||
let salt = user.salt.unwrap();
|
|
||||||
|
|
||||||
let hash = hash_plain_password_with_salt(&form.password, &salt).unwrap();
|
let Some(user) = User::read_for_login(pool.get_ref(), &form.email.to_lowercase()).await? else {
|
||||||
if hash == user.password.unwrap() {
|
return Ok(not_found_response);
|
||||||
Identity::login(&request.extensions(), user.id.to_string()).unwrap();
|
};
|
||||||
|
|
||||||
User::update_login_timestamp(pool.get_ref(), user.id)
|
if user.password.is_none() || user.salt.is_none() {
|
||||||
.await
|
return Ok(not_found_response);
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let location = form.next.unwrap_or("/".to_string());
|
|
||||||
|
|
||||||
return HttpResponse::Found()
|
|
||||||
.insert_header(("LOCATION", location.clone()))
|
|
||||||
.insert_header(("HX-LOCATION", location))
|
|
||||||
.finish();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse::BadRequest().body("E-Mail oder Passwort falsch.")
|
let password = user.password.unwrap();
|
||||||
|
let salt = user.salt.unwrap();
|
||||||
|
let hash = hash_plain_password_with_salt(&form.password, &salt)?;
|
||||||
|
|
||||||
|
if hash != password {
|
||||||
|
return Ok(not_found_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
Identity::login(&request.extensions(), user.id.to_string()).unwrap();
|
||||||
|
|
||||||
|
User::update_login_timestamp(pool.get_ref(), user.id).await?;
|
||||||
|
|
||||||
|
let location = form.next.unwrap_or("/".to_string());
|
||||||
|
|
||||||
|
Ok(HttpResponse::Found()
|
||||||
|
.insert_header(("LOCATION", location.clone()))
|
||||||
|
.insert_header(("HX-LOCATION", location))
|
||||||
|
.finish())
|
||||||
}
|
}
|
||||||
|
@ -1,90 +1,89 @@
|
|||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, Either, HttpResponse, Responder};
|
||||||
use maud::html;
|
use maud::html;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mail::Mailer,
|
mail::Mailer,
|
||||||
utils::{password_change::PasswordChangeBuilder, ApplicationError},
|
utils::{password_change::PasswordChangeBuilder, ApplicationError, HtmxTargetHeader},
|
||||||
};
|
};
|
||||||
use brass_db::models::{PasswordReset, User};
|
use brass_db::models::{PasswordReset, User};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct RequestResetPasswordForm {
|
||||||
|
email: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct ResetPasswordForm {
|
struct ResetPasswordForm {
|
||||||
email: Option<String>,
|
token: String,
|
||||||
token: Option<String>,
|
password: String,
|
||||||
password: Option<String>,
|
passwordretyped: String,
|
||||||
passwordretyped: Option<String>,
|
|
||||||
dry: Option<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::post("/reset-password")]
|
#[actix_web::post("/reset-password")]
|
||||||
async fn post(
|
async fn post(
|
||||||
form: web::Form<ResetPasswordForm>,
|
form: Either<web::Form<RequestResetPasswordForm>, web::Form<ResetPasswordForm>>,
|
||||||
|
header: web::Header<HtmxTargetHeader>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
mailer: web::Data<Mailer>,
|
mailer: web::Data<Mailer>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if form.email.is_some()
|
match form {
|
||||||
&& form.token.is_none()
|
Either::Left(form) => {
|
||||||
&& form.password.is_none()
|
if let Some(user) =
|
||||||
&& form.passwordretyped.is_none()
|
User::read_for_login(pool.get_ref(), &form.email.to_lowercase()).await?
|
||||||
{
|
{
|
||||||
if let Ok(user) =
|
let reset = PasswordReset::insert_new_for_user(pool.get_ref(), user.id).await?;
|
||||||
User::read_for_login(pool.get_ref(), &form.email.as_ref().unwrap().to_lowercase()).await
|
mailer
|
||||||
{
|
.send_forgot_password_mail(&user, &reset.token)
|
||||||
let reset = PasswordReset::insert_new_for_user(pool.get_ref(), user.id).await?;
|
.await?;
|
||||||
mailer
|
}
|
||||||
.send_forgot_password_mail(&user, &reset.token)
|
|
||||||
.await?;
|
return Ok(HttpResponse::Ok().body("E-Mail versandt!"));
|
||||||
}
|
}
|
||||||
|
Either::Right(form) => {
|
||||||
|
let is_dry = header.is_some_and_equal("password-strength");
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().body("E-Mail versandt!"))
|
let Some(token) = PasswordReset::does_token_exist(pool.get_ref(), &form.token).await?
|
||||||
} else if form.email.is_none()
|
else {
|
||||||
&& form.token.is_some()
|
return Ok(HttpResponse::NoContent().finish());
|
||||||
&& form.password.is_some()
|
};
|
||||||
&& form.passwordretyped.is_some()
|
|
||||||
{
|
|
||||||
// TODO: refactor into check if HX-TARGET = #password-strength exists
|
|
||||||
let is_dry = form.dry.is_some_and(|b| b);
|
|
||||||
|
|
||||||
let token = if let Some(token) =
|
let mut builder = PasswordChangeBuilder::<PasswordReset>::new(
|
||||||
PasswordReset::does_token_exist(pool.get_ref(), form.token.as_ref().unwrap()).await?
|
pool.get_ref(),
|
||||||
{
|
token.userid,
|
||||||
token
|
&form.password,
|
||||||
} else {
|
&form.passwordretyped,
|
||||||
return Ok(HttpResponse::NoContent().finish());
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut builder = PasswordChangeBuilder::<PasswordReset>::new(
|
|
||||||
pool.get_ref(),
|
|
||||||
token.userid,
|
|
||||||
&form.password.as_ref().unwrap(),
|
|
||||||
&form.passwordretyped.as_ref().unwrap(),
|
|
||||||
)
|
|
||||||
.with_token(token);
|
|
||||||
|
|
||||||
let change = builder.build();
|
|
||||||
|
|
||||||
let response = if is_dry {
|
|
||||||
change.validate_for_input().await.unwrap() // TODO:
|
|
||||||
} else {
|
|
||||||
change.validate().await.unwrap(); // TODO
|
|
||||||
change.commit().await?;
|
|
||||||
HttpResponse::Ok().body(
|
|
||||||
html! {
|
|
||||||
div class="block mb-3" {
|
|
||||||
"Passwort erfolgreich geändert."
|
|
||||||
}
|
|
||||||
a class="block button is-primary" hx-boost="true" href="/login"{
|
|
||||||
"Zum Login"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.into_string(),
|
|
||||||
)
|
)
|
||||||
};
|
.with_token(token);
|
||||||
|
|
||||||
return Ok(response);
|
let change = builder.build();
|
||||||
} else {
|
|
||||||
return Ok(HttpResponse::BadRequest().finish());
|
let response = if is_dry {
|
||||||
|
match change.validate_for_input().await {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => HttpResponse::UnprocessableEntity().body(e.message),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Err(e) = change.validate().await {
|
||||||
|
return Ok(HttpResponse::UnprocessableEntity().body(e.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
change.commit().await?;
|
||||||
|
HttpResponse::Ok().body(
|
||||||
|
html! {
|
||||||
|
div class="block mb-3" {
|
||||||
|
"Passwort erfolgreich geändert."
|
||||||
|
}
|
||||||
|
a class="block button is-primary" hx-boost="true" href="/login"{
|
||||||
|
"Zum Login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into_string(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user