2023-05-31 13:52:49 +00:00
|
|
|
//! # User controller
|
|
|
|
//!
|
|
|
|
//! The actions of the user on his account when he is authenticated.
|
|
|
|
|
2023-06-05 16:52:00 +00:00
|
|
|
use crate::constants::StaticConstraints;
|
2023-05-31 13:52:49 +00:00
|
|
|
use crate::controllers::HttpResult;
|
2023-06-05 16:41:56 +00:00
|
|
|
use crate::models::User;
|
2023-05-31 13:52:49 +00:00
|
|
|
use crate::services::login_token_service::LoginToken;
|
2023-06-05 17:02:51 +00:00
|
|
|
use crate::services::rate_limiter_service::RatedAction;
|
|
|
|
use crate::services::{rate_limiter_service, users_service};
|
|
|
|
use actix_remote_ip::RemoteIP;
|
2023-06-05 16:52:00 +00:00
|
|
|
use actix_web::web::Json;
|
2023-05-31 13:52:49 +00:00
|
|
|
use actix_web::HttpResponse;
|
|
|
|
|
2023-06-05 16:41:56 +00:00
|
|
|
#[derive(serde::Serialize)]
|
|
|
|
struct UserAPI<'a> {
|
|
|
|
#[serde(flatten)]
|
|
|
|
user: &'a User,
|
|
|
|
has_password: bool,
|
|
|
|
}
|
|
|
|
|
2023-05-31 13:52:49 +00:00
|
|
|
/// Get account information
|
|
|
|
pub async fn auth_info(token: LoginToken) -> HttpResult {
|
|
|
|
let user = users_service::get_by_id(token.user_id).await?;
|
|
|
|
|
2023-06-05 16:41:56 +00:00
|
|
|
Ok(HttpResponse::Ok().json(UserAPI {
|
|
|
|
user: &user,
|
|
|
|
has_password: user
|
|
|
|
.password
|
|
|
|
.as_deref()
|
|
|
|
.map(|p| !p.is_empty())
|
|
|
|
.unwrap_or_default(),
|
|
|
|
}))
|
2023-05-31 13:52:49 +00:00
|
|
|
}
|
2023-06-05 16:52:00 +00:00
|
|
|
|
|
|
|
#[derive(serde::Deserialize)]
|
2023-06-05 17:02:51 +00:00
|
|
|
pub struct ProfileUpdateQuery {
|
2023-06-05 16:52:00 +00:00
|
|
|
name: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update profile information
|
2023-06-05 17:02:51 +00:00
|
|
|
pub async fn update_profile(token: LoginToken, profile: Json<ProfileUpdateQuery>) -> HttpResult {
|
2023-06-05 16:52:00 +00:00
|
|
|
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;
|
2023-06-05 17:11:28 +00:00
|
|
|
users_service::update_account(&user).await?;
|
2023-06-05 16:52:00 +00:00
|
|
|
|
|
|
|
Ok(HttpResponse::Accepted().finish())
|
|
|
|
}
|
2023-06-05 17:02:51 +00:00
|
|
|
|
|
|
|
#[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<ReplacePasswordQuery>,
|
|
|
|
) -> 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
|
2023-06-05 17:04:31 +00:00
|
|
|
.validate(&q.new_password)
|
2023-06-05 17:02:51 +00:00
|
|
|
{
|
2023-06-14 13:23:23 +00:00
|
|
|
return Ok(HttpResponse::BadRequest().json("Invalid new password!"));
|
2023-06-05 17:02:51 +00:00
|
|
|
}
|
|
|
|
|
2023-06-05 17:11:28 +00:00
|
|
|
let mut user = users_service::get_by_id(token.user_id).await?;
|
2023-06-05 17:02:51 +00:00
|
|
|
if !user.check_password(&q.old_password) {
|
|
|
|
rate_limiter_service::record_action(
|
|
|
|
remote_ip.0,
|
|
|
|
RatedAction::RequestReplacePasswordSignedIn,
|
|
|
|
)
|
|
|
|
.await?;
|
2023-06-14 13:23:23 +00:00
|
|
|
return Ok(HttpResponse::Unauthorized().json("Invalid old password!"));
|
2023-06-05 17:02:51 +00:00
|
|
|
}
|
|
|
|
|
2023-06-05 17:11:28 +00:00
|
|
|
users_service::change_password(&mut user, &q.new_password).await?;
|
2023-06-05 17:02:51 +00:00
|
|
|
|
|
|
|
Ok(HttpResponse::Accepted().finish())
|
|
|
|
}
|
2023-06-06 07:47:52 +00:00
|
|
|
|
|
|
|
/// 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())
|
|
|
|
}
|
2023-06-06 08:02:41 +00:00
|
|
|
|
|
|
|
#[derive(serde::Deserialize)]
|
2023-06-06 08:19:54 +00:00
|
|
|
pub struct DeleteAccountTokenBody {
|
2023-06-06 08:02:41 +00:00
|
|
|
token: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(serde::Serialize)]
|
|
|
|
struct CheckDeleteTokenResponse {
|
|
|
|
email: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check delete account token
|
2023-06-06 08:19:54 +00:00
|
|
|
pub async fn check_delete_token(req: Json<DeleteAccountTokenBody>) -> HttpResult {
|
|
|
|
let user = users_service::get_by_account_delete_token(&req.token).await?;
|
2023-06-06 08:02:41 +00:00
|
|
|
|
2023-06-06 08:19:54 +00:00
|
|
|
Ok(HttpResponse::Ok().json(CheckDeleteTokenResponse { email: user.email }))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Delete account
|
|
|
|
pub async fn delete_account(req: Json<DeleteAccountTokenBody>) -> HttpResult {
|
|
|
|
let user = users_service::get_by_account_delete_token(&req.token).await?;
|
|
|
|
|
|
|
|
users_service::delete_account(&user).await?;
|
|
|
|
|
|
|
|
Ok(HttpResponse::Accepted().finish())
|
2023-06-06 08:02:41 +00:00
|
|
|
}
|