From e627ef99cc935d241bf54aaec8b20578329f6f1a Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Fri, 7 Jul 2023 18:18:03 +0100 Subject: [PATCH] refactor: use a third party package for email valildation --- project-words.txt | 2 ++ src/services/user.rs | 2 +- src/utils/mod.rs | 2 +- src/utils/regex.rs | 38 -------------------- src/utils/validation.rs | 79 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 40 deletions(-) delete mode 100644 src/utils/regex.rs create mode 100644 src/utils/validation.rs diff --git a/project-words.txt b/project-words.txt index b770bb51..59fe8833 100644 --- a/project-words.txt +++ b/project-words.txt @@ -15,11 +15,13 @@ Cyberneering datetime DATETIME Dont +dotless Grünwald hasher Hasher hexlify httpseeds +ICANN imagoodboy imdl indexadmin diff --git a/src/services/user.rs b/src/services/user.rs index fd1be257..f8a25b93 100644 --- a/src/services/user.rs +++ b/src/services/user.rs @@ -13,7 +13,7 @@ use crate::errors::ServiceError; use crate::mailer; use crate::mailer::VerifyClaims; use crate::models::user::{UserCompact, UserId, UserProfile}; -use crate::utils::regex::validate_email_address; +use crate::utils::validation::validate_email_address; use crate::web::api::v1::contexts::user::forms::RegistrationForm; /// Since user email could be optional, we need a way to represent "no email" diff --git a/src/utils/mod.rs b/src/utils/mod.rs index cf6e4d3a..ebb62358 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,4 +1,4 @@ pub mod clock; pub mod hex; pub mod parse_torrent; -pub mod regex; +pub mod validation; diff --git a/src/utils/regex.rs b/src/utils/regex.rs deleted file mode 100644 index 356c315d..00000000 --- a/src/utils/regex.rs +++ /dev/null @@ -1,38 +0,0 @@ -use regex::Regex; - -/// Validates an email address. -/// -/// # Panics -/// -/// It panics if the regex fails to compile. -#[must_use] -pub fn validate_email_address(email_address_to_be_checked: &str) -> bool { - let email_regex = Regex::new(r"^([a-z\d_+]([a-z\d_+.]*[a-z\d_+])?)@([a-z\d]+([\-.][a-z\d]+)*\.[a-z]{2,6})") - .expect("regex failed to compile"); - - email_regex.is_match(email_address_to_be_checked) -} - -#[cfg(test)] -mod tests { - use crate::utils::regex::validate_email_address; - - #[test] - fn validate_email_address_test() { - assert!(!validate_email_address("test")); - - assert!(!validate_email_address("test@")); - - assert!(!validate_email_address("test@torrust")); - - assert!(!validate_email_address("test@torrust.")); - - assert!(!validate_email_address("test@.")); - - assert!(!validate_email_address("test@.com")); - - assert!(validate_email_address("test@torrust.com")); - - assert!(validate_email_address("t@torrust.org")); - } -} diff --git a/src/utils/validation.rs b/src/utils/validation.rs new file mode 100644 index 00000000..9c4eeb8a --- /dev/null +++ b/src/utils/validation.rs @@ -0,0 +1,79 @@ +use std::str::FromStr; + +use email_address::EmailAddress; +use regex::Regex; + +const MIN_DOMAIN_LENGTH: usize = 4; + +/// Validates an email address. +/// +/// # Panics +/// +/// It panics if the email address is invalid. This should not happen +/// because the email address is previously validated. +#[must_use] +pub fn validate_email_address(email_address_to_be_checked: &str) -> bool { + if !EmailAddress::is_valid(email_address_to_be_checked) { + return false; + } + + let email = EmailAddress::from_str(email_address_to_be_checked).expect("Invalid email address"); + + // We reject anyway the email if it's a dotless domain name. + domain_has_extension(email.domain()) +} + +/// Returns true if the string representing a domain has an extension. +/// +/// It does not check if the extension is valid. +fn domain_has_extension(domain: &str) -> bool { + if domain.len() < MIN_DOMAIN_LENGTH { + return false; + } + + Regex::new(r".*\..*").expect("Invalid regex").is_match(domain) +} + +#[cfg(test)] +mod tests { + + mod for_email_validation { + use crate::utils::validation::validate_email_address; + + #[test] + fn it_should_accept_valid_email_addresses() { + assert!(validate_email_address("test@torrust.com")); + assert!(validate_email_address("t@torrust.org")); + } + + #[test] + fn it_should_not_accept_invalid_email_addresses() { + assert!(!validate_email_address("test")); + assert!(!validate_email_address("test@")); + assert!(!validate_email_address("test@torrust.")); + assert!(!validate_email_address("test@.")); + assert!(!validate_email_address("test@.com")); + + // Notice that local domain name with no TLD are valid, + // although ICANN highly discourages dotless email addresses + assert!(!validate_email_address("test@torrust")); + } + } + + mod for_domain_validation { + use crate::utils::validation::domain_has_extension; + + #[test] + fn it_should_accept_valid_domain_with_extension() { + assert!(domain_has_extension("a.io")); + assert!(domain_has_extension("a.com")); + } + + #[test] + fn it_should_not_accept_dotless_domains() { + assert!(!domain_has_extension("")); + assert!(!domain_has_extension(".")); + assert!(!domain_has_extension("a.")); + } + } +}