//! # User controller //! //! The actions of the user on his account when he is authenticated. use crate::constants::StaticConstraints; use crate::controllers::HttpResult; use crate::models::User; use crate::services::login_token_service::LoginToken; 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; #[derive(serde::Serialize)] struct UserAPI<'a> { #[serde(flatten)] user: &'a User, has_password: bool, } /// Get account information pub async fn auth_info(token: LoginToken) -> HttpResult { let user = users_service::get_by_id(token.user_id).await?; Ok(HttpResponse::Ok().json(UserAPI { user: &user, has_password: user .password .as_deref() .map(|p| !p.is_empty()) .unwrap_or_default(), })) } #[derive(serde::Deserialize)] pub struct ProfileUpdateQuery { name: String, } /// Update profile information pub async fn update_profile(token: LoginToken, profile: Json) -> HttpResult { if !StaticConstraints::default() .user_name_len .validate(&profile.name) { return Ok(HttpResponse::BadRequest().json("Nom invalide!")); } let mut user = users_service::get_by_id(token.user_id).await?; user.name = profile.0.name; users_service::update_account(&user).await?; 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.new_password) { return Ok(HttpResponse::BadRequest().json("Invalid new password!")); } let mut 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::Unauthorized().json("Invalid old password!")); } users_service::change_password(&mut user, &q.new_password).await?; Ok(HttpResponse::Accepted().finish()) } /// Request delete account pub async fn request_delete_account(remote_ip: RemoteIP, token: LoginToken) -> HttpResult { // Rate limiting if rate_limiter_service::should_block_action(remote_ip.0, RatedAction::RequestDeleteAccount) .await? { return Ok(HttpResponse::TooManyRequests().finish()); } rate_limiter_service::record_action(remote_ip.0, RatedAction::RequestDeleteAccount).await?; let mut user = users_service::get_by_id(token.user_id).await?; users_service::request_delete_account(&mut user).await?; Ok(HttpResponse::Accepted().finish()) } #[derive(serde::Deserialize)] pub struct DeleteAccountTokenBody { token: String, } #[derive(serde::Serialize)] struct CheckDeleteTokenResponse { email: String, } /// Check delete account token pub async fn check_delete_token(req: Json) -> HttpResult { let user = users_service::get_by_account_delete_token(&req.token).await?; Ok(HttpResponse::Ok().json(CheckDeleteTokenResponse { email: user.email })) } /// Delete account pub async fn delete_account(req: Json) -> HttpResult { let user = users_service::get_by_account_delete_token(&req.token).await?; users_service::delete_account(&user).await?; Ok(HttpResponse::Accepted().finish()) }