feat: move password reset tokens to valkey
Also just as useless to keep in DB
This commit is contained in:
parent
b223dff4ba
commit
419f37b108
5 changed files with 37 additions and 62 deletions
|
@ -0,0 +1,7 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
CREATE TABLE password_reset_tokens (
|
||||||
|
token VARCHAR(64) NOT NULL,
|
||||||
|
user_uuid uuid UNIQUE NOT NULL REFERENCES users(uuid),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL,
|
||||||
|
PRIMARY KEY (token, user_uuid)
|
||||||
|
);
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
DROP TABLE password_reset_tokens;
|
|
@ -26,13 +26,11 @@ struct Query {
|
||||||
///
|
///
|
||||||
#[get("/reset-password")]
|
#[get("/reset-password")]
|
||||||
pub async fn get(query: web::Query<Query>, data: web::Data<Data>) -> Result<HttpResponse, Error> {
|
pub async fn get(query: web::Query<Query>, data: web::Data<Data>) -> Result<HttpResponse, Error> {
|
||||||
let mut conn = data.pool.get().await?;
|
|
||||||
|
|
||||||
if let Ok(password_reset_token) =
|
if let Ok(password_reset_token) =
|
||||||
PasswordResetToken::get_with_identifier(&mut conn, query.identifier.clone()).await
|
PasswordResetToken::get_with_identifier(&data, query.identifier.clone()).await
|
||||||
{
|
{
|
||||||
if Utc::now().signed_duration_since(password_reset_token.created_at) > Duration::hours(1) {
|
if Utc::now().signed_duration_since(password_reset_token.created_at) > Duration::hours(1) {
|
||||||
password_reset_token.delete(&mut conn).await?;
|
password_reset_token.delete(&data).await?;
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::TooManyRequests(
|
return Err(Error::TooManyRequests(
|
||||||
"Please allow 1 hour before sending a new email".to_string(),
|
"Please allow 1 hour before sending a new email".to_string(),
|
||||||
|
@ -74,15 +72,8 @@ pub async fn post(
|
||||||
reset_password: web::Json<ResetPassword>,
|
reset_password: web::Json<ResetPassword>,
|
||||||
data: web::Data<Data>,
|
data: web::Data<Data>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let mut conn = data.pool.get().await?;
|
|
||||||
|
|
||||||
let password_reset_token =
|
let password_reset_token =
|
||||||
PasswordResetToken::get(&mut conn, reset_password.token.clone()).await?;
|
PasswordResetToken::get(&data, reset_password.token.clone()).await?;
|
||||||
|
|
||||||
if Utc::now().signed_duration_since(password_reset_token.created_at) > Duration::hours(24) {
|
|
||||||
password_reset_token.delete(&mut conn).await?;
|
|
||||||
return Ok(HttpResponse::Gone().finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
password_reset_token
|
password_reset_token
|
||||||
.set_password(&data, reset_password.password.clone())
|
.set_password(&data, reset_password.password.clone())
|
||||||
|
|
|
@ -4,23 +4,21 @@ use argon2::{
|
||||||
};
|
};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper, delete, dsl::now,
|
ExpressionMethods, QueryDsl, update,
|
||||||
insert_into, update,
|
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lettre::message::MultiPart;
|
use lettre::message::MultiPart;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Conn, Data,
|
Data,
|
||||||
error::Error,
|
error::Error,
|
||||||
schema::{password_reset_tokens, users},
|
schema::users,
|
||||||
utils::{PASSWORD_REGEX, generate_refresh_token, global_checks, user_uuid_from_identifier},
|
utils::{PASSWORD_REGEX, generate_refresh_token, global_checks, user_uuid_from_identifier},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Selectable, Queryable)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[diesel(table_name = password_reset_tokens)]
|
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
|
||||||
pub struct PasswordResetToken {
|
pub struct PasswordResetToken {
|
||||||
user_uuid: Uuid,
|
user_uuid: Uuid,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
|
@ -28,29 +26,22 @@ pub struct PasswordResetToken {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PasswordResetToken {
|
impl PasswordResetToken {
|
||||||
pub async fn get(conn: &mut Conn, token: String) -> Result<PasswordResetToken, Error> {
|
pub async fn get(data: &Data, token: String) -> Result<PasswordResetToken, Error> {
|
||||||
use password_reset_tokens::dsl;
|
let user_uuid: Uuid = serde_json::from_str(&data.get_cache_key(format!("{}", token)).await?)?;
|
||||||
let password_reset_token = dsl::password_reset_tokens
|
let password_reset_token = serde_json::from_str(&data.get_cache_key(format!("{}_password_reset", user_uuid)).await?)?;
|
||||||
.filter(dsl::token.eq(token))
|
|
||||||
.select(PasswordResetToken::as_select())
|
|
||||||
.get_result(conn)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(password_reset_token)
|
Ok(password_reset_token)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_with_identifier(
|
pub async fn get_with_identifier(
|
||||||
conn: &mut Conn,
|
data: &Data,
|
||||||
identifier: String,
|
identifier: String,
|
||||||
) -> Result<PasswordResetToken, Error> {
|
) -> Result<PasswordResetToken, Error> {
|
||||||
let user_uuid = user_uuid_from_identifier(conn, &identifier).await?;
|
let mut conn = data.pool.get().await?;
|
||||||
|
|
||||||
use password_reset_tokens::dsl;
|
let user_uuid = user_uuid_from_identifier(&mut conn, &identifier).await?;
|
||||||
let password_reset_token = dsl::password_reset_tokens
|
|
||||||
.filter(dsl::user_uuid.eq(user_uuid))
|
let password_reset_token = serde_json::from_str(&data.get_cache_key(format!("{}_password_reset", user_uuid)).await?)?;
|
||||||
.select(PasswordResetToken::as_select())
|
|
||||||
.get_result(conn)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(password_reset_token)
|
Ok(password_reset_token)
|
||||||
}
|
}
|
||||||
|
@ -72,15 +63,14 @@ impl PasswordResetToken {
|
||||||
.get_result(&mut conn)
|
.get_result(&mut conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
use password_reset_tokens::dsl;
|
let password_reset_token = PasswordResetToken {
|
||||||
insert_into(password_reset_tokens::table)
|
user_uuid,
|
||||||
.values((
|
token: token.clone(),
|
||||||
dsl::user_uuid.eq(user_uuid),
|
created_at: Utc::now(),
|
||||||
dsl::token.eq(&token),
|
};
|
||||||
dsl::created_at.eq(now),
|
|
||||||
))
|
data.set_cache_key(format!("{}_password_reset", user_uuid), password_reset_token, 86400).await?;
|
||||||
.execute(&mut conn)
|
data.set_cache_key(token.clone(), user_uuid, 86400).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut reset_endpoint = data.config.web.frontend_url.join("reset-password")?;
|
let mut reset_endpoint = data.config.web.frontend_url.join("reset-password")?;
|
||||||
|
|
||||||
|
@ -144,16 +134,12 @@ impl PasswordResetToken {
|
||||||
|
|
||||||
data.mail_client.send_mail(email).await?;
|
data.mail_client.send_mail(email).await?;
|
||||||
|
|
||||||
self.delete(&mut conn).await
|
self.delete(&data).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(&self, conn: &mut Conn) -> Result<(), Error> {
|
pub async fn delete(&self, data: &Data) -> Result<(), Error> {
|
||||||
use password_reset_tokens::dsl;
|
data.del_cache_key(format!("{}_password_reset", &self.user_uuid)).await?;
|
||||||
delete(password_reset_tokens::table)
|
data.del_cache_key(format!("{}", &self.token)).await?;
|
||||||
.filter(dsl::user_uuid.eq(self.user_uuid))
|
|
||||||
.filter(dsl::token.eq(&self.token))
|
|
||||||
.execute(conn)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,15 +80,6 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
password_reset_tokens (token, user_uuid) {
|
|
||||||
#[max_length = 64]
|
|
||||||
token -> Varchar,
|
|
||||||
user_uuid -> Uuid,
|
|
||||||
created_at -> Timestamptz,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
refresh_tokens (token) {
|
refresh_tokens (token) {
|
||||||
#[max_length = 64]
|
#[max_length = 64]
|
||||||
|
@ -154,7 +145,6 @@ diesel::joinable!(invites -> guilds (guild_uuid));
|
||||||
diesel::joinable!(invites -> users (user_uuid));
|
diesel::joinable!(invites -> users (user_uuid));
|
||||||
diesel::joinable!(messages -> channels (channel_uuid));
|
diesel::joinable!(messages -> channels (channel_uuid));
|
||||||
diesel::joinable!(messages -> users (user_uuid));
|
diesel::joinable!(messages -> users (user_uuid));
|
||||||
diesel::joinable!(password_reset_tokens -> users (user_uuid));
|
|
||||||
diesel::joinable!(refresh_tokens -> users (uuid));
|
diesel::joinable!(refresh_tokens -> users (uuid));
|
||||||
diesel::joinable!(role_members -> guild_members (member_uuid));
|
diesel::joinable!(role_members -> guild_members (member_uuid));
|
||||||
diesel::joinable!(roles -> guilds (guild_uuid));
|
diesel::joinable!(roles -> guilds (guild_uuid));
|
||||||
|
@ -168,7 +158,6 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
instance_permissions,
|
instance_permissions,
|
||||||
invites,
|
invites,
|
||||||
messages,
|
messages,
|
||||||
password_reset_tokens,
|
|
||||||
refresh_tokens,
|
refresh_tokens,
|
||||||
role_members,
|
role_members,
|
||||||
roles,
|
roles,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue