Can request account deletion
This commit is contained in:
parent
1a8211c13d
commit
4b8baa2416
@ -4,9 +4,11 @@ CREATE TABLE users (
|
|||||||
name VARCHAR(30) NOT NULL,
|
name VARCHAR(30) NOT NULL,
|
||||||
email VARCHAR(255) NOT NULL,
|
email VARCHAR(255) NOT NULL,
|
||||||
password VARCHAR NULL,
|
password VARCHAR NULL,
|
||||||
reset_password_token VARCHAR(150) NULL,
|
|
||||||
time_create BIGINT NOT NULL,
|
time_create BIGINT NOT NULL,
|
||||||
|
reset_password_token VARCHAR(150) NULL,
|
||||||
time_gen_reset_token BIGINT NOT NULL DEFAULT 0,
|
time_gen_reset_token BIGINT NOT NULL DEFAULT 0,
|
||||||
|
delete_account_token VARCHAR(150) NULL,
|
||||||
|
time_gen_delete_account_token BIGINT NOT NULL DEFAULT 0,
|
||||||
time_activate BIGINT NOT NULL DEFAULT 0,
|
time_activate BIGINT NOT NULL DEFAULT 0,
|
||||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
admin BOOLEAN NOT NULL DEFAULT FALSE
|
admin BOOLEAN NOT NULL DEFAULT FALSE
|
||||||
|
@ -88,6 +88,14 @@ pub struct AppConfig {
|
|||||||
)]
|
)]
|
||||||
pub reset_password_url: String,
|
pub reset_password_url: String,
|
||||||
|
|
||||||
|
/// Delete account URL
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
env,
|
||||||
|
default_value = "http://localhost:3000/delete_account#TOKEN"
|
||||||
|
)]
|
||||||
|
pub delete_account_url: String,
|
||||||
|
|
||||||
/// URL where the OpenID configuration can be found
|
/// URL where the OpenID configuration can be found
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
@ -154,6 +162,11 @@ impl AppConfig {
|
|||||||
self.reset_password_url.replace("TOKEN", token)
|
self.reset_password_url.replace("TOKEN", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get account delete URL
|
||||||
|
pub fn get_account_delete_url(&self, token: &str) -> String {
|
||||||
|
self.delete_account_url.replace("TOKEN", token)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get OpenID providers configuration
|
/// Get OpenID providers configuration
|
||||||
pub fn openid_providers(&self) -> Vec<OIDCProvider<'_>> {
|
pub fn openid_providers(&self) -> Vec<OIDCProvider<'_>> {
|
||||||
if self.disable_oidc {
|
if self.disable_oidc {
|
||||||
|
@ -97,3 +97,19 @@ pub async fn replace_password(
|
|||||||
|
|
||||||
Ok(HttpResponse::Accepted().finish())
|
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())
|
||||||
|
}
|
||||||
|
@ -62,6 +62,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
"/user/replace_password",
|
"/user/replace_password",
|
||||||
web::post().to(user_controller::replace_password),
|
web::post().to(user_controller::replace_password),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/user/request_delete",
|
||||||
|
web::get().to(user_controller::request_delete_account),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.bind(AppConfig::get().listen_address.as_str())?
|
.bind(AppConfig::get().listen_address.as_str())?
|
||||||
.run()
|
.run()
|
||||||
|
@ -12,11 +12,15 @@ pub struct User {
|
|||||||
pub email: String,
|
pub email: String,
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
pub password: Option<String>,
|
pub password: Option<String>,
|
||||||
#[serde(skip_serializing)]
|
|
||||||
pub reset_password_token: Option<String>,
|
|
||||||
pub time_create: i64,
|
pub time_create: i64,
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
|
pub reset_password_token: Option<String>,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
pub time_gen_reset_token: i64,
|
pub time_gen_reset_token: i64,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub delete_account_token: Option<String>,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub time_gen_delete_account_token: i64,
|
||||||
pub time_activate: i64,
|
pub time_activate: i64,
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub admin: bool,
|
pub admin: bool,
|
||||||
|
@ -6,9 +6,11 @@ diesel::table! {
|
|||||||
name -> Varchar,
|
name -> Varchar,
|
||||||
email -> Varchar,
|
email -> Varchar,
|
||||||
password -> Nullable<Varchar>,
|
password -> Nullable<Varchar>,
|
||||||
reset_password_token -> Nullable<Varchar>,
|
|
||||||
time_create -> Int8,
|
time_create -> Int8,
|
||||||
|
reset_password_token -> Nullable<Varchar>,
|
||||||
time_gen_reset_token -> Int8,
|
time_gen_reset_token -> Int8,
|
||||||
|
delete_account_token -> Nullable<Varchar>,
|
||||||
|
time_gen_delete_account_token -> Int8,
|
||||||
time_activate -> Int8,
|
time_activate -> Int8,
|
||||||
active -> Bool,
|
active -> Bool,
|
||||||
admin -> Bool,
|
admin -> Bool,
|
||||||
|
@ -11,6 +11,7 @@ pub enum RatedAction {
|
|||||||
FailedPasswordLogin,
|
FailedPasswordLogin,
|
||||||
StartOpenIDLogin,
|
StartOpenIDLogin,
|
||||||
RequestReplacePasswordSignedIn,
|
RequestReplacePasswordSignedIn,
|
||||||
|
RequestDeleteAccount,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RatedAction {
|
impl RatedAction {
|
||||||
@ -21,7 +22,8 @@ impl RatedAction {
|
|||||||
RatedAction::RequestNewPasswordResetLink => "req-pwd-reset-lnk",
|
RatedAction::RequestNewPasswordResetLink => "req-pwd-reset-lnk",
|
||||||
RatedAction::FailedPasswordLogin => "failed-login",
|
RatedAction::FailedPasswordLogin => "failed-login",
|
||||||
RatedAction::StartOpenIDLogin => "start-oidc-login",
|
RatedAction::StartOpenIDLogin => "start-oidc-login",
|
||||||
RatedAction::RequestReplacePasswordSignedIn => "rep-pwd-signed-in",
|
RatedAction::RequestReplacePasswordSignedIn => "req-pwd-signed-in",
|
||||||
|
RatedAction::RequestDeleteAccount => "req-del-acct",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +35,7 @@ impl RatedAction {
|
|||||||
RatedAction::FailedPasswordLogin => 15,
|
RatedAction::FailedPasswordLogin => 15,
|
||||||
RatedAction::StartOpenIDLogin => 30,
|
RatedAction::StartOpenIDLogin => 30,
|
||||||
RatedAction::RequestReplacePasswordSignedIn => 5,
|
RatedAction::RequestReplacePasswordSignedIn => 5,
|
||||||
|
RatedAction::RequestDeleteAccount => 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +103,37 @@ pub async fn request_reset_password(user: &mut User) -> anyhow::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request delete account
|
||||||
|
pub async fn request_delete_account(user: &mut User) -> anyhow::Result<()> {
|
||||||
|
// If required, regenerate token
|
||||||
|
if user.delete_account_token.is_none()
|
||||||
|
|| user.time_gen_delete_account_token as u64 + 3600 * 2 < time()
|
||||||
|
{
|
||||||
|
user.delete_account_token = Some(rand_str(149));
|
||||||
|
user.time_gen_delete_account_token = time() as i64;
|
||||||
|
|
||||||
|
update_account(user).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send mail
|
||||||
|
mail_service::send_mail(
|
||||||
|
&user.email,
|
||||||
|
"Suppression de votre compte",
|
||||||
|
format!(
|
||||||
|
"Bonjour, \n\n\
|
||||||
|
Vous avez demandé la suppression de votre compte GeneIT. Cette opération peut être effectuée via le lien suivant : {} \n\n\
|
||||||
|
Ce lien est valide durant 24 heures.\n\n\
|
||||||
|
Cordialement,\n\n\
|
||||||
|
L'équipe de GeneIT",
|
||||||
|
AppConfig::get()
|
||||||
|
.get_account_delete_url(user.delete_account_token.as_deref().unwrap_or(""))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete not validated accounts whose reset token has expired
|
/// Delete not validated accounts whose reset token has expired
|
||||||
pub async fn delete_not_validated_accounts() -> anyhow::Result<()> {
|
pub async fn delete_not_validated_accounts() -> anyhow::Result<()> {
|
||||||
db_connection::execute(|conn| {
|
db_connection::execute(|conn| {
|
||||||
@ -158,6 +189,9 @@ pub async fn update_account(user: &User) -> anyhow::Result<()> {
|
|||||||
users::dsl::email.eq(user.email.clone()),
|
users::dsl::email.eq(user.email.clone()),
|
||||||
users::dsl::time_gen_reset_token.eq(user.time_gen_reset_token),
|
users::dsl::time_gen_reset_token.eq(user.time_gen_reset_token),
|
||||||
users::dsl::reset_password_token.eq(user.reset_password_token.clone()),
|
users::dsl::reset_password_token.eq(user.reset_password_token.clone()),
|
||||||
|
users::dsl::time_gen_delete_account_token
|
||||||
|
.eq(user.time_gen_delete_account_token),
|
||||||
|
users::dsl::delete_account_token.eq(user.delete_account_token.clone()),
|
||||||
users::dsl::time_activate.eq(time() as i64),
|
users::dsl::time_activate.eq(time() as i64),
|
||||||
users::dsl::password.eq(user.password.clone()),
|
users::dsl::password.eq(user.password.clone()),
|
||||||
))
|
))
|
||||||
|
Loading…
Reference in New Issue
Block a user