diff --git a/src/main/java/it/cnr/isti/workflow/manager/auth/model/CreateUserResult.java b/src/main/java/it/cnr/isti/workflow/manager/auth/model/CreateUserResult.java index a69b1e9..69fbf66 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/auth/model/CreateUserResult.java +++ b/src/main/java/it/cnr/isti/workflow/manager/auth/model/CreateUserResult.java @@ -3,6 +3,7 @@ package it.cnr.isti.workflow.manager.auth.model; public enum CreateUserResult { SUCCESS, USER_ALREADY_EXISTS, + EMAIL_ALREADY_EXISTS, INVALID_EMAIL, INVALID_PASSWORD } diff --git a/src/main/java/it/cnr/isti/workflow/manager/auth/repo/AuthRepository.java b/src/main/java/it/cnr/isti/workflow/manager/auth/repo/AuthRepository.java index f655e70..31d0afc 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/auth/repo/AuthRepository.java +++ b/src/main/java/it/cnr/isti/workflow/manager/auth/repo/AuthRepository.java @@ -28,4 +28,7 @@ public interface AuthRepository extends JpaRepository { @Query("select u from LoginEntity u where u.active is null or u.active = true") List findByActiveTrue(); + @Query("select case when count(u) > 0 then true else false end from LoginEntity u where lower(u.email) = lower(:email)") + boolean existsByEmailIgnoreCase(@Param("email") String email); + } diff --git a/src/main/java/it/cnr/isti/workflow/manager/auth/repo/LoginEntity.java b/src/main/java/it/cnr/isti/workflow/manager/auth/repo/LoginEntity.java index c2ff0dc..b3ea73e 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/auth/repo/LoginEntity.java +++ b/src/main/java/it/cnr/isti/workflow/manager/auth/repo/LoginEntity.java @@ -1,6 +1,7 @@ package it.cnr.isti.workflow.manager.auth.repo; import it.cnr.isti.workflow.manager.auth.model.UserRole; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -17,6 +18,7 @@ public class LoginEntity { @Id private String username; private String password; + @Column(unique = true) private String email; private Boolean active; @Enumerated(EnumType.STRING) diff --git a/src/main/java/it/cnr/isti/workflow/manager/auth/services/AuthService.java b/src/main/java/it/cnr/isti/workflow/manager/auth/services/AuthService.java index c6730fa..82e97d7 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/auth/services/AuthService.java +++ b/src/main/java/it/cnr/isti/workflow/manager/auth/services/AuthService.java @@ -49,14 +49,18 @@ public class AuthService { if (authRepository.existsById(username)) { return CreateUserResult.USER_ALREADY_EXISTS; } - if (!isValidEmail(email)) { + String normalizedEmail = normalizeEmail(email); + if (!isValidEmail(normalizedEmail)) { return CreateUserResult.INVALID_EMAIL; } + if (authRepository.existsByEmailIgnoreCase(normalizedEmail)) { + return CreateUserResult.EMAIL_ALREADY_EXISTS; + } if (!isValidPassword(password)) { return CreateUserResult.INVALID_PASSWORD; } String hashedPassword = hashPassword(password); - authRepository.save(new LoginEntity(username, hashedPassword, email.trim(), role == null ? UserRole.USER : role)); + authRepository.save(new LoginEntity(username, hashedPassword, normalizedEmail, role == null ? UserRole.USER : role)); return CreateUserResult.SUCCESS; } @@ -164,7 +168,7 @@ public class AuthService { if (email == null) { return false; } - String normalizedEmail = email.trim(); + String normalizedEmail = normalizeEmail(email); if (normalizedEmail.isEmpty() || normalizedEmail.contains(" ")) { return false; } @@ -177,4 +181,8 @@ public class AuthService { && dotIndex < normalizedEmail.length() - 1; } + private String normalizeEmail(String email) { + return email == null ? null : email.trim().toLowerCase(java.util.Locale.ROOT); + } + } diff --git a/src/main/java/it/cnr/isti/workflow/manager/controllers/AuthController.java b/src/main/java/it/cnr/isti/workflow/manager/controllers/AuthController.java index 42e2ed0..b1418a4 100644 --- a/src/main/java/it/cnr/isti/workflow/manager/controllers/AuthController.java +++ b/src/main/java/it/cnr/isti/workflow/manager/controllers/AuthController.java @@ -90,7 +90,8 @@ public class AuthController { CreateUserResult result = authService.registerUser(request.getUsername(), request.getPassword(), request.getEmail()); return switch (result) { case SUCCESS -> ResponseEntity.ok().build(); - case USER_ALREADY_EXISTS -> ResponseEntity.badRequest().body("the user already exists"); + case USER_ALREADY_EXISTS -> ResponseEntity.badRequest().body("USER_ALREADY_EXISTS"); + case EMAIL_ALREADY_EXISTS -> ResponseEntity.badRequest().body("EMAIL_ALREADY_EXISTS"); case INVALID_EMAIL -> ResponseEntity.badRequest().body("INVALID_EMAIL"); case INVALID_PASSWORD -> ResponseEntity.badRequest().body("INVALID_PASSWORD"); }; @@ -140,7 +141,8 @@ public class AuthController { CreateUserResult result = authService.createUser(request.username(), request.password(), request.email(), request.role()); return switch (result) { case SUCCESS -> ResponseEntity.ok().build(); - case USER_ALREADY_EXISTS -> ResponseEntity.badRequest().body("the user already exists"); + case USER_ALREADY_EXISTS -> ResponseEntity.badRequest().body("USER_ALREADY_EXISTS"); + case EMAIL_ALREADY_EXISTS -> ResponseEntity.badRequest().body("EMAIL_ALREADY_EXISTS"); case INVALID_EMAIL -> ResponseEntity.badRequest().body("INVALID_EMAIL"); case INVALID_PASSWORD -> ResponseEntity.badRequest().body("INVALID_PASSWORD"); }; diff --git a/src/test/java/it/cnr/isti/workflow/manager/controllers/AuthControllerTest.java b/src/test/java/it/cnr/isti/workflow/manager/controllers/AuthControllerTest.java index e980399..0db5611 100644 --- a/src/test/java/it/cnr/isti/workflow/manager/controllers/AuthControllerTest.java +++ b/src/test/java/it/cnr/isti/workflow/manager/controllers/AuthControllerTest.java @@ -303,6 +303,34 @@ public class AuthControllerTest { assert "INVALID_EMAIL".equals(response.getBody()); } + @Test + public void registerRejectsDuplicateEmail() { + String email = "duplicate-" + uniqueSuffix() + "@example.com"; + + ResponseEntity firstResponse = authController.register( + new AuthRequest("first-" + uniqueSuffix(), "Validpass1!", email)); + assert firstResponse.getStatusCode().is2xxSuccessful(); + + ResponseEntity secondResponse = authController.register( + new AuthRequest("second-" + uniqueSuffix(), "Validpass1!", email.toUpperCase(java.util.Locale.ROOT))); + assert secondResponse.getStatusCode() == HttpStatus.BAD_REQUEST; + assert "EMAIL_ALREADY_EXISTS".equals(secondResponse.getBody()); + } + + @Test + public void registerRejectsDuplicateUsername() { + String username = "duplicate-user-" + uniqueSuffix(); + + ResponseEntity firstResponse = authController.register( + new AuthRequest(username, "Validpass1!", username + "@example.com")); + assert firstResponse.getStatusCode().is2xxSuccessful(); + + ResponseEntity secondResponse = authController.register( + new AuthRequest(username, "Validpass1!", "other-" + uniqueSuffix() + "@example.com")); + assert secondResponse.getStatusCode() == HttpStatus.BAD_REQUEST; + assert "USER_ALREADY_EXISTS".equals(secondResponse.getBody()); + } + @Test public void adminEndpointsAcceptJwtAdminRole() throws Exception { String username = "jwtadmin-" + uniqueSuffix();