use rustlavel::prelude::*;
use crate::models::password_history::PasswordHistory;
use crate::support::settings::Settings;
pub async fn keep(req: &Request) -> i64 {
let raw = match req.state::<Settings>() {
Some(settings) => settings.get("auth.password.reuse").await,
None => req.config().string("auth.password.reuse", "0"),
};
raw.parse::<i64>().unwrap_or(0).clamp(0, 24)
}
pub async fn was_used_before(db: &Database, user_id: i64, password: &str, keep: i64) -> Result<bool> {
if keep == 0 {
return Ok(false);
}
if let Some(current) = crate::models::user::User::find(db, user_id)
.await?
.and_then(|user| user.password_hash)
{
if rustlavel::auth::verify_password(password, ¤t) {
return Ok(true);
}
}
let history = PasswordHistory::get(db, PasswordHistory::for_user(user_id).limit(keep)).await?;
Ok(history.iter().any(|row| rustlavel::auth::verify_password(password, &row.password_hash)))
}
pub async fn remember_previous(db: &Database, user_id: i64, outgoing: Option<&str>, keep: i64) -> Result<()> {
let (Some(hash), true) = (outgoing, keep > 0) else {
return Ok(());
};
PasswordHistory { user_id, password_hash: hash.to_string(), ..Default::default() }
.insert(db)
.await?;
let window = (keep - 1).max(1);
let kept = PasswordHistory::get(db, PasswordHistory::for_user(user_id).limit(window)).await?;
if let Some(oldest) = kept.last() {
PasswordHistory::query()
.filter("user_id", user_id)
.filter_op("id", "<", oldest.id)
.delete(db)
.await?;
}
Ok(())
}
pub fn reuse_message(keep: i64) -> String {
format!("That is one of your last {keep} passwords. Choose one you have not used here before.")
}