diff --git a/Jenkinsfile_CNP b/Jenkinsfile_CNP index 72b30383..f11c6ef2 100644 --- a/Jenkinsfile_CNP +++ b/Jenkinsfile_CNP @@ -64,6 +64,7 @@ withPipeline(type, product, component) { env.S2S_URL = 'http://rpe-service-auth-provider-aat.service.core-compute-aat.internal' env.IDAM_API_URL = 'https://idam-api.aat.platform.hmcts.net' env.ROLE_ASSIGNMENT_HOST = 'http://am-role-assignment-service-aat.service.core-compute-aat.internal' + env.LAU_API_URL = 'http://lau-idam-backend-aat.service.core-compute-aat.internal' echo "Just waiting a while to ensure that the pod has run the job" sh "sleep 120s" diff --git a/Jenkinsfile_nightly b/Jenkinsfile_nightly index 4767df7a..58696e66 100644 --- a/Jenkinsfile_nightly +++ b/Jenkinsfile_nightly @@ -40,6 +40,7 @@ withNightlyPipeline(type, product, component) { env.S2S_URL = 'http://rpe-service-auth-provider-aat.service.core-compute-aat.internal' env.IDAM_API_URL = 'https://idam-api.aat.platform.hmcts.net' env.ROLE_ASSIGNMENT_HOST = 'http://am-role-assignment-service-aat.service.core-compute-aat.internal' + env.LAU_API_URL = 'http://lau-idam-backend-aat.service.core-compute-aat.internal' loadVaultSecrets(secrets) enableAksStagingDeployment() diff --git a/build.gradle b/build.gradle index 0004163f..0f861c05 100644 --- a/build.gradle +++ b/build.gradle @@ -272,6 +272,7 @@ dependencies { testImplementation group: 'io.cucumber', name: 'cucumber-java', version: versions.cucumber testImplementation group: 'com.github.tomakehurst', name: 'wiremock-jre8-standalone', version: '3.0.1' testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1' + testImplementation group: 'org.awaitility', name: 'awaitility', version: '3.0.0' testImplementation(platform(group: 'org.junit', name: 'junit-bom', version: '5.10.1')) testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine' diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/idam/UserDeletionFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/idam/UserDeletionFunctionalTest.java index 6cdd5438..a61ac7c5 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/idam/UserDeletionFunctionalTest.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/idam/UserDeletionFunctionalTest.java @@ -4,7 +4,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; @@ -21,6 +24,8 @@ @ActiveProfiles("functional") @RequiredArgsConstructor @Slf4j +@Execution(ExecutionMode.SAME_THREAD) +@Disabled class UserDeletionFunctionalTest { @Inject diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/idam/UserRestoreFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/idam/UserRestoreFunctionalTest.java index 7c64b168..6363b170 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/idam/UserRestoreFunctionalTest.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/idam/UserRestoreFunctionalTest.java @@ -1,22 +1,148 @@ package uk.gov.hmcts.reform.idam; +import jakarta.inject.Inject; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; +import uk.gov.hmcts.reform.idam.helpers.IdamUserDataProvider; +import uk.gov.hmcts.reform.idam.helpers.LauDeletionLogEntryProvider; +import uk.gov.hmcts.reform.idam.helpers.LauIdamBackendServiceProvider; +import uk.gov.hmcts.reform.idam.service.IdamUserRestorerService; +import uk.gov.hmcts.reform.idam.service.remote.responses.DeletionLog; +import uk.gov.hmcts.reform.idam.util.RestoreSummary; -import static org.junit.Assert.assertTrue; +import java.util.ArrayList; +import java.util.List; +import static org.awaitility.Awaitility.with; +import static org.junit.Assert.assertEquals; @SpringBootTest @ActiveProfiles("functional") @RequiredArgsConstructor @Slf4j +@Execution(ExecutionMode.SAME_THREAD) class UserRestoreFunctionalTest { + @Inject + IdamUserRestorerService idamUserRestoreService; + + @Inject + private IdamUserDataProvider idamUserDataProvider; + + @Inject + private LauDeletionLogEntryProvider lauDeletionLogEntryProvider; + + @Inject + LauIdamBackendServiceProvider lauIdamBackendServiceProvider; + + @Inject + private RestoreSummary restoreSummary; + + List logEntryUserIds = new ArrayList<>(); + @Test + @DirtiesContext void givenDeletedUserExistsThenShouldAbleToRestoreDeletedUsers() { - assertTrue("Dummy condition", true); + List lauDeletionLogs = lauDeletionLogEntryProvider.createDeletionLogLau(); + assertEquals("Deletion Log entry has not created", 1, lauDeletionLogs.size()); + logEntryUserIds.add(lauDeletionLogs.get(0).getUserId()); + + idamUserRestoreService.run(); + + with().await() + .untilAsserted(() -> { + List userIds = restoreSummary.getSuccessful(); + assertEquals("User has not been restored successfully", 1, userIds.size()); + }); } + + @Test + @DirtiesContext + void givenDeletedUserExistsInIdamThenFailedToRestoreDueToReinstatedAndActiveAccount() { + String userId = idamUserDataProvider.createIdamUser(); + List deletionLogs = lauDeletionLogEntryProvider.postDeletedLogEntryWithExistingUserId(userId, 4); + assertEquals("Deletion Log entry has not created", 1, deletionLogs.size()); + logEntryUserIds.add(deletionLogs.get(0).getUserId()); + + idamUserRestoreService.run(); + + with().await() + .untilAsserted(() -> { + List failedUserIds = restoreSummary.getFailedToRestoreDueToReinstatedAndActiveAccount(); + assertEquals("User has not been restored successfully", 1, failedUserIds.size()); + }); + } + + @Test + @DirtiesContext + void givenDeletedUserExistsInIdamAndRetiredThenFailedToRestoreDueToReinstatedAccount() { + String userId = idamUserDataProvider.createAndRetireIdamUser(); + List deletionLogs = lauDeletionLogEntryProvider.postDeletedLogEntryWithExistingUserId(userId, 6); + assertEquals("Deletion Log entry has not created", 1, deletionLogs.size()); + logEntryUserIds.add(deletionLogs.get(0).getUserId()); + + idamUserRestoreService.run(); + + with().await() + .untilAsserted(() -> { + List failedUserIds = restoreSummary.getFailedToRestoreDueToReinstatedAccount(); + assertEquals("User has not been restored successfully", 1, failedUserIds.size()); + }); + } + + @Test + @DirtiesContext + void givenDeletedUserExistsInIdamThenFailedToRestoreDueToNewAccountWithSameEmail() { + String emailAddress = idamUserDataProvider.createIdamUserAndReturnEmailAddress(); + List deletionLogs = lauDeletionLogEntryProvider + .postDeletedLogEntryWithExistingEmail(emailAddress, 8); + assertEquals("Deletion Log entry has not created", 1, deletionLogs.size()); + logEntryUserIds.add(deletionLogs.get(0).getUserId()); + + idamUserRestoreService.run(); + + with().await() + .untilAsserted(() -> { + List failedUserIds = restoreSummary.getFailedToRestoreDueToNewAccountWithSameEmail(); + assertEquals("There should be 1 user fail to restore", 1, failedUserIds.size()); + }); + } + + @Test + @DirtiesContext + void givenDeletedUserExistsInIdamWithEmailAndRetiredThenFailedToRestoreDueToDuplicateEmail() { + String emailAddress = idamUserDataProvider.createAndRetireIdamUserAndReturnEmailAddress(); + List deletionLogs = lauDeletionLogEntryProvider + .postDeletedLogEntryWithExistingEmail(emailAddress, 10); + + assertEquals("Deletion Log entry has not created", 1, deletionLogs.size()); + + logEntryUserIds.add(deletionLogs.get(0).getUserId()); + + idamUserRestoreService.run(); + + with().await() + .untilAsserted(() -> { + List failedUserIds = restoreSummary.getFailedToRestoreDueToDuplicateEmail(); + assertEquals("There should be 1 user fail to restore", 1, failedUserIds.size()); + }); + } + + @AfterEach + public void teardown() { + if (!logEntryUserIds.isEmpty()) { + for (String logEntryUserId : logEntryUserIds) { + int statusCode = lauIdamBackendServiceProvider.deleteLogEntry(logEntryUserId); + assertEquals("Log entry not deleted", 204, statusCode); + } + } + } + } diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/DeletedAccountsRequest.java b/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/DeletedAccountsRequest.java new file mode 100644 index 00000000..0324cb5f --- /dev/null +++ b/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/DeletedAccountsRequest.java @@ -0,0 +1,19 @@ +package uk.gov.hmcts.reform.idam.helpers; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.idam.service.remote.responses.DeletionLog; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Builder +public class DeletedAccountsRequest { + private List deletionLogs; +} diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/IdamUserDataProvider.java b/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/IdamUserDataProvider.java index 82166a4c..739259bd 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/IdamUserDataProvider.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/IdamUserDataProvider.java @@ -1,6 +1,8 @@ package uk.gov.hmcts.reform.idam.helpers; import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.json.JSONArray; @@ -23,7 +25,7 @@ public class IdamUserDataProvider { private final SecurityUtil securityUtil; @Value("${idam.api.url}") - private String idamApi = "https://idam-api.aat.platform.hmcts.net"; + private String idamApi; @Value("${stale-users.roles}") private String roleToDelete; @@ -32,43 +34,46 @@ public class IdamUserDataProvider { private static final String RETIRE_USER_PATH_TMPL = "api/v1/staleUsers/%s/retire"; public String setup() { - RestAssured.baseURI = idamApi; securityUtil.generateTokens(); - String userId = createUser(); + ExtractableResponse res = createUser("DisposerTest-@example.org","Lau ", + "Test","{Pass12345Y"); + String userId = res.path("id"); retireUser(userId); return userId; } - private String createUser() { + private ExtractableResponse createUser(String email, String foreName, String lastName, String password) { return RestAssured.given() .header("Authorization", idamTokenGenerator.getIdamAuthorizationHeader()) .header("Content-Type", "application/json") - .body(makeUser()) + .body(makeUser(email,foreName,lastName,password)) + .baseUri(idamApi) .when() .post(CREATE_USER_PATH) .then() .statusCode(201) - .extract() - .path("id"); + .extract(); } private void retireUser(String userId) { RestAssured.given() .header("Authorization", idamTokenGenerator.getIdamAuthorizationHeader()) .header("Content-Type", "application/json") + .baseUri(idamApi) .post(String.format(RETIRE_USER_PATH_TMPL, userId)) .then() .statusCode(200); } - private String makeUser() { + private String makeUser(String email, String foreName, String lastName, String password) { JSONObject user = new JSONObject(); try { String name = UUID.randomUUID().toString(); - user.put("email", "DisposerTest-" + name + "@example.org"); - user.put("forename", "Lau " + name); - user.put("surname", "Test"); - user.put("password", "{Pass12345Y"); + String[] emailParts = email.split("-"); + user.put("email", emailParts[0] + name + emailParts[1]); + user.put("forename", foreName + name); + user.put("surname", lastName); + user.put("password", password); JSONArray roles = new JSONArray(); JSONObject role = new JSONObject(); role.put("code", roleToDelete); @@ -80,4 +85,38 @@ private String makeUser() { return user.toString(); } + + public String createIdamUser() { + securityUtil.generateTokens(); + ExtractableResponse res = createRestorerUser(); + return res.path("id"); + } + + public String createAndRetireIdamUser() { + securityUtil.generateTokens(); + ExtractableResponse res = createRestorerUser(); + String userId = res.path("id"); + retireUser(userId); + return userId; + } + + public String createIdamUserAndReturnEmailAddress() { + securityUtil.generateTokens(); + ExtractableResponse res = createRestorerUser(); + return res.path("email"); + } + + public String createAndRetireIdamUserAndReturnEmailAddress() { + securityUtil.generateTokens(); + ExtractableResponse res = createRestorerUser(); + String emailAddress = res.path("email"); + String userId = res.path("id"); + retireUser(userId); + return emailAddress; + } + + public ExtractableResponse createRestorerUser() { + return createUser("DisposerRestorer-@example.org","LauRestorer ", + "TestRestorer","{Pass12345Y"); + } } diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/LauDeletionLogEntryProvider.java b/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/LauDeletionLogEntryProvider.java new file mode 100644 index 00000000..eef24ef9 --- /dev/null +++ b/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/LauDeletionLogEntryProvider.java @@ -0,0 +1,106 @@ +package uk.gov.hmcts.reform.idam.helpers; + +import jakarta.inject.Inject; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.reform.idam.service.remote.responses.DeletedUsersResponse; +import uk.gov.hmcts.reform.idam.service.remote.responses.DeletionLog; +import uk.gov.hmcts.reform.idam.util.SecurityUtil; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import static java.time.LocalDateTime.now; +import static wiremock.org.eclipse.jetty.util.StringUtil.valueOf; + +@RequiredArgsConstructor +@Component +@Slf4j +public class LauDeletionLogEntryProvider { + + private final SecurityUtil securityUtil; + + @Inject + private LauIdamBackendServiceProvider lauIdamBackendServiceProvider; + + public List createDeletionLogLau() { + securityUtil.generateTokens(); + DeletedAccountsRequest request = generateDeletedAccountsRequest(); + final DeletedUsersResponse deletedUsersResponse = lauIdamBackendServiceProvider.postLogEntry(request); + return deletedUsersResponse.getDeletionLogs(); + } + + public DeletedAccountsRequest generateDeletedAccountsRequest() { + String id = UUID.randomUUID().toString(); + DeletionLog deletedAccount = DeletionLog.builder() + .userId(id) + .emailAddress("DisposerRestorerTest-" + id + "@example.org") + .firstName("LauRestorer-" + id + "@example.org") + .lastName("TestRestorer-") + .deletionTimestamp(getTimeStamp(valueOf(now().plusDays(2).toString()))) + .build(); + + DeletedAccountsRequest request = new DeletedAccountsRequest(); + request.setDeletionLogs(Arrays.asList(deletedAccount)); + return request; + } + + private String getTimeStamp(String timeStamp) { + String pattern = "yyyy-MM-dd'T'HH:mm:ss.sss"; + SimpleDateFormat format = new SimpleDateFormat(pattern); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + try { + Date date = format.parse(timeStamp); + return dateFormat.format(date); + } catch (ParseException e) { + log.error("TimsStamp can't be parsed to : " + dateFormat); + return ""; + } + } + + public DeletedAccountsRequest generateDeletedAccountsRequestWithUserId(String userId, int days) { + DeletionLog deletedAccount = DeletionLog.builder() + .userId(userId) + .emailAddress("DisposerRestorerTest-" + userId + "@example.org") + .firstName("LauRestorer-" + userId + "@example.org") + .lastName("TestRestorerExisting") + .deletionTimestamp(getTimeStamp(valueOf(now().plusDays(days).toString()))) + .build(); + + DeletedAccountsRequest request = new DeletedAccountsRequest(); + request.setDeletionLogs(Arrays.asList(deletedAccount)); + return request; + } + + public List postDeletedLogEntryWithExistingUserId(String userId, int days) { + DeletedAccountsRequest request = generateDeletedAccountsRequestWithUserId(userId, days); + return lauIdamBackendServiceProvider.postLogEntry(request).getDeletionLogs(); + } + + public DeletedAccountsRequest generateDeletedAccountsRequestWithEmailAddress(String emailAddress, int days) { + String id = UUID.randomUUID().toString(); + DeletionLog deletedAccount = DeletionLog.builder() + .userId(id) + .emailAddress(emailAddress) + .firstName("LauRestorer-" + id + "@example.org") + .lastName("TestRestorerExisting") + .deletionTimestamp(getTimeStamp(valueOf(now().plusDays(days).toString()))) + .build(); + + DeletedAccountsRequest request = new DeletedAccountsRequest(); + request.setDeletionLogs(Arrays.asList(deletedAccount)); + return request; + } + + public List postDeletedLogEntryWithExistingEmail(String emailAddress, int days) { + DeletedAccountsRequest request = generateDeletedAccountsRequestWithEmailAddress(emailAddress, days); + return lauIdamBackendServiceProvider.postLogEntry(request).getDeletionLogs(); + } + + +} diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/LauIdamBackendServiceProvider.java b/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/LauIdamBackendServiceProvider.java new file mode 100644 index 00000000..dfabefcc --- /dev/null +++ b/src/functionalTest/java/uk/gov/hmcts/reform/idam/helpers/LauIdamBackendServiceProvider.java @@ -0,0 +1,46 @@ +package uk.gov.hmcts.reform.idam.helpers; + +import io.restassured.RestAssured; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import uk.gov.hmcts.reform.idam.service.remote.responses.DeletedUsersResponse; +import uk.gov.hmcts.reform.idam.util.IdamTokenGenerator; +import uk.gov.hmcts.reform.idam.util.ServiceTokenGenerator; + +@Component +@Slf4j +@RequiredArgsConstructor +public class LauIdamBackendServiceProvider { + private final IdamTokenGenerator idamTokenGenerator; + private final ServiceTokenGenerator serviceTokenGenerator; + + @Value("${lau.api.url}") + private String lauIdamBackendUrl; + + public int deleteLogEntry(final String userId) { + return RestAssured.given() + .baseUri(lauIdamBackendUrl) + .header("Authorization", idamTokenGenerator.getPasswordTypeAuthorizationHeader()) + .header("ServiceAuthorization", serviceTokenGenerator.getServiceAuthToken()) + .contentType("application/json") + .param("userId", userId) + .delete("/audit/idamUser/deleteIdamUserRecord") + .getStatusCode(); + } + + public DeletedUsersResponse postLogEntry(final DeletedAccountsRequest deletedAccountsRequest) { + return RestAssured.given() + .baseUri(lauIdamBackendUrl) + .header("Authorization", idamTokenGenerator.getPasswordTypeAuthorizationHeader()) + .header("ServiceAuthorization", serviceTokenGenerator.getServiceAuthToken()) + .contentType("application/json") + .accept("application/json") + .body(deletedAccountsRequest) + .when() + .post("/audit/deletedAccounts") + .then() + .extract().body().as(DeletedUsersResponse.class); + } +} diff --git a/src/functionalTest/resources/application-functional.yaml b/src/functionalTest/resources/application-functional.yaml index c5d6cf3f..72a30ab0 100644 --- a/src/functionalTest/resources/application-functional.yaml +++ b/src/functionalTest/resources/application-functional.yaml @@ -29,7 +29,7 @@ lau: service: enabled: ${DISPOSER_IDAM_USER_ENABLED:false} - restorer_enabled: ${IDAM_USER_RESTORER_ENABLED:true} + restorer_enabled: ${IDAM_USER_RESTORER_ENABLED:false} restorer: batch.size: ${DISPOSER_RESTORER_BATCH_SIZE:1} diff --git a/src/main/java/uk/gov/hmcts/reform/idam/service/remote/responses/DeletionLog.java b/src/main/java/uk/gov/hmcts/reform/idam/service/remote/responses/DeletionLog.java index 120091c4..1f79742e 100644 --- a/src/main/java/uk/gov/hmcts/reform/idam/service/remote/responses/DeletionLog.java +++ b/src/main/java/uk/gov/hmcts/reform/idam/service/remote/responses/DeletionLog.java @@ -1,9 +1,15 @@ package uk.gov.hmcts.reform.idam.service.remote.responses; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +@AllArgsConstructor +@NoArgsConstructor +@Setter @Getter @Builder public class DeletionLog { diff --git a/src/test/java/uk/gov/hmcts/reform/idam/service/LauIdamUserServiceTest.java b/src/test/java/uk/gov/hmcts/reform/idam/service/LauIdamUserServiceTest.java index 4256b493..04b92dbe 100644 --- a/src/test/java/uk/gov/hmcts/reform/idam/service/LauIdamUserServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/idam/service/LauIdamUserServiceTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) +@SuppressWarnings("PMD.TooManyMethods") class LauIdamUserServiceTest { @Mock