fix(users): add password validations (#4555)

Co-authored-by: Rachit Naithani <rachit.naithani@juspay.in>
This commit is contained in:
Riddhiagrawal001
2024-05-07 18:38:46 +05:30
committed by GitHub
parent 1b5b566387
commit 25fe4deb8e
3 changed files with 59 additions and 7 deletions

View File

@ -1,3 +1,5 @@
pub const MAX_NAME_LENGTH: usize = 70;
pub const MAX_COMPANY_NAME_LENGTH: usize = 70;
pub const BUSINESS_EMAIL: &str = "biz@hyperswitch.io";
pub const MAX_PASSWORD_LENGTH: usize = 70;
pub const MIN_PASSWORD_LENGTH: usize = 8;

View File

@ -158,11 +158,43 @@ pub struct UserPassword(Secret<String>);
impl UserPassword {
pub fn new(password: Secret<String>) -> UserResult<Self> {
let password = password.expose();
if password.is_empty() {
Err(UserErrors::PasswordParsingError.into())
} else {
Ok(Self(password.into()))
let mut has_upper_case = false;
let mut has_lower_case = false;
let mut has_numeric_value = false;
let mut has_special_character = false;
let mut has_whitespace = false;
for c in password.chars() {
has_upper_case = has_upper_case || c.is_uppercase();
has_lower_case = has_lower_case || c.is_lowercase();
has_numeric_value = has_numeric_value || c.is_numeric();
has_special_character =
has_special_character || !(c.is_alphanumeric() && c.is_whitespace());
has_whitespace = has_whitespace || c.is_whitespace();
}
let is_password_format_valid = has_upper_case
&& has_lower_case
&& has_numeric_value
&& has_special_character
&& !has_whitespace;
let is_too_long = password.graphemes(true).count() > consts::user::MAX_PASSWORD_LENGTH;
let is_too_short = password.graphemes(true).count() < consts::user::MIN_PASSWORD_LENGTH;
if is_too_short || is_too_long || !is_password_format_valid {
return Err(UserErrors::PasswordParsingError.into());
}
Ok(Self(password.into()))
}
pub fn new_password_without_validation(password: Secret<String>) -> UserResult<Self> {
let password = password.expose();
if password.is_empty() {
return Err(UserErrors::PasswordParsingError.into());
}
Ok(Self(password.into()))
}
pub fn get_secret(&self) -> Secret<String> {
@ -636,7 +668,7 @@ impl TryFrom<user_api::ConnectAccountRequest> for NewUser {
let user_id = uuid::Uuid::new_v4().to_string();
let email = value.email.clone().try_into()?;
let name = UserName::try_from(value.email.clone())?;
let password = UserPassword::new(uuid::Uuid::new_v4().to_string().into())?;
let password = UserPassword::new(password::get_temp_password())?;
let new_merchant = NewUserMerchant::try_from(value)?;
Ok(Self {
@ -680,7 +712,7 @@ impl TryFrom<UserMerchantCreateRequestWithToken> for NewUser {
user_id: user.0.user_id,
name: UserName::new(user.0.name)?,
email: user.0.email.clone().try_into()?,
password: UserPassword::new(user.0.password)?,
password: UserPassword::new_password_without_validation(user.0.password)?,
new_merchant,
})
}
@ -692,7 +724,7 @@ impl TryFrom<InviteeUserRequestWithInvitedUserToken> for NewUser {
let user_id = uuid::Uuid::new_v4().to_string();
let email = value.0.email.clone().try_into()?;
let name = UserName::new(value.0.name.clone())?;
let password = UserPassword::new(uuid::Uuid::new_v4().to_string().into())?;
let password = UserPassword::new(password::get_temp_password())?;
let new_merchant = NewUserMerchant::try_from(value)?;
Ok(Self {

View File

@ -8,6 +8,7 @@ use argon2::{
use common_utils::errors::CustomResult;
use error_stack::ResultExt;
use masking::{ExposeInterface, Secret};
use rand::{seq::SliceRandom, Rng};
use crate::core::errors::UserErrors;
@ -38,3 +39,20 @@ pub fn is_correct_password(
}
.change_context(UserErrors::InternalServerError)
}
pub fn get_temp_password() -> Secret<String> {
let uuid_pass = uuid::Uuid::new_v4().to_string();
let mut rng = rand::thread_rng();
let special_chars: Vec<char> = "!@#$%^&*()-_=+[]{}|;:,.<>?".chars().collect();
let special_char = special_chars.choose(&mut rng).unwrap_or(&'@');
Secret::new(format!(
"{}{}{}{}{}",
uuid_pass,
rng.gen_range('A'..='Z'),
special_char,
rng.gen_range('a'..='z'),
rng.gen_range('0'..='9'),
))
}