diff --git a/lambdas/check-mobile-app-vc-receipt/build.gradle b/lambdas/check-mobile-app-vc-receipt/build.gradle index 2431dda654..8640c8ca12 100644 --- a/lambdas/check-mobile-app-vc-receipt/build.gradle +++ b/lambdas/check-mobile-app-vc-receipt/build.gradle @@ -8,10 +8,14 @@ plugins { dependencies { implementation libs.bundles.awsLambda, + project(":libs:audit-service"), project(":libs:common-services"), project(":libs:cri-response-service"), project(':libs:journey-uris'), - project(":libs:verifiable-credentials") + project(":libs:verifiable-credentials"), + project(":libs:cimit-service"), + project(":libs:user-identity-service"), + project(":lambdas:process-cri-callback") aspect libs.powertoolsLogging, libs.powertoolsTracing, diff --git a/lambdas/check-mobile-app-vc-receipt/src/main/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandler.java b/lambdas/check-mobile-app-vc-receipt/src/main/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandler.java index 9250338856..246686f80c 100644 --- a/lambdas/check-mobile-app-vc-receipt/src/main/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandler.java +++ b/lambdas/check-mobile-app-vc-receipt/src/main/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandler.java @@ -13,43 +13,63 @@ import uk.gov.di.ipv.core.checkmobileappvcreceipt.dto.CheckMobileAppVcReceiptRequest; import uk.gov.di.ipv.core.checkmobileappvcreceipt.exception.InvalidCheckMobileAppVcReceiptRequestException; import uk.gov.di.ipv.core.library.annotations.ExcludeFromGeneratedCoverageReport; +import uk.gov.di.ipv.core.library.cimit.exception.CiRetrievalException; import uk.gov.di.ipv.core.library.domain.Cri; import uk.gov.di.ipv.core.library.domain.ErrorResponse; -import uk.gov.di.ipv.core.library.exceptions.ClientOauthSessionNotFoundException; +import uk.gov.di.ipv.core.library.domain.JourneyErrorResponse; +import uk.gov.di.ipv.core.library.domain.JourneyResponse; +import uk.gov.di.ipv.core.library.exceptions.ConfigException; import uk.gov.di.ipv.core.library.exceptions.CredentialParseException; +import uk.gov.di.ipv.core.library.exceptions.HttpResponseExceptionWithErrorBody; import uk.gov.di.ipv.core.library.exceptions.IpvSessionNotFoundException; +import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.ApiGatewayResponseGenerator; import uk.gov.di.ipv.core.library.helpers.LogHelper; import uk.gov.di.ipv.core.library.helpers.RequestHelper; +import uk.gov.di.ipv.core.library.service.AuditService; +import uk.gov.di.ipv.core.library.service.CimitService; +import uk.gov.di.ipv.core.library.service.CimitUtilityService; import uk.gov.di.ipv.core.library.service.ClientOAuthSessionDetailsService; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.CriResponseService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.service.UserIdentityService; import uk.gov.di.ipv.core.library.service.exception.InvalidCriResponseException; +import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.ipv.core.library.verifiablecredential.service.VerifiableCredentialService; +import uk.gov.di.ipv.core.processcricallback.service.CriCheckingService; -import static uk.gov.di.ipv.core.library.helpers.LogHelper.buildErrorMessage; +import java.util.List; + +import static uk.gov.di.ipv.core.library.journeyuris.JourneyUris.JOURNEY_ABANDON_PATH; +import static uk.gov.di.ipv.core.library.journeyuris.JourneyUris.JOURNEY_ERROR_PATH; public class CheckMobileAppVcReceiptHandler implements RequestHandler { private static final Logger LOGGER = LogManager.getLogger(); + private static final JourneyResponse JOURNEY_ABANDON = + new JourneyResponse(JOURNEY_ABANDON_PATH); + private static final JourneyResponse JOURNEY_ERROR = new JourneyResponse(JOURNEY_ERROR_PATH); private final ConfigService configService; private final IpvSessionService ipvSessionService; private final ClientOAuthSessionDetailsService clientOAuthSessionDetailsService; private final CriResponseService criResponseService; private final VerifiableCredentialService verifiableCredentialService; + private final CriCheckingService criCheckingService; public CheckMobileAppVcReceiptHandler( ConfigService configService, IpvSessionService ipvSessionService, ClientOAuthSessionDetailsService clientOAuthSessionDetailsService, CriResponseService criResponseService, - VerifiableCredentialService verifiableCredentialService) { + VerifiableCredentialService verifiableCredentialService, + CriCheckingService criCheckingService) { this.configService = configService; this.ipvSessionService = ipvSessionService; this.clientOAuthSessionDetailsService = clientOAuthSessionDetailsService; this.criResponseService = criResponseService; this.verifiableCredentialService = verifiableCredentialService; + this.criCheckingService = criCheckingService; } @ExcludeFromGeneratedCoverageReport @@ -58,7 +78,19 @@ public CheckMobileAppVcReceiptHandler() { ipvSessionService = new IpvSessionService(configService); clientOAuthSessionDetailsService = new ClientOAuthSessionDetailsService(configService); criResponseService = new CriResponseService(configService); - this.verifiableCredentialService = new VerifiableCredentialService(configService); + verifiableCredentialService = new VerifiableCredentialService(configService); + + var sessionCredentialsService = new SessionCredentialsService(configService); + var cimitService = new CimitService(configService); + + criCheckingService = + new CriCheckingService( + configService, + AuditService.create(configService), + new UserIdentityService(configService), + cimitService, + new CimitUtilityService(configService), + sessionCredentialsService); } @Override @@ -69,24 +101,37 @@ public APIGatewayProxyResponseEvent handleRequest( try { var callbackRequest = parseCallbackRequest(input); - var status = getStatus(callbackRequest); + var journeyResponse = getJourneyResponse(callbackRequest); + + if (journeyResponse != null) { + // Frontend will end polling + return ApiGatewayResponseGenerator.proxyJsonResponse( + HttpStatus.SC_OK, journeyResponse); + } - return ApiGatewayResponseGenerator.proxyResponse(status); + return ApiGatewayResponseGenerator.proxyResponse(HttpStatus.SC_NOT_FOUND); } catch (InvalidCheckMobileAppVcReceiptRequestException e) { - LOGGER.info(buildErrorMessage(e.getErrorResponse())); - return ApiGatewayResponseGenerator.proxyResponse(HttpStatus.SC_BAD_REQUEST); + return buildErrorResponse(e, HttpStatus.SC_BAD_REQUEST, e.getErrorResponse()); } catch (IpvSessionNotFoundException e) { - LOGGER.info(buildErrorMessage(ErrorResponse.IPV_SESSION_NOT_FOUND)); - return ApiGatewayResponseGenerator.proxyResponse(HttpStatus.SC_BAD_REQUEST); - } catch (ClientOauthSessionNotFoundException e) { - LOGGER.info(buildErrorMessage(e.getErrorResponse())); - return ApiGatewayResponseGenerator.proxyResponse(HttpStatus.SC_BAD_REQUEST); + return buildErrorResponse( + e, HttpStatus.SC_BAD_REQUEST, ErrorResponse.IPV_SESSION_NOT_FOUND); + } catch (HttpResponseExceptionWithErrorBody e) { + return buildErrorResponse(e, HttpStatus.SC_BAD_REQUEST, e.getErrorResponse()); } catch (InvalidCriResponseException e) { - LOGGER.info(buildErrorMessage(e.getErrorResponse())); - return ApiGatewayResponseGenerator.proxyResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR); + return buildErrorResponse(e, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getErrorResponse()); } catch (CredentialParseException e) { - LOGGER.info(buildErrorMessage(ErrorResponse.FAILED_TO_PARSE_ISSUED_CREDENTIALS)); - return ApiGatewayResponseGenerator.proxyResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR); + return buildErrorResponse( + e, + HttpStatus.SC_INTERNAL_SERVER_ERROR, + ErrorResponse.FAILED_TO_PARSE_ISSUED_CREDENTIALS); + } catch (VerifiableCredentialException e) { + return buildErrorResponse(e, HttpStatus.SC_BAD_REQUEST, e.getErrorResponse()); + } catch (ConfigException e) { + return buildErrorResponse( + e, HttpStatus.SC_INTERNAL_SERVER_ERROR, ErrorResponse.FAILED_TO_PARSE_CONFIG); + } catch (CiRetrievalException e) { + return buildErrorResponse( + e, HttpStatus.SC_INTERNAL_SERVER_ERROR, ErrorResponse.FAILED_TO_GET_STORED_CIS); } } @@ -98,10 +143,11 @@ private CheckMobileAppVcReceiptRequest parseCallbackRequest(APIGatewayProxyReque RequestHelper.getFeatureSet(input.getHeaders())); } - private int getStatus(CheckMobileAppVcReceiptRequest callbackRequest) + private JourneyResponse getJourneyResponse(CheckMobileAppVcReceiptRequest callbackRequest) throws InvalidCheckMobileAppVcReceiptRequestException, IpvSessionNotFoundException, - ClientOauthSessionNotFoundException, InvalidCriResponseException, - CredentialParseException { + HttpResponseExceptionWithErrorBody, InvalidCriResponseException, + CredentialParseException, VerifiableCredentialException, ConfigException, + CiRetrievalException { // Validate callback sessions validateSessionId(callbackRequest); @@ -120,19 +166,32 @@ private int getStatus(CheckMobileAppVcReceiptRequest callbackRequest) LogHelper.attachFeatureSetToLogs(callbackRequest.getFeatureSet()); LogHelper.attachComponentId(configService); - // Retrieve and check cri response + // Retrieve and validate cri response and vc var criResponse = criResponseService.getCriResponseItem(userId, Cri.DCMAW_ASYNC); if (criResponse == null) { - return HttpStatus.SC_INTERNAL_SERVER_ERROR; + throw new InvalidCriResponseException(ErrorResponse.CRI_RESPONSE_ITEM_NOT_FOUND); + } + + var vc = verifiableCredentialService.getVc(userId, Cri.DCMAW_ASYNC.toString()); + + if (CriResponseService.STATUS_PENDING.equals(criResponse.getStatus()) && vc == null) { + return null; } - if (!CriResponseService.STATUS_PENDING.equals(criResponse.getStatus()) - || verifiableCredentialService.getVc(userId, Cri.DCMAW_ASYNC.toString()) != null) { - return HttpStatus.SC_OK; + if (CriResponseService.STATUS_ERROR.equals(criResponse.getStatus())) { + return JOURNEY_ERROR; } - return HttpStatus.SC_NOT_FOUND; + if (CriResponseService.STATUS_ABANDON.equals(criResponse.getStatus())) { + return JOURNEY_ABANDON; + } + + return criCheckingService.checkVcResponse( + List.of(vc), + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem); } private void validateSessionId(CheckMobileAppVcReceiptRequest callbackRequest) @@ -144,4 +203,13 @@ private void validateSessionId(CheckMobileAppVcReceiptRequest callbackRequest) ErrorResponse.MISSING_IPV_SESSION_ID); } } + + private APIGatewayProxyResponseEvent buildErrorResponse( + Exception e, int status, ErrorResponse errorResponse) { + LOGGER.error(LogHelper.buildErrorMessage(errorResponse.getMessage(), e)); + return ApiGatewayResponseGenerator.proxyJsonResponse( + status, + new JourneyErrorResponse( + JOURNEY_ERROR_PATH, status, errorResponse, e.getMessage())); + } } diff --git a/lambdas/check-mobile-app-vc-receipt/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/CheckMobileAppVcReceiptHandlerTest.java b/lambdas/check-mobile-app-vc-receipt/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/CheckMobileAppVcReceiptHandlerTest.java index 6209d3426f..6cf0d43905 100644 --- a/lambdas/check-mobile-app-vc-receipt/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/CheckMobileAppVcReceiptHandlerTest.java +++ b/lambdas/check-mobile-app-vc-receipt/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/CheckMobileAppVcReceiptHandlerTest.java @@ -2,6 +2,8 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import org.apache.http.HttpStatus; @@ -12,6 +14,9 @@ import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.di.ipv.core.checkmobileappvcreceipt.CheckMobileAppVcReceiptHandler; import uk.gov.di.ipv.core.library.domain.Cri; +import uk.gov.di.ipv.core.library.domain.ErrorResponse; +import uk.gov.di.ipv.core.library.domain.JourneyErrorResponse; +import uk.gov.di.ipv.core.library.domain.JourneyResponse; import uk.gov.di.ipv.core.library.domain.VerifiableCredential; import uk.gov.di.ipv.core.library.exceptions.IpvSessionNotFoundException; import uk.gov.di.ipv.core.library.persistence.item.ClientOAuthSessionItem; @@ -22,15 +27,22 @@ import uk.gov.di.ipv.core.library.service.CriResponseService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.verifiablecredential.service.VerifiableCredentialService; +import uk.gov.di.ipv.core.processcricallback.service.CriCheckingService; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; +import static uk.gov.di.ipv.core.library.journeyuris.JourneyUris.JOURNEY_ABANDON_PATH; +import static uk.gov.di.ipv.core.library.journeyuris.JourneyUris.JOURNEY_ERROR_PATH; +import static uk.gov.di.ipv.core.library.journeyuris.JourneyUris.JOURNEY_NEXT_PATH; @ExtendWith(MockitoExtension.class) class CheckMobileAppVcReceiptHandlerTest { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String TEST_IPV_SESSION_ID = "test_ipv_session_id"; private static final String TEST_CLIENT_OAUTH_SESSION_ID = "test_client_oauth_id"; private static final String TEST_USER_ID = "test_user_id"; @@ -40,20 +52,29 @@ class CheckMobileAppVcReceiptHandlerTest { @Mock private IpvSessionService ipvSessionService; @Mock private ClientOAuthSessionDetailsService clientOAuthSessionDetailsService; @Mock private CriResponseService criResponseService; + @Mock private CriCheckingService criCheckingService; @Mock private VerifiableCredentialService verifiableCredentialService; @InjectMocks private CheckMobileAppVcReceiptHandler checkMobileAppVcReceiptHandler; @Test - void shouldReturnErrorWhenCallbackRequestMissingIpvSessionId() { + void shouldReturnErrorWhenCallbackRequestMissingIpvSessionId() throws JsonProcessingException { // Arrange var requestEvent = buildValidRequestEventWithState(); requestEvent.setHeaders(Map.of()); // Act var response = checkMobileAppVcReceiptHandler.handleRequest(requestEvent, mockContext); + var journeyResponse = + OBJECT_MAPPER.readValue(response.getBody(), JourneyErrorResponse.class); // Assert assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); + assertEquals( + new JourneyErrorResponse( + JOURNEY_ERROR_PATH, + HttpStatus.SC_BAD_REQUEST, + ErrorResponse.MISSING_IPV_SESSION_ID), + journeyResponse); } @Test @@ -65,9 +86,17 @@ void shouldReturnErrorWhenIpvSessionNotFound() throws Exception { // Act var response = checkMobileAppVcReceiptHandler.handleRequest(requestEvent, mockContext); + var journeyResponse = + OBJECT_MAPPER.readValue(response.getBody(), JourneyErrorResponse.class); // Assert assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); + assertEquals( + new JourneyErrorResponse( + JOURNEY_ERROR_PATH, + HttpStatus.SC_BAD_REQUEST, + ErrorResponse.IPV_SESSION_NOT_FOUND), + journeyResponse); } @Test @@ -82,37 +111,61 @@ void shouldReturn500WhenCriResponseNotFound() throws Exception { // Act var response = checkMobileAppVcReceiptHandler.handleRequest(requestEvent, mockContext); + var journeyResponse = + OBJECT_MAPPER.readValue(response.getBody(), JourneyErrorResponse.class); // Assert assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertEquals( + new JourneyErrorResponse( + JOURNEY_ERROR_PATH, + HttpStatus.SC_INTERNAL_SERVER_ERROR, + ErrorResponse.CRI_RESPONSE_ITEM_NOT_FOUND), + journeyResponse); } @Test void shouldReturn200WhenStatusNotPending() throws Exception { // Arrange var requestEvent = buildValidRequestEventWithState(); - when(ipvSessionService.getIpvSession(TEST_IPV_SESSION_ID)) - .thenReturn(buildValidIpvSessionItem()); + var ipvSessionItem = buildValidIpvSessionItem(); + var clientOAuthSessionItem = buildValidClientOAuthSessionItem(); + when(ipvSessionService.getIpvSession(TEST_IPV_SESSION_ID)).thenReturn(ipvSessionItem); when(clientOAuthSessionDetailsService.getClientOAuthSession(TEST_CLIENT_OAUTH_SESSION_ID)) .thenReturn(buildValidClientOAuthSessionItem()); when(criResponseService.getCriResponseItem(TEST_USER_ID, Cri.DCMAW_ASYNC)) .thenReturn(buildValidCriResponseItem(CriResponseService.STATUS_RECEIVED)); + when(mockSignedJwt.getJWTClaimsSet()) + .thenReturn( + JWTClaimsSet.parse( + Map.of( + "vc", + Map.of("type", List.of("IdentityAssertionCredential"))))); + var vc = VerifiableCredential.fromValidJwt(TEST_USER_ID, Cri.DCMAW_ASYNC, mockSignedJwt); + when(verifiableCredentialService.getVc(TEST_USER_ID, Cri.DCMAW_ASYNC.toString())) + .thenReturn(vc); + when(criCheckingService.checkVcResponse( + eq(List.of(vc)), eq(null), eq(clientOAuthSessionItem), eq(ipvSessionItem))) + .thenReturn(new JourneyResponse(JOURNEY_NEXT_PATH)); // Act var response = checkMobileAppVcReceiptHandler.handleRequest(requestEvent, mockContext); + var journeyResponse = OBJECT_MAPPER.readValue(response.getBody(), JourneyResponse.class); // Assert assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + assertEquals(new JourneyResponse(JOURNEY_NEXT_PATH), journeyResponse); } @Test void shouldReturn200WhenCriResponseStatusPendingButVcExists() throws Exception { // Arrange var requestEvent = buildValidRequestEventWithState(); - when(ipvSessionService.getIpvSession(TEST_IPV_SESSION_ID)) - .thenReturn(buildValidIpvSessionItem()); + var ipvSessionItem = buildValidIpvSessionItem(); + var clientOAuthSessionItem = buildValidClientOAuthSessionItem(); + when(ipvSessionService.getIpvSession(TEST_IPV_SESSION_ID)).thenReturn(ipvSessionItem); when(clientOAuthSessionDetailsService.getClientOAuthSession(TEST_CLIENT_OAUTH_SESSION_ID)) - .thenReturn(buildValidClientOAuthSessionItem()); + .thenReturn(clientOAuthSessionItem); var criResponseItem = buildValidCriResponseItem(CriResponseService.STATUS_PENDING); when(criResponseService.getCriResponseItem(TEST_USER_ID, Cri.DCMAW_ASYNC)) .thenReturn(criResponseItem); @@ -125,12 +178,59 @@ void shouldReturn200WhenCriResponseStatusPendingButVcExists() throws Exception { var vc = VerifiableCredential.fromValidJwt(TEST_USER_ID, Cri.DCMAW_ASYNC, mockSignedJwt); when(verifiableCredentialService.getVc(TEST_USER_ID, Cri.DCMAW_ASYNC.toString())) .thenReturn(vc); + when(criCheckingService.checkVcResponse( + eq(List.of(vc)), eq(null), eq(clientOAuthSessionItem), eq(ipvSessionItem))) + .thenReturn(new JourneyResponse(JOURNEY_NEXT_PATH)); + + // Act + var response = checkMobileAppVcReceiptHandler.handleRequest(requestEvent, mockContext); + var journeyResponse = OBJECT_MAPPER.readValue(response.getBody(), JourneyResponse.class); + + // Assert + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + assertEquals(new JourneyResponse(JOURNEY_NEXT_PATH), journeyResponse); + } + + @Test + void shouldReturnAbandonJourneyResponseWhenCriResponseStatusAbandon() throws Exception { + // Arrange + var requestEvent = buildValidRequestEventWithState(); + when(ipvSessionService.getIpvSession(TEST_IPV_SESSION_ID)) + .thenReturn(buildValidIpvSessionItem()); + when(clientOAuthSessionDetailsService.getClientOAuthSession(TEST_CLIENT_OAUTH_SESSION_ID)) + .thenReturn(buildValidClientOAuthSessionItem()); + var criResponseItem = buildValidCriResponseItem(CriResponseService.STATUS_ABANDON); + when(criResponseService.getCriResponseItem(TEST_USER_ID, Cri.DCMAW_ASYNC)) + .thenReturn(criResponseItem); + + // Act + var response = checkMobileAppVcReceiptHandler.handleRequest(requestEvent, mockContext); + var journeyResponse = OBJECT_MAPPER.readValue(response.getBody(), JourneyResponse.class); + + // Assert + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + assertEquals(new JourneyResponse(JOURNEY_ABANDON_PATH), journeyResponse); + } + + @Test + void shouldReturnErrorJourneyResponseWhenCriResponseStatusError() throws Exception { + // Arrange + var requestEvent = buildValidRequestEventWithState(); + when(ipvSessionService.getIpvSession(TEST_IPV_SESSION_ID)) + .thenReturn(buildValidIpvSessionItem()); + when(clientOAuthSessionDetailsService.getClientOAuthSession(TEST_CLIENT_OAUTH_SESSION_ID)) + .thenReturn(buildValidClientOAuthSessionItem()); + var criResponseItem = buildValidCriResponseItem(CriResponseService.STATUS_ERROR); + when(criResponseService.getCriResponseItem(TEST_USER_ID, Cri.DCMAW_ASYNC)) + .thenReturn(criResponseItem); // Act var response = checkMobileAppVcReceiptHandler.handleRequest(requestEvent, mockContext); + var journeyResponse = OBJECT_MAPPER.readValue(response.getBody(), JourneyResponse.class); // Assert assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + assertEquals(new JourneyResponse(JOURNEY_ERROR_PATH), journeyResponse); } @Test @@ -150,6 +250,7 @@ void shouldReturn404WhenCriResponseStatusPendingAndVcNotFound() throws Exception // Assert assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); + assertNull(response.getBody()); } private APIGatewayProxyRequestEvent buildValidRequestEventWithState() { diff --git a/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandler.java b/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandler.java index 8f8ea48af7..ef86d0e110 100644 --- a/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandler.java +++ b/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandler.java @@ -294,7 +294,7 @@ private JourneyResponse getJourneyResponse(CriCallbackRequest callbackRequest) ipvSessionItem); return criCheckingService.checkVcResponse( - vcs, callbackRequest, clientOAuthSessionItem, ipvSessionItem); + vcs, callbackRequest.getIpAddress(), clientOAuthSessionItem, ipvSessionItem); } private List validateAndStoreResponse( diff --git a/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/service/CriCheckingService.java b/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/service/CriCheckingService.java index e386966e2c..762c9d9438 100644 --- a/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/service/CriCheckingService.java +++ b/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/service/CriCheckingService.java @@ -221,7 +221,7 @@ public void validatePendingVcResponse( public JourneyResponse checkVcResponse( List newVcs, - CriCallbackRequest callbackRequest, + String ipAddress, ClientOAuthSessionItem clientOAuthSessionItem, IpvSessionItem ipvSessionItem) throws CiRetrievalException, ConfigException, HttpResponseExceptionWithErrorBody, @@ -233,7 +233,7 @@ public JourneyResponse checkVcResponse( cimitService.getContraIndicators( clientOAuthSessionItem.getUserId(), clientOAuthSessionItem.getGovukSigninJourneyId(), - callbackRequest.getIpAddress()); + ipAddress); // Check CIs only against the target Vot so we don't send the user on an unnecessary // mitigation journey. diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandlerTest.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandlerTest.java index 54bd1725c5..da133f6873 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandlerTest.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandlerTest.java @@ -116,7 +116,10 @@ void shouldReturnNextWhenAllChecksPassForCreatedVcs() throws Exception { when(mockVerifiableCredentialValidator.parseAndValidate(any(), any(), any(), any(), any())) .thenReturn(vcs); when(mockCriCheckingService.checkVcResponse( - any(), eq(callbackRequest), eq(clientOAuthSessionItem), eq(ipvSessionItem))) + any(), + eq(callbackRequest.getIpAddress()), + eq(clientOAuthSessionItem), + eq(ipvSessionItem))) .thenReturn(new JourneyResponse(JOURNEY_NEXT_PATH)); when(mockConfigService.getOauthCriConfig(any())) .thenReturn( @@ -173,7 +176,10 @@ void shouldReturnNextWhenAllChecksPassForPendingVcs() throws Exception { when(mockCriApiService.fetchVerifiableCredential(bearerToken, ADDRESS, criOAuthSessionItem)) .thenReturn(vcResponse); when(mockCriCheckingService.checkVcResponse( - List.of(), callbackRequest, clientOAuthSessionItem, ipvSessionItem)) + List.of(), + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem)) .thenReturn(new JourneyResponse(JOURNEY_NEXT_PATH)); // Act @@ -245,7 +251,10 @@ void shouldReturnJourneyErrorResponseWhenCriCheckingServiceThrows() throws Excep .signingKey(TestFixtures.TEST_EC_PUBLIC_JWK) .build()); when(mockCriCheckingService.checkVcResponse( - any(), eq(callbackRequest), eq(clientOAuthSessionItem), eq(ipvSessionItem))) + any(), + eq(callbackRequest.getIpAddress()), + eq(clientOAuthSessionItem), + eq(ipvSessionItem))) .thenThrow(new ConfigException("bad config")); // Act diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/service/CriCheckingServiceTest.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/service/CriCheckingServiceTest.java index 2bb7e87f00..318a3b3a34 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/service/CriCheckingServiceTest.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/service/CriCheckingServiceTest.java @@ -475,7 +475,10 @@ void checkVcResponseShouldReturnNextWhenAllChecksPass() throws Exception { // Act JourneyResponse result = criCheckingService.checkVcResponse( - vcs, callbackRequest, clientOAuthSessionItem, ipvSessionItem); + vcs, + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem); // Assert assertEquals(new JourneyResponse(JOURNEY_NEXT_PATH), result); @@ -500,7 +503,10 @@ void checkVcResponseShouldReturnNextWhenAllChecksPassForLowerConfidenceVot() thr // Act JourneyResponse result = criCheckingService.checkVcResponse( - vcs, callbackRequest, clientOAuthSessionItem, ipvSessionItem); + vcs, + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem); // Assert assertEquals(new JourneyResponse(JOURNEY_NEXT_PATH), result); @@ -521,7 +527,10 @@ void checkVcResponseShouldReturnFailWithCiWhenUserBreachesCiThreshold() throws E // Act JourneyResponse result = criCheckingService.checkVcResponse( - List.of(), callbackRequest, clientOAuthSessionItem, ipvSessionItem); + List.of(), + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem); // Assert assertEquals(new JourneyResponse(JOURNEY_FAIL_WITH_CI_PATH), result); @@ -539,7 +548,10 @@ void checkVcResponseDoesNotCheckForCIsWhenOnReverificationJourney() throws Excep // Act JourneyResponse result = criCheckingService.checkVcResponse( - List.of(), callbackRequest, clientOAuthSessionItem, ipvSessionItem); + List.of(), + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem); // Assert assertEquals(new JourneyResponse(JOURNEY_VCS_NOT_CORRELATED), result); @@ -561,7 +573,10 @@ void checkVcResponseShouldReturnMitigatedJourneyWhenCiMitigationIsPossible() thr // Act JourneyResponse result = criCheckingService.checkVcResponse( - List.of(), callbackRequest, clientOAuthSessionItem, ipvSessionItem); + List.of(), + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem); // Assert assertEquals(new JourneyResponse("/journey/mitigation-journey"), result); @@ -582,7 +597,10 @@ void checkVcResponseShouldReturnVcsNotCorrelatedWhenVcsNotCorrelated() throws Ex // Act JourneyResponse result = criCheckingService.checkVcResponse( - List.of(), callbackRequest, clientOAuthSessionItem, ipvSessionItem); + List.of(), + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem); // Assert assertEquals(new JourneyResponse(JOURNEY_VCS_NOT_CORRELATED), result); @@ -606,7 +624,10 @@ void checkVcResponseShouldReturnFailWithNoCiWhenVcsNotSuccessful() throws Except // Act JourneyResponse result = criCheckingService.checkVcResponse( - vcs, callbackRequest, clientOAuthSessionItem, ipvSessionItem); + vcs, + callbackRequest.getIpAddress(), + clientOAuthSessionItem, + ipvSessionItem); // Assert assertEquals(new JourneyResponse(JOURNEY_FAIL_WITH_NO_CI_PATH), result); @@ -637,7 +658,7 @@ void checkVcResponseShouldReturnDlAuthSourceCheckForDlDcmawVcAndNoDrivingLicence JourneyResponse result = criCheckingService.checkVcResponse( List.of(M1B_DCMAW_VC), - callbackRequest, + callbackRequest.getIpAddress(), clientOAuthSessionItem, ipvSessionItem); @@ -663,7 +684,7 @@ void checkVcResponseShouldReturnDlAuthSourceCheckForDlDcmawVcAndNoDrivingLicence JourneyResponse result = criCheckingService.checkVcResponse( List.of(M1B_DCMAW_VC), - callbackRequest, + callbackRequest.getIpAddress(), clientOAuthSessionItem, ipvSessionItem); @@ -685,7 +706,7 @@ void checkVcResponseShouldReturnDlAuthSourceCheckForDlDcmawVcAndNoDrivingLicence JourneyResponse result = criCheckingService.checkVcResponse( List.of(M1B_DCMAW_VC), - callbackRequest, + callbackRequest.getIpAddress(), clientOAuthSessionItem, ipvSessionItem); @@ -705,7 +726,7 @@ void checkVcResponseShouldNotReturnDlAuthSourceCheckForNonDlDcmawVc() throws Exc JourneyResponse result = criCheckingService.checkVcResponse( List.of(DCMAW_PASSPORT_VC), - callbackRequest, + callbackRequest.getIpAddress(), clientOAuthSessionItem, ipvSessionItem); diff --git a/lambdas/process-mobile-app-callback/src/main/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandler.java b/lambdas/process-mobile-app-callback/src/main/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandler.java index d007241738..cc2829f652 100644 --- a/lambdas/process-mobile-app-callback/src/main/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandler.java +++ b/lambdas/process-mobile-app-callback/src/main/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandler.java @@ -155,7 +155,8 @@ private void validateCallbackRequest(MobileAppCallbackRequest callbackRequest) private void validateCriResponse(Optional criResponse) throws InvalidMobileAppCallbackRequestException, InvalidCriResponseException { if (criResponse.isEmpty()) { - throw new InvalidMobileAppCallbackRequestException(ErrorResponse.INVALID_OAUTH_STATE); + throw new InvalidMobileAppCallbackRequestException( + ErrorResponse.CRI_RESPONSE_ITEM_NOT_FOUND); } if (CriResponseService.STATUS_ERROR.equals(criResponse.get().getStatus())) { diff --git a/lambdas/process-mobile-app-callback/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandlerTest.java b/lambdas/process-mobile-app-callback/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandlerTest.java index a9538e58ac..b42f140c3b 100644 --- a/lambdas/process-mobile-app-callback/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandlerTest.java +++ b/lambdas/process-mobile-app-callback/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandlerTest.java @@ -155,7 +155,7 @@ void shouldReturnErrorWhenCriResponseNotFound() throws Exception { new JourneyErrorResponse( JOURNEY_ERROR_PATH, HttpStatus.SC_BAD_REQUEST, - ErrorResponse.INVALID_OAUTH_STATE), + ErrorResponse.CRI_RESPONSE_ITEM_NOT_FOUND), journeyResponse); } diff --git a/libs/common-services/src/main/java/uk/gov/di/ipv/core/library/domain/ErrorResponse.java b/libs/common-services/src/main/java/uk/gov/di/ipv/core/library/domain/ErrorResponse.java index 2e998d41f4..0fb17d2c92 100644 --- a/libs/common-services/src/main/java/uk/gov/di/ipv/core/library/domain/ErrorResponse.java +++ b/libs/common-services/src/main/java/uk/gov/di/ipv/core/library/domain/ErrorResponse.java @@ -108,10 +108,11 @@ public enum ErrorResponse { MISSING_LANGUAGE(1095, "Missing language choice from the frontend"), FAILED_TO_VALIDATE_VERIFIABLE_CREDENTIAL_RESPONSE( 1096, "Failed to validate verifiable credential response"), - ERROR_MOBILE_APP_RESPONSE_STATUS(1097, "Mobile app response has status error"), + ERROR_MOBILE_APP_RESPONSE_STATUS(1097, "Mobile app cri response has status error"), INVALID_PROCESS_MOBILE_APP_JOURNEY_TYPE(1098, "Invalid process mobile app journey type"), FAILED_TO_PARSE_MOBILE_APP_CALLBACK_REQUEST_BODY( - 1099, "Failed to parse mobile app callback request bpdy"); + 1099, "Failed to parse mobile app callback request bpdy"), + CRI_RESPONSE_ITEM_NOT_FOUND(1100, "CRI response item cannot be found"); private static final String ERROR = "error"; private static final String ERROR_DESCRIPTION = "error_description"; diff --git a/libs/cri-response-service/src/main/java/uk/gov/di/ipv/core/library/service/CriResponseService.java b/libs/cri-response-service/src/main/java/uk/gov/di/ipv/core/library/service/CriResponseService.java index 7022473f1b..eb6fceeead 100644 --- a/libs/cri-response-service/src/main/java/uk/gov/di/ipv/core/library/service/CriResponseService.java +++ b/libs/cri-response-service/src/main/java/uk/gov/di/ipv/core/library/service/CriResponseService.java @@ -18,6 +18,7 @@ public class CriResponseService { public static final String STATUS_RECEIVED = "received"; public static final String STATUS_PENDING = "pending"; public static final String STATUS_ERROR = "error"; + public static final String STATUS_ABANDON = "abandon"; private final DataStore dataStore; @ExcludeFromGeneratedCoverageReport diff --git a/libs/journey-uris/src/main/java/uk/gov/di/ipv/core/library/journeyuris/JourneyUris.java b/libs/journey-uris/src/main/java/uk/gov/di/ipv/core/library/journeyuris/JourneyUris.java index a7f15df0b1..dd0303904f 100644 --- a/libs/journey-uris/src/main/java/uk/gov/di/ipv/core/library/journeyuris/JourneyUris.java +++ b/libs/journey-uris/src/main/java/uk/gov/di/ipv/core/library/journeyuris/JourneyUris.java @@ -40,4 +40,5 @@ private JourneyUris() { "/journey/temporarily-unavailable"; public static final String JOURNEY_UNMET_PATH = "/journey/unmet"; public static final String JOURNEY_VCS_NOT_CORRELATED = "/journey/vcs-not-correlated"; + public static final String JOURNEY_ABANDON_PATH = "/journey/abandon"; } diff --git a/settings.gradle b/settings.gradle index af50eb1366..d4f339aae5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -63,3 +63,7 @@ dependencyResolutionManagement { } include 'lambdas:check-mobile-app-vc-receipt' findProject(':lambdas:check-mobile-app-vc-receipt')?.name = 'check-mobile-app-vc-receipt' +include 'libs:CriCheckingService' +findProject(':libs:CriCheckingService')?.name = 'CriCheckingService' +include 'libs:cri-checking-service' +findProject(':libs:cri-checking-service')?.name = 'cri-checking-service'