diff --git a/src/main/java/br/com/notehub/application/controller/user/UserController.java b/src/main/java/br/com/notehub/application/controller/user/UserController.java index c9c0524..bb9ce40 100644 --- a/src/main/java/br/com/notehub/application/controller/user/UserController.java +++ b/src/main/java/br/com/notehub/application/controller/user/UserController.java @@ -5,6 +5,7 @@ import br.com.notehub.application.dto.response.page.PageRES; import br.com.notehub.application.dto.response.user.CreateUserRES; import br.com.notehub.application.dto.response.user.DetailUserRES; +import br.com.notehub.domain.token.TokenService; import br.com.notehub.domain.user.Subscription; import br.com.notehub.domain.user.User; import br.com.notehub.domain.user.UserService; @@ -20,6 +21,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; @@ -48,6 +50,7 @@ public class UserController { private String client; private final UserService service; + private final TokenService tokenService; private final MailProducer producer; private UUID getSubject(String bearerToken) { @@ -94,25 +97,33 @@ public ResponseEntity activeUser( @Operation(summary = "Reset user password", description = "Resets the user's password using the provided token.") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "User's password updated successfully."), - @ApiResponse(responseCode = "400", description = "Invalid or same password."), + @ApiResponse(responseCode = "400", description = """ + - `INVALID_DEVICE_ID`: Missing or invalid X-Device-Id + - `SAME_PASSWORD`: Invalid password or new password is identical to the current one + """), @ApiResponse(responseCode = "403", description = "Invalid token."), @ApiResponse(responseCode = "404", description = "User not found."), @ApiResponse(responseCode = "500", description = "Internal server error.") }) @PatchMapping("/change-password") public ResponseEntity patchPassword( + HttpServletRequest request, @Parameter(hidden = true) @RequestHeader("Authorization") String token, @Valid @RequestBody ChangePasswordREQ dto ) { String emailFromToken = getSubject(token, "password"); service.changePassword(emailFromToken, dto.password()); + if (dto.disconnectAll()) tokenService.disconnectAll(request, dto.keepCurrentSession(), emailFromToken); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } @Operation(summary = "Update email", description = "Updates the user's email address.") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Email updated successfully."), - @ApiResponse(responseCode = "400", description = "Invalid or same email."), + @ApiResponse(responseCode = "400", description = """ + - `INVALID_DEVICE_ID`: Missing or invalid X-Device-Id + - `SAME_EMAIL`: Invalid email or new email is identical to the current one + """), @ApiResponse(responseCode = "403", description = "Invalid token."), @ApiResponse(responseCode = "404", description = "User not found."), @ApiResponse(responseCode = "406", description = "Email already exists."), @@ -120,11 +131,13 @@ public ResponseEntity patchPassword( }) @PatchMapping("/change-email") public ResponseEntity patchEmail( + HttpServletRequest request, @Parameter(hidden = true) @RequestHeader("Authorization") String token, @Valid @RequestBody ChangeEmailREQ dto ) { String emailFromToken = getSubject(token, "email"); service.changeEmail(emailFromToken, dto.email()); + if (dto.disconnectAll()) tokenService.disconnectAll(request, dto.keepCurrentSession(), emailFromToken); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } diff --git a/src/main/java/br/com/notehub/application/dto/request/user/ChangeEmailREQ.java b/src/main/java/br/com/notehub/application/dto/request/user/ChangeEmailREQ.java index 7eff3e8..f80dbc8 100644 --- a/src/main/java/br/com/notehub/application/dto/request/user/ChangeEmailREQ.java +++ b/src/main/java/br/com/notehub/application/dto/request/user/ChangeEmailREQ.java @@ -9,6 +9,8 @@ public record ChangeEmailREQ( regexp = "(?i)[a-z0-9!#$%&'*+=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message = "Email inválido" ) - String email + String email, + boolean disconnectAll, + boolean keepCurrentSession ) { } \ No newline at end of file diff --git a/src/main/java/br/com/notehub/application/dto/request/user/ChangePasswordREQ.java b/src/main/java/br/com/notehub/application/dto/request/user/ChangePasswordREQ.java index b2975c7..dd50a57 100644 --- a/src/main/java/br/com/notehub/application/dto/request/user/ChangePasswordREQ.java +++ b/src/main/java/br/com/notehub/application/dto/request/user/ChangePasswordREQ.java @@ -6,6 +6,8 @@ public record ChangePasswordREQ( @NotBlank(message = "Não pode ser vazio") @Size(min = 4, max = 255, message = "Tamanho inválido") - String password + String password, + boolean disconnectAll, + boolean keepCurrentSession ) { } \ No newline at end of file diff --git a/src/main/java/br/com/notehub/application/implementation/token/TokenServiceImpl.java b/src/main/java/br/com/notehub/application/implementation/token/TokenServiceImpl.java index adae8f0..c60b20a 100644 --- a/src/main/java/br/com/notehub/application/implementation/token/TokenServiceImpl.java +++ b/src/main/java/br/com/notehub/application/implementation/token/TokenServiceImpl.java @@ -259,6 +259,14 @@ public void logout(HttpServletRequest request) { repository.delete(token); } + @Override + public void disconnectAll(HttpServletRequest request, boolean keepCurrentSession, String email) { + List connections = repository.findAllByUserEmail(email); + UUID device = validateDevice(request); + if (keepCurrentSession) connections = connections.stream().filter(t -> !t.getDevice().equals(device)).toList(); + repository.deleteAll(connections); + } + @Override public void cleanExpiredTokens() { List expiredTokens = repository.findExpiredTokens(Instant.now()); diff --git a/src/main/java/br/com/notehub/domain/token/TokenRepository.java b/src/main/java/br/com/notehub/domain/token/TokenRepository.java index b1d37be..afa3734 100644 --- a/src/main/java/br/com/notehub/domain/token/TokenRepository.java +++ b/src/main/java/br/com/notehub/domain/token/TokenRepository.java @@ -15,6 +15,8 @@ public interface TokenRepository extends JpaRepository { Optional findByDevice(UUID id); + List findAllByUserEmail(String email); + @Query("SELECT t FROM Token t WHERE t.expiresAt < :now") List findExpiredTokens(@Param("now") Instant now); diff --git a/src/main/java/br/com/notehub/domain/token/TokenService.java b/src/main/java/br/com/notehub/domain/token/TokenService.java index 954bb08..c6f8e28 100644 --- a/src/main/java/br/com/notehub/domain/token/TokenService.java +++ b/src/main/java/br/com/notehub/domain/token/TokenService.java @@ -36,6 +36,8 @@ public interface TokenService { void logout(HttpServletRequest request); + void disconnectAll(HttpServletRequest request, boolean keepCurrentSession, String email); + void cleanExpiredTokens(); } \ No newline at end of file