diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/dto/CaptchaRequest.java b/esignet-core/src/main/java/io/mosip/esignet/core/dto/CaptchaRequest.java new file mode 100644 index 000000000..34e0410f6 --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/dto/CaptchaRequest.java @@ -0,0 +1,10 @@ +package io.mosip.esignet.core.dto; + +import lombok.Data; + +@Data +public class CaptchaRequest { + private static final long serialVersionUID = 1L; + private String captchaToken; + private String moduleName; +} diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/util/CaptchaHelper.java b/esignet-core/src/main/java/io/mosip/esignet/core/util/CaptchaHelper.java new file mode 100644 index 000000000..342c91f04 --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/util/CaptchaHelper.java @@ -0,0 +1,77 @@ +package io.mosip.esignet.core.util; + +import io.mosip.esignet.core.constants.ErrorConstants; +import io.mosip.esignet.core.dto.CaptchaRequest; +import io.mosip.esignet.core.dto.RequestWrapper; +import io.mosip.esignet.core.dto.ResponseWrapper; +import io.mosip.esignet.core.exception.EsignetException; +import org.springframework.beans.factory.annotation.Value; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import static io.mosip.esignet.core.constants.Constants.UTC_DATETIME_PATTERN; + +@Service +@Slf4j +public class CaptchaHelper { + + @Autowired + private RestTemplate restTemplate; + + @Value("${mosip.esignet.captcha.module-name}") + private String moduleName; + + @Value("${mosip.esignet.captcha.validator-url}") + private String validatorUrl; + + public boolean validateCaptcha(String captchaToken) { + + if (captchaToken == null || captchaToken.isBlank()) { + throw new EsignetException(ErrorConstants.INVALID_CAPTCHA); + } + + try{ + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + CaptchaRequest captchaRequest = new CaptchaRequest(); + captchaRequest.setCaptchaToken(captchaToken); + captchaRequest.setModuleName(moduleName); + + RequestWrapper requestWrapper = new RequestWrapper<>(); + requestWrapper.setRequestTime(ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); + requestWrapper.setRequest(captchaRequest); + RequestEntity> requestEntity = RequestEntity + .post(URI.create(validatorUrl)) + .headers(headers) + .body(requestWrapper); + + ResponseEntity responseEntity = restTemplate.exchange( + requestEntity, + ResponseWrapper.class + ); + + if (responseEntity.getStatusCode().is2xxSuccessful() && responseEntity.getBody() != null) { + ResponseWrapper responseWrapper = (ResponseWrapper) responseEntity.getBody(); + if (responseWrapper != null && responseWrapper.getResponse() != null) { + log.info("Captcha Validation Successful"); + return true; + } + log.error("Errors received from CaptchaService: {}", responseWrapper.getErrors()); //NOSONAR responseWrapper is already evaluated to be not null + throw new EsignetException(ErrorConstants.INVALID_CAPTCHA); + } + log.error("Errors received from CaptchaService: {}", responseEntity.getStatusCode()); + throw new EsignetException(ErrorConstants.INVALID_CAPTCHA); + } catch (Exception e) { + throw new EsignetException(ErrorConstants.INVALID_CAPTCHA); + } + } + +} \ No newline at end of file diff --git a/esignet-core/src/test/java/io/mosip/esignet/core/CaptchaHelperTest.java b/esignet-core/src/test/java/io/mosip/esignet/core/CaptchaHelperTest.java new file mode 100644 index 000000000..ee39d883c --- /dev/null +++ b/esignet-core/src/test/java/io/mosip/esignet/core/CaptchaHelperTest.java @@ -0,0 +1,88 @@ +package io.mosip.esignet.core; + +import io.mosip.esignet.core.dto.ResponseWrapper; +import io.mosip.esignet.core.exception.EsignetException; +import io.mosip.esignet.core.util.CaptchaHelper; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.*; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.ArrayList; + +import static org.mockito.ArgumentMatchers.any; + +@SpringBootTest +@RunWith(MockitoJUnitRunner.class) +public class CaptchaHelperTest { + + @Mock + RestTemplate restTemplate; + + @InjectMocks + CaptchaHelper captchaHelper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + ReflectionTestUtils.setField(captchaHelper, "validatorUrl", "https://api-internal.camdgc-dev1.mosip.net/v1/captcha/validatecaptcha"); + ReflectionTestUtils.setField(captchaHelper,"moduleName","esignet"); + } + + @Test + public void validateCaptcha_WithNullCaptchaToken_ThrowsException() { + CaptchaHelper captchaHelper=new CaptchaHelper(); + Assert.assertThrows(EsignetException.class,()->captchaHelper.validateCaptcha(null)); + } + + @Test + public void validateCaptcha_WithCaptchaToken_ThrowsException() { + CaptchaHelper captchaHelper=new CaptchaHelper(); + Assert.assertThrows(EsignetException.class,()->captchaHelper.validateCaptcha("")); + } + + @Test + public void validateCaptcha_WithNullResponse_ThrowsException() { + Mockito.when(restTemplate.exchange((RequestEntity) any(), (Class) any())).thenReturn(null); + Assert.assertThrows(EsignetException.class,()->captchaHelper.validateCaptcha("captchaToken")); + } + + @Test + public void validateCaptcha_SuccessfulResponse() { + ResponseWrapper responseWrapper = new ResponseWrapper(); + responseWrapper.setResponse("success"); + ResponseEntity responseEntity = ResponseEntity.ok(responseWrapper); + Mockito.when(restTemplate.exchange(Mockito.any(RequestEntity.class), Mockito.eq(ResponseWrapper.class))) + .thenReturn(responseEntity); + boolean result = captchaHelper.validateCaptcha("captchaToken"); + Assert.assertTrue(result); + } + + @Test + public void validateCaptcha_UnsuccessfulValidation_ThrowsEsignetException() { + ResponseWrapper responseWrapper = new ResponseWrapper(); + responseWrapper.setErrors(new ArrayList<>()); + ResponseEntity responseEntity = ResponseEntity.ok(responseWrapper); + Mockito.when(restTemplate.exchange(Mockito.any(RequestEntity.class), Mockito.eq(ResponseWrapper.class))) + .thenReturn(responseEntity); + Assert.assertThrows(EsignetException.class, () -> captchaHelper.validateCaptcha("captchaToken")); + } + + @Test + public void validateCaptcha_RequestException_ThrowsEsignetException() { + Mockito.when(restTemplate.exchange(Mockito.any(RequestEntity.class), Mockito.eq(ResponseWrapper.class))) + .thenThrow(new RestClientException("Request failed")); + Assert.assertThrows(EsignetException.class, () -> captchaHelper.validateCaptcha("captchaToken")); + } + +} diff --git a/esignet-service/src/main/resources/application-dev.properties b/esignet-service/src/main/resources/application-dev.properties index e35d60673..545e259af 100644 --- a/esignet-service/src/main/resources/application-dev.properties +++ b/esignet-service/src/main/resources/application-dev.properties @@ -19,6 +19,8 @@ mosip.esignet.header-filter.paths-to-validate={'${server.servlet.path}/authoriza #This property is used for captcha validation and allowed values are send-otp and pwd. #captcha validation is enabled for send-otp and pwd. mosip.esignet.captcha.required=send-otp,pwd +mosip.esignet.captcha.validator-url=${mosipbox.public.url}${server.servlet.path}/v1/captcha/validatecaptcha +mosip.esignet.captcha.module-name=esignet ## ------------------------------------------ e-Signet binding --------------------------------------------------------- diff --git a/esignet-service/src/main/resources/application-local.properties b/esignet-service/src/main/resources/application-local.properties index 113f1e2ec..dd6147cbf 100644 --- a/esignet-service/src/main/resources/application-local.properties +++ b/esignet-service/src/main/resources/application-local.properties @@ -54,6 +54,8 @@ mosip.esignet.header-filter.paths-to-validate={'${server.servlet.path}/authoriza #This property is used for captcha validation and allowed values are send-otp and pwd. # captcha validation is enabled for the auth-factors - otp, pwd, bio and pin. mosip.esignet.captcha.required=send-otp,pwd,bio,pin +mosip.esignet.captcha.validator-url=${mosipbox.public.url}${server.servlet.path}/v1/captcha/validatecaptcha +mosip.esignet.captcha.module-name=esignet mosip.esignet.host=localhost mosip.esignet.signup-id-token-expire-seconds=180 diff --git a/esignet-service/src/test/resources/application-test.properties b/esignet-service/src/test/resources/application-test.properties index d23abbd04..d53bd356f 100644 --- a/esignet-service/src/test/resources/application-test.properties +++ b/esignet-service/src/test/resources/application-test.properties @@ -43,6 +43,8 @@ mosip.esignet.header-filter.paths-to-validate={'${server.servlet.path}/authoriza #This property is used for captcha validation and allowed values are send-otp and pwd. #captcha validation is enabled for send-otp and pwd. mosip.esignet.captcha.required=pwd +mosip.esignet.captcha.validator-url=${mosipbox.public.url}${server.servlet.path}/v1/captcha/validatecaptcha +mosip.esignet.captcha.module-name=esignet mosip.esignet.host=http://localhost:8088 mosip.esignet.signup-id-token-expire-seconds=180