-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
AYS-674 | Email Address Validation Has Been Refactored (#433)
- Loading branch information
1 parent
03ff13a
commit d892c24
Showing
5 changed files
with
219 additions
and
203 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,76 +4,76 @@ | |
import jakarta.validation.ConstraintValidatorContext; | ||
import org.springframework.util.StringUtils; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* A custom validator implementation for the {@link EmailAddress} annotation. | ||
* Validates whether the provided email matches a specified set of rules | ||
* to ensure it's in a valid format. | ||
*/ | ||
class EmailAddressValidator implements ConstraintValidator<EmailAddress, String> { | ||
|
||
private static final String EMAIL_REGEX = | ||
"^(?!.*\\.\\.|.*--|.*-@|.*@\\.|.*\\.-|.*-\\.).+[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"; | ||
@SuppressWarnings("java:S5998") | ||
private static final Pattern EMAIL_REGEX = Pattern.compile( | ||
"^(?!.*\\.{2})" + | ||
"[\\p{Alnum}][\\p{Alnum}._%+\\-]*" + | ||
"@" + | ||
"(?!-)(?:[\\p{Alnum}]+(?<!-)\\.)+" + | ||
"[\\p{Alpha}]{2,}$", | ||
Pattern.UNICODE_CHARACTER_CLASS | ||
); | ||
|
||
/** | ||
* Checks whether the given value is a valid email or not. | ||
* Checks whether the given value is a valid emailAddress or not. | ||
* | ||
* <p>Some valid emails are:</p> | ||
* <ul> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* </ul> | ||
* | ||
* <p>Some invalid emails are:</p> | ||
* <ul> | ||
* <li>user@invalid</li> | ||
* <li>user@invalid!.com</li> | ||
* <li>u@[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>user</li> | ||
* <li>plainaddress</li> | ||
* <li>@missingusername.com</li> | ||
* <li>[email protected]</li> | ||
* <li>username@gmail</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected]</li> | ||
* <li>[email protected].</li> | ||
* <li>[email protected]</li> | ||
* <li>username@[email protected]</li> | ||
* <li>username(john.doe)@gmail.com</li> | ||
* <li>user@domain(comment).com</li> | ||
* <li>usernamegmail.com</li> | ||
* <li>username@gmail,com</li> | ||
* <li>username@gmail space.co</li> | ||
* <li>[email protected]</li> | ||
* <li>user#gmail.com</li> | ||
* </ul> | ||
* | ||
* @param email object to validate | ||
* @param emailAddress object to validate | ||
* @return true if the value is valid, false otherwise | ||
*/ | ||
@Override | ||
public boolean isValid(String email, ConstraintValidatorContext constraintValidatorContext) { | ||
public boolean isValid(String emailAddress, ConstraintValidatorContext constraintValidatorContext) { | ||
|
||
if (!StringUtils.hasText(email)) { | ||
if (!StringUtils.hasText(emailAddress)) { | ||
return true; | ||
} | ||
|
||
if (email.startsWith(" ") || email.endsWith(" ")) { | ||
return this.buildViolation(constraintValidatorContext, "email must not start or end with whitespace"); | ||
} | ||
|
||
if (email.chars().filter(ch -> ch == '@').count() != 1) { | ||
return this.buildViolation(constraintValidatorContext, "email must contain exactly one '@' character"); | ||
} | ||
|
||
if (email.matches(".*[()#\\[\\]\";,\\s].*")) { | ||
return this.buildViolation(constraintValidatorContext, "email contains invalid special characters"); | ||
} | ||
|
||
String[] parts = email.split("@", 2); | ||
if (parts[0].isEmpty() || !Character.isLetterOrDigit(parts[0].charAt(0))) { | ||
return this.buildViolation(constraintValidatorContext, "email local part must start with a letter or number"); | ||
} | ||
|
||
String domainPart = parts[1]; | ||
if (domainPart.startsWith("-") || domainPart.endsWith("-")) { | ||
return this.buildViolation(constraintValidatorContext, "domain must not start or end with a hyphen"); | ||
} | ||
|
||
if (!email.matches(EMAIL_REGEX)) { | ||
return this.buildViolation(constraintValidatorContext, "email is not in a valid format"); | ||
} | ||
|
||
return true; | ||
return EMAIL_REGEX.matcher(emailAddress).matches(); | ||
} | ||
|
||
private boolean buildViolation(ConstraintValidatorContext constraintValidatorContext, String message) { | ||
constraintValidatorContext.disableDefaultConstraintViolation(); | ||
constraintValidatorContext.buildConstraintViolationWithTemplate(message).addConstraintViolation(); | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -363,13 +363,28 @@ void givenIdAndAdminRegisterApplication_whenAdminApplicationNotFound_thenReturnU | |
} | ||
|
||
|
||
@Test | ||
void givenValidAdminRegisterRequest_whenAdminRegistered_thenReturnSuccessResponse() throws Exception { | ||
@ParameterizedTest | ||
@ValueSource(strings = { | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]" | ||
}) | ||
void givenValidAdminRegisterRequest_whenAdminRegistered_thenReturnSuccessResponse(String mockEmailAddress) throws Exception { | ||
|
||
// Given | ||
String mockId = "e8de09dc-a44e-40eb-bcc7-cf0141f8733c"; | ||
AdminRegistrationApplicationCompleteRequest mockRequest = new AdminRegistrationApplicationCompleteRequestBuilder() | ||
.withValidValues().build(); | ||
.withValidValues() | ||
.withEmailAddress(mockEmailAddress) | ||
.build(); | ||
|
||
// When | ||
Mockito.doNothing().when(adminRegistrationCompleteService).complete(Mockito.anyString(), Mockito.any()); | ||
|
@@ -574,34 +589,32 @@ void givenInvalidAdminRegisterApplicationCompleteRequestWithParametrizedInvalidN | |
|
||
@ParameterizedTest | ||
@ValueSource(strings = { | ||
"[email protected]", | ||
"abc.def@mail#archive.com", | ||
"abc.def@mail", | ||
"[email protected]", | ||
"[email protected]", | ||
"admin@[email protected]", | ||
"[email protected]", | ||
"plainaddress", | ||
"@missingusername.com", | ||
"[email protected]", | ||
"username@gmail", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected].", | ||
"[email protected]", | ||
"username@[email protected]", | ||
"username(john.doe)@gmail.com", | ||
"user@domain(comment).com", | ||
"usernamegmail.com", | ||
"username@gmail,com", | ||
"username@gmail space.co", | ||
"[email protected]", | ||
"user@ example.com", | ||
"[email protected]", | ||
"[email protected]", | ||
"(user)@example.com", | ||
"user@[192.168.1.1", | ||
"user@exam ple.com", | ||
"[email protected]", | ||
"[email protected]", | ||
" [email protected]", | ||
"[email protected] ", | ||
" [email protected] ", | ||
"@missingusername.com" | ||
"user#gmail.com" | ||
}) | ||
void givenInvalidAdminRegisterApplicationCompleteRequestWithParametrizedInvalidEmails_whenEmailsAreNotValid_thenReturnValidationError(String invalidEmail) throws Exception { | ||
void givenInvalidAdminRegisterApplicationCompleteRequestWithParametrizedInvalidEmails_whenEmailsAreNotValid_thenReturnValidationError(String mockEmailAddress) throws Exception { | ||
|
||
// Given | ||
String mockId = "53617d24-e32c-4249-b9e6-b10e63a439bd"; | ||
AdminRegistrationApplicationCompleteRequest mockRequest = new AdminRegistrationApplicationCompleteRequestBuilder() | ||
.withValidValues() | ||
.withEmailAddress(invalidEmail) | ||
.withEmailAddress(mockEmailAddress) | ||
.build(); | ||
|
||
// Then | ||
|
@@ -622,43 +635,6 @@ void givenInvalidAdminRegisterApplicationCompleteRequestWithParametrizedInvalidE | |
.complete(Mockito.anyString(), Mockito.any()); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = { | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
"[email protected]", | ||
}) | ||
void givenValidAdminRegisterApplicationCompleteRequestWithParametrizedValidEmails_whenEmailsAreValid_thenReturnSuccessResponse(String validEmail) throws Exception { | ||
// Given | ||
String mockId = "fe3760f1-8b44-4587-99a6-43e426c8c6d1"; | ||
AdminRegistrationApplicationCompleteRequest mockRequest = new AdminRegistrationApplicationCompleteRequestBuilder() | ||
.withValidValues() | ||
.withEmailAddress(validEmail) | ||
.build(); | ||
|
||
// When | ||
Mockito.doNothing().when(adminRegistrationCompleteService).complete(Mockito.anyString(), Mockito.any()); | ||
|
||
// Then | ||
String endpoint = BASE_PATH.concat("/admin-registration-application/").concat(mockId).concat("/complete"); | ||
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders | ||
.post(endpoint, mockRequest); | ||
|
||
AysResponse<Void> mockResponse = AysResponseBuilder.success(); | ||
|
||
aysMockMvc.perform(mockHttpServletRequestBuilder, mockResponse) | ||
.andExpect(AysMockResultMatchersBuilders.status() | ||
.isOk()) | ||
.andExpect(AysMockResultMatchersBuilders.response() | ||
.doesNotExist()); | ||
|
||
// Verify | ||
Mockito.verify(adminRegistrationCompleteService, Mockito.times(1)) | ||
.complete(Mockito.anyString(), Mockito.any()); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = { | ||
"g", | ||
|
Oops, something went wrong.