Skip to content

Commit ecb15e6

Browse files
committed
feat: [torrust#802] New admin action service and reset user password function
1 parent 15cfdda commit ecb15e6

File tree

1 file changed

+60
-0
lines changed

1 file changed

+60
-0
lines changed

src/services/user.rs

+60
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
88
#[cfg(test)]
99
use mockall::automock;
1010
use pbkdf2::password_hash::rand_core::OsRng;
11+
use rand::seq::IteratorRandom;
1112
use serde_derive::Deserialize;
1213
use tracing::{debug, info};
1314

1415
use super::authentication::DbUserAuthenticationRepository;
1516
use super::authorization::{self, ACTION};
17+
use crate::config::v2::auth::Auth;
1618
use crate::config::{Configuration, PasswordConstraints};
1719
use crate::databases::database::{Database, Error};
1820
use crate::errors::ServiceError;
@@ -405,6 +407,44 @@ impl ListingService {
405407
}
406408
}
407409

410+
pub struct AdminActionsService {
411+
authorization_service: Arc<authorization::Service>,
412+
user_authentication_repository: Arc<DbUserAuthenticationRepository>,
413+
}
414+
415+
impl AdminActionsService {
416+
/// Resets the password of the selected user.
417+
///
418+
/// # Errors
419+
///
420+
/// This function will return a:
421+
///
422+
/// * `ServiceError::InvalidPassword` if the current password supplied is invalid.
423+
/// * `ServiceError::PasswordsDontMatch` if the supplied passwords do not match.
424+
/// * `ServiceError::PasswordTooShort` if the supplied password is too short.
425+
/// * `ServiceError::PasswordTooLong` if the supplied password is too long.
426+
/// * An error if unable to successfully hash the password.
427+
/// * An error if unable to change the password in the database.
428+
/// * An error if it is not possible to authorize the action
429+
pub async fn reset_user_password(&self, maybe_user_id: Option<UserId>, user_info: UserProfile) -> Result<(), ServiceError> {
430+
self.authorization_service
431+
.authorize(ACTION::ResetUserPassword, maybe_user_id)
432+
.await?;
433+
434+
info!("Resetting user password for user ID: {}", user_info.username);
435+
436+
let new_password = generate_random_password();
437+
438+
let password_hash = hash_password(&new_password)?;
439+
440+
self.user_authentication_repository
441+
.change_password(user_info.user_id, &password_hash)
442+
.await?;
443+
444+
Ok(())
445+
}
446+
}
447+
408448
#[cfg_attr(test, automock)]
409449
#[async_trait]
410450
pub trait Repository: Sync + Send {
@@ -578,3 +618,23 @@ fn hash_password(password: &str) -> Result<String, ServiceError> {
578618

579619
Ok(password_hash)
580620
}
621+
622+
//Generates a random password with numbers, letters and special characters with a length of the max length allow for users's passwords
623+
fn generate_random_password() -> String {
624+
let charset = "2A&,B;C8D!G?HIJ@KL5MN1OPQ#RST]U`VW*XYZ\
625+
{ab)c~d$ef=g.h<i_jklmn%op>qr/st6u+vw}xyz\
626+
|0-EF3^4[7(:9\
627+
";
628+
629+
let mut rng = rand::thread_rng();
630+
631+
let password_constraints = Auth::default().password_constraints;
632+
633+
let password_length = password_constraints.max_password_length;
634+
635+
let password: String = (0..password_length)
636+
.map(|_| charset.chars().choose(&mut rng).unwrap())
637+
.collect();
638+
639+
password
640+
}

0 commit comments

Comments
 (0)