Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AYS-660 | Change Error Code and Message for Already Passive Users #436

Merged
merged 13 commits into from
Jan 28, 2025
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
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());
}

}
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
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());
agitrubard marked this conversation as resolved.
Show resolved Hide resolved
Assertions.assertNull(userFromDatabase.get().getUpdatedAt());
}

agitrubard marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading