diff --git a/src/main/java/io/mosip/mimoto/controller/CredentialShareController.java b/src/main/java/io/mosip/mimoto/controller/CredentialShareController.java index b821ac82..86eea863 100644 --- a/src/main/java/io/mosip/mimoto/controller/CredentialShareController.java +++ b/src/main/java/io/mosip/mimoto/controller/CredentialShareController.java @@ -10,6 +10,7 @@ import com.google.gson.Gson; import io.mosip.kernel.core.util.JsonUtils; +import io.mosip.mimoto.util.*; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -17,6 +18,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -39,10 +41,8 @@ import io.mosip.mimoto.model.EventModel; import io.mosip.mimoto.service.RestClientService; import io.mosip.mimoto.service.impl.CredentialShareServiceImpl; -import io.mosip.mimoto.util.CryptoCoreUtil; -import io.mosip.mimoto.util.DateUtils; -import io.mosip.mimoto.util.LoggerUtil; -import io.mosip.mimoto.util.Utilities; + +import javax.validation.Valid; @RestController @RequestMapping(value = "/credentialshare") @@ -71,6 +71,9 @@ public class CredentialShareController { @Autowired public CryptoCoreUtil cryptoCoreUtil; + @Autowired + RequestValidator requestValidator; + private Gson gson = new Gson(); @Autowired @@ -184,14 +187,17 @@ public ResponseEntity requestStatus(@PathVariable String requestId) * @throws Exception */ @PostMapping(path = "/download", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity download(@RequestBody CredentialDownloadRequestDTO requestDTO) + public ResponseEntity download(@Valid @RequestBody CredentialDownloadRequestDTO requestDTO, BindingResult result) throws Exception { try { + requestValidator.validateInputRequest(result); JsonNode decryptedCredentialJSON = utilities.getDecryptedVC(requestDTO.getRequestId()); - + JsonNode requestedCredentialJSON = utilities.getRequestVC(requestDTO.getRequestId()); JsonNode credentialJSON = utilities.getVC(requestDTO.getRequestId()); + // Combine original encrypted verifiable credential and decrypted if (decryptedCredentialJSON != null && credentialJSON != null) { + requestValidator.validateCredentialDownloadRequest(requestDTO, requestedCredentialJSON); CredentialDownloadResponseDTO credentialDownloadBody = new CredentialDownloadResponseDTO(); credentialDownloadBody.setCredential(decryptedCredentialJSON); credentialDownloadBody.setVerifiableCredential(credentialJSON); diff --git a/src/main/java/io/mosip/mimoto/controller/IdpController.java b/src/main/java/io/mosip/mimoto/controller/IdpController.java index 77c8ae58..29758e3f 100644 --- a/src/main/java/io/mosip/mimoto/controller/IdpController.java +++ b/src/main/java/io/mosip/mimoto/controller/IdpController.java @@ -9,21 +9,20 @@ import io.mosip.mimoto.core.http.ResponseWrapper; import io.mosip.mimoto.dto.ErrorDTO; import io.mosip.mimoto.dto.mimoto.*; -import io.mosip.mimoto.exception.ApisResourceAccessException; import io.mosip.mimoto.exception.IdpException; import io.mosip.mimoto.exception.PlatformErrorMessages; import io.mosip.mimoto.service.RestClientService; -import io.mosip.mimoto.util.DateUtils; -import io.mosip.mimoto.util.JoseUtil; -import io.mosip.mimoto.util.LoggerUtil; +import io.mosip.mimoto.util.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import javax.validation.Valid; import java.util.List; @RestController @@ -43,10 +42,15 @@ public class IdpController { @Autowired private ObjectMapper objectMapper; + @Autowired + RequestValidator requestValidator; + @PostMapping("/binding-otp") @SuppressWarnings("unchecked") - public ResponseEntity otpRequest(@RequestBody BindingOtpRequestDto requestDTO) throws Exception { + public ResponseEntity otpRequest(@Valid @RequestBody BindingOtpRequestDto requestDTO, BindingResult result) throws Exception { logger.debug("Received binding-otp request : " + JsonUtils.javaObjectToJsonString(requestDTO)); + requestValidator.validateInputRequest(result); + requestValidator.validateNotificationChannel(requestDTO.getRequest().getOtpChannels()); ResponseWrapper response = null; try { response = (ResponseWrapper) restClientService diff --git a/src/main/java/io/mosip/mimoto/controller/ResidentServiceController.java b/src/main/java/io/mosip/mimoto/controller/ResidentServiceController.java index 22aca63b..ff8fc462 100644 --- a/src/main/java/io/mosip/mimoto/controller/ResidentServiceController.java +++ b/src/main/java/io/mosip/mimoto/controller/ResidentServiceController.java @@ -2,11 +2,13 @@ import io.mosip.mimoto.dto.mimoto.*; import io.mosip.mimoto.dto.resident.*; +import io.mosip.mimoto.util.RequestValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -19,6 +21,8 @@ import io.mosip.mimoto.util.DateUtils; import io.mosip.mimoto.util.LoggerUtil; +import javax.validation.Valid; + @RestController public class ResidentServiceController { @@ -27,6 +31,9 @@ public class ResidentServiceController { @Autowired public RestClientService restClientService; + @Autowired + RequestValidator requestValidator; + @Autowired Environment env; @@ -39,7 +46,9 @@ public class ResidentServiceController { */ @PostMapping("/req/otp") @SuppressWarnings("unchecked") - public ResponseEntity otpRequest(@RequestBody AppOTPRequestDTO requestDTO) throws Exception { + public ResponseEntity otpRequest(@Valid @RequestBody AppOTPRequestDTO requestDTO, BindingResult result) throws Exception { + requestValidator.validateInputRequest(result); + requestValidator.validateNotificationChannel(requestDTO.getOtpChannel()); OTPRequestDTO mosipOTPRequestPayload = new OTPRequestDTO(); mosipOTPRequestPayload.setVersion("1.0"); mosipOTPRequestPayload.setId("mosip.identity.otp.internal"); diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java b/src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java index 74c59fc4..9d8320f5 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java @@ -3,10 +3,19 @@ import java.util.List; import lombok.Data; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + @Data public class AppOTPRequestDTO { + @NotNull private String individualId; + @Pattern(regexp = "UIN|VID", message = "Only UIN or VID is allowed") private String individualIdType; + @NotEmpty + @NotNull private List otpChannel; + @NotNull private String transactionID; -} \ No newline at end of file +} diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpInnerReqDto.java b/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpInnerReqDto.java index bc1b7d23..828a49b1 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpInnerReqDto.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpInnerReqDto.java @@ -2,10 +2,15 @@ import lombok.Data; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; import java.util.List; @Data public class BindingOtpInnerReqDto { + @NotNull private String individualId; + @NotNull + @NotEmpty private List otpChannels; } diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpRequestDto.java b/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpRequestDto.java index 1b0d1a79..f9a8bab6 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpRequestDto.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpRequestDto.java @@ -4,10 +4,16 @@ import lombok.Data; import lombok.NoArgsConstructor; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + @Data @AllArgsConstructor @NoArgsConstructor public class BindingOtpRequestDto { private String requestTime; + + @Valid + @NotNull private BindingOtpInnerReqDto request; } diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDownloadRequestDTO.java b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDownloadRequestDTO.java index 21d3e511..ab740912 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDownloadRequestDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDownloadRequestDTO.java @@ -2,8 +2,12 @@ import lombok.Data; +import javax.validation.constraints.NotNull; + @Data public class CredentialDownloadRequestDTO { + @NotNull private String individualId; + @NotNull private String requestId; } diff --git a/src/main/java/io/mosip/mimoto/exception/InvalidInputException.java b/src/main/java/io/mosip/mimoto/exception/InvalidInputException.java index 279ed823..edb6e5c5 100644 --- a/src/main/java/io/mosip/mimoto/exception/InvalidInputException.java +++ b/src/main/java/io/mosip/mimoto/exception/InvalidInputException.java @@ -24,4 +24,8 @@ public InvalidInputException(String errorCode, String errorMessage) { super(errorCode, errorMessage); } + public InvalidInputException(String errorMessage) { + super(PlatformErrorMessages.MIMOTO_PGS_INVALID_INPUT_PARAMETER.getCode(), errorMessage); + } + } diff --git a/src/main/java/io/mosip/mimoto/exception/PlatformErrorMessages.java b/src/main/java/io/mosip/mimoto/exception/PlatformErrorMessages.java index 2ea060bc..43dbda3f 100644 --- a/src/main/java/io/mosip/mimoto/exception/PlatformErrorMessages.java +++ b/src/main/java/io/mosip/mimoto/exception/PlatformErrorMessages.java @@ -35,7 +35,7 @@ public enum PlatformErrorMessages { /** The rgs json parsing exception. */ MIMOTO_RGS_JSON_PARSING_EXCEPTION(PlatformConstants.PREFIX + "017", "JSON Parsing Failed"), /** The invalid input parameter. */ - MIMOTO_PGS_INVALID_INPUT_PARAMETER(PlatformConstants.PREFIX + "011", "Invalid Input Parameter - %s"), + MIMOTO_PGS_INVALID_INPUT_PARAMETER(PlatformConstants.PREFIX + "011", "Invalid Input Parameter"), /** The rgs json mapping exception. */ MIMOTO_RGS_JSON_MAPPING_EXCEPTION(PlatformConstants.PREFIX + "016", "JSON Mapping Failed"), MIMOTO_PDF_SIGNATURE_EXCEPTION(PlatformConstants.PREFIX + "024", "PDF Signature error"), diff --git a/src/main/java/io/mosip/mimoto/util/CommonExceptionHandler.java b/src/main/java/io/mosip/mimoto/util/CommonExceptionHandler.java new file mode 100644 index 00000000..4bf41a46 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/CommonExceptionHandler.java @@ -0,0 +1,25 @@ +package io.mosip.mimoto.util; + + +import io.mosip.mimoto.dto.ErrorDTO; +import io.mosip.mimoto.dto.resident.CredentialRequestResponseDTO; +import io.mosip.mimoto.exception.InvalidInputException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import java.util.Collections; + +@ControllerAdvice +public class CommonExceptionHandler{ + @ExceptionHandler( value = InvalidInputException.class) + public ResponseEntity handleInvalidInput(InvalidInputException ex) { + CredentialRequestResponseDTO credentialRequestResponseDTO = new CredentialRequestResponseDTO(); + ErrorDTO errors = new ErrorDTO(ex.getErrorCode(), ex.getMessage()); + credentialRequestResponseDTO.setVersion("1.0"); + credentialRequestResponseDTO.setErrors(Collections.singletonList(errors)); + return new ResponseEntity<>(credentialRequestResponseDTO, HttpStatus.BAD_REQUEST); + } + +} diff --git a/src/main/java/io/mosip/mimoto/util/RequestValidator.java b/src/main/java/io/mosip/mimoto/util/RequestValidator.java new file mode 100644 index 00000000..a904172d --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/RequestValidator.java @@ -0,0 +1,59 @@ +package io.mosip.mimoto.util; + +import com.fasterxml.jackson.databind.JsonNode; +import io.mosip.mimoto.dto.mimoto.AppOTPRequestDTO; +import io.mosip.mimoto.dto.mimoto.CredentialDownloadRequestDTO; +import io.mosip.mimoto.exception.InvalidInputException; +import io.mosip.mimoto.exception.PlatformErrorMessages; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + + +@Component +public class RequestValidator { + + private static Logger logger = LoggerFactory.getLogger(RequestValidator.class); + + @Value("${mosip.notificationtype:EMAIL|PHONE}") + private String notificationType; + public void validateInputRequest(BindingResult result){ + if(result.hasErrors()){ + FieldError fieldError = result.getFieldError(); + if(fieldError != null) { + String errorMessage = fieldError.getField() + " " + fieldError.getDefaultMessage(); + throw new InvalidInputException(errorMessage); + } + } + } + + public void validateNotificationChannel(List otpChannelList){ + List incorrectNotificationChannel = new ArrayList(); + logger.info("\n Notification Types from application-default.properties in mosip-config - > " + notificationType); + incorrectNotificationChannel = otpChannelList + .stream() + .filter(otpChannel -> !notificationType.contains(otpChannel)) + .collect(Collectors.toList()); + if(incorrectNotificationChannel.size() > 0) { + logger.error("Invalid Input is received in otpChannels " + String.join(",", incorrectNotificationChannel)); + throw new InvalidInputException(PlatformErrorMessages.MIMOTO_PGS_INVALID_INPUT_PARAMETER.getMessage() + " - " + "otpChannels"); + } + } + + public void validateCredentialDownloadRequest(CredentialDownloadRequestDTO requestDTO, JsonNode requestedCredentialJSON) throws IOException, NoSuchFieldException { + if(requestedCredentialJSON.has("response") && requestedCredentialJSON.get("response").has("id")) { + String requestedIndividualId = requestedCredentialJSON.get("response").get("id").asText(); + if (!requestDTO.getIndividualId().equals(requestedIndividualId)) { + throw new InvalidInputException(PlatformErrorMessages.MIMOTO_PGS_INVALID_INPUT_PARAMETER.getMessage() + " - " + "individualId"); + } + } + } +} diff --git a/src/main/java/io/mosip/mimoto/util/Utilities.java b/src/main/java/io/mosip/mimoto/util/Utilities.java index ecaddeee..e36ddb78 100644 --- a/src/main/java/io/mosip/mimoto/util/Utilities.java +++ b/src/main/java/io/mosip/mimoto/util/Utilities.java @@ -111,6 +111,14 @@ public JsonNode getVC(String requestId) throws JsonParseException, JsonMappingEx return null; } + public JsonNode getRequestVC(String requestId) throws JsonParseException, JsonMappingException, IOException { + Path resourcePath = Path.of(dataPath, String.format(CredentialShareServiceImpl.VC_REQUEST_FILE_NAME, requestId)); + if (Files.exists(resourcePath)) { + return objectMapper.readValue(resourcePath.toFile(), JsonNode.class); + } + return null; + } + public JsonNode getDecryptedVC(String requestId) throws JsonParseException, JsonMappingException, IOException { Path resourcePath = Path.of(dataPath, String.format(CredentialShareServiceImpl.CARD_JSON_FILE_NAME, requestId)); if (Files.exists(resourcePath)) { diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index 52477743..0140736e 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -1,9 +1,9 @@ # MOSIP -mosipbox.public.url=https://api-internal.dev2.mosip.net -public.internet.url=https://api.dev2.mosip.net -public.url=https://api-internal.dev2.mosip.net -keycloak.internal.url=https://iam.dev2.mosip.net/ +mosipbox.public.url=https://api-internal.qatriple.mosip.net +public.internet.url=https://api.qatriple.mosip.net +public.url=https://api-internal.qatriple.mosip.net +keycloak.internal.url=https://iam.qatriple.mosip.net/ # Resident service mosip.resident.base.url=${mosipbox.public.url}/resident/v1 @@ -149,7 +149,7 @@ server.tomcat.accesslog.prefix=stdout server.tomcat.accesslog.buffered=false server.tomcat.accesslog.suffix= server.tomcat.accesslog.file-date-format= -server.tomcat.accesslog.pattern={"@timestamp":"%{yyyy-MM-dd'T'HH:mm:ss.SSS'Z'}t","level":"ACCESS","level_value":70000,"traceId":"%{X-B3-TraceId}i","statusCode":%s,"req.requestURI":"%U","bytesSent":%b,"timeTaken":%T,"appName":"${spring.application.name}"} +server.tomcat.accesslog.pattern={"@timestamp":"%{yyyy-MM-dd'T'HH:mm:ss.SSS'Z'}t","level":"ACCESS","level_value":70000,"traceId":"%{X-B3-TraceId}i","appId":"%{X-AppId}i","statusCode":%s,"req.requestURI":"%U","bytesSent":%b,"timeTaken":%T,"appName":"${spring.application.name}"} server.tomcat.accesslog.className=io.mosip.kernel.core.logger.config.SleuthValve registration.processor.unMaskedUin.length=5 @@ -169,9 +169,14 @@ mosip.iam.adapter.clientid=mpartner-default-mobile # dev3 # mosip.iam.adapter.clientsecret=nD1CevIBri1X0Jzj # dev2 -mosip.iam.adapter.clientsecret=l22WdJg9Qdrf3B90 +# mosip.iam.adapter.clientsecret=l22WdJg9Qdrf3B90 # qa1201 # mosip.iam.adapter.clientsecret=KXGkd7XMbsoxCUz9 + + +# qatriple +mosip.iam.adapter.clientsecret=vl3ox2rvSeW78LzL + mosip.iam.adapter.appid=partner mosip.iam.adapter.issuerURL=${mosipbox.public.url}/auth/realms/mosip diff --git a/src/test/java/io/mosip/mimoto/controller/InjiControllerTest.java b/src/test/java/io/mosip/mimoto/controller/InjiControllerTest.java index 118409ce..9426286f 100644 --- a/src/test/java/io/mosip/mimoto/controller/InjiControllerTest.java +++ b/src/test/java/io/mosip/mimoto/controller/InjiControllerTest.java @@ -207,6 +207,7 @@ public void downloadTest() throws Exception { CredentialDownloadRequestDTO requestDTO = new CredentialDownloadRequestDTO(); requestDTO.setRequestId("requestId"); + requestDTO.setIndividualId("individualId"); JsonNode credentialJSON = JsonNodeFactory.instance.objectNode(); Mockito.when(utilities.getVC(Mockito.anyString())).thenReturn(credentialJSON); @@ -214,6 +215,9 @@ public void downloadTest() throws Exception { JsonNode decryptedCredentialJSON = JsonNodeFactory.instance.objectNode(); Mockito.when(utilities.getDecryptedVC(Mockito.anyString())).thenReturn(decryptedCredentialJSON); + JsonNode requestCredentialJSON = JsonNodeFactory.instance.objectNode(); + Mockito.when(utilities.getRequestVC(Mockito.anyString())).thenReturn(requestCredentialJSON); + this.mockMvc.perform(post("/credentialshare/download").contentType(MediaType.APPLICATION_JSON_VALUE) .content(JsonUtils.javaObjectToJsonString(requestDTO))) .andExpect(status().isOk());