diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index c8e807e..02c0112 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -1,7 +1,7 @@ use std::net::{SocketAddr, IpAddr, Ipv4Addr}; use portfolio_core::{ - crypto::random_8_char_string, + crypto::random_12_char_string, services::{admin_service::AdminService, candidate_service::CandidateService, application_service::ApplicationService, portfolio_service::PortfolioService}, models::candidate::{BaseCandidateResponse, CreateCandidateResponse, ApplicationDetails}, sea_orm::prelude::Uuid, Query, error::ServiceError, utils::csv, }; use requests::{AdminLoginRequest, RegisterRequest}; @@ -90,7 +90,7 @@ pub async fn create_candidate( let db = conn.into_inner(); let form = request.into_inner(); - let plain_text_password = random_8_char_string(); + let plain_text_password = random_12_char_string(); ApplicationService::create_candidate_with_parent( db, @@ -287,6 +287,6 @@ pub mod tests { let cookies = admin_login(&client); let response = create_candidate(&client, cookies, 1031511, "0".to_string()); - assert_eq!(response.password.len(), 8); + assert_eq!(response.password.len(), 12); } } \ No newline at end of file diff --git a/core/src/crypto.rs b/core/src/crypto.rs index cc6f0a4..ae36c4a 100644 --- a/core/src/crypto.rs +++ b/core/src/crypto.rs @@ -13,24 +13,29 @@ use std::str::FromStr; use crate::error::ServiceError; -/// Foolproof random 8 char string -/// only uppercase letters (except for 0 and O) and numbers -pub fn random_8_char_string() -> String { - let iterator = rand::thread_rng() +/// Foolproof random 12 char string +/// Only uppercase letters (except for O) and numbers (except for 0) +pub fn random_12_char_string() -> String { + let random_chars_12: Vec = rand::thread_rng() .sample_iter(&rand::distributions::Alphanumeric) - .map(char::from); + .map(char::from) + .filter(is_usable_char) + .take(12) + .collect(); + + random_chars_12 + .iter() + .map(|c| c.to_string()) + .collect::>() + .join("") +} - let mut s = String::new(); - for c in iterator { - // add all characters except for: lowercase chars, 0 and O - if ('1'..='9').contains(&c) || ('A'..='N').contains(&c) || ('P'..'Z').contains(&c) { - s.push(c); - if s.len() == 8 { - break; - } - } - } - s +/// Exclude O and 0, lowercase letters +fn is_usable_char(c: &char) -> bool { + ('1'..='9').contains(&c) || + ('A'..='N').contains(&c) || + ('P'..'Z').contains(&c) || + ['@', '#', '$', '%'].contains(&c) } pub async fn hash_password(password_plain_text: String) -> Result { @@ -334,11 +339,11 @@ pub async fn decrypt_file_with_private_key_as_buffer>( #[cfg(test)] mod tests { #[test] - fn test_random_8_char_string() { + fn test_random_12_char_string() { for _ in 0..1000 { - let s = super::random_8_char_string(); + let s = super::random_12_char_string(); // Is 8 chars long - assert_eq!(s.len(), 8); + assert_eq!(s.len(), 12); // Does not contain possibly confusing characters assert!(!s.contains('0')); assert!(!s.contains('O')); @@ -388,7 +393,7 @@ mod tests { ); assert!(key_2.len() >= 32); - let key_3 = super::convert_key_aes256(&super::random_8_char_string()); + let key_3 = super::convert_key_aes256(&super::random_12_char_string()); assert!(key_3.len() >= 32); } diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index d38d365..dc0639a 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -105,7 +105,7 @@ impl CandidateService { let parents = Query::find_candidate_parents(db, &candidate).await?; - let new_password_plain = crypto::random_8_char_string(); + let new_password_plain = crypto::random_12_char_string(); let new_password_hash = crypto::hash_password(new_password_plain.clone()).await?; let (pubkey, priv_key_plain_text) = crypto::create_identity();