diff --git a/geneit_backend/src/controllers/user_controller.rs b/geneit_backend/src/controllers/user_controller.rs index 9319f20..1f52408 100644 --- a/geneit_backend/src/controllers/user_controller.rs +++ b/geneit_backend/src/controllers/user_controller.rs @@ -6,7 +6,9 @@ use crate::constants::StaticConstraints; use crate::controllers::HttpResult; use crate::models::User; use crate::services::login_token_service::LoginToken; -use crate::services::users_service; +use crate::services::rate_limiter_service::RatedAction; +use crate::services::{rate_limiter_service, users_service}; +use actix_remote_ip::RemoteIP; use actix_web::web::Json; use actix_web::HttpResponse; @@ -32,12 +34,12 @@ pub async fn auth_info(token: LoginToken) -> HttpResult { } #[derive(serde::Deserialize)] -pub struct ProfileUpdate { +pub struct ProfileUpdateQuery { name: String, } /// Update profile information -pub async fn update_profile(token: LoginToken, profile: Json) -> HttpResult { +pub async fn update_profile(token: LoginToken, profile: Json) -> HttpResult { if !StaticConstraints::default() .user_name_len .validate(&profile.name) @@ -51,3 +53,47 @@ pub async fn update_profile(token: LoginToken, profile: Json) -> Ok(HttpResponse::Accepted().finish()) } + +#[derive(serde::Deserialize)] +pub struct ReplacePasswordQuery { + old_password: String, + new_password: String, +} + +/// Replace user password +pub async fn replace_password( + remote_ip: RemoteIP, + token: LoginToken, + q: Json, +) -> HttpResult { + // Rate limiting + if rate_limiter_service::should_block_action( + remote_ip.0, + RatedAction::RequestReplacePasswordSignedIn, + ) + .await? + { + return Ok(HttpResponse::TooManyRequests().finish()); + } + + if !StaticConstraints::default() + .password_len + .validate(&q.old_password) + { + return Ok(HttpResponse::BadRequest().json("Nouveau mot de passe invalide!")); + } + + let user = users_service::get_by_id(token.user_id).await?; + if !user.check_password(&q.old_password) { + rate_limiter_service::record_action( + remote_ip.0, + RatedAction::RequestReplacePasswordSignedIn, + ) + .await?; + return Ok(HttpResponse::BadRequest().json("Ancien mot de passe invalide !")); + } + + users_service::change_password(&user, &q.new_password).await?; + + Ok(HttpResponse::Accepted().finish()) +} diff --git a/geneit_backend/src/main.rs b/geneit_backend/src/main.rs index b262fe6..6f1deca 100644 --- a/geneit_backend/src/main.rs +++ b/geneit_backend/src/main.rs @@ -58,6 +58,10 @@ async fn main() -> std::io::Result<()> { "/user/update_profile", web::post().to(user_controller::update_profile), ) + .route( + "/user/replace_password", + web::post().to(user_controller::replace_password), + ) }) .bind(AppConfig::get().listen_address.as_str())? .run() diff --git a/geneit_backend/src/services/rate_limiter_service.rs b/geneit_backend/src/services/rate_limiter_service.rs index e819029..560be1a 100644 --- a/geneit_backend/src/services/rate_limiter_service.rs +++ b/geneit_backend/src/services/rate_limiter_service.rs @@ -10,6 +10,7 @@ pub enum RatedAction { RequestNewPasswordResetLink, FailedPasswordLogin, StartOpenIDLogin, + RequestReplacePasswordSignedIn, } impl RatedAction { @@ -20,6 +21,7 @@ impl RatedAction { RatedAction::RequestNewPasswordResetLink => "req-pwd-reset-lnk", RatedAction::FailedPasswordLogin => "failed-login", RatedAction::StartOpenIDLogin => "start-oidc-login", + RatedAction::RequestReplacePasswordSignedIn => "rep-pwd-signed-in", } } @@ -30,6 +32,7 @@ impl RatedAction { RatedAction::RequestNewPasswordResetLink => 5, RatedAction::FailedPasswordLogin => 15, RatedAction::StartOpenIDLogin => 30, + RatedAction::RequestReplacePasswordSignedIn => 5, } }