From 25b96b3be9d7e18148b85a2a192d22061026689d Mon Sep 17 00:00:00 2001 From: Rainnny7 Date: Tue, 17 Sep 2024 14:16:36 -0400 Subject: [PATCH] fix captchas not being validated properly --- .../api/config/WebSecurityConfig.java | 1 + .../api/controller/v1/UserController.java | 7 +++++ .../api/model/user/input/UserLoginInput.java | 7 +---- .../cc/pulseapp/api/service/AuthService.java | 16 ++--------- .../pulseapp/api/service/CaptchaService.java | 6 +++- .../cc/pulseapp/api/service/UserService.java | 28 ++++++++++++++++++- 6 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/main/java/cc/pulseapp/api/config/WebSecurityConfig.java b/src/main/java/cc/pulseapp/api/config/WebSecurityConfig.java index b2e21f8..b3a1159 100644 --- a/src/main/java/cc/pulseapp/api/config/WebSecurityConfig.java +++ b/src/main/java/cc/pulseapp/api/config/WebSecurityConfig.java @@ -60,6 +60,7 @@ public class WebSecurityConfig { .requestMatchers(AntPathRequestMatcher.antMatcher("/error")).permitAll() .requestMatchers(AntPathRequestMatcher.antMatcher("/v*/auth/register")).permitAll() .requestMatchers(AntPathRequestMatcher.antMatcher("/v*/auth/login")).permitAll() + .requestMatchers(AntPathRequestMatcher.antMatcher("/v*/user/exists")).permitAll() .anyRequest().authenticated()) .exceptionHandling(exceptionHandling -> exceptionHandling .authenticationEntryPoint((request, response, authException) -> { // Handle invalid access tokens 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 0697c9e..459dfb9 100644 --- a/src/main/java/cc/pulseapp/api/controller/v1/UserController.java +++ b/src/main/java/cc/pulseapp/api/controller/v1/UserController.java @@ -8,6 +8,8 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.Map; + /** * This controller is responsible for * handling user authentication requests. @@ -37,4 +39,9 @@ public final class UserController { public ResponseEntity getUser() { return ResponseEntity.ok(userService.getUser()); } + + @GetMapping("/exists") @ResponseBody @NonNull + public ResponseEntity> doesUserExist(@RequestParam @NonNull String email) { + return ResponseEntity.ok(Map.of("exists", userService.doesUserExist(email))); + } } \ No newline at end of file diff --git a/src/main/java/cc/pulseapp/api/model/user/input/UserLoginInput.java b/src/main/java/cc/pulseapp/api/model/user/input/UserLoginInput.java index 8b0a4a1..8c4cb9e 100644 --- a/src/main/java/cc/pulseapp/api/model/user/input/UserLoginInput.java +++ b/src/main/java/cc/pulseapp/api/model/user/input/UserLoginInput.java @@ -17,11 +17,6 @@ public final class UserLoginInput { */ private final String email; - /** - * The username of the user to login with. - */ - private final String username; - /** * The password of the user to login with. */ @@ -38,7 +33,7 @@ public final class UserLoginInput { * @return whether this input is valid */ public boolean isValid() { - return (email != null && (!email.isBlank()) || username != null && (!username.isBlank())) + return email != null && (!email.isBlank()) && password != null && (!password.isBlank()) && captchaResponse != null && (!captchaResponse.isBlank()); } diff --git a/src/main/java/cc/pulseapp/api/service/AuthService.java b/src/main/java/cc/pulseapp/api/service/AuthService.java index f495a58..89b7156 100644 --- a/src/main/java/cc/pulseapp/api/service/AuthService.java +++ b/src/main/java/cc/pulseapp/api/service/AuthService.java @@ -23,7 +23,6 @@ import org.springframework.stereotype.Service; import java.util.Base64; import java.util.Date; -import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -100,8 +99,7 @@ public final class AuthService { validateLoginInput(input); // Ensure the input is valid // Lookup the user by the email or username and ensure the user exists - User user = input.getEmail() == null ? userRepository.findByUsernameIgnoreCase(Objects.requireNonNull(input.getUsername())) - : userRepository.findByEmailIgnoreCase(input.getEmail()); + User user = userRepository.findByEmailIgnoreCase(input.getEmail()); if (user == null) { throw new BadRequestException(Error.USER_NOT_FOUND); } @@ -197,9 +195,7 @@ public final class AuthService { throw new BadRequestException(passwordError); } // Finally validate the captcha - if (EnvironmentUtils.isProduction()) { - captchaService.validateCaptcha(input.getCaptchaResponse()); - } + captchaService.validateCaptcha(input.getCaptchaResponse()); } /** @@ -217,14 +213,8 @@ public final class AuthService { if (input.getEmail() != null && (!StringUtils.isValidEmail(input.getEmail()))) { throw new BadRequestException(Error.EMAIL_INVALID); } - // Ensure the username is valid - if (input.getUsername() != null && (!StringUtils.isValidUsername(input.getUsername()))) { - throw new BadRequestException(Error.USERNAME_INVALID); - } // Finally validate the captcha - if (EnvironmentUtils.isProduction()) { - captchaService.validateCaptcha(input.getCaptchaResponse()); - } + captchaService.validateCaptcha(input.getCaptchaResponse()); } private enum Error implements IGenericResponse { diff --git a/src/main/java/cc/pulseapp/api/service/CaptchaService.java b/src/main/java/cc/pulseapp/api/service/CaptchaService.java index 90ed150..bdc3d79 100644 --- a/src/main/java/cc/pulseapp/api/service/CaptchaService.java +++ b/src/main/java/cc/pulseapp/api/service/CaptchaService.java @@ -8,6 +8,7 @@ import kong.unirest.core.JsonNode; import kong.unirest.core.Unirest; import lombok.NonNull; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; /** @@ -28,7 +29,10 @@ public final class CaptchaService { JsonObject body = new JsonObject(); body.addProperty("secret", secretKey); body.addProperty("response", captchaResponse); - HttpResponse response = Unirest.post("https://challenges.cloudflare.com/turnstile/v0/siteverify").body(body).asJson(); + HttpResponse response = Unirest.post("https://challenges.cloudflare.com/turnstile/v0/siteverify") + .header(HttpHeaders.CONTENT_TYPE, "application/json") + .body(body) + .asJson(); if (!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 370eda1..6afa0c9 100644 --- a/src/main/java/cc/pulseapp/api/service/UserService.java +++ b/src/main/java/cc/pulseapp/api/service/UserService.java @@ -1,7 +1,9 @@ package cc.pulseapp.api.service; +import cc.pulseapp.api.common.StringUtils; import cc.pulseapp.api.model.user.User; import cc.pulseapp.api.model.user.UserDTO; +import cc.pulseapp.api.repository.UserRepository; import lombok.NonNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -13,6 +15,9 @@ import java.util.Date; */ @Service public final class UserService { + /** + * The auth service to use. + */ @NonNull private final AuthService authService; /** @@ -20,15 +25,36 @@ public final class UserService { */ @NonNull private final SnowflakeService snowflakeService; + /** + * The user repository to use. + */ + @NonNull private final UserRepository userRepository; + @Autowired - public UserService(@NonNull AuthService authService, @NonNull SnowflakeService snowflakeService) { + public UserService(@NonNull AuthService authService, @NonNull SnowflakeService snowflakeService, @NonNull UserRepository userRepository) { this.authService = authService; this.snowflakeService = snowflakeService; + this.userRepository = userRepository; } + /** + * Get the currently authenticated user. + * + * @return the authenticated user + */ @NonNull public UserDTO getUser() { User user = authService.getAuthenticatedUser(); return UserDTO.asDTO(user, new Date(snowflakeService.extractCreationTime(user.getSnowflake()))); } + + /** + * Check if the user with the given email exists. + * + * @param email the email to check + * @return whether the user exists + */ + public boolean doesUserExist(@NonNull String email) { + return StringUtils.isValidEmail(email) && userRepository.findByEmailIgnoreCase(email) != null; + } } \ No newline at end of file