Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions k8s/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ spec:
containers:
- name: security-service
image: docker.io/dockerforomar/micropay-security:latest
imagePullPolicy: Always
ports:
- containerPort: 8150
envFrom:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.micropay.security.exception;

public class DuplicateObjectException extends RuntimeException {

public DuplicateObjectException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

public class InternalServiceCommunicationException extends RuntimeException {

public InternalServiceCommunicationException(String message) {
super(message);
}

public InternalServiceCommunicationException(String message, Throwable cause) {
super(message, cause);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -27,6 +26,16 @@ public ResponseEntity<ErrorResponse> handleGeneric(Exception exception) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body);
}

@ExceptionHandler(DuplicateObjectException.class)
public ResponseEntity<ErrorResponse> 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<ErrorResponse> handleUserNotFoundException(UserNotFoundException exception) {
ErrorResponse body = new ErrorResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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."));
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<UserResponse>() {}, () -> {
Expand All @@ -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));

Expand All @@ -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) {
Expand All @@ -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."));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down