From 478d20aec73cdebdf8e9fcc5de05fdd8894635ed Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 25 Jul 2024 11:49:10 +0300 Subject: [PATCH] Status '410 Gone' when token is expired --- .../server/controller/AuthController.java | 76 +++++++++---------- .../server/controller/BaseController.java | 6 ++ .../server/controller/ImageController.java | 2 +- .../MobileApplicationController.java | 3 +- .../server/controller/AuthControllerTest.java | 4 +- 5 files changed, 47 insertions(+), 44 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index 5d10648be7..aa194d688e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -21,7 +21,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -132,28 +131,28 @@ public class AuthController extends BaseController { @ApiOperation(value = "Check Activate User Token (checkActivateToken)", notes = "Checks the activation token and forwards user to 'Create Password' page. " + "If token is valid, returns '303 See Other' (redirect) response code with the correct address of 'Create Password' page and same 'activateToken' specified in the URL parameters. " + - "If token is not valid, returns '409 Conflict'.") + "If token is not valid, returns '409 Conflict'. " + + "If token is expired, returns '410 Gone'.") @GetMapping(value = "/noauth/activate", params = {"activateToken"}) - public ResponseEntity checkActivateToken( + public ResponseEntity checkActivateToken( @Parameter(description = "The activate token string.") @RequestParam(value = "activateToken") String activateToken) { - HttpHeaders headers = new HttpHeaders(); - HttpStatus responseStatus; UserCredentials userCredentials = userService.findUserCredentialsByActivateToken(TenantId.SYS_TENANT_ID, activateToken); - if (userCredentials != null && !userCredentials.isActivationTokenExpired()) { - String createURI = "/login/createPassword"; - try { - URI location = new URI(createURI + "?activateToken=" + activateToken); - headers.setLocation(location); - responseStatus = HttpStatus.SEE_OTHER; - } catch (URISyntaxException e) { - log.error("Unable to create URI with address [{}]", createURI); - responseStatus = HttpStatus.BAD_REQUEST; - } - } else { - responseStatus = HttpStatus.CONFLICT; + if (userCredentials == null) { + return response(HttpStatus.CONFLICT); + } else if (userCredentials.isActivationTokenExpired()) { + return response(HttpStatus.GONE); + } + + String createURI = "/login/createPassword"; + try { + URI location = new URI(createURI + "?activateToken=" + activateToken); + return ResponseEntity.status(HttpStatus.SEE_OTHER) + .location(location).build(); + } catch (URISyntaxException e) { + log.error("Unable to create URI with address [{}]", createURI); + return response(HttpStatus.BAD_REQUEST); } - return new ResponseEntity<>(headers, responseStatus); } @ApiOperation(value = "Request reset password email (requestResetPasswordByEmail)", @@ -181,32 +180,31 @@ public class AuthController extends BaseController { @ApiOperation(value = "Check password reset token (checkResetToken)", notes = "Checks the password reset token and forwards user to 'Reset Password' page. " + "If token is valid, returns '303 See Other' (redirect) response code with the correct address of 'Reset Password' page and same 'resetToken' specified in the URL parameters. " + - "If token is not valid, returns '409 Conflict'.") + "If token is not valid, returns '409 Conflict'. " + + "If token is expired, returns '410 Gone'.") @GetMapping(value = "/noauth/resetPassword", params = {"resetToken"}) - public ResponseEntity checkResetToken( + public ResponseEntity checkResetToken( @Parameter(description = "The reset token string.") @RequestParam(value = "resetToken") String resetToken) { - HttpHeaders headers = new HttpHeaders(); - HttpStatus responseStatus; - String resetURI = "/login/resetPassword"; UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken); - - if (userCredentials != null && !userCredentials.isResetTokenExpired()) { - if (!rateLimitService.checkRateLimit(LimitedApi.PASSWORD_RESET, userCredentials.getUserId(), defaultLimitsConfiguration)) { - return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build(); - } - try { - URI location = new URI(resetURI + "?resetToken=" + resetToken); - headers.setLocation(location); - responseStatus = HttpStatus.SEE_OTHER; - } catch (URISyntaxException e) { - log.error("Unable to create URI with address [{}]", resetURI); - responseStatus = HttpStatus.BAD_REQUEST; - } - } else { - responseStatus = HttpStatus.CONFLICT; + if (userCredentials == null) { + return response(HttpStatus.CONFLICT); + } else if (userCredentials.isResetTokenExpired()) { + return response(HttpStatus.GONE); + } + if (!rateLimitService.checkRateLimit(LimitedApi.PASSWORD_RESET, userCredentials.getUserId(), defaultLimitsConfiguration)) { + return response(HttpStatus.TOO_MANY_REQUESTS); + } + + String resetURI = "/login/resetPassword"; + try { + URI location = new URI(resetURI + "?resetToken=" + resetToken); + return ResponseEntity.status(HttpStatus.SEE_OTHER) + .location(location).build(); + } catch (URISyntaxException e) { + log.error("Unable to create URI with address [{}]", resetURI); + return response(HttpStatus.BAD_REQUEST); } - return new ResponseEntity<>(headers, responseStatus); } @ApiOperation(value = "Activate User", diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index da956d4fa5..52772bbf2b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -27,7 +27,9 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -867,4 +869,8 @@ public abstract class BaseController { } } + protected ResponseEntity response(HttpStatus status) { + return ResponseEntity.status(status).build(); + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index e8e7ca1d8f..0228739399 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -290,7 +290,7 @@ public class ImageController extends BaseController { if (StringUtils.isNotEmpty(etag)) { etag = StringUtils.remove(etag, '\"'); // etag is wrapped in double quotes due to HTTP specification if (etag.equals(tbImageService.getETag(cacheKey))) { - return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); + return response(HttpStatus.NOT_MODIFIED); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java index ca8b11ee80..11f017b85f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java @@ -183,8 +183,7 @@ public class MobileApplicationController extends BaseController { .header("Location", appStoreLink) .build(); } else { - return ResponseEntity.status(HttpStatus.NOT_FOUND) - .build(); + return response(HttpStatus.NOT_FOUND); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java index f3c0beab8f..dba5a84e1b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java @@ -193,7 +193,7 @@ public class AuthControllerTest extends AbstractControllerTest { userCredentialsDao.save(tenantId, userCredentials); doGet("/api/noauth/resetPassword?resetToken={resetToken}", this.currentResetPasswordToken) - .andExpect(status().isConflict()); + .andExpect(status().isGone()); JsonNode resetPasswordRequest = JacksonUtil.newObjectNode() .put("resetToken", this.currentResetPasswordToken) .put("password", "wefwefe"); @@ -223,7 +223,7 @@ public class AuthControllerTest extends AbstractControllerTest { userCredentials.setActivateTokenExpTime(System.currentTimeMillis() - 1); userCredentialsDao.save(tenantId, userCredentials); doGet("/api/noauth/activate?activateToken={activateToken}", initialActivationToken) - .andExpect(status().isConflict()); + .andExpect(status().isGone()); doPost("/api/noauth/activate", JacksonUtil.newObjectNode() .put("activateToken", initialActivationToken) .put("password", "wefewe")).andExpect(status().isBadRequest())