diff --git a/binding-service-impl/pom.xml b/binding-service-impl/pom.xml index 83f3c6db9..000295b03 100644 --- a/binding-service-impl/pom.xml +++ b/binding-service-impl/pom.xml @@ -29,5 +29,5 @@ esignet-core ${esignet.core.version} - + diff --git a/binding-service-impl/src/main/java/io/mosip/esignet/services/KeyBindingServiceImpl.java b/binding-service-impl/src/main/java/io/mosip/esignet/services/KeyBindingServiceImpl.java index 6f8fd1ec1..497eb729c 100644 --- a/binding-service-impl/src/main/java/io/mosip/esignet/services/KeyBindingServiceImpl.java +++ b/binding-service-impl/src/main/java/io/mosip/esignet/services/KeyBindingServiceImpl.java @@ -20,6 +20,7 @@ import io.mosip.esignet.api.spi.KeyBinder; import io.mosip.esignet.core.dto.*; import io.mosip.esignet.core.exception.EsignetException; +import io.mosip.esignet.core.util.CaptchaHelper; import io.mosip.esignet.repository.PublicKeyRegistryRepository; import io.mosip.kernel.keymanagerservice.util.KeymanagerUtil; import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers; @@ -54,12 +55,16 @@ public class KeyBindingServiceImpl implements KeyBindingService { @Autowired private KeyBindingHelperService keyBindingHelperService; + @Autowired + private CaptchaHelper captchaHelper; + @Value("${mosip.esignet.binding.encrypt-binding-id:true}") private boolean encryptBindingId; @Override - public BindingOtpResponse sendBindingOtp(BindingOtpRequest bindingOtpRequest, Map requestHeaders) throws EsignetException { + public BindingOtpResponse sendBindingOtp(BindingOtpRequest bindingOtpRequest, Map requestHeaders) + throws EsignetException { log.debug("sendBindingOtp :: Request headers >> {}", requestHeaders); SendOtpResult sendOtpResult; try { @@ -69,18 +74,24 @@ public BindingOtpResponse sendBindingOtp(BindingOtpRequest bindingOtpRequest, Ma log.error("Failed to send binding otp: {}", e); throw new EsignetException(e.getErrorCode()); } - if (sendOtpResult == null) { log.error("send-otp Failed wrapper returned null result!"); throw new EsignetException(SEND_OTP_FAILED); } - BindingOtpResponse otpResponse = new BindingOtpResponse(); otpResponse.setMaskedEmail(sendOtpResult.getMaskedEmail()); otpResponse.setMaskedMobile(sendOtpResult.getMaskedMobile()); return otpResponse; } + @Override + public BindingOtpResponse sendBindingOtpV2(BindingOtpRequestV2 bindingOtpRequestV2, Map requestHeaders) + throws EsignetException { + captchaHelper.validateCaptchaToken(bindingOtpRequestV2.getCaptchaToken(), "binding-otp"); + return sendBindingOtp(bindingOtpRequestV2, requestHeaders); + } + + private void validateChallengeListAuthFormat(List challengeList){ if(!challengeList.stream().allMatch(challenge->keyBindingWrapper.getSupportedChallengeFormats(challenge.getAuthFactorType()). contains(challenge.getFormat()))) { diff --git a/binding-service-impl/src/test/java/io/mosip/esignet/KeyBindingServiceTest.java b/binding-service-impl/src/test/java/io/mosip/esignet/KeyBindingServiceTest.java index 582ce1eb2..5703cf4e2 100644 --- a/binding-service-impl/src/test/java/io/mosip/esignet/KeyBindingServiceTest.java +++ b/binding-service-impl/src/test/java/io/mosip/esignet/KeyBindingServiceTest.java @@ -5,21 +5,9 @@ */ package io.mosip.esignet; -import static io.mosip.esignet.api.util.ErrorConstants.SEND_OTP_FAILED; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.time.LocalDateTime; -import java.util.*; - import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.KeyUse; import com.nimbusds.jose.jwk.RSAKey; import io.mosip.esignet.api.dto.AuthChallenge; @@ -28,10 +16,14 @@ import io.mosip.esignet.api.exception.KeyBindingException; import io.mosip.esignet.api.exception.SendOtpException; import io.mosip.esignet.api.spi.KeyBinder; -import io.mosip.esignet.entity.PublicKeyRegistry; -import io.mosip.esignet.core.dto.*; -import io.mosip.esignet.core.exception.EsignetException; import io.mosip.esignet.core.constants.ErrorConstants; +import io.mosip.esignet.core.dto.BindingOtpRequest; +import io.mosip.esignet.core.dto.BindingOtpRequestV2; +import io.mosip.esignet.core.dto.BindingOtpResponse; +import io.mosip.esignet.core.dto.WalletBindingRequest; +import io.mosip.esignet.core.exception.EsignetException; +import io.mosip.esignet.core.util.CaptchaHelper; +import io.mosip.esignet.entity.PublicKeyRegistry; import io.mosip.esignet.repository.PublicKeyRegistryRepository; import io.mosip.esignet.services.KeyBindingHelperService; import io.mosip.esignet.services.KeyBindingServiceImpl; @@ -47,9 +39,21 @@ import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.RestTemplate; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.jwk.JWK; +import java.io.IOException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.time.LocalDateTime; +import java.util.*; + +import static io.mosip.esignet.api.util.ErrorConstants.SEND_OTP_FAILED; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @Slf4j @RunWith(MockitoJUnitRunner.class) @@ -70,10 +74,16 @@ public class KeyBindingServiceTest { @Mock KeymanagerUtil keymanagerUtil; + CaptchaHelper captchaHelper; + + @Mock + RestTemplate restTemplate; + private JWK clientJWK = generateJWK_RSA(); private ObjectMapper objectMapper = new ObjectMapper(); + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -82,13 +92,17 @@ public void setUp() { mockKeyBindingWrapperService = mock(KeyBinder.class); when(mockKeyBindingWrapperService.getSupportedChallengeFormats(Mockito.anyString())) .thenReturn(Arrays.asList("jwt", "alpha-numeric")); + + captchaHelper = new CaptchaHelper(restTemplate, "https://api-internal.camdgc-dev1.mosip.net/v1/captcha/validatecaptcha", + "esignet", List.of("binding-otp")); + ReflectionTestUtils.setField(keyBindingService, "keyBindingWrapper", mockKeyBindingWrapperService); keyBindingHelperService = mock(KeyBindingHelperService.class); ReflectionTestUtils.setField(keyBindingHelperService, "saltLength", 10); ReflectionTestUtils.setField(keyBindingHelperService, "publicKeyRegistryRepository", publicKeyRegistryRepository); ReflectionTestUtils.setField(keyBindingHelperService, "keymanagerUtil", keymanagerUtil); - + ReflectionTestUtils.setField(keyBindingService, "captchaHelper", captchaHelper); ReflectionTestUtils.setField(keyBindingService, "keyBindingHelperService", keyBindingHelperService); } @@ -106,7 +120,8 @@ public void sendBindingOtp_withValidDetails_thenPass() throws SendOtpException { BindingOtpResponse otpResponse = keyBindingService.sendBindingOtp(otpRequest, headers); Assert.assertNotNull(otpResponse); } - + + @Test(expected = EsignetException.class) public void sendBindingOtp_withInvalidRequest_thenFail() throws SendOtpException { BindingOtpRequest otpRequest = new BindingOtpRequest(); @@ -119,6 +134,41 @@ public void sendBindingOtp_withInvalidRequest_thenFail() throws SendOtpException keyBindingService.sendBindingOtp(otpRequest, headers); } + + @Test + public void sendBindingOtpV2_withInvalidCaptcha_thenFail() throws SendOtpException { + + BindingOtpRequestV2 otpRequest = new BindingOtpRequestV2(); + otpRequest.setIndividualId("8267411571"); + otpRequest.setOtpChannels(Arrays.asList("OTP")); + otpRequest.setCaptchaToken("qwerty"); + + Map headers = new HashMap<>(); + + try { + keyBindingService.sendBindingOtpV2(otpRequest, headers); + } catch (EsignetException e) { + Assert.assertTrue(e.getErrorCode().equals(ErrorConstants.INVALID_CAPTCHA)); + } + } + + @Test + public void sendBindingOtpV2_withEmptyCaptcha_thenFail() throws SendOtpException { + + BindingOtpRequestV2 otpRequest = new BindingOtpRequestV2(); + otpRequest.setIndividualId("8267411571"); + otpRequest.setOtpChannels(Arrays.asList("OTP")); + otpRequest.setCaptchaToken(""); + + Map headers = new HashMap<>(); + + try { + keyBindingService.sendBindingOtpV2(otpRequest, headers); + } catch (EsignetException e) { + Assert.assertTrue(e.getErrorCode().equals(ErrorConstants.INVALID_CAPTCHA)); + } + } + @Test public void sendBindingOtp_withNullResponseFromWrapper_thenFail() throws SendOtpException { BindingOtpRequest otpRequest = new BindingOtpRequest(); @@ -368,4 +418,5 @@ public static JWK generateJWK_RSA() { } return null; } + } \ No newline at end of file diff --git a/docs/esignet-openapi.yaml b/docs/esignet-openapi.yaml index c042380ca..2d40f0043 100644 --- a/docs/esignet-openapi.yaml +++ b/docs/esignet-openapi.yaml @@ -4428,6 +4428,112 @@ paths: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: id: t315hcecaulyy + /binding/v2/binding-otp: + post: + tags: + - WALLET BACKEND + summary: Send Binding OTP Endpoint + description: Send wallet binding OTP endpoint is invoked by Mimoto server. + operationId: post-binding-otp + parameters: + - name: partner-api-key + in: header + description: 'API key of the binding partner, this will be passed to binder implementation to interact with authentication system.' + schema: + type: string + - name: partner-id + in: header + description: 'Binding partner Identifier, this will be passed to binder implementation to interact with authentication system.' + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + requestTime: + type: string + request: + type: object + properties: + individualId: + type: string + description: User Id (UIN/VID) + otpChannels: + type: array + description: Channels to which OTP should be delivered. + items: + type: string + captchaToken: + type: string + description: 'Captcha token, if enabled.' + required: + - individualId + - otpChannels + required: + - requestTime + - request + examples: + Example 1: + value: + requestTime: '2023-09-22T08:01:13.000Z' + request: + individualId: '24554655645' + otpChannels: + - sms + - email + captchaToken: ALSKDJFURIEOQPZMKFURHFVBH + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + responseTIme: + type: string + response: + type: object + properties: + maskedEmail: + type: string + description: Masked email id of the individualId user. + maskedMobile: + type: string + description: Masked mobile number of the individualId user. + errors: + type: array + items: + type: object + properties: + errorCode: + type: string + enum: + - invalid_otp_channel + - unknown_error + - invalid_individual_id + - send_otp_failed + - invalid_captcha + errorMessage: + type: string + required: + - responseTIme + examples: + Example 1: + value: + responseTIme: '2023-09-22T08:01:16.000Z' + response: + maskedEmail: XXdXXaXXhXXkX@gmail.com + maskedMobile: XXXXXXX357934 + errors: [ ] + security: + - Authorization-send_binding_otp: [ ] + servers: + - url: 'https://esignet.collab.mosip.net/v1/esignet' + x-stoplight: + id: xnl3gyq4v4bh4 /binding/wallet-binding: post: tags: @@ -5290,8 +5396,6 @@ components: - - diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/config/SharedComponentConfig.java b/esignet-core/src/main/java/io/mosip/esignet/core/config/SharedComponentConfig.java index 40643213f..a0e418941 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/config/SharedComponentConfig.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/config/SharedComponentConfig.java @@ -7,6 +7,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; +import java.util.List; + @Configuration public class SharedComponentConfig { @@ -15,7 +17,8 @@ public class SharedComponentConfig { @Bean public CaptchaHelper captchaHelper(@Value("${mosip.esignet.captcha.validator-url}") String validatorUrl, - @Value("${mosip.esignet.captcha.module-name}") String moduleName) { - return new CaptchaHelper(restTemplate, validatorUrl, moduleName); + @Value("${mosip.esignet.captcha.module-name}") String moduleName, + @Value("#{'${mosip.esignet.captcha.required}'}")List captchaRequired) { + return new CaptchaHelper(restTemplate, validatorUrl, moduleName, captchaRequired); } } diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/dto/BindingOtpRequest.java b/esignet-core/src/main/java/io/mosip/esignet/core/dto/BindingOtpRequest.java index a9b2dd872..9aa090885 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/dto/BindingOtpRequest.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/dto/BindingOtpRequest.java @@ -24,4 +24,5 @@ public class BindingOtpRequest { @NotNull(message = ErrorConstants.INVALID_OTP_CHANNEL) @Size(min = 1, message = ErrorConstants.INVALID_OTP_CHANNEL) private List<@OtpChannel String> otpChannels; + } diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/dto/BindingOtpRequestV2.java b/esignet-core/src/main/java/io/mosip/esignet/core/dto/BindingOtpRequestV2.java new file mode 100644 index 000000000..9086b1414 --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/dto/BindingOtpRequestV2.java @@ -0,0 +1,15 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.esignet.core.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class BindingOtpRequestV2 extends BindingOtpRequest{ + private String captchaToken; +} diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/spi/KeyBindingService.java b/esignet-core/src/main/java/io/mosip/esignet/core/spi/KeyBindingService.java index f927e7e66..ab80bcb5f 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/spi/KeyBindingService.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/spi/KeyBindingService.java @@ -5,11 +5,8 @@ */ package io.mosip.esignet.core.spi; -import io.mosip.esignet.core.dto.BindingOtpResponse; +import io.mosip.esignet.core.dto.*; import io.mosip.esignet.core.exception.EsignetException; -import io.mosip.esignet.core.dto.BindingOtpRequest; -import io.mosip.esignet.core.dto.WalletBindingRequest; -import io.mosip.esignet.core.dto.WalletBindingResponse; import java.util.Map; @@ -17,5 +14,7 @@ public interface KeyBindingService { BindingOtpResponse sendBindingOtp(BindingOtpRequest otpRequest, Map requestHeaders) throws EsignetException; + BindingOtpResponse sendBindingOtpV2(BindingOtpRequestV2 otpRequest, Map requestHeaders) throws EsignetException; + WalletBindingResponse bindWallet(WalletBindingRequest walletBindingRequest, Map requestHeaders) throws EsignetException; } 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 index f6ce7808e..e53147559 100644 --- 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 @@ -11,16 +11,22 @@ import io.mosip.esignet.core.dto.ResponseWrapper; import io.mosip.esignet.core.exception.EsignetException; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import java.net.URI; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.List; import static io.mosip.esignet.core.constants.Constants.UTC_DATETIME_PATTERN; +import static io.mosip.esignet.core.constants.ErrorConstants.INVALID_CAPTCHA; @Slf4j public class CaptchaHelper { @@ -29,12 +35,29 @@ public class CaptchaHelper { private String moduleName; private String validatorUrl; - public CaptchaHelper(RestTemplate restTemplate, String validatorUrl, String moduleName) { + private List captchaRequired; + + public CaptchaHelper(RestTemplate restTemplate, String validatorUrl, String moduleName, List captchaRequired) { this.restTemplate = restTemplate; this.validatorUrl = validatorUrl; this.moduleName = moduleName; + this.captchaRequired = captchaRequired; + } + + public void validateCaptchaToken(String captchaToken, String authFactor) { + if(!captchaRequired.contains(authFactor)) { + log.warn("captcha validation is disabled for {} request!", authFactor); + return; + } + if(!StringUtils.hasText(captchaToken)) { + log.error("Captcha token is Null or Empty"); + throw new EsignetException(INVALID_CAPTCHA); + } + if (!validateCaptcha(captchaToken)) + throw new EsignetException(INVALID_CAPTCHA); } + public boolean validateCaptcha(String captchaToken) { if (captchaToken == null || captchaToken.isBlank()) { 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 index 95b8bc056..ec7dbc8ff 100644 --- a/esignet-core/src/test/java/io/mosip/esignet/core/CaptchaHelperTest.java +++ b/esignet-core/src/test/java/io/mosip/esignet/core/CaptchaHelperTest.java @@ -1,5 +1,6 @@ package io.mosip.esignet.core; +import io.mosip.esignet.core.constants.ErrorConstants; import io.mosip.esignet.core.dto.ResponseWrapper; import io.mosip.esignet.core.exception.EsignetException; import io.mosip.esignet.core.util.CaptchaHelper; @@ -7,10 +8,8 @@ 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.*; @@ -18,8 +17,8 @@ import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; -import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import static org.mockito.ArgumentMatchers.any; @@ -35,7 +34,28 @@ public class CaptchaHelperTest { @Before public void setUp() { captchaHelper = new CaptchaHelper(restTemplate, "https://api-internal.camdgc-dev1.mosip.net/v1/captcha/validatecaptcha", - "esignet"); + "esignet", List.of("binding-otp")); + } + + @Test + public void validateCaptchaToken_withEmptyToken_thenFail() { + ReflectionTestUtils.setField(captchaHelper, "captchaRequired", List.of("binding-otp")); + try { + captchaHelper.validateCaptchaToken("", "binding-otp"); + } catch(EsignetException e) { + Assert.assertEquals(ErrorConstants.INVALID_CAPTCHA, e.getErrorCode()); + } + } + + + @Test + public void validateCaptchaToken_withInValidToken_thenFail() { + ReflectionTestUtils.setField(captchaHelper, "captchaRequired", List.of("binding-otp")); + try { + captchaHelper.validateCaptchaToken("captcha-token", "binding-otp"); + } catch(EsignetException e) { + Assert.assertEquals(ErrorConstants.INVALID_CAPTCHA, e.getErrorCode()); + } } @Test @@ -48,6 +68,18 @@ public void validateCaptcha_withEmptyCaptchaToken_thenFail() { Assert.assertThrows(EsignetException.class,()->captchaHelper.validateCaptcha("")); } + @Test + public void validateCaptchaToken_withValidData_thenPass() { + 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_withNullResponse_thenFail() { Mockito.when(restTemplate.exchange((RequestEntity) any(), (Class) any())).thenReturn(null); diff --git a/esignet-service/src/main/java/io/mosip/esignet/controllers/KeyBindingController.java b/esignet-service/src/main/java/io/mosip/esignet/controllers/KeyBindingController.java index 1ba2346d0..312dc9689 100644 --- a/esignet-service/src/main/java/io/mosip/esignet/controllers/KeyBindingController.java +++ b/esignet-service/src/main/java/io/mosip/esignet/controllers/KeyBindingController.java @@ -50,7 +50,27 @@ public ResponseWrapper sendBindingOtp(@Valid @RequestBody RequestWr } return responseWrapper; } - + + + @PostMapping(value = "/v2/binding-otp", consumes = {MediaType.APPLICATION_JSON_VALUE}, + produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseWrapper sendBindingOtpV2(@Valid @RequestBody RequestWrapper requestWrapper, + @RequestHeader Map headers) + throws EsignetException { + ResponseWrapper responseWrapper = new ResponseWrapper(); + try { + responseWrapper.setResponse(keyBindingService.sendBindingOtpV2(requestWrapper.getRequest(), headers)); + responseWrapper.setResponseTime(IdentityProviderUtil.getUTCDateTime()); + auditPlugin.logAudit(Action.SEND_BINDING_OTP, ActionStatus.SUCCESS, + AuditHelper.buildAuditDto("individualId", null), null); + } catch (EsignetException ex) { + auditPlugin.logAudit(Action.SEND_BINDING_OTP, ActionStatus.ERROR, + AuditHelper.buildAuditDto("individualId", null), ex); + throw ex; + } + return responseWrapper; + } + @PostMapping(value = "wallet-binding", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) public ResponseWrapper bindWallet(@Valid @RequestBody RequestWrapper requestWrapper, diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index 5fd499d4b..91b4a5c8a 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -60,7 +60,7 @@ mosip.esignet.header-filter.paths-to-validate={'${server.servlet.path}/authoriza '${server.servlet.path}/authorization/complete-signup-redirect' } ## captcha validation is enabled for the auth-factors - otp, pwd, bio and pin. -mosip.esignet.captcha.required=send-otp,pwd,kbi +mosip.esignet.captcha.required=send-otp,pwd,kbi,binding-otp mosip.esignet.captcha.validator-url=http://captcha.captcha/v1/captcha/validatecaptcha mosip.esignet.captcha.module-name=esignet mosip.esignet.captcha.site-key=${esignet.captcha.site.key} diff --git a/esignet-service/src/test/java/io/mosip/esignet/controllers/KeyBindingControllerTest.java b/esignet-service/src/test/java/io/mosip/esignet/controllers/KeyBindingControllerTest.java index 5e880349f..1869af3b2 100644 --- a/esignet-service/src/test/java/io/mosip/esignet/controllers/KeyBindingControllerTest.java +++ b/esignet-service/src/test/java/io/mosip/esignet/controllers/KeyBindingControllerTest.java @@ -141,6 +141,72 @@ public void sendBindingOtp_withInvalidChannel_thenPass() throws Exception { .andExpect(jsonPath("$.errors[0].errorCode").value(ErrorConstants.INVALID_OTP_CHANNEL)); } + @Test + public void sendBindingOtpV2_withValidRequest_thenPass() throws Exception { + BindingOtpRequest otpRequest = new BindingOtpRequest(); + otpRequest.setIndividualId("8267411571"); + otpRequest.setOtpChannels(Arrays.asList("email")); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + RequestWrapper wrapper = new RequestWrapper<>(); + wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); + wrapper.setRequest(otpRequest); + + BindingOtpResponse otpResponse = new BindingOtpResponse(); + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json;charset=UTF-8"); + headers.put("Content-Length", "106"); + when(keyBindingService.sendBindingOtp(otpRequest, headers)).thenReturn(otpResponse); + when(authenticationWrapper.isSupportedOtpChannel(Mockito.anyString())).thenReturn(true); + + mockMvc.perform(post("/binding/v2/binding-otp").content(objectMapper.writeValueAsString(wrapper)) + .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); + } + + @Test + public void sendBindingOtpV2_withInvalidIndividualId_thenFail() throws Exception { + BindingOtpRequest otpRequest = new BindingOtpRequest(); + otpRequest.setIndividualId(""); + otpRequest.setOtpChannels(Arrays.asList("email")); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + RequestWrapper wrapper = new RequestWrapper<>(); + wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); + wrapper.setRequest(otpRequest); + + BindingOtpResponse otpResponse = new BindingOtpResponse(); + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json;charset=UTF-8"); + headers.put("Content-Length", "106"); + when(keyBindingService.sendBindingOtp(otpRequest, headers)).thenReturn(otpResponse); + when(authenticationWrapper.isSupportedOtpChannel(Mockito.anyString())).thenReturn(true); + + mockMvc.perform(post("/binding/v2/binding-otp").content(objectMapper.writeValueAsString(wrapper)) + .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorCode").value(INVALID_IDENTIFIER)); + } + + @Test + public void sendBindingOtpV2_withInvalidChannel_thenPass() throws Exception { + BindingOtpRequest otpRequest = new BindingOtpRequest(); + otpRequest.setIndividualId("121323123s"); + otpRequest.setOtpChannels(Arrays.asList()); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + RequestWrapper wrapper = new RequestWrapper<>(); + wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); + wrapper.setRequest(otpRequest); + + BindingOtpResponse otpResponse = new BindingOtpResponse(); + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json;charset=UTF-8"); + headers.put("Content-Length", "106"); + when(keyBindingService.sendBindingOtp(otpRequest, headers)).thenReturn(otpResponse); + + mockMvc.perform(post("/binding/v2/binding-otp").content(objectMapper.writeValueAsString(wrapper)) + .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorCode").value(ErrorConstants.INVALID_OTP_CHANNEL)); + } + @Test public void bindWallet_withValidDetails_thenPass() throws Exception { WalletBindingRequest walletBindingRequest = getWalletBindingRequest();