use crate::{ authentication::{self, AuthError, AuthenticatedUser, Credentials, validate_credentials}, routes::{AdminError, AppError}, startup::AppState, templates::MessageTemplate, }; use askama::Template; use axum::{ Extension, Form, extract::State, response::{Html, IntoResponse, Response}, }; use secrecy::{ExposeSecret, SecretString}; #[derive(serde::Deserialize)] pub struct PasswordFormData { pub current_password: SecretString, pub new_password: SecretString, pub new_password_check: SecretString, } pub async fn change_password( Extension(AuthenticatedUser { user_id, username }): Extension, State(AppState { connection_pool, .. }): State, Form(form): Form, ) -> Result { let credentials = Credentials { username, password: form.current_password, }; if form.new_password.expose_secret() != form.new_password_check.expose_secret() { Err(AdminError::ChangePassword(anyhow::anyhow!( "You entered two different passwords - the field values must match." )) .into()) } else if let Err(e) = validate_credentials(credentials, &connection_pool).await { match e { AuthError::UnexpectedError(error) => Err(AdminError::UnexpectedError(error).into()), AuthError::InvalidCredentials(_) => Err(AdminError::ChangePassword(anyhow::anyhow!( "The current password is incorrect." )) .into()), } } else if let Err(e) = verify_password(form.new_password.expose_secret()) { Err(AdminError::ChangePassword(e).into()) } else { authentication::change_password(user_id, form.new_password, &connection_pool) .await .map_err(AdminError::ChangePassword)?; let template = MessageTemplate::success("Your password has been changed.".to_string()); Ok(Html(template.render().unwrap()).into_response()) } } fn verify_password(password: &str) -> Result<(), anyhow::Error> { if password.len() < 12 || password.len() > 128 { anyhow::bail!("The password must contain between 12 and 128 characters."); } Ok(()) }