From 5e85a32f61b246ae9d6998eced5df64845bc8e32 Mon Sep 17 00:00:00 2001 From: Naeem Ullah Date: Tue, 6 Jan 2026 17:09:28 +0500 Subject: [PATCH] Add password change and user uniqueness validation Introduced ChangePasswordDTO and implemented password change functionality in UserService and AuthenticationController. Added custom exceptions for existing email, username, and incorrect old password. Updated User entity and DTOs to include porOrgacode, and enforced uniqueness checks for userId and email. Enhanced ERRCode with new error codes and improved boolean field handling in User entity. --- .../controller/AuthenticationController.java | 13 +++++-- .../security/dto/ChangePasswordDTO.java | 11 ++++++ .../service/AuthenticationService.java | 2 +- .../aconnect/usermanagement/dto/UserDTOs.java | 2 ++ .../EmailAlreadyExistException.java | 11 ++++++ .../exceptions/OldPasswordNotMatch.java | 12 +++++++ .../UsernameAlreadyExistException.java | 10 ++++++ .../aconnect/usermanagement/model/User.java | 14 +++++--- .../repository/UserRepository.java | 2 ++ .../service/PermissionService.java | 4 +-- .../usermanagement/service/UserService.java | 34 +++++++++++++++---- .../configuration/constant/ERRCode.java | 7 +++- .../configuration/exception/ErrorMessage.java | 1 - 13 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 aconnect/src/main/java/com/mfsys/aconnect/security/dto/ChangePasswordDTO.java create mode 100644 aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/EmailAlreadyExistException.java create mode 100644 aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/OldPasswordNotMatch.java create mode 100644 aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/UsernameAlreadyExistException.java diff --git a/aconnect/src/main/java/com/mfsys/aconnect/security/controller/AuthenticationController.java b/aconnect/src/main/java/com/mfsys/aconnect/security/controller/AuthenticationController.java index 8ca6b0c..aa5544d 100644 --- a/aconnect/src/main/java/com/mfsys/aconnect/security/controller/AuthenticationController.java +++ b/aconnect/src/main/java/com/mfsys/aconnect/security/controller/AuthenticationController.java @@ -1,6 +1,7 @@ package com.mfsys.aconnect.security.controller; import com.mfsys.aconnect.security.constant.SecurityURI; +import com.mfsys.aconnect.security.dto.ChangePasswordDTO; import com.mfsys.aconnect.security.dto.LoginRequest; import com.mfsys.aconnect.security.dto.LoginResponse; import com.mfsys.aconnect.security.service.AuthenticationService; @@ -15,6 +16,9 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.HashMap; +import java.util.Map; + @RestController @RequestMapping(SecurityURI.AUTHENTICATION) public class AuthenticationController { @@ -46,9 +50,12 @@ public class AuthenticationController { } @PostMapping(SecurityURI.CHANGE_PASSWORD) - public ResponseEntity changePassword(@RequestBody UserDTOs.UserRequest request) { - UserDTOs.UserResponse response = userService.createUser(request); - return new ResponseEntity<>(response, HttpStatus.CREATED); + public ResponseEntity> changePassword(@RequestBody ChangePasswordDTO request) { + String message = userService.changePassword(request); + Map response = new HashMap<>(); + response.put("message", message); + + return new ResponseEntity<>(response, HttpStatus.OK); } } diff --git a/aconnect/src/main/java/com/mfsys/aconnect/security/dto/ChangePasswordDTO.java b/aconnect/src/main/java/com/mfsys/aconnect/security/dto/ChangePasswordDTO.java new file mode 100644 index 0000000..a4444f1 --- /dev/null +++ b/aconnect/src/main/java/com/mfsys/aconnect/security/dto/ChangePasswordDTO.java @@ -0,0 +1,11 @@ +package com.mfsys.aconnect.security.dto; + +import lombok.Data; + +@Data +public class ChangePasswordDTO { + private String userId; + private String porOrgacode; + private String oldPassword; + private String newPassword; +} diff --git a/aconnect/src/main/java/com/mfsys/aconnect/security/service/AuthenticationService.java b/aconnect/src/main/java/com/mfsys/aconnect/security/service/AuthenticationService.java index b90ec02..4efce05 100644 --- a/aconnect/src/main/java/com/mfsys/aconnect/security/service/AuthenticationService.java +++ b/aconnect/src/main/java/com/mfsys/aconnect/security/service/AuthenticationService.java @@ -36,7 +36,7 @@ public class AuthenticationService { String token = jwtService.generateToken(loginRequest.getUserId()); - return new LoginResponse(user, false, user.getIsFirstLogin(), token); + return new LoginResponse(user, false, user.isFirstLogin(), token); } } diff --git a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/dto/UserDTOs.java b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/dto/UserDTOs.java index b4d1651..2a37c97 100644 --- a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/dto/UserDTOs.java +++ b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/dto/UserDTOs.java @@ -16,6 +16,7 @@ public class UserDTOs { @NoArgsConstructor public static class UserRequest { private String userId; + private String porOrgacode; private String userFullname; private String password; private String email; @@ -26,6 +27,7 @@ public class UserDTOs { @NoArgsConstructor public static class UserResponse { private String userId; + private String porOrgacode; private String userFullname; private String email; private Role role; diff --git a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/EmailAlreadyExistException.java b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/EmailAlreadyExistException.java new file mode 100644 index 0000000..4ed9cb7 --- /dev/null +++ b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/EmailAlreadyExistException.java @@ -0,0 +1,11 @@ +package com.mfsys.aconnect.usermanagement.exceptions; + +import com.mfsys.common.configuration.constant.ERRCode; +import com.mfsys.common.configuration.exception.ApplicationException; + +public class EmailAlreadyExistException extends ApplicationException { + public EmailAlreadyExistException(String porOrgacode) { + super(porOrgacode, ERRCode.EMAIL_ALREADY_EXIST); + } +} + diff --git a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/OldPasswordNotMatch.java b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/OldPasswordNotMatch.java new file mode 100644 index 0000000..8187c8b --- /dev/null +++ b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/OldPasswordNotMatch.java @@ -0,0 +1,12 @@ +package com.mfsys.aconnect.usermanagement.exceptions; + +import com.mfsys.common.configuration.constant.ERRCode; +import com.mfsys.common.configuration.exception.ResourceNotFoundException; + +public class OldPasswordNotMatch extends ResourceNotFoundException { + + public OldPasswordNotMatch(String porOrgacode) { + super(porOrgacode, ERRCode.PASSWORD_ALREADY_EXIST); + } +} + diff --git a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/UsernameAlreadyExistException.java b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/UsernameAlreadyExistException.java new file mode 100644 index 0000000..9accee6 --- /dev/null +++ b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/exceptions/UsernameAlreadyExistException.java @@ -0,0 +1,10 @@ +package com.mfsys.aconnect.usermanagement.exceptions; + +import com.mfsys.common.configuration.constant.ERRCode; +import com.mfsys.common.configuration.exception.ApplicationException; + +public class UsernameAlreadyExistException extends ApplicationException { + public UsernameAlreadyExistException(String porOrgacode) { + super(porOrgacode, ERRCode.USERNAME_ALREADY_EXIST); + } +} \ No newline at end of file diff --git a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/model/User.java b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/model/User.java index 34a03e7..2402c6a 100644 --- a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/model/User.java +++ b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/model/User.java @@ -1,5 +1,6 @@ package com.mfsys.aconnect.usermanagement.model; +import com.mfsys.common.configuration.constant.FieldNameLength; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; @@ -11,7 +12,7 @@ import java.time.LocalDateTime; import java.util.HashSet; import java.util.Set; -@Entity +@Entity(name = "users") @Table(name = "users") @Data @NoArgsConstructor @@ -22,6 +23,9 @@ public class User { @Column(name = "user_id", unique = true, nullable = false) private String userId; + @Column(name = "POR_ORGACODE", nullable = false, updatable = false, columnDefinition = FieldNameLength.POR_ORGACODE) + private String porOrgacode; + @Column(name = "user_fullname", nullable = false) private String userFullname; @@ -38,11 +42,11 @@ public class User { @Column(name = "permissions", columnDefinition = "TEXT") private String permissions; - @Column(name = "is_first_login") - private Boolean isFirstLogin = true; + @Column(name = "is_first_login", columnDefinition = FieldNameLength.BOOLEAN_BIT) + private boolean isFirstLogin = true; - @Column(name = "is_active") - private Boolean isActive = true; + @Column(name = "is_active", columnDefinition = FieldNameLength.BOOLEAN_BIT) + private boolean isActive = true; @CreationTimestamp @Column(name = "created_at", updatable = false) diff --git a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/repository/UserRepository.java b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/repository/UserRepository.java index a379e55..b04fa1d 100644 --- a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/repository/UserRepository.java +++ b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/repository/UserRepository.java @@ -9,4 +9,6 @@ import java.util.Optional; @Repository public interface UserRepository extends JpaRepository { Optional findByUserIdAndIsActiveTrue(String userId); + boolean existsByUserId(String userId); + boolean existsByEmail(String email); } diff --git a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/service/PermissionService.java b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/service/PermissionService.java index 6e8b494..8daaa4b 100644 --- a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/service/PermissionService.java +++ b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/service/PermissionService.java @@ -61,8 +61,8 @@ public class PermissionService { response.setUserFullname(user.getUserFullname()); response.setEmail(user.getEmail()); response.setRole(user.getRole()); - response.setIsFirstLogin(user.getIsFirstLogin()); - response.setIsActive(user.getIsActive()); + response.setIsFirstLogin(user.isFirstLogin()); + response.setIsActive(user.isActive()); response.setCreatedAt(user.getCreatedAt()); response.setUpdatedAt(user.getUpdatedAt()); return response; diff --git a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/service/UserService.java b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/service/UserService.java index f5ad9eb..7fe0bf5 100644 --- a/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/service/UserService.java +++ b/aconnect/src/main/java/com/mfsys/aconnect/usermanagement/service/UserService.java @@ -1,8 +1,11 @@ package com.mfsys.aconnect.usermanagement.service; +import com.mfsys.aconnect.security.dto.ChangePasswordDTO; +import com.mfsys.aconnect.usermanagement.exceptions.EmailAlreadyExistException; +import com.mfsys.aconnect.usermanagement.exceptions.OldPasswordNotMatch; +import com.mfsys.aconnect.usermanagement.exceptions.UsernameAlreadyExistException; import com.mfsys.common.configuration.service.PasswordEncryptionService; import com.mfsys.aconnect.usermanagement.dto.UserDTOs; -import com.mfsys.aconnect.usermanagement.dto.PermissionDTO; import com.mfsys.aconnect.usermanagement.model.User; import com.mfsys.aconnect.usermanagement.repository.UserRepository; import jakarta.persistence.EntityNotFoundException; @@ -10,7 +13,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; @Service @@ -26,8 +28,15 @@ public class UserService { @Transactional public UserDTOs.UserResponse createUser(UserDTOs.UserRequest request) { + if (userRepository.existsByUserId(request.getUserId())) { + throw new UsernameAlreadyExistException(request.getPorOrgacode()); + } + if (userRepository.existsByEmail(request.getEmail())) { + throw new EmailAlreadyExistException(request.getPorOrgacode()); + } User user = new User(); user.setUserId(request.getUserId()); + user.setPorOrgacode(request.getPorOrgacode()); user.setUserFullname(request.getUserFullname()); user.setEmail(request.getEmail()); user.setRole(request.getRole()); @@ -36,6 +45,20 @@ public class UserService { return mapToResponseDTO(savedUser); } + @Transactional + public String changePassword(ChangePasswordDTO request) { + User user = userRepository.findById(request.getUserId()) + .orElseThrow(() -> new EntityNotFoundException("User not found with ID: " + request.getUserId())); + + boolean isPasswordValid = PasswordEncryptionService.verifyPassword(request.getOldPassword(), request.getNewPassword()); + if(!isPasswordValid) { + throw new OldPasswordNotMatch(request.getPorOrgacode()); + } + user.setPassword(passwordEncryptionService.hashPassword(request.getNewPassword())); + userRepository.save(user); + return "Password changed successfully"; + } + public List getAllUsers() { return userRepository.findAll().stream() .map(this::mapToResponseDTO) @@ -68,9 +91,6 @@ public class UserService { userRepository.deleteById(userId); } - public Optional findActiveUserById(String userId) { - return userRepository.findByUserIdAndIsActiveTrue(userId); - } private UserDTOs.UserResponse mapToResponseDTO(User user) { UserDTOs.UserResponse response = new UserDTOs.UserResponse(); @@ -78,8 +98,8 @@ public class UserService { response.setUserFullname(user.getUserFullname()); response.setEmail(user.getEmail()); response.setRole(user.getRole()); - response.setIsFirstLogin(user.getIsFirstLogin()); - response.setIsActive(user.getIsActive()); + response.setIsFirstLogin(user.isFirstLogin()); + response.setIsActive(user.isActive()); response.setCreatedAt(user.getCreatedAt()); response.setUpdatedAt(user.getUpdatedAt()); return response; diff --git a/common/src/main/java/com/mfsys/common/configuration/constant/ERRCode.java b/common/src/main/java/com/mfsys/common/configuration/constant/ERRCode.java index 423ae36..01ffc07 100644 --- a/common/src/main/java/com/mfsys/common/configuration/constant/ERRCode.java +++ b/common/src/main/java/com/mfsys/common/configuration/constant/ERRCode.java @@ -2,7 +2,12 @@ package com.mfsys.common.configuration.constant; import com.mfsys.common.configuration.exception.ErrorMessage; -public enum ERRCode implements ErrorMessage {; +public enum ERRCode implements ErrorMessage { + EMAIL_ALREADY_EXIST("ERR_SEC_0001", "Email already exists"), + USERNAME_ALREADY_EXIST("ERR_SEC_0002", "Username already exists"), + PASSWORD_ALREADY_EXIST("ERR_SEC_0003", "Old Password is not correct"); + + private String code; private String description; diff --git a/common/src/main/java/com/mfsys/common/configuration/exception/ErrorMessage.java b/common/src/main/java/com/mfsys/common/configuration/exception/ErrorMessage.java index 0c082a8..d6cab4e 100644 --- a/common/src/main/java/com/mfsys/common/configuration/exception/ErrorMessage.java +++ b/common/src/main/java/com/mfsys/common/configuration/exception/ErrorMessage.java @@ -2,6 +2,5 @@ package com.mfsys.common.configuration.exception; public interface ErrorMessage { public String getCode(); - public String getDescription(); }