diff --git a/src/main/java/ru/soune/nocopy/controller/UserController.java b/src/main/java/ru/soune/nocopy/controller/UserController.java index cdb376c..eb12863 100644 --- a/src/main/java/ru/soune/nocopy/controller/UserController.java +++ b/src/main/java/ru/soune/nocopy/controller/UserController.java @@ -34,7 +34,7 @@ public class UserController { @GetMapping("/all") public ResponseEntity> getAllUsers() { List allUsers = userRepository.findAll().stream() - .map(u -> new UserDTO(u.getFullName(), u.getCompany(), u.getEmail(), u.getIsActive(), + .map(u -> new UserDTO(u.getFullName(), u.getCompany(), u.getEmail(), u.isActive(), u.getPhone(), u.getGenderType(), u.getBirthday(), u.getCreatedAt(), u.getSubscriptionType())) .toList(); @@ -52,19 +52,19 @@ public class UserController { AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found")); if (authToken != null) { - Optional user = userRepository.findByEmail(email); + User user = userRepository.findByEmail(email); //TODO add mapper - if (user.isPresent()) { - UserDTO userDTO = userMapper.toDTO(user.get()); + if (user != null) { + UserDTO userDTO = userMapper.toDTO(user); userDTO.setEmail(email); - userDTO.setFullName(user.get().getFullName()); - userDTO.setCompany(user.get().getCompany()); - userDTO.setPhone(user.get().getPhone()); - userDTO.setGenderType(user.get().getGenderType()); - userDTO.setBirthday(user.get().getBirthday()); - userDTO.setCreatedAt(user.get().getCreatedAt()); - userDTO.setSubscriptionType(user.get().getSubscriptionType()); - userDTO.setActive(user.get().getIsActive()); + userDTO.setFullName(user.getFullName()); + userDTO.setCompany(user.getCompany()); + userDTO.setPhone(user.getPhone()); + userDTO.setGenderType(user.getGenderType()); + userDTO.setBirthday(user.getBirthday()); + userDTO.setCreatedAt(user.getCreatedAt()); + userDTO.setSubscriptionType(user.getSubscriptionType()); + userDTO.setActive(user.isActive()); return ResponseEntity.ok(userDTO); } diff --git a/src/main/java/ru/soune/nocopy/dto/MessageCode.java b/src/main/java/ru/soune/nocopy/dto/MessageCode.java index 5c75246..e927005 100644 --- a/src/main/java/ru/soune/nocopy/dto/MessageCode.java +++ b/src/main/java/ru/soune/nocopy/dto/MessageCode.java @@ -6,9 +6,11 @@ public enum MessageCode { REG_EMAIL_OR_PHONE_EXISTS(1, "Email or phone already registered"), INVALID_FIELD(2, "Invalid field"), INVALID_TOKEN(2, "Invalid token"), + TOKEN_IS_ALIVE(2, "Token is alive"), INVALID_ACTION(2, "Invalid action"), FILE_UPLOAD_ERROR(2, "File upload error"), FILE_DOWNLOAD_ERROR(2, "File download error"), + USER_NOT_VERIFIED(2, "User not verified"), FILE_DOWNLOAD_ERROR_NOT_CORRECT_FIELD(2, "Not correct field"), IMAGE_FOUND_ERROR(2, "Image found error"), INVALID_JSON_BODY(2, "Invalid fields in JSON object"), diff --git a/src/main/java/ru/soune/nocopy/dto/register/LoginAnswer.java b/src/main/java/ru/soune/nocopy/dto/register/LoginAnswer.java index b244fd8..42a8e55 100644 --- a/src/main/java/ru/soune/nocopy/dto/register/LoginAnswer.java +++ b/src/main/java/ru/soune/nocopy/dto/register/LoginAnswer.java @@ -13,5 +13,9 @@ import java.util.Map; public class LoginAnswer { private String Token; + private boolean isActive; + + private boolean isVerified; + private List> fieldErrors; } diff --git a/src/main/java/ru/soune/nocopy/dto/register/RegAnswer.java b/src/main/java/ru/soune/nocopy/dto/register/RegAnswer.java index 5b265ad..a1cbd01 100644 --- a/src/main/java/ru/soune/nocopy/dto/register/RegAnswer.java +++ b/src/main/java/ru/soune/nocopy/dto/register/RegAnswer.java @@ -13,5 +13,9 @@ import java.util.Map; public class RegAnswer { private String Token; + private boolean isActive; + + private boolean isVerified; + private List> fieldErrors; } \ No newline at end of file diff --git a/src/main/java/ru/soune/nocopy/dto/register/VerifyUserRequest.java b/src/main/java/ru/soune/nocopy/dto/register/VerifyUserRequest.java index a0e3472..8bb0c2a 100644 --- a/src/main/java/ru/soune/nocopy/dto/register/VerifyUserRequest.java +++ b/src/main/java/ru/soune/nocopy/dto/register/VerifyUserRequest.java @@ -14,4 +14,7 @@ public class VerifyUserRequest { @JsonProperty("user_id") private Long userId; + + @JsonProperty("resend") + private Integer resend; } diff --git a/src/main/java/ru/soune/nocopy/entity/user/EmailVerificationToken.java b/src/main/java/ru/soune/nocopy/entity/user/EmailVerificationToken.java index 1b02bcf..d836e45 100644 --- a/src/main/java/ru/soune/nocopy/entity/user/EmailVerificationToken.java +++ b/src/main/java/ru/soune/nocopy/entity/user/EmailVerificationToken.java @@ -28,4 +28,8 @@ public class EmailVerificationToken { @Column(name = "created_at", nullable = false) private LocalDateTime createdAt; + + public boolean isExpired() { + return LocalDateTime.now().isAfter(expiresAt); + } } diff --git a/src/main/java/ru/soune/nocopy/entity/user/User.java b/src/main/java/ru/soune/nocopy/entity/user/User.java index 0aa4990..c61d15d 100644 --- a/src/main/java/ru/soune/nocopy/entity/user/User.java +++ b/src/main/java/ru/soune/nocopy/entity/user/User.java @@ -69,7 +69,7 @@ public class User { private LocalDateTime lastLoginAt; @Column(name = "is_active") - private Boolean isActive = false; + private boolean isActive = false; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) @JsonIgnore diff --git a/src/main/java/ru/soune/nocopy/handler/LoginRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/LoginRequestHandler.java index 5c023ce..2df819b 100644 --- a/src/main/java/ru/soune/nocopy/handler/LoginRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/LoginRequestHandler.java @@ -7,12 +7,14 @@ import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.register.LoginAnswer; import ru.soune.nocopy.dto.register.LoginRequest; import ru.soune.nocopy.entity.user.AuthToken; +import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.repository.UserRepository; import ru.soune.nocopy.service.register.AuthService; import java.util.Arrays; import java.util.Map; +import java.util.Optional; @Component @RequiredArgsConstructor @@ -25,8 +27,9 @@ public class LoginRequestHandler implements RequestHandler { @Override public BaseResponse handle(BaseRequest request) { LoginRequest loginRequest = objectMapper.convertValue(request.getMessageBody(), LoginRequest.class); + User user = userRepository.findByEmail(loginRequest.getEmail()); - if (!userRepository.existsByEmail(loginRequest.getEmail())) { + if (user != null) { LoginAnswer loginAnswer = new LoginAnswer(); loginAnswer.setFieldErrors(Arrays.asList(Map.of("email", loginRequest.getEmail()))); @@ -35,6 +38,17 @@ public class LoginRequestHandler implements RequestHandler { MessageCode.AUTH_EMAIL_NOT_FOUND.getDescription(), loginAnswer)); } + if (!user.isActive() && !user.isEmailVerified()) { + LoginAnswer loginAnswer = new LoginAnswer(); + + loginAnswer.setFieldErrors(Arrays.asList(Map.of( + "verified", String.valueOf(user.isEmailVerified()), + "isActive", String.valueOf(user.isActive())))); + + return new BaseResponse(request.getMsgId(), MessageCode.USER_NOT_VERIFIED.getCode(), + MessageCode.USER_NOT_VERIFIED.getDescription(), loginAnswer); + } + AuthToken authToken = authService.login(loginRequest); LoginAnswer loginAnswer = new LoginAnswer(); diff --git a/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java index 3e655e0..7eb6fe0 100644 --- a/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java @@ -65,6 +65,8 @@ public class RegRequestHandler implements RequestHandler { RegAnswer regAnswer = new RegAnswer(); regAnswer.setToken(authToken.getToken()); + regAnswer.setVerified(false); + regAnswer.setActive(false); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), regAnswer); diff --git a/src/main/java/ru/soune/nocopy/handler/VerifyRegisterUserHandler.java b/src/main/java/ru/soune/nocopy/handler/VerifyRegisterUserHandler.java index b6163b7..50f2dc0 100644 --- a/src/main/java/ru/soune/nocopy/handler/VerifyRegisterUserHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/VerifyRegisterUserHandler.java @@ -11,10 +11,14 @@ import ru.soune.nocopy.dto.register.RegAnswer; import ru.soune.nocopy.dto.register.VerifyUserRequest; import ru.soune.nocopy.entity.user.AuthToken; import ru.soune.nocopy.entity.user.EmailVerificationToken; +import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.exception.NotValidFieldException; -import ru.soune.nocopy.exception.TokenNotFoundException; +import ru.soune.nocopy.exception.UserNotFoundException; import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.repository.EmailVerificationTokenRepository; +import ru.soune.nocopy.repository.UserRepository; +import ru.soune.nocopy.service.mail.EmailService; +import ru.soune.nocopy.service.register.AuthService; import ru.soune.nocopy.service.user.UserService; import java.util.Map; @@ -23,46 +27,92 @@ import java.util.Optional; @Component @Slf4j @RequiredArgsConstructor -public class VerifyRegisterUserHandler implements RequestHandler{ - +public class VerifyRegisterUserHandler implements RequestHandler { private final ObjectMapper objectMapper; private final EmailVerificationTokenRepository emailVerificationTokenRepository; + private final EmailService emailService; + + private final UserRepository userRepository; + private final UserService userService; private final AuthTokenRepository authTokenRepository; + private final AuthService authService; + @Override public BaseResponse handle(BaseRequest request) { - VerifyUserRequest verifyUserRequest = objectMapper.convertValue(request.getMessageBody(), - VerifyUserRequest.class); + VerifyUserRequest verifyUserRequest = objectMapper.convertValue(request.getMessageBody(), VerifyUserRequest.class); - String verifyToken = verifyUserRequest.getVerifyToken(); Long userId = verifyUserRequest.getUserId(); - EmailVerificationToken emailVerificationToken = - emailVerificationTokenRepository.findByUserIdAndToken(userId, verifyToken); + User user = userRepository.findById(userId) + .orElseThrow(() -> new UserNotFoundException("User not found with id: " + userId)); - if (emailVerificationToken == null) { - throw new NotValidFieldException("Token was incorrected:" + verifyToken, - new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), - MessageCode.INVALID_TOKEN.getDescription(), Map.of( - "userId", userId, - "token", verifyToken))); + Optional userAuthTokens = authTokenRepository.findByUserIdAndIsActive(userId, false); + + AuthToken authToken = userAuthTokens.isEmpty() ? authService.generateAuthToken(user): userAuthTokens.get(); + + if (verifyUserRequest.getResend() != null && verifyUserRequest.getResend() == 1) { + return handleResend(request, user, authToken); } - userService.activateAndVerifyUser(userId); - emailVerificationTokenRepository.delete(emailVerificationToken); + return handleVerify(request, user, authToken, verifyUserRequest.getVerifyToken()); + } - Optional byUserIdAndIsActive = authTokenRepository.findByUserIdAndIsActive(userId, true); - AuthToken authToken = byUserIdAndIsActive.orElseThrow(() -> - new TokenNotFoundException("Token not found with user id: " + userId)); + private BaseResponse handleResend(BaseRequest request, User user, AuthToken authToken) { + + EmailVerificationToken token = emailVerificationTokenRepository.findByUserId(user.getId()); + + if (token != null && !token.isExpired()) { + emailService.sendVerificationEmail(user, token.getToken()); + + log.info("Verification token resent for user {}", user.getEmail()); + return successResponse(request, authToken, "Verification code has been resent", false, false); + } + + if (token != null) { + emailVerificationTokenRepository.delete(token); + } + + EmailVerificationToken newToken = emailService.createEmailVerificationToken(authToken); + + emailService.sendVerificationEmail(user, newToken.getToken()); + + log.info("New verification token generated for user {}", user.getEmail()); + + return successResponse(request, authToken, "New verification code has been sent", + false, false); + } + + + private BaseResponse handleVerify(BaseRequest request, User user, AuthToken authToken, String providedToken) { + + EmailVerificationToken token = emailVerificationTokenRepository.findByUserIdAndToken(user.getId(), providedToken); + + if (token == null || token.isExpired()) { + throw new NotValidFieldException("Invalid or expired token", + new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), + MessageCode.INVALID_TOKEN.getDescription(), Map.of("userId", user.getId()))); + } + + User updateUser = userService.activateAndVerifyUser(user.getId()); + emailVerificationTokenRepository.delete(token); + + return successResponse(request, authToken, MessageCode.SUCCESS.getDescription(), updateUser.isActive(), + updateUser.isEmailVerified()); + } + + private BaseResponse successResponse(BaseRequest request, AuthToken authToken, String message, boolean isActive, + boolean isVerified) { RegAnswer regAnswer = new RegAnswer(); regAnswer.setToken(authToken.getToken()); + regAnswer.setVerified(isVerified); + regAnswer.setActive(isActive); - return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), - MessageCode.SUCCESS.getDescription(), regAnswer); + return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), message, regAnswer); } } diff --git a/src/main/java/ru/soune/nocopy/repository/EmailVerificationTokenRepository.java b/src/main/java/ru/soune/nocopy/repository/EmailVerificationTokenRepository.java index 34a3233..91649d8 100644 --- a/src/main/java/ru/soune/nocopy/repository/EmailVerificationTokenRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/EmailVerificationTokenRepository.java @@ -11,4 +11,5 @@ public interface EmailVerificationTokenRepository extends JpaRepository findByToken(String token); EmailVerificationToken findByUserIdAndToken(Long userId, String token); List findByExpiresAtBefore(LocalDateTime expiresAtBefore); + EmailVerificationToken findByUserId(Long userId); } diff --git a/src/main/java/ru/soune/nocopy/repository/UserRepository.java b/src/main/java/ru/soune/nocopy/repository/UserRepository.java index af1a3d9..b0e8032 100644 --- a/src/main/java/ru/soune/nocopy/repository/UserRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/UserRepository.java @@ -7,7 +7,7 @@ import ru.soune.nocopy.entity.user.User; import java.util.Optional; public interface UserRepository extends JpaRepository { - Optional findByEmail(String email); + User findByEmail(String email); boolean existsByEmail(String email); boolean existsByPhone(@Size(min = 11, max = 14) String phone); } diff --git a/src/main/java/ru/soune/nocopy/service/register/AuthService.java b/src/main/java/ru/soune/nocopy/service/register/AuthService.java index d410236..e8d3259 100644 --- a/src/main/java/ru/soune/nocopy/service/register/AuthService.java +++ b/src/main/java/ru/soune/nocopy/service/register/AuthService.java @@ -50,17 +50,14 @@ public class AuthService { User savedUser = userRepository.save(user); - AuthToken authToken = new AuthToken(); - authToken.setToken(generateAuthToken()); - authToken.setUser(savedUser); + AuthToken authToken = genereateAuthToken(savedUser); return authTokenRepository.save(authToken); } @Transactional public AuthToken login(LoginRequest request) { - Optional userOpt = userRepository.findByEmail(request.getEmail()); - User user = userOpt.get(); + User user = userRepository.findByEmail(request.getEmail()); if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { LoginAnswer loginAnswer = new LoginAnswer(); @@ -87,6 +84,18 @@ public class AuthService { .ifPresent(authTokenRepository::delete); } + public AuthToken generateAuthToken(User user) { + return authTokenRepository.save(genereateAuthToken(user)); + } + + private AuthToken genereateAuthToken(User user) { + AuthToken authToken = new AuthToken(); + authToken.setToken(generateAuthToken()); + authToken.setUser(user); + + return authToken; + } + private String generateAuthToken() { byte[] bytes = new byte[32]; secureRandom.nextBytes(bytes); diff --git a/src/main/java/ru/soune/nocopy/service/user/UserService.java b/src/main/java/ru/soune/nocopy/service/user/UserService.java index 56c6341..efce0e4 100644 --- a/src/main/java/ru/soune/nocopy/service/user/UserService.java +++ b/src/main/java/ru/soune/nocopy/service/user/UserService.java @@ -47,13 +47,13 @@ public class UserService { } @Transactional - public void activateAndVerifyUser(long userId) { + public User activateAndVerifyUser(long userId) { Optional users = userRepository.findById(userId); User user = users.orElseThrow(() -> new UserNotFoundException("User not found with id: " + userId)); - user.setIsActive(true); + user.setActive(true); user.setEmailVerified(true); - userRepository.save(user); + return userRepository.save(user); } private void updateUserFromRequest(User user, UserRequest request) {