person::{PasswordReset, PasswordResetResponse},
utils::send_password_reset_email,
};
+use lemmy_db_schema::source::password_reset_request::PasswordResetRequest;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
.await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_that_username_or_email"))?;
+ // Check for too many attempts (to limit potential abuse)
+ let recent_resets_count = PasswordResetRequest::get_recent_password_resets_count(
+ context.pool(),
+ local_user_view.local_user.id,
+ )
+ .await?;
+ if recent_resets_count >= 3 {
+ return Err(LemmyError::from_message("password_reset_limit_reached"));
+ }
+
// Email the pure token to the user.
send_password_reset_email(&local_user_view, context.pool(), context.settings()).await?;
Ok(PasswordResetResponse {})
use crate::{
newtypes::LocalUserId,
- schema::password_reset_request::dsl::{password_reset_request, published, token_encrypted},
+ schema::password_reset_request::dsl::{
+ local_user_id,
+ password_reset_request,
+ published,
+ token_encrypted,
+ },
source::password_reset_request::{PasswordResetRequest, PasswordResetRequestForm},
traits::Crud,
utils::{get_conn, DbPool},
.first::<Self>(conn)
.await
}
+
+ pub async fn get_recent_password_resets_count(
+ pool: &DbPool,
+ user_id: LocalUserId,
+ ) -> Result<i64, Error> {
+ let conn = &mut get_conn(pool).await?;
+ password_reset_request
+ .filter(local_user_id.eq(user_id))
+ .filter(published.gt(now - 1.days()))
+ .count()
+ .get_result(conn)
+ .await
+ }
}
fn bytes_to_hex(bytes: Vec<u8>) -> String {