use crate::{ authentication::{self, 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( "You entered two different passwords - the field values must match.".to_string(), ) .into()) } else if validate_credentials(credentials, &connection_pool) .await .is_err() { Err(AdminError::ChangePassword("The current password is incorrect.".to_string()).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(|e| AdminError::ChangePassword(e.to_string()))?; let template = MessageTemplate::Success { message: "Your password has been changed.".to_string(), }; Ok(Html(template.render().unwrap()).into_response()) } } fn verify_password(password: &str) -> Result<(), String> { if password.len() < 12 || password.len() > 128 { return Err("The password must contain between 12 and 128 characters.".into()); } Ok(()) }