Do not update user email if there is an error when sending the validation link to the new email
This commit is contained in:
parent
45f4e2d169
commit
3ab168fd67
10 changed files with 103 additions and 68 deletions
|
|
@ -8,6 +8,7 @@ use crate::{
|
|||
consts,
|
||||
data::model,
|
||||
hash::{hash, verify_password},
|
||||
services::user,
|
||||
};
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
|
|
@ -20,8 +21,8 @@ pub enum SignUpResult {
|
|||
#[derive(Debug, Display)]
|
||||
pub enum UpdateUserResult {
|
||||
EmailAlreadyTaken,
|
||||
/// Validation token.
|
||||
UserUpdatedWaitingForRevalidation(String),
|
||||
/// (New validation token, old email, old validation token, old validation time).
|
||||
UserUpdatedWaitingForRevalidation(String, String, Option<String>, String),
|
||||
Ok,
|
||||
}
|
||||
|
||||
|
|
@ -105,11 +106,22 @@ FROM [UserLoginToken] WHERE [token] = $1
|
|||
let mut tx = self.tx().await?;
|
||||
let hashed_new_password = new_password.map(|p| hash(p).unwrap());
|
||||
|
||||
let (email, name, default_servings, first_day_of_the_week, hashed_password) = sqlx::query_as::<
|
||||
_,
|
||||
(String, String, u32, u8, String),
|
||||
>(
|
||||
"SELECT [email], [name], [default_servings], [first_day_of_the_week], [password] FROM [User] WHERE [id] = $1",
|
||||
let (
|
||||
email,
|
||||
name,
|
||||
default_servings,
|
||||
first_day_of_the_week,
|
||||
hashed_password,
|
||||
validation_token,
|
||||
validation_token_datetime,
|
||||
) = sqlx::query_as::<_, (String, String, u32, u8, String, Option<String>, String)>(
|
||||
r#"
|
||||
SELECT
|
||||
[email], [name], [default_servings],
|
||||
[first_day_of_the_week], [password],
|
||||
[validation_token], [validation_token_datetime]
|
||||
FROM [User] WHERE [id] = $1
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_one(&mut *tx)
|
||||
|
|
@ -119,7 +131,7 @@ FROM [UserLoginToken] WHERE [token] = $1
|
|||
let email_changed = new_email.is_some_and(|new_email| new_email != email);
|
||||
|
||||
// Check if email not already taken.
|
||||
let validation_token = if email_changed {
|
||||
let new_validation_token = if email_changed {
|
||||
if sqlx::query_scalar(
|
||||
r#"
|
||||
SELECT COUNT(*) > 0
|
||||
|
|
@ -170,13 +182,41 @@ WHERE [id] = $1
|
|||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(if let Some(validation_token) = validation_token {
|
||||
UpdateUserResult::UserUpdatedWaitingForRevalidation(validation_token)
|
||||
Ok(if let Some(new_validation_token) = new_validation_token {
|
||||
UpdateUserResult::UserUpdatedWaitingForRevalidation(
|
||||
new_validation_token,
|
||||
email,
|
||||
validation_token,
|
||||
validation_token_datetime,
|
||||
)
|
||||
} else {
|
||||
UpdateUserResult::Ok
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn update_user_email_and_token(
|
||||
&self,
|
||||
user_id: i64,
|
||||
email: &str,
|
||||
validation_token: Option<&str>,
|
||||
validation_token_datetime: &str,
|
||||
) -> Result<()> {
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE [User]
|
||||
SET [email] = $2, [validation_token] = $3, [validation_token_datetime] = $4
|
||||
WHERE [id] = $1"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(email)
|
||||
.bind(validation_token)
|
||||
.bind(validation_token_datetime)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(DBError::from)
|
||||
}
|
||||
|
||||
pub async fn set_user_lang(&self, user_id: i64, lang: &str) -> Result<()> {
|
||||
sqlx::query("UPDATE [User] SET [lang] = $2 WHERE [id] = $1")
|
||||
.bind(user_id)
|
||||
|
|
@ -975,7 +1015,7 @@ VALUES
|
|||
assert_eq!(user.name, "paul");
|
||||
assert_eq!(user.email, "paul@atreides.com");
|
||||
|
||||
if let UpdateUserResult::UserUpdatedWaitingForRevalidation(token) = connection
|
||||
if let UpdateUserResult::UserUpdatedWaitingForRevalidation(token, _, _, _) = connection
|
||||
.update_user(
|
||||
1,
|
||||
Some("muaddib@fremen.com"),
|
||||
|
|
|
|||
|
|
@ -474,9 +474,6 @@ pub async fn ask_reset_password_post(
|
|||
email,
|
||||
message_email: match error {
|
||||
AskResetPasswordError::InvalidEmail => context.tr.t(Sentence::InvalidEmail),
|
||||
_ => "",
|
||||
},
|
||||
message: match error {
|
||||
AskResetPasswordError::EmailAlreadyReset => {
|
||||
context.tr.t(Sentence::AskResetEmailAlreadyResetError)
|
||||
}
|
||||
|
|
@ -484,6 +481,9 @@ pub async fn ask_reset_password_post(
|
|||
AskResetPasswordError::UnableSendEmail(_) => {
|
||||
context.tr.t(Sentence::UnableToSendResetEmail)
|
||||
}
|
||||
_ => "",
|
||||
},
|
||||
message: match error {
|
||||
AskResetPasswordError::DatabaseError(_) => {
|
||||
context.tr.t(Sentence::DatabaseError)
|
||||
}
|
||||
|
|
@ -853,7 +853,12 @@ pub async fn edit_user_post(
|
|||
Ok(db::user::UpdateUserResult::EmailAlreadyTaken) => {
|
||||
return error_response(ProfileUpdateError::EmailAlreadyTaken, &form_data, context);
|
||||
}
|
||||
Ok(db::user::UpdateUserResult::UserUpdatedWaitingForRevalidation(token)) => {
|
||||
Ok(db::user::UpdateUserResult::UserUpdatedWaitingForRevalidation(
|
||||
token,
|
||||
old_email,
|
||||
old_token,
|
||||
old_token_datetime,
|
||||
)) => {
|
||||
let url = utils::get_url_from_host(&host);
|
||||
let email = form_data.email.clone();
|
||||
match email_service
|
||||
|
|
@ -874,6 +879,22 @@ pub async fn edit_user_post(
|
|||
message = context.tr.t(Sentence::ProfileEmailSent);
|
||||
}
|
||||
Err(error) => {
|
||||
// If the email can't be set we revert the changes about email and token.
|
||||
if let Err(error) = connection
|
||||
.update_user_email_and_token(
|
||||
user.id,
|
||||
&old_email,
|
||||
old_token.as_deref(),
|
||||
&old_token_datetime,
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
"Unable to set email and token: (email={}): {}",
|
||||
email, error
|
||||
);
|
||||
}
|
||||
|
||||
return error_response(
|
||||
ProfileUpdateError::UnableToSendEmail(error),
|
||||
&form_data,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue