//! # Users service use crate::app_config::AppConfig; use crate::connections::db_connection; use crate::constants::{ACCOUNT_DELETE_TOKEN_DURATION, PASSWORD_RESET_TOKEN_DURATION}; use crate::models::{NewUser, User, UserID}; use crate::schema::users; use crate::services::mail_service; use crate::utils::string_utils::rand_str; use crate::utils::time_utils::time; use bcrypt::DEFAULT_COST; use diesel::prelude::*; use std::io::ErrorKind; /// Get the information of a user, by its id pub async fn get_by_id(id: UserID) -> anyhow::Result { db_connection::execute(|conn| Ok(users::table.filter(users::dsl::id.eq(id.0)).first(conn)?)) } /// Get the information of a user, by its mail pub async fn get_by_mail(mail: &str) -> anyhow::Result { db_connection::execute(|conn| { Ok(users::table .filter(users::dsl::email.eq(mail)) .first(conn)?) }) } /// Get the information of a user, by its password reset token pub async fn get_by_pwd_reset_token(token: &str) -> anyhow::Result { if token.is_empty() { return Err(anyhow::Error::from(std::io::Error::new( ErrorKind::Other, "Token is empty!", ))); } db_connection::execute(|conn| { Ok(users::table .filter( users::dsl::reset_password_token.eq(token.to_string()).and( users::dsl::time_gen_reset_token .ge(time() as i64 - PASSWORD_RESET_TOKEN_DURATION.as_secs() as i64), ), ) .first(conn)?) }) } /// Get the information of a user, by its account deletion token pub async fn get_by_account_delete_token(token: &str) -> anyhow::Result { if token.is_empty() { return Err(anyhow::Error::from(std::io::Error::new( ErrorKind::Other, "Token is empty!", ))); } db_connection::execute(|conn| { Ok(users::table .filter( users::dsl::delete_account_token.eq(token.to_string()).and( users::dsl::time_gen_delete_account_token .ge(time() as i64 - ACCOUNT_DELETE_TOKEN_DURATION.as_secs() as i64), ), ) .first(conn)?) }) } /// Create a new account pub async fn create_account(name: &str, email: &str) -> anyhow::Result { db_connection::execute(|conn| { let res = diesel::insert_into(users::table) .values(&NewUser { name: name.trim(), email: email.trim(), time_create: time() as i64, }) .get_result(conn)?; Ok(res) }) } /// Check if an email address is already associated with an account pub async fn exists_email(email: &str) -> anyhow::Result { db_connection::execute(|conn| { let count: i64 = users::table .filter(users::email.eq(email)) .count() .get_result(conn)?; Ok(count != 0) }) } /// Request password reset pub async fn request_reset_password(user: &mut User) -> anyhow::Result<()> { // If required, regenerate reset token if user.reset_password_token.is_none() || user.time_gen_reset_token as u64 + 3600 * 2 < time() { user.reset_password_token = Some(rand_str(149)); user.time_gen_reset_token = time() as i64; update_account(user).await?; } // Send mail mail_service::send_mail( &user.email, "Réinitialisation de votre mot de passe", format!( "Bonjour, \n\n\ Vous pouvez réinitialiser le mot de passe de votre compte à l'adresse suivante : {} \n\n\ Ce lien est valide durant 24 heures.\n\n\ Cordialement,\n\n\ L'équipe de GeneIT", AppConfig::get() .get_password_reset_url(user.reset_password_token.as_deref().unwrap_or("")) ), ) .await?; 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 12 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 pub async fn delete_not_validated_accounts() -> anyhow::Result<()> { db_connection::execute(|conn| { diesel::delete( users::dsl::users.filter( users::dsl::time_activate.eq(0).and( users::dsl::time_gen_reset_token .lt(time() as i64 - PASSWORD_RESET_TOKEN_DURATION.as_secs() as i64), ), ), ) .execute(conn)?; Ok(()) }) } /// Mark account as validated pub async fn validate_account(user: &mut User) -> anyhow::Result<()> { if user.time_activate > 0 { log::debug!( "Did not activate account {} because it is already activated!", user.id ); return Ok(()); } user.time_activate = time() as i64; update_account(user).await?; mail_service::send_mail( &user.email, "Activation de votre compte GeneIT", "Bonjour,\n\n\ Votre compte GeneIT a été activé avec succès !\n\n\ Cordialement,\n\n\ L'équipe de GeneIT", ) .await?; Ok(()) } /// Update account information pub async fn update_account(user: &User) -> anyhow::Result<()> { db_connection::execute(|conn| { Ok( diesel::update(users::dsl::users.filter(users::dsl::id.eq(user.id))) .set(( users::dsl::name.eq(user.name.clone()), users::dsl::admin.eq(user.admin), users::dsl::active.eq(user.active), users::dsl::email.eq(user.email.clone()), 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::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::password.eq(user.password.clone()), )) .execute(conn)?, ) })?; Ok(()) } /// Change user paswsord pub async fn change_password(user: &mut User, new_password: &str) -> anyhow::Result<()> { let hash = bcrypt::hash(new_password, DEFAULT_COST)?; user.reset_password_token = None; user.password = Some(hash); update_account(user).await?; Ok(()) }