Skip to content

Commit 659fd92

Browse files
committed
refactor: [#618] extract duplicate code
for password validation and hashing.
1 parent a4de33a commit 659fd92

File tree

1 file changed

+59
-46
lines changed

1 file changed

+59
-46
lines changed

src/services/user.rs

+59-46
Original file line numberDiff line numberDiff line change
@@ -95,29 +95,18 @@ impl RegistrationService {
9595
}
9696
}
9797

98-
if registration_form.password != registration_form.confirm_password {
99-
return Err(ServiceError::PasswordsDontMatch);
100-
}
101-
102-
let password_length = registration_form.password.len();
103-
104-
if password_length <= settings.auth.min_password_length {
105-
return Err(ServiceError::PasswordTooShort);
106-
}
107-
108-
if password_length >= settings.auth.max_password_length {
109-
return Err(ServiceError::PasswordTooLong);
110-
}
111-
112-
let salt = SaltString::generate(&mut OsRng);
98+
let password_constraints = PasswordConstraints {
99+
min_password_length: settings.auth.min_password_length,
100+
max_password_length: settings.auth.max_password_length,
101+
};
113102

114-
// Argon2 with default params (Argon2id v19)
115-
let argon2 = Argon2::default();
103+
validate_password(
104+
&registration_form.password,
105+
&registration_form.confirm_password,
106+
&password_constraints,
107+
)?;
116108

117-
// Hash password to PHC string ($argon2id$v=19$...)
118-
let password_hash = argon2
119-
.hash_password(registration_form.password.as_bytes(), &salt)?
120-
.to_string();
109+
let password_hash = hash_password(&registration_form.password)?;
121110

122111
let user_id = self
123112
.user_repository
@@ -217,35 +206,24 @@ impl ProfileService {
217206

218207
let settings = self.configuration.settings.read().await;
219208

220-
// todo:
221-
// - Validate current password
222-
// - Remove duplicate code for password validation and hashing
209+
// todo: guard that current password matches the one provided in change password form
223210

224-
if change_password_form.password != change_password_form.confirm_password {
225-
return Err(ServiceError::PasswordsDontMatch);
226-
}
227-
228-
let password_length = change_password_form.password.len();
229-
230-
if password_length <= settings.auth.min_password_length {
231-
return Err(ServiceError::PasswordTooShort);
232-
}
233-
234-
if password_length >= settings.auth.max_password_length {
235-
return Err(ServiceError::PasswordTooLong);
236-
}
237-
238-
let salt = SaltString::generate(&mut OsRng);
211+
let password_constraints = PasswordConstraints {
212+
min_password_length: settings.auth.min_password_length,
213+
max_password_length: settings.auth.max_password_length,
214+
};
239215

240-
// Argon2 with default params (Argon2id v19)
241-
let argon2 = Argon2::default();
216+
validate_password(
217+
&change_password_form.password,
218+
&change_password_form.confirm_password,
219+
&password_constraints,
220+
)?;
242221

243-
// Hash password to PHC string ($argon2id$v=19$...)
244-
let new_password_hash = argon2
245-
.hash_password(change_password_form.password.as_bytes(), &salt)?
246-
.to_string();
222+
let password_hash = hash_password(&change_password_form.password)?;
247223

248-
self.user_authentication_repository.change_password(user_id, &new_password_hash).await?;
224+
self.user_authentication_repository
225+
.change_password(user_id, &password_hash)
226+
.await?;
249227

250228
Ok(())
251229
}
@@ -429,3 +407,38 @@ impl DbBannedUserList {
429407
self.database.ban_user(*user_id, &reason, date_expiry).await
430408
}
431409
}
410+
411+
struct PasswordConstraints {
412+
pub min_password_length: usize,
413+
pub max_password_length: usize,
414+
}
415+
416+
fn validate_password(password: &str, confirm_password: &str, password_rules: &PasswordConstraints) -> Result<(), ServiceError> {
417+
if password != confirm_password {
418+
return Err(ServiceError::PasswordsDontMatch);
419+
}
420+
421+
let password_length = password.len();
422+
423+
if password_length <= password_rules.min_password_length {
424+
return Err(ServiceError::PasswordTooShort);
425+
}
426+
427+
if password_length >= password_rules.max_password_length {
428+
return Err(ServiceError::PasswordTooLong);
429+
}
430+
431+
Ok(())
432+
}
433+
434+
fn hash_password(password: &str) -> Result<String, ServiceError> {
435+
let salt = SaltString::generate(&mut OsRng);
436+
437+
// Argon2 with default params (Argon2id v19)
438+
let argon2 = Argon2::default();
439+
440+
// Hash password to PHC string ($argon2id$v=19$...)
441+
let password_hash = argon2.hash_password(password.as_bytes(), &salt)?.to_string();
442+
443+
Ok(password_hash)
444+
}

0 commit comments

Comments
 (0)