Skip to content

Commit

Permalink
AYS-660 | Change Error Code and Message for Already Passive Users (#436)
Browse files Browse the repository at this point in the history
  • Loading branch information
egehanasal authored Jan 28, 2025
1 parent 8c3fe14 commit 83b2f09
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.ays.auth.exception;

import org.ays.common.exception.AysConflictException;

import java.io.Serial;

/**
* Exception thrown when a user is already in a passive state.
*/
public final class AysUserAlreadyPassiveException extends AysConflictException {

/**
* Unique identifier for serialization.
*/
@Serial
private static final long serialVersionUID = 2484662602911824448L;

/**
* Constructs a new {@link AysUserAlreadyPassiveException} with the specified detail message.
*/
public AysUserAlreadyPassiveException() {
super("user is already passive!");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.ays.auth.exception;

import org.ays.auth.model.enums.AysUserStatus;
import org.ays.common.exception.AysAuthException;

import java.io.Serial;

/**
* Exception thrown when attempting to authenticate a user that is not active.
*/
public final class AysUserNotActiveAuthException extends AysAuthException {

/**
* Unique identifier for serialization.
*/
@Serial
private static final long serialVersionUID = -5218287176856317070L;

/**
* Constructs a new {@link AysUserNotActiveAuthException}with the specified userId.
*
* @param userId the id of the user that is not active.
*/
public AysUserNotActiveAuthException(String userId) {
super("user is not active! userId:" + userId);
}

/**
* Constructs a new {@link AysUserNotActiveAuthException} with the specified user status.
*
* @param status the status of the user that is not active.
*/
public AysUserNotActiveAuthException(AysUserStatus status) {
super("user is not active! currentStatus: " + status.name());
}

}
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
package org.ays.auth.exception;

import org.ays.auth.model.enums.AysUserStatus;
import org.ays.common.exception.AysAuthException;
import org.ays.common.exception.AysConflictException;

import java.io.Serial;

/**
* Exception thrown when attempting to authenticate a user that is not active.
* Exception thrown when a user does not in an active state.
*/
public final class AysUserNotActiveException extends AysAuthException {
public final class AysUserNotActiveException extends AysConflictException {

/**
* Unique identifier for serialization.
*/
@Serial
private static final long serialVersionUID = -5218287176856317070L;
private static final long serialVersionUID = 3508025652421021710L;

/**
* Constructs a new UserNotActiveException with the specified userId.
* Constructs a new {@link AysUserNotActiveException} with the specified userId.
*
* @param userId the userId of the user that is not active
* @param userId the id of the user that is not active.
*/
public AysUserNotActiveException(String userId) {
super("user is not active! userId:" + userId);
}

/**
* Constructs a new UserNotActiveException with the specified user status.
*
* @param status the status of the user that is not active
*/
public AysUserNotActiveException(AysUserStatus status) {
super("user is not active! currentStatus: " + status.name());
}

}
2 changes: 2 additions & 0 deletions src/main/java/org/ays/auth/service/AysUserUpdateService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.ays.auth.service;

import org.ays.auth.exception.AysUserAlreadyPassiveException;
import org.ays.auth.exception.AysUserNotActiveException;
import org.ays.auth.exception.AysUserNotExistByIdException;
import org.ays.auth.exception.AysUserNotPassiveException;
Expand Down Expand Up @@ -31,6 +32,7 @@ public interface AysUserUpdateService {
*
* @param id The unique identifier of the user to be passivated.
* @throws AysUserNotExistByIdException if a user with the given ID does not exist.
* @throws AysUserAlreadyPassiveException if the user is already in a passive state.
* @throws AysUserNotActiveException if the user is not in an active state.
*/
void passivate(String id);
Expand Down
11 changes: 5 additions & 6 deletions src/main/java/org/ays/auth/service/impl/AysAuthServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.ays.auth.exception.AysPasswordNotValidException;
import org.ays.auth.exception.AysUserDoesNotAccessPageException;
import org.ays.auth.exception.AysUserIdNotValidException;
import org.ays.auth.exception.AysUserNotActiveException;
import org.ays.auth.exception.AysUserNotActiveAuthException;
import org.ays.auth.model.AysIdentity;
import org.ays.auth.model.AysRole;
import org.ays.auth.model.AysToken;
Expand Down Expand Up @@ -64,7 +64,7 @@ class AysAuthServiceImpl implements AysAuthService {
* @return {@link AysToken} representing the access token generated upon successful authentication.
* @throws AysEmailAddressNotValidException If the provided email address is not valid or does not exist.
* @throws AysPasswordNotValidException If the provided password is not valid.
* @throws AysUserNotActiveException If the user's status is not active.
* @throws AysUserNotActiveAuthException If the user's status is not active.
* @throws AysUserDoesNotAccessPageException If the user does not have permission to access the requested page.
*/
@Override
Expand Down Expand Up @@ -125,7 +125,7 @@ private void validateUserSourcePagePermission(final AysUser user,
* @param refreshToken The refresh token used to generate a new access token.
* @return A new {@link AysToken} containing the refreshed access token.
* @throws AysUserIdNotValidException If the user ID extracted from the refresh token is not valid.
* @throws AysUserNotActiveException If the user associated with the refresh token is not active.
* @throws AysUserNotActiveAuthException If the user associated with the refresh token is not active.
*/
@Override
public AysToken refreshAccessToken(final String refreshToken) {
Expand All @@ -151,15 +151,14 @@ public AysToken refreshAccessToken(final String refreshToken) {

/**
* Validates the status of the user.
* Throws {@link AysUserNotActiveException} if the user is not active.
*
* @param user The {@link AysUser} object whose status needs to be validated.
* @throws AysUserNotActiveException If the user is not active.
* @throws AysUserNotActiveAuthException If the user is not active.
*/
private void validateUserStatus(final AysUser user) {

if (!user.isActive()) {
throw new AysUserNotActiveException(user.getId());
throw new AysUserNotActiveAuthException(user.getId());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import lombok.RequiredArgsConstructor;
import org.ays.auth.exception.AysEmailAddressNotValidException;
import org.ays.auth.exception.AysUserNotActiveException;
import org.ays.auth.exception.AysUserDoesNotAccessPageException;
import org.ays.auth.exception.AysUserNotActiveAuthException;
import org.ays.auth.exception.AysUserPasswordCannotChangedException;
import org.ays.auth.exception.AysUserPasswordDoesNotExistException;
import org.ays.auth.model.AysRole;
Expand Down Expand Up @@ -52,7 +52,7 @@ class AysUserPasswordServiceImpl implements AysUserPasswordService {
*
* @param forgotPasswordRequest the request containing the user's email address.
* @throws AysEmailAddressNotValidException if the email address does not correspond to any existing user.
* @throws AysUserNotActiveException if the user status is not active.
* @throws AysUserNotActiveAuthException if the user status is not active.
* @throws AysUserDoesNotAccessPageException if the user lacks permission to access the source page.
*/
@Override
Expand All @@ -64,7 +64,7 @@ public void forgotPassword(final AysPasswordForgotRequest forgotPasswordRequest)
.orElseThrow(() -> new AysEmailAddressNotValidException(emailAddress));

if(!user.isActive()) {
throw new AysUserNotActiveException(user.getStatus());
throw new AysUserNotActiveAuthException(user.getStatus());
}

this.validateUserSourcePagePermission(user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.ays.auth.exception.AysUserAlreadyDeletedException;
import org.ays.auth.exception.AysUserAlreadyExistsByEmailAddressException;
import org.ays.auth.exception.AysUserAlreadyExistsByPhoneNumberException;
import org.ays.auth.exception.AysUserAlreadyPassiveException;
import org.ays.auth.exception.AysUserIsNotActiveOrPassiveException;
import org.ays.auth.exception.AysUserNotActiveException;
import org.ays.auth.exception.AysUserNotExistByIdException;
Expand Down Expand Up @@ -131,7 +132,8 @@ public void activate(String id) {
*
* @param id The unique identifier of the user to be passivated.
* @throws AysUserNotExistByIdException if a user with the given ID does not exist.
* @throws AysUserNotActiveException if the user is not in an active state and cannot be passivated.
* @throws AysUserAlreadyPassiveException if the user is already in a passive state.
* @throws AysUserNotActiveException if the user is not in an active state.
*/
@Override
public void passivate(String id) {
Expand All @@ -140,6 +142,10 @@ public void passivate(String id) {
.filter(userFromDatabase -> identity.getInstitutionId().equals(userFromDatabase.getInstitution().getId()))
.orElseThrow(() -> new AysUserNotExistByIdException(id));

if (user.isPassive()) {
throw new AysUserAlreadyPassiveException();
}

if (!user.isActive()) {
throw new AysUserNotActiveException(id);
}
Expand Down
101 changes: 101 additions & 0 deletions src/test/java/org/ays/auth/controller/AysUserEndToEndTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.ays.auth.port.AysUserSavePort;
import org.ays.common.model.AysPhoneNumberBuilder;
import org.ays.common.model.request.AysPhoneNumberRequestBuilder;
import org.ays.common.model.response.AysErrorResponse;
import org.ays.common.model.response.AysErrorResponseBuilder;
import org.ays.common.model.response.AysPageResponse;
import org.ays.common.model.response.AysResponse;
import org.ays.common.model.response.AysResponseBuilder;
Expand All @@ -31,8 +33,11 @@
import org.ays.util.UUIDTestUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -596,4 +601,100 @@ void givenValidId_whenPassivateUser_thenReturnSuccess() throws Exception {
Assertions.assertTrue(UUIDTestUtil.isValid(userFromDatabase.get().getUpdatedUser()));
}

@Test
void givenAlreadyPassiveUserId_whenTryToPassivate_thenReturnUserAlreadyPassiveError() throws Exception {

// Initialize
Institution institution = new InstitutionBuilder()
.withId(AysValidTestData.Admin.INSTITUTION_ID)
.build();

List<AysRole> roles = roleReadPort.findAllActivesByInstitutionId(institution.getId());

AysUser user = userSavePort.save(
new AysUserBuilder()
.withValidValues()
.withoutId()
.withRoles(roles)
.withInstitution(institution)
.withStatus(AysUserStatus.PASSIVE)
.build()
);

// Given
String userId = user.getId();

// Then
String endpoint = BASE_PATH.concat("/user/").concat(userId).concat("/passivate");
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.patch(endpoint, adminToken.getAccessToken());

AysErrorResponse mockErrorResponse = AysErrorResponseBuilder.CONFLICT_ERROR;

aysMockMvc.perform(mockHttpServletRequestBuilder, mockErrorResponse)
.andExpect(AysMockResultMatchersBuilders.status()
.isConflict())
.andExpect(MockMvcResultMatchers.jsonPath("$.message")
.value("user is already passive!"));

// Verify
Optional<AysUser> userFromDatabase = userReadPort.findById(userId);

Assertions.assertTrue(userFromDatabase.isPresent());
Assertions.assertEquals(userFromDatabase.get().getId(), user.getId());
Assertions.assertEquals(AysUserStatus.PASSIVE, userFromDatabase.get().getStatus());
Assertions.assertNull(userFromDatabase.get().getUpdatedUser());
Assertions.assertNull(userFromDatabase.get().getUpdatedAt());
}

@ParameterizedTest
@EnumSource(value = AysUserStatus.class, names = {
"NOT_VERIFIED",
"DELETED"
})
void givenInactiveUserId_whenTryToPassivate_thenReturnUserNotActiveError(AysUserStatus status) throws Exception {

// Initialize
Institution institution = new InstitutionBuilder()
.withId(AysValidTestData.Admin.INSTITUTION_ID)
.build();

List<AysRole> roles = roleReadPort.findAllActivesByInstitutionId(institution.getId());

AysUser user = userSavePort.save(
new AysUserBuilder()
.withValidValues()
.withoutId()
.withRoles(roles)
.withInstitution(institution)
.withStatus(status)
.build()
);

// Given
String userId = user.getId();

// Then
String endpoint = BASE_PATH.concat("/user/").concat(userId).concat("/passivate");
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.patch(endpoint, adminToken.getAccessToken());

AysErrorResponse mockErrorResponse = AysErrorResponseBuilder.CONFLICT_ERROR;

aysMockMvc.perform(mockHttpServletRequestBuilder, mockErrorResponse)
.andExpect(AysMockResultMatchersBuilders.status()
.isConflict())
.andExpect(MockMvcResultMatchers.jsonPath("$.message")
.value("user is not active! userId:" + userId));

// Verify
Optional<AysUser> userFromDatabase = userReadPort.findById(userId);

Assertions.assertTrue(userFromDatabase.isPresent());
Assertions.assertEquals(userFromDatabase.get().getId(), user.getId());
Assertions.assertEquals(status, userFromDatabase.get().getStatus());
Assertions.assertNull(userFromDatabase.get().getUpdatedUser());
Assertions.assertNull(userFromDatabase.get().getUpdatedAt());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import org.ays.auth.exception.AysTokenNotValidException;
import org.ays.auth.exception.AysUserDoesNotAccessPageException;
import org.ays.auth.exception.AysUserIdNotValidException;
import org.ays.auth.exception.AysUserNotActiveException;
import org.ays.auth.exception.AysUserNotActiveAuthException;
import org.ays.auth.model.AysIdentity;
import org.ays.auth.model.AysPermission;
import org.ays.auth.model.AysPermissionBuilder;
Expand Down Expand Up @@ -284,7 +284,7 @@ void givenValidLoginRequest_whenUserNotActive_thenThrowUserNotActiveException()

// Then
Assertions.assertThrows(
AysUserNotActiveException.class,
AysUserNotActiveAuthException.class,
() -> userAuthService.authenticate(mockLoginRequest)
);

Expand Down Expand Up @@ -606,7 +606,7 @@ void givenValidRefreshToken_whenUserNotActive_thenThrowUserNotActiveException()

// Then
Assertions.assertThrows(
AysUserNotActiveException.class,
AysUserNotActiveAuthException.class,
() -> userAuthService.refreshAccessToken(mockRefreshToken)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import org.ays.AysUnitTest;
import org.ays.auth.exception.AysEmailAddressNotValidException;
import org.ays.auth.exception.AysUserNotActiveException;
import org.ays.auth.exception.AysUserDoesNotAccessPageException;
import org.ays.auth.exception.AysUserNotActiveAuthException;
import org.ays.auth.exception.AysUserPasswordCannotChangedException;
import org.ays.auth.exception.AysUserPasswordDoesNotExistException;
import org.ays.auth.model.AysRole;
Expand Down Expand Up @@ -206,7 +206,7 @@ void givenValidForgotPasswordRequest_whenUserStatusNotActive_thenThrowUserNotAct

// Then
Assertions.assertThrows(
AysUserNotActiveException.class,
AysUserNotActiveAuthException.class,
() -> userPasswordService.forgotPassword(mockForgotPasswordRequest)
);

Expand Down
Loading

0 comments on commit 83b2f09

Please sign in to comment.