Tell the user if the reset password token is expired.
This commit is contained in:
parent
ed979719b5
commit
37f6de7a89
4 changed files with 34 additions and 25 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -40,9 +40,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.18"
|
version = "0.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
|
|
@ -381,9 +381,9 @@ checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.36"
|
version = "1.1.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70"
|
checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
@ -737,9 +737,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.1.1"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
|
|
@ -2351,9 +2351,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.13.0"
|
version = "3.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
|
|
||||||
|
|
@ -74,12 +74,18 @@ pub enum AuthenticationResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GetTokenResetPassword {
|
pub enum GetTokenResetPasswordResult {
|
||||||
PasswordAlreadyReset,
|
PasswordAlreadyReset,
|
||||||
EmailUnknown,
|
EmailUnknown,
|
||||||
Ok(String),
|
Ok(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ResetPasswordResult {
|
||||||
|
ResetTokenExpired,
|
||||||
|
Ok,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Connection {
|
pub struct Connection {
|
||||||
pool: Pool<Sqlite>,
|
pool: Pool<Sqlite>,
|
||||||
|
|
@ -440,7 +446,7 @@ WHERE [id] = $1
|
||||||
&self,
|
&self,
|
||||||
email: &str,
|
email: &str,
|
||||||
validation_time: Duration,
|
validation_time: Duration,
|
||||||
) -> Result<GetTokenResetPassword> {
|
) -> Result<GetTokenResetPasswordResult> {
|
||||||
let mut tx = self.tx().await?;
|
let mut tx = self.tx().await?;
|
||||||
|
|
||||||
if let Some(db_datetime_nullable) = sqlx::query_scalar::<_, Option<DateTime<Utc>>>(
|
if let Some(db_datetime_nullable) = sqlx::query_scalar::<_, Option<DateTime<Utc>>>(
|
||||||
|
|
@ -456,11 +462,11 @@ WHERE [email] = $1
|
||||||
{
|
{
|
||||||
if let Some(db_datetime) = db_datetime_nullable {
|
if let Some(db_datetime) = db_datetime_nullable {
|
||||||
if Utc::now() - db_datetime <= validation_time {
|
if Utc::now() - db_datetime <= validation_time {
|
||||||
return Ok(GetTokenResetPassword::PasswordAlreadyReset);
|
return Ok(GetTokenResetPasswordResult::PasswordAlreadyReset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(GetTokenResetPassword::EmailUnknown);
|
return Ok(GetTokenResetPasswordResult::EmailUnknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = generate_token();
|
let token = generate_token();
|
||||||
|
|
@ -480,7 +486,7 @@ WHERE [email] = $1
|
||||||
|
|
||||||
tx.commit().await?;
|
tx.commit().await?;
|
||||||
|
|
||||||
Ok(GetTokenResetPassword::Ok(token))
|
Ok(GetTokenResetPasswordResult::Ok(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn reset_password(
|
pub async fn reset_password(
|
||||||
|
|
@ -488,7 +494,7 @@ WHERE [email] = $1
|
||||||
new_password: &str,
|
new_password: &str,
|
||||||
token: &str,
|
token: &str,
|
||||||
validation_time: Duration,
|
validation_time: Duration,
|
||||||
) -> Result<()> {
|
) -> Result<ResetPasswordResult> {
|
||||||
let mut tx = self.tx().await?;
|
let mut tx = self.tx().await?;
|
||||||
// There is no index on [password_reset_token]. Is it useful?
|
// There is no index on [password_reset_token]. Is it useful?
|
||||||
if let (user_id, Some(db_datetime)) = sqlx::query_as::<_, (i64, Option<DateTime<Utc>>)>(
|
if let (user_id, Some(db_datetime)) = sqlx::query_as::<_, (i64, Option<DateTime<Utc>>)>(
|
||||||
|
|
@ -503,9 +509,7 @@ WHERE [password_reset_token] = $1
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
if Utc::now() - db_datetime > validation_time {
|
if Utc::now() - db_datetime > validation_time {
|
||||||
return Err(DBError::Other(
|
return Ok(ResetPasswordResult::ResetTokenExpired);
|
||||||
"Can't reset password: validation time exceeded".to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all login tokens (for security reasons).
|
// Remove all login tokens (for security reasons).
|
||||||
|
|
@ -530,7 +534,7 @@ WHERE [id] = $1
|
||||||
|
|
||||||
tx.commit().await?;
|
tx.commit().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(ResetPasswordResult::Ok)
|
||||||
} else {
|
} else {
|
||||||
Err(DBError::Other(
|
Err(DBError::Other(
|
||||||
"Can't reset password: stored token or datetime not set (NULL)".to_string(),
|
"Can't reset password: stored token or datetime not set (NULL)".to_string(),
|
||||||
|
|
@ -983,7 +987,7 @@ VALUES (
|
||||||
.get_token_reset_password(email, Duration::hours(1))
|
.get_token_reset_password(email, Duration::hours(1))
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
GetTokenResetPassword::EmailUnknown => Ok(()), // Nominal case.
|
GetTokenResetPasswordResult::EmailUnknown => Ok(()), // Nominal case.
|
||||||
other => panic!("{:?}", other),
|
other => panic!("{:?}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1031,7 +1035,7 @@ VALUES (
|
||||||
.get_token_reset_password(email, Duration::hours(1))
|
.get_token_reset_password(email, Duration::hours(1))
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
GetTokenResetPassword::Ok(token) => token,
|
GetTokenResetPasswordResult::Ok(token) => token,
|
||||||
other => panic!("{:?}", other),
|
other => panic!("{:?}", other),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -574,15 +574,15 @@ pub async fn ask_reset_password_post(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(db::GetTokenResetPassword::PasswordAlreadyReset) => error_response(
|
Ok(db::GetTokenResetPasswordResult::PasswordAlreadyReset) => error_response(
|
||||||
AskResetPasswordError::EmailAlreadyReset,
|
AskResetPasswordError::EmailAlreadyReset,
|
||||||
&form_data.email,
|
&form_data.email,
|
||||||
user,
|
user,
|
||||||
),
|
),
|
||||||
Ok(db::GetTokenResetPassword::EmailUnknown) => {
|
Ok(db::GetTokenResetPasswordResult::EmailUnknown) => {
|
||||||
error_response(AskResetPasswordError::EmailUnknown, &form_data.email, user)
|
error_response(AskResetPasswordError::EmailUnknown, &form_data.email, user)
|
||||||
}
|
}
|
||||||
Ok(db::GetTokenResetPassword::Ok(token)) => {
|
Ok(db::GetTokenResetPasswordResult::Ok(token)) => {
|
||||||
let url = utils::get_url_from_host(&host);
|
let url = utils::get_url_from_host(&host);
|
||||||
match email::send_email(
|
match email::send_email(
|
||||||
&form_data.email,
|
&form_data.email,
|
||||||
|
|
@ -663,6 +663,7 @@ pub struct ResetPasswordForm {
|
||||||
enum ResetPasswordError {
|
enum ResetPasswordError {
|
||||||
PasswordsNotEqual,
|
PasswordsNotEqual,
|
||||||
InvalidPassword,
|
InvalidPassword,
|
||||||
|
TokenExpired,
|
||||||
DatabaseError,
|
DatabaseError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -691,6 +692,7 @@ pub async fn reset_password_post(
|
||||||
}
|
}
|
||||||
.to_string(),
|
.to_string(),
|
||||||
message: match error {
|
message: match error {
|
||||||
|
ResetPasswordError::TokenExpired => "Token expired, try to reset password again",
|
||||||
ResetPasswordError::DatabaseError => "Database error",
|
ResetPasswordError::DatabaseError => "Database error",
|
||||||
_ => "",
|
_ => "",
|
||||||
}
|
}
|
||||||
|
|
@ -717,11 +719,14 @@ pub async fn reset_password_post(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => Ok(MessageTemplate {
|
Ok(db::ResetPasswordResult::Ok) => Ok(MessageTemplate {
|
||||||
user,
|
user,
|
||||||
message: "Your password has been reset",
|
message: "Your password has been reset",
|
||||||
}
|
}
|
||||||
.into_response()),
|
.into_response()),
|
||||||
|
Ok(db::ResetPasswordResult::ResetTokenExpired) => {
|
||||||
|
error_response(ResetPasswordError::TokenExpired, &form_data, user)
|
||||||
|
}
|
||||||
Err(_) => error_response(ResetPasswordError::DatabaseError, &form_data, user),
|
Err(_) => error_response(ResetPasswordError::DatabaseError, &form_data, user),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue