refactor: use same logic for all password reset locations
This commit is contained in:
parent
ea35f5475f
commit
77a71787bd
@ -14,7 +14,7 @@ pub fn generate_salt_and_hash_plain_password(plain: &str) -> anyhow::Result<(Str
|
|||||||
Ok((hash, salt.to_string()))
|
Ok((hash, salt.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_plain_password_with_salt(plain: &str, salt_string: &str) -> anyhow::Result<String> {
|
pub fn hash_plain_password_with_salt(plain: &str, salt_string: &str) -> Result<String, argon2::password_hash::Error> {
|
||||||
let salt = SaltString::from_b64(salt_string)?;
|
let salt = SaltString::from_b64(salt_string)?;
|
||||||
|
|
||||||
let hash = Argon2::default()
|
let hash = Argon2::default()
|
||||||
|
@ -34,7 +34,7 @@ pub async fn get(
|
|||||||
token,
|
token,
|
||||||
title: "Brass - Passwort zurücksetzen",
|
title: "Brass - Passwort zurücksetzen",
|
||||||
endpoint: "/reset-password",
|
endpoint: "/reset-password",
|
||||||
new_password_label: "/reset-password",
|
new_password_label: "neues Passwort:",
|
||||||
retype_label: "neues Passwort wiederholen:",
|
retype_label: "neues Passwort wiederholen:",
|
||||||
submit_button_label: "Passwort zurücksetzen",
|
submit_button_label: "Passwort zurücksetzen",
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
use crate::filters;
|
use crate::auth::utils::hash_plain_password_with_salt;
|
||||||
use crate::models::{Area, Role, User};
|
use crate::models::{Area, Role, Token, User};
|
||||||
|
use crate::utils::{password_help, ApplicationError};
|
||||||
|
use crate::{auth, filters};
|
||||||
|
use actix_web::HttpResponse;
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
use zxcvbn::{zxcvbn, Score};
|
||||||
|
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod get_changepassword;
|
pub mod get_changepassword;
|
||||||
@ -10,15 +15,15 @@ pub mod get_logout;
|
|||||||
pub mod get_new;
|
pub mod get_new;
|
||||||
pub mod get_overview;
|
pub mod get_overview;
|
||||||
pub mod get_profile;
|
pub mod get_profile;
|
||||||
|
pub mod get_register;
|
||||||
pub mod get_reset;
|
pub mod get_reset;
|
||||||
pub mod post_changepassword;
|
pub mod post_changepassword;
|
||||||
pub mod post_edit;
|
pub mod post_edit;
|
||||||
pub mod post_login;
|
pub mod post_login;
|
||||||
pub mod post_new;
|
pub mod post_new;
|
||||||
|
pub mod post_register;
|
||||||
pub mod post_reset;
|
pub mod post_reset;
|
||||||
pub mod post_toggle;
|
pub mod post_toggle;
|
||||||
pub mod get_register;
|
|
||||||
pub mod post_register;
|
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "user/new_or_edit.html")]
|
#[template(path = "user/new_or_edit.html")]
|
||||||
@ -41,5 +46,96 @@ struct ResetPasswordTemplate<'a> {
|
|||||||
endpoint: &'a str,
|
endpoint: &'a str,
|
||||||
new_password_label: &'a str,
|
new_password_label: &'a str,
|
||||||
retype_label: &'a str,
|
retype_label: &'a str,
|
||||||
submit_button_label: &'a str
|
submit_button_label: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_password_change_request(
|
||||||
|
pool: &PgPool,
|
||||||
|
token: Option<&impl Token>,
|
||||||
|
user_id: i32,
|
||||||
|
password: &str,
|
||||||
|
password_retyped: &str,
|
||||||
|
current_password: Option<&str>,
|
||||||
|
generate_message: bool,
|
||||||
|
) -> Result<HttpResponse, ApplicationError> {
|
||||||
|
let no_message = HttpResponse::NoContent().finish();
|
||||||
|
|
||||||
|
if password.chars().count() > 256 {
|
||||||
|
if generate_message {
|
||||||
|
return Ok(
|
||||||
|
HttpResponse::BadRequest().body(password_help::format_message(
|
||||||
|
"danger",
|
||||||
|
"Password darf nicht länger als 256 Zeichen sein.",
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(no_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = User::read_by_id(pool, user_id).await?;
|
||||||
|
let mut split_names: Vec<&str> = user.name.as_str().split_whitespace().collect();
|
||||||
|
let mut user_inputs = vec![user.email.as_str()];
|
||||||
|
user_inputs.append(&mut split_names);
|
||||||
|
let entropy = zxcvbn(password, &user_inputs);
|
||||||
|
|
||||||
|
if entropy.score() < Score::Three {
|
||||||
|
if generate_message {
|
||||||
|
let message = password_help::generate_for_entropy(&entropy);
|
||||||
|
return Ok(HttpResponse::BadRequest().body(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(no_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if generate_message {
|
||||||
|
if entropy.score() == Score::Three {
|
||||||
|
return Ok(HttpResponse::Ok().body(password_help::format_message(
|
||||||
|
"success",
|
||||||
|
"Sicheres Passwort.",
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
return Ok(HttpResponse::Ok().body(password_help::format_message(
|
||||||
|
"success",
|
||||||
|
"Sehr sicheres Passwort.",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(current_password) = current_password {
|
||||||
|
// unwraps are safe, as login only works with password and salt and this call site requires login
|
||||||
|
let hash = user.password.as_ref().unwrap();
|
||||||
|
let salt = user.salt.as_ref().unwrap();
|
||||||
|
|
||||||
|
if hash != &hash_plain_password_with_salt(current_password, salt)? {
|
||||||
|
return Ok(HttpResponse::BadRequest().body("Aktuelles Passwort ist falsch."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if password != password_retyped {
|
||||||
|
return Ok(HttpResponse::BadRequest().body("Passwörter stimmen nicht überein."));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (hash, salt) = auth::utils::generate_salt_and_hash_plain_password(password).unwrap();
|
||||||
|
|
||||||
|
User::update(
|
||||||
|
pool,
|
||||||
|
user_id,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(&hash),
|
||||||
|
Some(&salt),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(token) = token {
|
||||||
|
token.delete(pool).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().body(r#"<div class="block">Registrierung abgeschlossen.</div><a class="block button is-primary" hx-boost="true" href="/login">Zum Login</a>"#))
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ async fn post(
|
|||||||
form: web::Form<ChangePasswordForm>,
|
form: web::Form<ChangePasswordForm>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
|
// TODO: hier weiter gleichziehen mit post reset und post register
|
||||||
if user.password.as_ref().is_some_and(|p|
|
if user.password.as_ref().is_some_and(|p|
|
||||||
p == &utils::hash_plain_password_with_salt(
|
p == &utils::hash_plain_password_with_salt(
|
||||||
&form.currentpassword,
|
&form.currentpassword,
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
use actix_web::{post, web, HttpResponse, Responder};
|
use actix_web::{post, web, HttpResponse, Responder};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use zxcvbn::{zxcvbn, Score};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
auth,
|
endpoints::user::handle_password_change_request, models::Registration, utils::ApplicationError,
|
||||||
models::{Registration, User},
|
|
||||||
utils::{password_help, ApplicationError},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -22,65 +19,25 @@ async fn post(
|
|||||||
form: web::Form<RegisterForm>,
|
form: web::Form<RegisterForm>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
|
// TODO: refactor into check if HX-TARGET = #password-strength exists
|
||||||
let is_dry = form.dry.unwrap_or(false);
|
let is_dry = form.dry.unwrap_or(false);
|
||||||
// TODO: flip unauthorized with not found or unwrap result in a other way
|
let token =
|
||||||
let token = Registration::does_token_exist(pool.get_ref(), &form.token).await?.ok_or(ApplicationError::Unauthorized)?;
|
if let Some(token) = Registration::does_token_exist(pool.get_ref(), &form.token).await? {
|
||||||
|
token
|
||||||
if form.password.chars().count() > 256 {
|
|
||||||
if is_dry {
|
|
||||||
return Ok(HttpResponse::BadRequest().body("<div id=\"password-strength\" class=\"mb-3 help content is-danger\">Password darf nicht länger als 256 Zeichen sein.</div>"));
|
|
||||||
} else {
|
} else {
|
||||||
return Ok(HttpResponse::NoContent().finish());
|
return Ok(HttpResponse::NoContent().finish());
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let user = User::read_by_id(pool.get_ref(), token.userid).await?;
|
let response = handle_password_change_request(
|
||||||
let mut split_names: Vec<&str> = user.name.as_str().split_whitespace().collect();
|
|
||||||
let mut user_inputs = vec![user.email.as_str()];
|
|
||||||
user_inputs.append(&mut split_names);
|
|
||||||
let entropy = zxcvbn(&form.password, &user_inputs);
|
|
||||||
|
|
||||||
if entropy.score() < Score::Three {
|
|
||||||
if is_dry {
|
|
||||||
let message = password_help::generate_for_entropy(&entropy);
|
|
||||||
|
|
||||||
return Ok(HttpResponse::BadRequest().body(message));
|
|
||||||
} else {
|
|
||||||
return Ok(HttpResponse::NoContent().finish());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_dry {
|
|
||||||
if entropy.score() == Score::Three {
|
|
||||||
return Ok(HttpResponse::Ok()
|
|
||||||
.body("<div id=\"password-strength\" class=\"mb-3 help content is-success\">Sicheres Passwort.</div>"));
|
|
||||||
} else {
|
|
||||||
return Ok(HttpResponse::Ok()
|
|
||||||
.body("<div id=\"password-strength\" class=\"mb-3 help content is-success\">Sehr sicheres Passwort.</div>"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.password != form.passwordretyped {
|
|
||||||
return Ok(HttpResponse::BadRequest().body("Passwörter stimmen nicht überein!"));
|
|
||||||
}
|
|
||||||
let (hash, salt) = auth::utils::generate_salt_and_hash_plain_password(&form.password).unwrap();
|
|
||||||
|
|
||||||
User::update(
|
|
||||||
pool.get_ref(),
|
pool.get_ref(),
|
||||||
token.userid,
|
Some(&token),
|
||||||
None,
|
token.id,
|
||||||
None,
|
&form.password,
|
||||||
Some(&hash),
|
&form.passwordretyped,
|
||||||
Some(&salt),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
None,
|
||||||
|
is_dry,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Registration::delete(pool.get_ref(), &token.token).await?;
|
Ok(response)
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().body(r#"<div class="block">Registrierung abgeschlossen.</div><a class="block button is-primary" hx-boost="true" href="/login">Zum Login</a>"#))
|
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,11 @@ use actix_web::{web, HttpResponse, Responder};
|
|||||||
use lettre::{SmtpTransport, Transport};
|
use lettre::{SmtpTransport, Transport};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use zxcvbn::{zxcvbn, Score};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::{self},
|
endpoints::user::handle_password_change_request,
|
||||||
models::{PasswordReset, User},
|
models::{PasswordReset, User},
|
||||||
utils::{email, password_help},
|
utils::{email, ApplicationError},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
@ -24,104 +23,50 @@ async fn post(
|
|||||||
form: web::Form<ResetPasswordForm>,
|
form: web::Form<ResetPasswordForm>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
mailer: web::Data<SmtpTransport>,
|
mailer: web::Data<SmtpTransport>,
|
||||||
) -> impl Responder {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if form.email.is_some()
|
if form.email.is_some()
|
||||||
&& form.token.is_none()
|
&& form.token.is_none()
|
||||||
&& form.password.is_none()
|
&& form.password.is_none()
|
||||||
&& form.passwordretyped.is_none()
|
&& form.passwordretyped.is_none()
|
||||||
{
|
{
|
||||||
if let Ok(user) = User::read_for_login(pool.get_ref(), form.email.as_ref().unwrap()).await {
|
if let Ok(user) = User::read_for_login(pool.get_ref(), form.email.as_ref().unwrap()).await {
|
||||||
let reset = PasswordReset::insert_new_for_user(pool.get_ref(), user.id)
|
let reset = PasswordReset::insert_new_for_user(pool.get_ref(), user.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let message = email::build_forgot_password_message(&user, &reset.token);
|
let message = email::build_forgot_password_message(&user, &reset.token);
|
||||||
|
|
||||||
mailer.send(&message).unwrap();
|
mailer.send(&message)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse::Ok().body("E-Mail versandt!");
|
return Ok(HttpResponse::Ok().body("E-Mail versandt!"));
|
||||||
} else if form.email.is_none()
|
} else if form.email.is_none()
|
||||||
&& form.token.is_some()
|
&& form.token.is_some()
|
||||||
&& form.password.is_some()
|
&& form.password.is_some()
|
||||||
&& form.passwordretyped.is_some()
|
&& form.passwordretyped.is_some()
|
||||||
{
|
{
|
||||||
let password = form.password.as_ref().unwrap();
|
// TODO: refactor into check if HX-TARGET = #password-strength exists
|
||||||
let is_dry = form.dry.is_some_and(|b| b);
|
let is_dry = form.dry.is_some_and(|b| b);
|
||||||
|
|
||||||
let token =
|
let token = if let Some(token) =
|
||||||
PasswordReset::does_token_exist(pool.get_ref(), form.token.as_ref().unwrap()).await;
|
PasswordReset::does_token_exist(pool.get_ref(), &form.token.as_ref().unwrap()).await?
|
||||||
|
{
|
||||||
|
token
|
||||||
|
} else {
|
||||||
|
return Ok(HttpResponse::NoContent().finish());
|
||||||
|
};
|
||||||
|
|
||||||
if token.is_err() {
|
let response = handle_password_change_request(
|
||||||
return HttpResponse::BadRequest().body("Token existiert nicht bzw. ist abgelaufen!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if password.chars().count() > 256 {
|
|
||||||
if is_dry {
|
|
||||||
return HttpResponse::BadRequest().body("<div id=\"password-strength\" class=\"mb-3 help content is-danger\">Password darf nicht länger als 256 Zeichen sein.</div>");
|
|
||||||
} else {
|
|
||||||
return HttpResponse::NoContent().finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let user = User::read_by_id(pool.get_ref(), token.as_ref().unwrap().userid)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let mut split_names: Vec<&str> = user.name.as_str().split_whitespace().collect();
|
|
||||||
let mut user_inputs = vec![user.email.as_str()];
|
|
||||||
user_inputs.append(&mut split_names);
|
|
||||||
let entropy = zxcvbn(password, &user_inputs);
|
|
||||||
|
|
||||||
if entropy.score() < Score::Three {
|
|
||||||
if is_dry {
|
|
||||||
let message = password_help::generate_for_entropy(&entropy);
|
|
||||||
|
|
||||||
return HttpResponse::BadRequest().body(message);
|
|
||||||
} else {
|
|
||||||
return HttpResponse::NoContent().finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_dry {
|
|
||||||
if entropy.score() == Score::Three {
|
|
||||||
return HttpResponse::Ok()
|
|
||||||
.body("<div id=\"password-strength\" class=\"mb-3 help content is-success\">Sicheres Passwort.</div>");
|
|
||||||
} else {
|
|
||||||
return HttpResponse::Ok()
|
|
||||||
.body("<div id=\"password-strength\" class=\"mb-3 help content is-success\">Sehr sicheres Passwort.</div>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if password != form.passwordretyped.as_ref().unwrap() {
|
|
||||||
return HttpResponse::BadRequest().body("Passwörter stimmen nicht überein!");
|
|
||||||
}
|
|
||||||
|
|
||||||
let (hash, salt) =
|
|
||||||
auth::utils::generate_salt_and_hash_plain_password(form.password.as_ref().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
User::update(
|
|
||||||
pool.get_ref(),
|
pool.get_ref(),
|
||||||
token.as_ref().unwrap().userid,
|
Some(&token),
|
||||||
None,
|
token.id,
|
||||||
None,
|
form.password.as_ref().unwrap(),
|
||||||
Some(&hash),
|
form.passwordretyped.as_ref().unwrap(),
|
||||||
Some(&salt),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
None,
|
||||||
|
is_dry,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
PasswordReset::delete(pool.get_ref(), &token.unwrap().token)
|
return Ok(response);
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
return HttpResponse::Ok().body(r#"<div class="block">Passwort wurde geändert.</div><a class="block button is-primary" hx-boost="true" href="/login">Zum Login</a>"#);
|
|
||||||
} else {
|
} else {
|
||||||
return HttpResponse::BadRequest().finish();
|
return Ok(HttpResponse::BadRequest().finish());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ pub use location::Location;
|
|||||||
pub use role::Role;
|
pub use role::Role;
|
||||||
pub use user::User;
|
pub use user::User;
|
||||||
pub use assignement::Assignment;
|
pub use assignement::Assignment;
|
||||||
pub use password_reset::PasswordReset;
|
pub use password_reset::{PasswordReset, Token};
|
||||||
pub use registration::Registration;
|
pub use registration::Registration;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, sqlx::Error>;
|
type Result<T> = std::result::Result<T, sqlx::Error>;
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use chrono::{NaiveDateTime, TimeDelta};
|
use chrono::{NaiveDateTime, TimeDelta};
|
||||||
use sqlx::{query_as, PgPool};
|
use sqlx::{query_as, PgPool};
|
||||||
|
|
||||||
|
use super::Result;
|
||||||
use crate::utils::token_generation::generate_token_and_expiration;
|
use crate::utils::token_generation::generate_token_and_expiration;
|
||||||
|
|
||||||
|
pub trait Token {
|
||||||
|
async fn delete(&self, pool: &PgPool) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PasswordReset {
|
pub struct PasswordReset {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub userid: i32,
|
pub userid: i32,
|
||||||
pub expires: NaiveDateTime
|
pub expires: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PasswordReset {
|
impl PasswordReset {
|
||||||
pub async fn insert_new_for_user(pool: &PgPool, user_id: i32) -> Result<PasswordReset>{
|
pub async fn insert_new_for_user(pool: &PgPool, user_id: i32) -> Result<PasswordReset> {
|
||||||
let (token, expires) = generate_token_and_expiration(64, TimeDelta::hours(24));
|
let (token, expires) = generate_token_and_expiration(64, TimeDelta::hours(24));
|
||||||
|
|
||||||
let inserted = query_as!(
|
let inserted = query_as!(
|
||||||
@ -29,17 +33,19 @@ impl PasswordReset {
|
|||||||
Ok(inserted)
|
Ok(inserted)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn does_token_exist(pool: &PgPool, token: &str) -> Result<PasswordReset> {
|
pub async fn does_token_exist(pool: &PgPool, token: &str) -> Result<Option<PasswordReset>> {
|
||||||
let result = query_as!(PasswordReset,
|
let result = query_as!(
|
||||||
"SELECT * FROM passwordReset WHERE token = $1 AND expires > NOW();", token
|
PasswordReset,
|
||||||
|
"SELECT * FROM passwordReset WHERE token = $1 AND expires > NOW();",
|
||||||
|
token
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_optional(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(pool: &PgPool, token: &str) -> anyhow::Result<()> {
|
pub async fn delete(pool: &PgPool, token: &str) -> Result<()> {
|
||||||
sqlx::query!("DELETE FROM passwordReset WHERE token = $1;", token)
|
sqlx::query!("DELETE FROM passwordReset WHERE token = $1;", token)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
.await?;
|
.await?;
|
||||||
@ -47,3 +53,9 @@ impl PasswordReset {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Token for PasswordReset {
|
||||||
|
async fn delete(&self, pool: &PgPool) -> Result<()> {
|
||||||
|
PasswordReset::delete(pool, &self.token).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@ pub struct Registration {
|
|||||||
pub expires: NaiveDateTime,
|
pub expires: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
use super::Result;
|
use super::{password_reset::Token, Result};
|
||||||
|
|
||||||
impl Registration {
|
impl Registration {
|
||||||
pub async fn insert_new_for_user(pool: &PgPool, user_id: i32) -> Result<Registration> {
|
pub async fn insert_new_for_user(pool: &PgPool, user_id: i32) -> Result<Registration> {
|
||||||
@ -48,3 +48,9 @@ impl Registration {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Token for Registration {
|
||||||
|
async fn delete(&self, pool: &PgPool) -> Result<()> {
|
||||||
|
Registration::delete(pool, &self.token).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,8 @@ pub enum ApplicationError {
|
|||||||
Email(#[from] lettre::error::Error),
|
Email(#[from] lettre::error::Error),
|
||||||
#[error("email transport not working")]
|
#[error("email transport not working")]
|
||||||
EmailTransport(#[from] lettre::transport::smtp::Error),
|
EmailTransport(#[from] lettre::transport::smtp::Error),
|
||||||
|
#[error("hashfunction failed")]
|
||||||
|
Hash(#[from] argon2::password_hash::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl actix_web::error::ResponseError for ApplicationError {
|
impl actix_web::error::ResponseError for ApplicationError {
|
||||||
@ -29,6 +31,7 @@ impl actix_web::error::ResponseError for ApplicationError {
|
|||||||
ApplicationError::EmailAdress(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
ApplicationError::EmailAdress(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
ApplicationError::Email(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
ApplicationError::Email(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
ApplicationError::EmailTransport(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
ApplicationError::EmailTransport(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
ApplicationError::Hash(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,13 +39,11 @@ impl actix_web::error::ResponseError for ApplicationError {
|
|||||||
let mut response = HttpResponse::build(self.status_code());
|
let mut response = HttpResponse::build(self.status_code());
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
ApplicationError::UnsupportedEnumValue { .. } => response.body(self.to_string()),
|
|
||||||
ApplicationError::Unauthorized { .. } => response.body(self.to_string()),
|
|
||||||
ApplicationError::Database(e) => response.body(format!("{self} - {e}")),
|
ApplicationError::Database(e) => response.body(format!("{self} - {e}")),
|
||||||
ApplicationError::EnvVariable(_) => response.body(self.to_string()),
|
|
||||||
ApplicationError::EmailAdress(e) => response.body(format!("{self} - {e}")),
|
ApplicationError::EmailAdress(e) => response.body(format!("{self} - {e}")),
|
||||||
ApplicationError::Email(e) => response.body(format!("{self} - {e}")),
|
ApplicationError::Email(e) => response.body(format!("{self} - {e}")),
|
||||||
ApplicationError::EmailTransport(e) => response.body(format!("{self} - {e}")),
|
ApplicationError::EmailTransport(e) => response.body(format!("{self} - {e}")),
|
||||||
|
_ => response.body(self.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,6 @@ Viele Grüße"##, user.name))
|
|||||||
pub fn build_registration_message(user: &User, token: &str) -> Result<Message, ApplicationError> {
|
pub fn build_registration_message(user: &User, token: &str) -> Result<Message, ApplicationError> {
|
||||||
let hostname = env::var("HOSTNAME")?;
|
let hostname = env::var("HOSTNAME")?;
|
||||||
let register_url = format!("https://{hostname}/register?token={token}");
|
let register_url = format!("https://{hostname}/register?token={token}");
|
||||||
println!("{user:?}");
|
|
||||||
|
|
||||||
let message = Message::builder()
|
let message = Message::builder()
|
||||||
.from("noreply <noreply@brasiwa-leipzig.de>".parse()?)
|
.from("noreply <noreply@brasiwa-leipzig.de>".parse()?)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use zxcvbn::{feedback::{Suggestion, Warning}, Entropy};
|
use zxcvbn::{
|
||||||
|
feedback::{Suggestion, Warning},
|
||||||
|
Entropy,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn generate_for_entropy(entropy: &Entropy) -> String {
|
pub fn generate_for_entropy(entropy: &Entropy) -> String {
|
||||||
let feedback = entropy.feedback().unwrap();
|
let feedback = entropy.feedback().unwrap();
|
||||||
@ -72,3 +75,10 @@ pub fn generate_for_entropy(entropy: &Entropy) -> String {
|
|||||||
|
|
||||||
format!("<div id=\"password-strength\" class=\"mb-3 help content is-danger\"><p>{warning}</p>{vorschlag_text}:<ul>{suggestion}</ul></div>")
|
format!("<div id=\"password-strength\" class=\"mb-3 help content is-danger\"><p>{warning}</p>{vorschlag_text}:<ul>{suggestion}</ul></div>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format_message(level: &str, message: &str) -> String {
|
||||||
|
format!(
|
||||||
|
r#"<div id="password-strength" class="mb-3 help content is-{}">{}</div>"#,
|
||||||
|
level, message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input is-static" type="email" value="{{ user.name }}" readonly>
|
<input class="input is-static" type="email" value="{{ user.email }}" readonly>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user