diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index a35e88f..7d75964 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -16,6 +16,7 @@ spec: containers: - name: security-service image: docker.io/dockerforomar/micropay-security:latest + imagePullPolicy: Always ports: - containerPort: 8150 envFrom: diff --git a/src/main/java/com/micropay/security/exception/DuplicateObjectException.java b/src/main/java/com/micropay/security/exception/DuplicateObjectException.java new file mode 100644 index 0000000..2b202a0 --- /dev/null +++ b/src/main/java/com/micropay/security/exception/DuplicateObjectException.java @@ -0,0 +1,8 @@ +package com.micropay.security.exception; + +public class DuplicateObjectException extends RuntimeException { + + public DuplicateObjectException(String message) { + super(message); + } +} diff --git a/src/main/java/com/micropay/security/exception/InternalServiceCommunicationException.java b/src/main/java/com/micropay/security/exception/InternalServiceCommunicationException.java index 1c400e9..a3825b3 100644 --- a/src/main/java/com/micropay/security/exception/InternalServiceCommunicationException.java +++ b/src/main/java/com/micropay/security/exception/InternalServiceCommunicationException.java @@ -2,10 +2,6 @@ public class InternalServiceCommunicationException extends RuntimeException { - public InternalServiceCommunicationException(String message) { - super(message); - } - public InternalServiceCommunicationException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/com/micropay/security/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/micropay/security/exception/handler/GlobalExceptionHandler.java index d38f961..ae5afb4 100644 --- a/src/main/java/com/micropay/security/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/micropay/security/exception/handler/GlobalExceptionHandler.java @@ -1,9 +1,8 @@ package com.micropay.security.exception.handler; -import com.micropay.security.exception.CredentialNotFoundException; +import com.micropay.security.exception.DuplicateObjectException; import com.micropay.security.exception.NotActiveUserException; import com.micropay.security.exception.UserNotFoundException; -import org.apache.tomcat.websocket.AuthenticationException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import com.micropay.security.dto.response.ErrorResponse; @@ -27,6 +26,16 @@ public ResponseEntity handleGeneric(Exception exception) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body); } + @ExceptionHandler(DuplicateObjectException.class) + public ResponseEntity handleDuplicateObjectException(DuplicateObjectException exception) { + ErrorResponse body = new ErrorResponse( + HttpStatus.CONFLICT.value(), + exception.getMessage(), + LocalDateTime.now() + ); + return ResponseEntity.status(HttpStatus.CONFLICT).body(body); + } + @ExceptionHandler(UserNotFoundException.class) public ResponseEntity handleUserNotFoundException(UserNotFoundException exception) { ErrorResponse body = new ErrorResponse( diff --git a/src/main/java/com/micropay/security/service/security/impl/UserAuthenticationServiceImpl.java b/src/main/java/com/micropay/security/service/security/impl/UserAuthenticationServiceImpl.java index 70d0d26..e22ede7 100644 --- a/src/main/java/com/micropay/security/service/security/impl/UserAuthenticationServiceImpl.java +++ b/src/main/java/com/micropay/security/service/security/impl/UserAuthenticationServiceImpl.java @@ -2,10 +2,7 @@ import com.micropay.security.dto.request.RegisterRequest; import com.micropay.security.dto.response.AuthResponse; -import com.micropay.security.exception.CredentialNotFoundException; -import com.micropay.security.exception.InvalidRoleException; -import com.micropay.security.exception.NotActiveUserException; -import com.micropay.security.exception.UserNotFoundException; +import com.micropay.security.exception.*; import com.micropay.security.mapper.CredentialMapper; import com.micropay.security.mapper.UserMapper; import com.micropay.security.model.CustomUserDetails; @@ -48,7 +45,10 @@ public class UserAuthenticationServiceImpl implements UserAuthenticationService @Override @Transactional public AuthResponse registerUser(RegisterRequest registerRequest) { - log.info("Registering user with phone number: {}", registerRequest.phoneNumber()); + final String phoneNumber = registerRequest.phoneNumber(); + + log.info("Registering user with phone number: {}", phoneNumber); + checkAccountExistence(phoneNumber); Role role = roleRepository.findById(1) .orElseThrow(() -> new InvalidRoleException("Default USER role not found.")); @@ -64,6 +64,14 @@ public AuthResponse registerUser(RegisterRequest registerRequest) { return jwtService.generateTokens(user); } + private void checkAccountExistence(String phoneNumber) { + userRepository.findByPhoneNumber(phoneNumber).ifPresent(user -> { + throw new DuplicateObjectException( + "Account with phone number: " + phoneNumber + " already exists." + ); + }); + } + @Override public UserDetails loadUserByUsername(String phoneNumber) throws UsernameNotFoundException { log.info("Loading user with phone number: {}", phoneNumber); diff --git a/src/main/java/com/micropay/security/service/user/impl/UserManagementServiceImpl.java b/src/main/java/com/micropay/security/service/user/impl/UserManagementServiceImpl.java index d702ea0..68c4e64 100644 --- a/src/main/java/com/micropay/security/service/user/impl/UserManagementServiceImpl.java +++ b/src/main/java/com/micropay/security/service/user/impl/UserManagementServiceImpl.java @@ -18,12 +18,14 @@ import com.micropay.security.service.user.UserManagementService; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.List; import java.util.UUID; +@Slf4j @Service @RequiredArgsConstructor public class UserManagementServiceImpl implements UserManagementService { @@ -37,6 +39,8 @@ public class UserManagementServiceImpl implements UserManagementService { @Override public UserResponse getUserData(UUID userId) { + log.info("[UserManagementService] - Fetching user data for userId: {}", userId); + String cacheKey = userId.toString(); return cacheService.getOrPut( "userData", cacheKey, new TypeReference() {}, () -> { @@ -50,6 +54,8 @@ public UserResponse getUserData(UUID userId) { @Override @Transactional public UserResponse updateUserData(UUID userId, UpdateUserRequest updateUserRequest) { + log.info("[UserManagementService] - Updating user data for userId: {}", userId); + User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException(userId)); @@ -59,47 +65,55 @@ public UserResponse updateUserData(UUID userId, UpdateUserRequest updateUserRequ if (updateUserRequest.email() != null) { user.setEmail(updateUserRequest.email()); } + User savedUser = userRepository.save(user); evictCaches(userId); - return userMapper.toResponse(userRepository.save(user)); + + return userMapper.toResponse(savedUser); } @Override @Transactional public void blockUser(UUID userId) { + log.info("[UserManagementService] - Blocking user with ID: {}", userId); + User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException(userId)); user.setStatus(UserStatus.BLOCKED); walletServiceAdapter.closeWallet(userId); - evictCaches(userId); userRepository.save(user); + evictCaches(userId); } @Override @Transactional public void activateUser(UUID userId) { + log.info("[UserManagementService] - Activating user with ID: {}", userId); + User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException(userId)); user.setStatus(UserStatus.ACTIVE); walletServiceAdapter.activateWallet(userId); - evictCaches(userId); userRepository.save(user); + evictCaches(userId); } @Override @Transactional public void suspendUser(UUID userId) { + log.info("[UserManagementService] - Suspending user with ID: {}", userId); + User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException(userId)); user.setStatus(UserStatus.SUSPENDED); walletServiceAdapter.deactivateWallet(userId); - evictCaches(userId); userRepository.save(user); + evictCaches(userId); } private void evictCaches(UUID userId) { @@ -109,6 +123,8 @@ private void evictCaches(UUID userId) { @Override public UserWalletResponse getUserWalletId(UserWalletRequest userWalletRequest) { + log.info("[UserManagementService] - Fetching walletId for phoneNumber: {}", userWalletRequest.phoneNumber()); + User user = userRepository.findByPhoneNumber(userWalletRequest.phoneNumber()) .orElseThrow(() -> new UserNotFoundException("User is not using MicroPay yet.")); diff --git a/src/test/java/com/micropay/security/service/security/impl/UserAuthenticationServiceImplTest.java b/src/test/java/com/micropay/security/service/security/impl/UserAuthenticationServiceImplTest.java index efb6e65..9c5bd37 100644 --- a/src/test/java/com/micropay/security/service/security/impl/UserAuthenticationServiceImplTest.java +++ b/src/test/java/com/micropay/security/service/security/impl/UserAuthenticationServiceImplTest.java @@ -82,6 +82,8 @@ void setUp() { @Test void registerUser_ShouldRegisterSuccessfully() { + when(userRepository.findByPhoneNumber(registerRequest.phoneNumber())).thenReturn(Optional.empty()); + when(roleRepository.findById(1)).thenReturn(Optional.of(role)); when(userMapper.buildEntity(registerRequest, role)).thenReturn(user); when(pinManagementService.hashPin("1234")).thenReturn("hashed"); @@ -99,8 +101,24 @@ void registerUser_ShouldRegisterSuccessfully() { @Test void registerUser_ShouldThrow_WhenRoleNotFound() { + when(userRepository.findByPhoneNumber(registerRequest.phoneNumber())).thenReturn(Optional.empty()); when(roleRepository.findById(1)).thenReturn(Optional.empty()); assertThrows(InvalidRoleException.class, () -> service.registerUser(registerRequest)); + verify(userRepository, never()).save(any()); + verify(credentialRepository, never()).save(any()); + verify(cacheService, never()).evictAll(anyString()); + } + + @Test + void registerUser_ShouldThrow_WhenUserAlreadyExists() { + when(userRepository.findByPhoneNumber(registerRequest.phoneNumber())).thenReturn(Optional.of(user)); + + assertThrows(DuplicateObjectException.class, () -> service.registerUser(registerRequest)); + + verify(userRepository, never()).save(any()); + verify(credentialRepository, never()).save(any()); + verify(cacheService, never()).evictAll(anyString()); + verify(jwtService, never()).generateTokens(any()); } @Test diff --git a/src/test/java/com/micropay/security/service/user/impl/UserManagementServiceImplTest.java b/src/test/java/com/micropay/security/service/user/impl/UserManagementServiceImplTest.java index 297f2f6..1e6dd3c 100644 --- a/src/test/java/com/micropay/security/service/user/impl/UserManagementServiceImplTest.java +++ b/src/test/java/com/micropay/security/service/user/impl/UserManagementServiceImplTest.java @@ -114,6 +114,8 @@ void activateUser_ShouldActivateUser_AndOpenWallet() { assertEquals(UserStatus.ACTIVE, user.getStatus()); verify(walletServiceAdapter).activateWallet(USER_ID); + verify(cacheService).evict("userData", USER_ID.toString()); + verify(cacheService).evictAll("users"); verify(userRepository).save(user); } @@ -125,6 +127,8 @@ void suspendUser_ShouldSuspendUser_AndDeactivateWallet() { assertEquals(UserStatus.SUSPENDED, user.getStatus()); verify(walletServiceAdapter).deactivateWallet(USER_ID); + verify(cacheService).evict("userData", USER_ID.toString()); + verify(cacheService).evictAll("users"); verify(userRepository).save(user); }