Public Access
fix(registration): check input and store emails in lower case
This commit is contained in:
1 parent
ea4ae089ff
commit
68919eaab4
15 files changed
+173
-11
No files matched your search
@@ -13,6 +13,7 @@ import auth_service.shared.security.SessionProperties;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import auth_service.shared.test.AbstractWebMvcTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters;
|
||||
import org.springframework.boot.test.json.JacksonTester;
|
||||
@@ -26,6 +27,7 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -183,4 +185,18 @@ class LoginControllerTest extends AbstractWebMvcTest {
|
||||
|
||||
verify(refreshTokenService).revoke("the-refresh");
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_Email_IsTrimmedAndLowerCase() throws Exception {
|
||||
when(loginService.login(any(User.class), any(), any())).thenReturn(new Session("mock-access", "mock-refresh"));
|
||||
|
||||
mockMvc.perform(post("/api/auth/login")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(loginRequestJson.write(new LoginRequest(" Bob@Example.COM ", "password")).getJson()))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
ArgumentCaptor<User> user = ArgumentCaptor.forClass(User.class);
|
||||
verify(loginService).login(user.capture(), any(), any());
|
||||
assertEquals("bob@example.com", user.getValue().getEmail());
|
||||
}
|
||||
}
|
||||
@@ -57,4 +57,13 @@ class MinimalUserRepositoryTest extends AbstractPersistenceTest {
|
||||
var updated = entityManager.find(MinimalUserEntity.class, entity.getId());
|
||||
assertNotNull(updated.getLastLoginDate());
|
||||
}
|
||||
|
||||
// An access token from before V19 still carries the email as it was typed.
|
||||
@Test
|
||||
void findByEmail_IgnoresCase() {
|
||||
entityManager.persist(MinimalUserEntity.builder().email("case@example.com").password("password").enabled(true).build());
|
||||
entityManager.flush();
|
||||
|
||||
assertTrue(minimalUserRepository.findByEmail("Case@Example.com").isPresent());
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import auth_service.modules.registration.model.Registration;
|
||||
import auth_service.modules.registration.service.RegistrationService;
|
||||
import auth_service.shared.test.AbstractWebMvcTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters;
|
||||
@@ -187,4 +189,53 @@ class RegistrationControllerTest extends AbstractWebMvcTest {
|
||||
.andExpect(header().string("Retry-After", "900"))
|
||||
.andExpect(jsonPath("$.code").value("TOO_MANY_ATTEMPTS"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
" |SuperSecret123!|code",
|
||||
"no-at-sign|SuperSecret123!|code",
|
||||
"test@example.com| |code",
|
||||
"test@example.com|short|code",
|
||||
"test@example.com|SuperSecret123!| ",
|
||||
})
|
||||
void register_InvalidInput_Is400WithoutRegistering(String email, String password, String inviteCode) throws Exception {
|
||||
var request = RegistrationRequest.builder().email(email).password(password).inviteCode(inviteCode).build();
|
||||
|
||||
mockMvc.perform(post("/api/registration/new")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(registrationRequestJson.write(request).getJson()))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
verifyNoInteractions(registrationService);
|
||||
}
|
||||
|
||||
// BCrypt uses only 72 bytes: a longer password would mostly be ignored, or refused with a 500.
|
||||
@Test
|
||||
void register_PasswordOver72Bytes_Is400() throws Exception {
|
||||
var request = RegistrationRequest.builder().email("test@example.com").password("é".repeat(37))
|
||||
.inviteCode("code").build();
|
||||
|
||||
mockMvc.perform(post("/api/registration/new")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(registrationRequestJson.write(request).getJson()))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
verifyNoInteractions(registrationService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_Email_IsTrimmedAndLowerCase() throws Exception {
|
||||
var request = RegistrationRequest.builder().email(" Bob@Example.COM ").password("SuperSecret123!")
|
||||
.inviteCode("code").build();
|
||||
when(registrationService.register(any(Registration.class), any())).thenReturn(1L);
|
||||
|
||||
mockMvc.perform(post("/api/registration/new")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(registrationRequestJson.write(request).getJson()))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
ArgumentCaptor<Registration> registration = ArgumentCaptor.forClass(Registration.class);
|
||||
verify(registrationService).register(registration.capture(), any());
|
||||
assertEquals("bob@example.com", registration.getValue().getEmail());
|
||||
}
|
||||
}
|
||||
+15
@@ -54,4 +54,19 @@ class RegisteredUserRepositoryTest extends AbstractPersistenceTest {
|
||||
assertNotNull(saved);
|
||||
assertEquals("save@example.com", saved.getEmail());
|
||||
}
|
||||
|
||||
// V19: a second account under the same email in other case is refused by the database too.
|
||||
@Test
|
||||
void emailsThatDifferOnlyInCase_AreOneAccount() {
|
||||
entityManager.persist(RegisteredUserEntity.builder().email("dup@example.com").password("password")
|
||||
.enabled(true).registrationDate(java.time.LocalDateTime.now()).build());
|
||||
entityManager.flush();
|
||||
|
||||
assertTrue(registeredUserRepository.existsByEmail("Dup@Example.com"));
|
||||
assertThrows(org.hibernate.exception.ConstraintViolationException.class, () -> {
|
||||
entityManager.persist(RegisteredUserEntity.builder().email("Dup@example.com").password("password")
|
||||
.enabled(true).registrationDate(java.time.LocalDateTime.now()).build());
|
||||
entityManager.flush();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package auth_service.modules.user.persistence.mapper;
|
||||
|
||||
import auth_service.modules.user.persistence.entities.UserEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class EntityMapperTest {
|
||||
|
||||
@Test
|
||||
void mapToModel_UserWithoutSettingsRow_HasNoLanguage() {
|
||||
UserEntity entity = UserEntity.builder().id(1L).email("a@b.c").role("USER").build();
|
||||
|
||||
assertThat(EntityMapper.mapToModel(entity).getSettings().getLanguage()).isEmpty();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user