From f3d5ead0173e0b6e188a10115f1b303c8fb20b7a Mon Sep 17 00:00:00 2001 From: Rainnny7 Date: Thu, 19 Sep 2024 08:48:04 -0400 Subject: [PATCH] /user/exists now requires a captcha --- .../api/controller/v1/UserController.java | 9 ++--- .../api/model/user/input/UserExistsInput.java | 35 +++++++++++++++++++ .../pulseapp/api/service/CaptchaService.java | 2 +- .../cc/pulseapp/api/service/UserService.java | 25 +++++++++---- 4 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 src/main/java/cc/pulseapp/api/model/user/input/UserExistsInput.java diff --git a/src/main/java/cc/pulseapp/api/controller/v1/UserController.java b/src/main/java/cc/pulseapp/api/controller/v1/UserController.java index 5d35e9a..48a4434 100644 --- a/src/main/java/cc/pulseapp/api/controller/v1/UserController.java +++ b/src/main/java/cc/pulseapp/api/controller/v1/UserController.java @@ -5,6 +5,7 @@ import cc.pulseapp.api.model.user.User; import cc.pulseapp.api.model.user.UserDTO; import cc.pulseapp.api.model.user.input.CompleteOnboardingInput; import cc.pulseapp.api.model.user.input.EnableTFAInput; +import cc.pulseapp.api.model.user.input.UserExistsInput; import cc.pulseapp.api.model.user.response.UserSetupTFAResponse; import cc.pulseapp.api.service.UserService; import lombok.NonNull; @@ -50,12 +51,12 @@ public final class UserController { * A GET endpoint to check if a * user exists with the given email. * - * @param email the email to check + * @param input the input to check * @return the response */ - @GetMapping("/exists") @ResponseBody @NonNull - public ResponseEntity> doesUserExist(@RequestParam @NonNull String email) { - return ResponseEntity.ok(Map.of("exists", userService.doesUserExist(email))); + @PostMapping("/exists") @ResponseBody @NonNull + public ResponseEntity> doesUserExist(UserExistsInput input) { + return ResponseEntity.ok(Map.of("exists", userService.doesUserExist(input))); } /** diff --git a/src/main/java/cc/pulseapp/api/model/user/input/UserExistsInput.java b/src/main/java/cc/pulseapp/api/model/user/input/UserExistsInput.java new file mode 100644 index 0000000..306d5a6 --- /dev/null +++ b/src/main/java/cc/pulseapp/api/model/user/input/UserExistsInput.java @@ -0,0 +1,35 @@ +package cc.pulseapp.api.model.user.input; + +import cc.pulseapp.api.model.user.User; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +/** + * The input to check if + * a {@link User} exists. + * + * @author Braydon + */ +@AllArgsConstructor @Getter @ToString +public final class UserExistsInput { + /** + * The email of the user to check. + */ + private final String email; + + /** + * The captcha response token to validate. + */ + private final String captchaResponse; + + /** + * Check if this input is valid. + * + * @return whether this input is valid + */ + public boolean isValid() { + return email != null && (!email.isBlank()) + && captchaResponse != null && (!captchaResponse.isBlank()); + } +} \ No newline at end of file diff --git a/src/main/java/cc/pulseapp/api/service/CaptchaService.java b/src/main/java/cc/pulseapp/api/service/CaptchaService.java index 299dbef..fffdbca 100644 --- a/src/main/java/cc/pulseapp/api/service/CaptchaService.java +++ b/src/main/java/cc/pulseapp/api/service/CaptchaService.java @@ -34,7 +34,7 @@ public final class CaptchaService { .header(HttpHeaders.CONTENT_TYPE, "application/json") .body(body) .asJson(); - if (EnvironmentUtils.isProduction() && !response.getBody().getObject().getBoolean("success")) { + if (/*EnvironmentUtils.isProduction() && */!response.getBody().getObject().getBoolean("success")) { throw new BadRequestException(Error.CAPTCHA_INVALID); } } diff --git a/src/main/java/cc/pulseapp/api/service/UserService.java b/src/main/java/cc/pulseapp/api/service/UserService.java index 8dbec01..726e475 100644 --- a/src/main/java/cc/pulseapp/api/service/UserService.java +++ b/src/main/java/cc/pulseapp/api/service/UserService.java @@ -11,6 +11,7 @@ import cc.pulseapp.api.model.user.UserDTO; import cc.pulseapp.api.model.user.UserFlag; import cc.pulseapp.api.model.user.input.CompleteOnboardingInput; import cc.pulseapp.api.model.user.input.EnableTFAInput; +import cc.pulseapp.api.model.user.input.UserExistsInput; import cc.pulseapp.api.model.user.response.UserSetupTFAResponse; import cc.pulseapp.api.repository.SessionRepository; import cc.pulseapp.api.repository.UserRepository; @@ -56,6 +57,11 @@ public final class UserService { */ @NonNull private final TFAService tfaService; + /** + * The captcha service to use. + */ + @NonNull private final CaptchaService captchaService; + /** * The user repository to use. */ @@ -80,13 +86,14 @@ public final class UserService { @Autowired public UserService(@NonNull AuthService authService, @NonNull SnowflakeService snowflakeService, @NonNull OrganizationService orgService, @NonNull StatusPageService statusPageService, - @NonNull TFAService tfaService, @NonNull UserRepository userRepository, - @NonNull SessionRepository sessionRepository) { + @NonNull TFAService tfaService, @NonNull CaptchaService captchaService, + @NonNull UserRepository userRepository, @NonNull SessionRepository sessionRepository) { this.authService = authService; this.snowflakeService = snowflakeService; this.orgService = orgService; this.statusPageService = statusPageService; this.tfaService = tfaService; + this.captchaService = captchaService; this.userRepository = userRepository; this.sessionRepository = sessionRepository; } @@ -103,13 +110,18 @@ public final class UserService { } /** - * Check if the user with the given email exists. + * Check if a user exists + * with the given email. * - * @param email the email to check + * @param input the input to check * @return whether the user exists */ - public boolean doesUserExist(@NonNull String email) { - return StringUtils.isValidEmail(email) && userRepository.findByEmailIgnoreCase(email) != null; + public boolean doesUserExist(UserExistsInput input) { + if (input == null || (!input.isValid())) { // Ensure the input was provided + throw new BadRequestException(Error.MALFORMED_USER_EXISTS_INPUT); + } + captchaService.validateCaptcha(input.getCaptchaResponse()); + return StringUtils.isValidEmail(input.getEmail()) && userRepository.findByEmailIgnoreCase(input.getEmail()) != null; } /** @@ -215,6 +227,7 @@ public final class UserService { * User errors. */ private enum Error implements IGenericResponse { + MALFORMED_USER_EXISTS_INPUT, MALFORMED_ONBOARDING_INPUT, MALFORMED_ENABLE_TFA_INPUT, ORGANIZATION_SLUG_INVALID,