Skip to content

Commit

Permalink
Merge pull request #100 from tw-mosip/release-0.9.x
Browse files Browse the repository at this point in the history
[Release 01-08-2023] : Security Fixes, API Request Validation & Including App ID in the Loggers
  • Loading branch information
MonobikashDas authored Aug 3, 2023
2 parents 9502ac4 + 9e73cb3 commit f46cbd9
Show file tree
Hide file tree
Showing 14 changed files with 168 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
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;
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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -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")
Expand Down Expand Up @@ -71,6 +71,9 @@ public class CredentialShareController {
@Autowired
public CryptoCoreUtil cryptoCoreUtil;

@Autowired
RequestValidator requestValidator;

private Gson gson = new Gson();

@Autowired
Expand Down Expand Up @@ -184,14 +187,17 @@ public ResponseEntity<Object> requestStatus(@PathVariable String requestId)
* @throws Exception
*/
@PostMapping(path = "/download", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<CredentialDownloadResponseDTO> download(@RequestBody CredentialDownloadRequestDTO requestDTO)
public ResponseEntity<CredentialDownloadResponseDTO> 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);
Expand Down
14 changes: 9 additions & 5 deletions src/main/java/io/mosip/mimoto/controller/IdpController.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,10 +42,15 @@ public class IdpController {
@Autowired
private ObjectMapper objectMapper;

@Autowired
RequestValidator requestValidator;

@PostMapping("/binding-otp")
@SuppressWarnings("unchecked")
public ResponseEntity<Object> otpRequest(@RequestBody BindingOtpRequestDto requestDTO) throws Exception {
public ResponseEntity<Object> 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<BindingOtpResponseDto> response = null;
try {
response = (ResponseWrapper<BindingOtpResponseDto>) restClientService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,6 +21,8 @@
import io.mosip.mimoto.util.DateUtils;
import io.mosip.mimoto.util.LoggerUtil;

import javax.validation.Valid;

@RestController
public class ResidentServiceController {

Expand All @@ -27,6 +31,9 @@ public class ResidentServiceController {
@Autowired
public RestClientService<Object> restClientService;

@Autowired
RequestValidator requestValidator;

@Autowired
Environment env;

Expand All @@ -39,7 +46,9 @@ public class ResidentServiceController {
*/
@PostMapping("/req/otp")
@SuppressWarnings("unchecked")
public ResponseEntity<Object> otpRequest(@RequestBody AppOTPRequestDTO requestDTO) throws Exception {
public ResponseEntity<Object> 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");
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> otpChannel;
@NotNull
private String transactionID;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> otpChannels;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

import lombok.Data;

import javax.validation.constraints.NotNull;

@Data
public class CredentialDownloadRequestDTO {
@NotNull
private String individualId;
@NotNull
private String requestId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/io/mosip/mimoto/util/CommonExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -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<CredentialRequestResponseDTO> 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);
}

}
59 changes: 59 additions & 0 deletions src/main/java/io/mosip/mimoto/util/RequestValidator.java
Original file line number Diff line number Diff line change
@@ -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<String> otpChannelList){
List<String> incorrectNotificationChannel = new ArrayList<String>();
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");
}
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/io/mosip/mimoto/util/Utilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
17 changes: 11 additions & 6 deletions src/main/resources/application-local.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,17 @@ 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);

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());
Expand Down

0 comments on commit f46cbd9

Please sign in to comment.