diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/builder/AuthTransactionBuilder.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/builder/AuthTransactionBuilder.java index 9abbd8fac49..54502173c64 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/builder/AuthTransactionBuilder.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/builder/AuthTransactionBuilder.java @@ -262,12 +262,14 @@ public AutnTxn build(EnvUtil env, IdaUinHashSaltRepo uinHashSaltRepo, autnTxn.setAuthTknId(authTokenId); autnTxn.setCrDTimes(DateUtils.getUTCCurrentDateTime()); LocalDateTime strUTCDate = DateUtils.getUTCCurrentDateTime(); - try { - strUTCDate = DateUtils.parseToLocalDateTime(DateUtils.getUTCTimeFromDate( - DateUtils.parseToDate(reqTime, EnvUtil.getDateTimePattern()))); - } catch (ParseException e) { - mosipLogger.warn(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), e.getMessage(), - "Invalid Request Time - setting to current date time"); + if (reqTime != null && !reqTime.trim().isEmpty()) { + try { + strUTCDate = DateUtils.parseToLocalDateTime(DateUtils.getUTCTimeFromDate( + DateUtils.parseToDate(reqTime, EnvUtil.getDateTimePattern()))); + } catch (ParseException e) { + mosipLogger.warn(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), e.getMessage(), + "Invalid Request Time - setting to current date time"); + } } autnTxn.setRequestDTtimes(strUTCDate); autnTxn.setResponseDTimes(DateUtils.getUTCCurrentDateTime()); diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/filter/BaseIDAFilter.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/filter/BaseIDAFilter.java index f74ad9f18d8..75870cef93c 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/filter/BaseIDAFilter.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/filter/BaseIDAFilter.java @@ -11,6 +11,7 @@ import java.time.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.time.temporal.Temporal; import java.util.LinkedHashMap; import java.util.List; @@ -221,7 +222,15 @@ private CharResponseWrapper sendErrorResponse(ServletResponse response, CharResp boolean hasUnableToProcessError = errors.stream() .anyMatch(err -> err.getErrorCode() .equals(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS.getErrorCode())); - if(!hasUnableToProcessError) { + // MOSIP-45564: id/version missing or invalid is already a specific, well-formed + // validation error (raised only by handleException() for id/version). Wrapping it + // here re-adds UNABLE_TO_PROCESS as a cause, and since BaseCheckedException copies + // the cause's info items into the wrapper, both error codes end up in the response. + boolean isIdOrVersionValidationError = errors.stream() + .anyMatch(err -> err.getErrorCode().equals(IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode()) + || err.getErrorCode().equals(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode()) + || err.getErrorCode().equals(IdAuthenticationErrorConstants.INVALID_ENCRYPTION.getErrorCode())); + if(!hasUnableToProcessError && !isIdOrVersionValidationError) { exception = new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS.getErrorCode(), IdAuthenticationErrorConstants.UNABLE_TO_PROCESS.getErrorMessage(), ex); } @@ -306,16 +315,26 @@ private void logTime(String timeInTheAllowedPattern, String type, Temporal actua if (timeInTheAllowedPattern == null || timeInTheAllowedPattern.isEmpty()) { timeInTheAllowedPattern = IdaRequestResponsConsumerUtil.getResponseTime(null, dateTimePattern); } - mosipLogger.info(IdAuthCommonConstants.SESSION_ID, EVENT_FILTER, BASE_IDA_FILTER, type + " at : " + timeInTheAllowedPattern); - long duration = Duration - .between(actualRequestTime, - LocalDateTime.parse(timeInTheAllowedPattern, - DateTimeFormatter - .ofPattern(dateTimePattern))) - .toMillis(); - mosipLogger.info(IdAuthCommonConstants.SESSION_ID, EVENT_FILTER, BASE_IDA_FILTER, - "Time difference between request and response in millis:" + duration - + ". Time difference between request and response in Seconds: " + ((double) duration / 1000)); + // timeInTheAllowedPattern is taken verbatim from the raw request body, so it must + // never be echoed into logs unsanitized - only that a timestamp was received/parsed. + mosipLogger.info(IdAuthCommonConstants.SESSION_ID, EVENT_FILTER, BASE_IDA_FILTER, type + " timestamp received"); + // It's also ahead of (and independent of) request validation, so an invalid + // requestTime must not let this purely diagnostic duration calculation crash + // response processing. + try { + long duration = Duration + .between(actualRequestTime, + LocalDateTime.parse(timeInTheAllowedPattern, + DateTimeFormatter + .ofPattern(dateTimePattern))) + .toMillis(); + mosipLogger.info(IdAuthCommonConstants.SESSION_ID, EVENT_FILTER, BASE_IDA_FILTER, + "Time difference between request and response in millis:" + duration + + ". Time difference between request and response in Seconds: " + ((double) duration / 1000)); + } catch (DateTimeParseException e) { + mosipLogger.warn(IdAuthCommonConstants.SESSION_ID, EVENT_FILTER, BASE_IDA_FILTER, + "Unable to compute request/response time difference - invalid requestTime format"); + } } protected boolean needStoreAuthTransaction() { @@ -490,7 +509,14 @@ protected String consumeResponse(ResettableStreamHttpServletRequest requestWrapp String inputReqTimeStr = inputRequestTime instanceof String? (String) inputRequestTime : null; logTime(inputReqTimeStr, IdAuthCommonConstants.RESPONSE, actualRequestTime); return responseAsString; - } catch (IdAuthenticationAppException e ) { + } catch (Exception e) { + // By this point responseAsString is already the fully-built response (success + // or a structured error already resolved by IdAuthExceptionHandler upstream). + // Everything in this try block past that is auxiliary post-processing (signing, + // storing the auth transaction, storing the anonymous profile) - a failure there + // (e.g. an unchecked DB/serialization exception, not just IdAuthenticationAppException) + // must not discard the already-correct response and fall through to the + // container's default error page. mosipLogger.error(IdAuthCommonConstants.SESSION_ID, EVENT_FILTER, BASE_IDA_FILTER, e.getMessage()); return responseAsString; } diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/IdAuthValidator.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/IdAuthValidator.java index 63fc141a0c5..143050d9cdc 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/IdAuthValidator.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/IdAuthValidator.java @@ -1,483 +1,498 @@ -package io.mosip.authentication.common.service.validator; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.ID; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.IDV_ID; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.IDV_ID_TYPE; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.REQUEST; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.REQ_TIME; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.SESSION_ID; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.TRANSACTION_ID; - -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeParseException; -import java.time.temporal.ChronoUnit; -import java.util.Date; -import java.util.Optional; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.validation.Errors; -import org.springframework.validation.Validator; - -import io.mosip.authentication.common.service.util.EnvUtil; -import io.mosip.authentication.core.constant.IdAuthCommonConstants; -import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants; -import io.mosip.authentication.core.exception.IdAuthenticationBusinessException; -import io.mosip.authentication.core.indauth.dto.IdType; -import io.mosip.authentication.core.logger.IdaLogger; -import io.mosip.authentication.core.util.IdValidationUtil; -import io.mosip.kernel.core.exception.ExceptionUtils; -import io.mosip.kernel.core.exception.ParseException; -import io.mosip.kernel.core.function.FunctionWithThrowable; -import io.mosip.kernel.core.logger.spi.Logger; -import io.mosip.kernel.core.util.DateUtils; -import io.mosip.kernel.core.util.StringUtils; - -/** - * The Class IdAuthValidator - abstract class containing common validations. - * - * @author Manoj SP - */ -@Component -public abstract class IdAuthValidator implements Validator { - - /** The Constant VALIDATE_REQUEST_TIMED_OUT. */ - private static final String VALIDATE_REQUEST_TIMED_OUT = "validateRequestTimedOut"; - - /** The Constant MISSING_INPUT_PARAMETER. */ - protected static final String MISSING_INPUT_PARAMETER = "MISSING_INPUT_PARAMETER - "; - - /** The Constant VALIDATE. */ - protected static final String VALIDATE = "VALIDATE"; - - /** The Constant A_Z0_9_10. */ - private static final Pattern A_Z0_9_10 = Pattern.compile("^[A-Za-z0-9]{10}"); - - /** The mosip logger. */ - private static Logger mosipLogger = IdaLogger.getLogger(IdAuthValidator.class); - - /** The Constant CONSENT_OBTAINED. */ - private static final String CONSENT_OBTAINED = "consentObtained"; - - @Autowired - IdValidationUtil idValidator; - - - /** - * Validate id - check whether id is null or not. - * - * @param id the id - * @param errors the errors - */ - public void validateId(String id, Errors errors) { - // TODO check id based on the request and add validation for version. - if (StringUtils.isEmpty(id)) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - MISSING_INPUT_PARAMETER + " - id"); - errors.rejectValue(ID, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { ID }, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - } - - /** - * Validate idv id. - * - * @param id the id - * @param idType the id type - * @param errors the errors - */ - public void validateIdvId(String id, String idType, Errors errors) { - validateIdvId(id, idType, errors, REQUEST); - } - - /** - * Validate individual's id - check whether id is null or not and if valid, - * validates idType and UIN/VID. - * - * @param id the id - * @param idType the id type - * @param errors the errors - * @param idFieldName the id field name - */ - public void validateIdvId(String id, String idType, Errors errors, String idFieldName) { - if (id == null || StringUtils.isEmpty(id.trim())) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, MISSING_INPUT_PARAMETER + IDV_ID); - errors.rejectValue(idFieldName, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { IDV_ID }, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } else { - validateIdtypeUinVid(id, idType, errors, idFieldName); - } - } - - /** - * Validate txn id - check whether it is of length 10 and alphanumeric. - * - * @param txnID the txn ID - * @param errors the errors - * @param paramName the param name - */ - protected void validateTxnId(String txnID, Errors errors, String paramName) { - if (StringUtils.isEmpty(txnID)) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - MISSING_INPUT_PARAMETER + TRANSACTION_ID + paramName); - errors.rejectValue(TRANSACTION_ID, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { paramName }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } else if (!A_Z0_9_10.matcher(txnID).matches()) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "INVALID_INPUT_PARAMETER - txnID - value -> " + txnID + paramName); - errors.rejectValue(TRANSACTION_ID, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { paramName }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - - /** - * Validate req time. - * - * @param reqTime the req time - * @param errors the errors - * @param paramName the param name - */ - protected void validateReqTime(String reqTime, Errors errors, String paramName) { - validateReqTime(reqTime, errors, paramName, this::requestTimeParser); - } - - /** - * Validate req time. - * - * @param reqTime the req time - * @param errors the errors - * @param paramName the param name - * @param dateTimeParser the date time parser - */ - protected void validateReqTime(String reqTime, Errors errors, String paramName, FunctionWithThrowable dateTimeParser) { - validateReqTime(reqTime, errors, paramName, REQ_TIME, dateTimeParser); - } - - /** - * Validate req time. - * - * @param reqTime the req time - * @param errors the errors - * @param paramName the param name - * @param fieldName the field name - * @param parser the parser - */ - private void validateReqTime(String reqTime, Errors errors, String paramName, String fieldName, FunctionWithThrowable parser) { - - if (StringUtils.isEmpty(reqTime)) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - MISSING_INPUT_PARAMETER + paramName); - errors.rejectValue(fieldName, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { paramName }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } else { - checkFutureReqTime(reqTime, errors, paramName, fieldName, parser); - } - } - - /** - * Check future req time. - * - * @param reqTime the req time - * @param errors the errors - * @param paramName the param name - * @param fieldName the field name - * @param dateTimeParser the date time parser - */ - private void checkFutureReqTime(String reqTime, Errors errors, String paramName, String fieldName, FunctionWithThrowable dateTimeParser) { - Date reqDateAndTime = null; - try { - reqDateAndTime = dateTimeParser.apply(reqTime); - } catch (ParseException e) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "ParseException : Invalid Date\n" + ExceptionUtils.getStackTrace(e)); - errors.rejectValue(fieldName, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { paramName }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - - Date plusAdjustmentTime = getCurrentTimePlusAdjutsmentTime(); - - if (reqDateAndTime != null && DateUtils.after(reqDateAndTime, plusAdjustmentTime)) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, "Invalid Date"); - Long reqDateMaxTimeLong = EnvUtil.getAuthRequestReceivedTimeAllowedInSeconds(); - String message; - if (paramName == null) { - message = IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(); - } else { - message = String.format("%s. Attribute: %s", IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(), paramName); - } - errors.rejectValue(IdAuthCommonConstants.REQ_TIME, - IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorCode(), - new Object[] { reqDateMaxTimeLong }, - message); - } - } - - /** - * Gets the current time plus adjutsment time. - * - * @return the current time plus adjutsment time - */ - private Date getCurrentTimePlusAdjutsmentTime() { - return getAdjustmentTime(LocalDateTime.now(), LocalDateTime::plusSeconds); - } - - /** - * Gets the adjustment time. - * - * @param originalLdt the original ldt - * @param adjustmentFunc the adjustment func - * @return the adjustment time - */ - private Date getAdjustmentTime(LocalDateTime originalLdt, BiFunction adjustmentFunc) { - long adjustmentSeconds = EnvUtil.getRequestTimeAdjustmentSeconds(); - LocalDateTime ldt = adjustmentFunc.apply(originalLdt, adjustmentSeconds); - Date plusAdjustmentTime = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); - return plusAdjustmentTime; - } - - /** - * Validate request timed out. - * - * @param reqTime the req time - * @param errors the errors - */ - protected void validateRequestTimedOut(String reqTime, Errors errors) { - validateRequestTimedOut(reqTime, errors, this::requestTimeParser, null); - } - - /** - * Validate request timed out. - * - * @param reqTime the req time - * @param errors the errors - * @param dateTimeParser the date time parser - * @param paramName the param name - */ - protected void validateRequestTimedOut(String reqTime, Errors errors, FunctionWithThrowable dateTimeParser, String paramName) { - try { - Instant reqTimeInstance = dateTimeParser.apply(reqTime).toInstant(); - Instant now = Instant.now(); - mosipLogger.debug(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - VALIDATE_REQUEST_TIMED_OUT, - "reqTimeInstance" + reqTimeInstance.toString() + " -- current time : " + now.toString()); - Long reqDateMaxTimeLong = EnvUtil.getAuthRequestReceivedTimeAllowedInSeconds(); - Long adjustmentSeconds = EnvUtil.getRequestTimeAdjustmentSeconds(); - Instant maxAllowedEarlyInstant = now.minus(reqDateMaxTimeLong + adjustmentSeconds, ChronoUnit.SECONDS); - if (reqTimeInstance.isBefore(maxAllowedEarlyInstant)) { - mosipLogger.debug(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - VALIDATE_REQUEST_TIMED_OUT, - "Time difference in sec : " + Duration.between(reqTimeInstance, now).toSeconds()); - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - VALIDATE_REQUEST_TIMED_OUT, - "INVALID_AUTH_REQUEST_TIMESTAMP -- " - + String.format(IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(), - Duration.between(reqTimeInstance, now).toSeconds() - reqDateMaxTimeLong)); - String message; - if (paramName == null) { - message = IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(); - } else { - message = String.format("%s. Attribute: %s", IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(), paramName); - } - errors.rejectValue(IdAuthCommonConstants.REQ_TIME, - IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorCode(), - new Object[] { reqDateMaxTimeLong }, - message); - } - } catch (DateTimeParseException | ParseException e) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - VALIDATE_REQUEST_TIMED_OUT, - IdAuthCommonConstants.INVALID_INPUT_PARAMETER + IdAuthCommonConstants.REQ_TIME); - errors.rejectValue(IdAuthCommonConstants.REQ_TIME, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { IdAuthCommonConstants.REQ_TIME }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - - } - - /** - * Validate UIN, VID. - * - * @param id the id - * @param idTypeOrAlias the id type - * @param errors the errors - * @param idFieldName the id field name - */ - private void validateIdtypeUinVid(String id, String idTypeOrAlias, Errors errors, String idFieldName) { - Set allowedIdTypeSet = getAllowedIdTypes(); - // Checks for null IdType - if (StringUtils.isEmpty(idTypeOrAlias)) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - MISSING_INPUT_PARAMETER + IDV_ID_TYPE); - errors.rejectValue(idFieldName, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { IDV_ID_TYPE }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } // checks IdType is Allowed or Not - else if (allowedIdTypeSet.contains(IdType.getIDTypeStrOrSameStr(idTypeOrAlias))) { - Optional idTypeOpt = IdType.getIDType(idTypeOrAlias); - if(idTypeOpt.isPresent()) { - IdType idType = idTypeOpt.get(); - //If UIN alias is configured only that is allowed - if (idType.getAliasOrType().equals(idTypeOrAlias)) { - if (idType == IdType.UIN) { - try { - idValidator.validateUIN(id); - } catch (IdAuthenticationBusinessException e) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "InvalidIDException - " + e); - errors.rejectValue(idFieldName, IdAuthenticationErrorConstants.INVALID_UIN.getErrorCode(), - IdAuthenticationErrorConstants.INVALID_UIN.getErrorMessage()); - - } - } else if (idType == IdType.VID) { - try { - idValidator.validateVID(id); - } catch (IdAuthenticationBusinessException e) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "InvalidIDException - " + e); - errors.rejectValue(idFieldName, IdAuthenticationErrorConstants.INVALID_VID.getErrorCode(), - IdAuthenticationErrorConstants.INVALID_VID.getErrorMessage()); - } - } - } else { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "INCORRECT_IDTYPE - " + idTypeOrAlias); - errors.rejectValue(IDV_ID_TYPE, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { IDV_ID_TYPE }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } else { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "INCORRECT_IDTYPE - " + idTypeOrAlias); - errors.rejectValue(IDV_ID_TYPE, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { IDV_ID_TYPE }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } else { - // Checks idType is valid or invalid.If Valid and not configured - // IDENTITYTYPE_NOT_ALLOWED error is thrown else INVALID_INPUT_PARAMETER will be - // thrown. - if (IdType.getIDType(idTypeOrAlias) - .filter(idType -> idType.getAliasOrType().equals(idTypeOrAlias)).isPresent()) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "NOT ALLOWED IDENTITY TYPE - " + idTypeOrAlias); - errors.rejectValue(IDV_ID_TYPE, IdAuthenticationErrorConstants.IDENTITYTYPE_NOT_ALLOWED.getErrorCode(), - new Object[] { idTypeOrAlias }, - IdAuthenticationErrorConstants.IDENTITYTYPE_NOT_ALLOWED.getErrorMessage()); - } else { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "INCORRECT_IDTYPE - " + idTypeOrAlias); - errors.rejectValue(IDV_ID_TYPE, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { IDV_ID_TYPE }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - } - - /** - * Gets the allowed id types. - * - * @return the allowed id types - */ - protected Set getAllowedIdTypes() { - return Stream.of(getAllowedIdTypesConfigVal().split(",")) - .map(String::trim) - .filter(str -> !str.isEmpty()) - .collect(Collectors.toSet()); - } - - /** - * Gets the allowed auth types. - * - * @return the allowed auth types - */ - protected Set getAllowedAuthTypes() { - return getAllowedAuthTypes(getAllowedAuthTypeProperty()); - } - - /** - * Extract auth info. - * - * @param configKey the config key - * @return the sets the - */ - private Set getAllowedAuthTypes(String configKey) { - return Stream.of(configKey.split(",")) - .map(String::trim) - .filter(str -> !str.isEmpty()) - .collect(Collectors.toSet()); - } - - /** - * Gets the allowed auth type property. - * - * @return the allowedAuthType - */ - protected String getAllowedAuthTypeProperty() { - return EnvUtil.getAllowedAuthType(); - } - - /** - * Gets the allowed id types config key. - * - * @return the allowed id types config key - */ - protected String getAllowedIdTypesConfigVal() { - return EnvUtil.getAllowedIdTypes(); - } - - /** - * Validates the ConsentRequest on request. - * - * @param consentValue the consent value - * @param errors the errors - */ - protected void validateConsentReq(boolean consentValue, Errors errors) { - if (!consentValue) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - "consentObtained - " + consentValue); - errors.rejectValue(CONSENT_OBTAINED, IdAuthenticationErrorConstants.CONSENT_NOT_AVAILABLE.getErrorCode(), - String.format(IdAuthenticationErrorConstants.CONSENT_NOT_AVAILABLE.getErrorMessage(), - CONSENT_OBTAINED)); - } - } - - /** - * Validate txn id. - * - * @param transactionID the transaction ID - * @param requestTransactionID the request transaction ID - * @param errors the errors - */ - protected void validateTxnId(String transactionID, String requestTransactionID, Errors errors) { - if (!StringUtils.isEmpty(requestTransactionID) && !StringUtils.isEmpty(transactionID) - && !transactionID.equals(requestTransactionID)) { - errors.rejectValue(TRANSACTION_ID, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { TRANSACTION_ID }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - - /** - * Request time parser. - * - * @param reqTime the req time - * @return the date - * @throws ParseException the parse exception - */ - protected Date requestTimeParser(String reqTime) throws ParseException { - return DateUtils.parseToDate(reqTime, EnvUtil.getDateTimePattern()); - } - -} +package io.mosip.authentication.common.service.validator; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.ID; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.IDV_ID; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.IDV_ID_TYPE; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.REQUEST; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.REQ_TIME; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.SESSION_ID; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.TRANSACTION_ID; + +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +import io.mosip.authentication.common.service.util.EnvUtil; +import io.mosip.authentication.core.constant.IdAuthCommonConstants; +import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants; +import io.mosip.authentication.core.exception.IdAuthenticationBusinessException; +import io.mosip.authentication.core.indauth.dto.IdType; +import io.mosip.authentication.core.logger.IdaLogger; +import io.mosip.authentication.core.util.IdValidationUtil; +import io.mosip.kernel.core.exception.ExceptionUtils; +import io.mosip.kernel.core.exception.ParseException; +import io.mosip.kernel.core.function.FunctionWithThrowable; +import io.mosip.kernel.core.logger.spi.Logger; +import io.mosip.kernel.core.util.DateUtils; +import io.mosip.kernel.core.util.StringUtils; + +/** + * The Class IdAuthValidator - abstract class containing common validations. + * + * @author Manoj SP + */ +@Component +public abstract class IdAuthValidator implements Validator { + + /** The Constant VALIDATE_REQUEST_TIMED_OUT. */ + private static final String VALIDATE_REQUEST_TIMED_OUT = "validateRequestTimedOut"; + + /** The Constant MISSING_INPUT_PARAMETER. */ + protected static final String MISSING_INPUT_PARAMETER = "MISSING_INPUT_PARAMETER - "; + + /** The Constant VALIDATE. */ + protected static final String VALIDATE = "VALIDATE"; + + /** The Constant A_Z0_9_10. */ + private static final Pattern A_Z0_9_10 = Pattern.compile("^[A-Za-z0-9]{10}"); + + /** The mosip logger. */ + private static Logger mosipLogger = IdaLogger.getLogger(IdAuthValidator.class); + + /** The Constant CONSENT_OBTAINED. */ + private static final String CONSENT_OBTAINED = "consentObtained"; + + @Autowired + IdValidationUtil idValidator; + + + /** + * Validate id - check whether id is null or not. + * + * @param id the id + * @param errors the errors + */ + public void validateId(String id, Errors errors) { + // TODO check id based on the request and add validation for version. + if (StringUtils.isEmpty(id)) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + MISSING_INPUT_PARAMETER + " - id"); + errors.rejectValue(ID, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { ID }, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + } + + /** + * Validate idv id. + * + * @param id the id + * @param idType the id type + * @param errors the errors + */ + public void validateIdvId(String id, String idType, Errors errors) { + validateIdvId(id, idType, errors, REQUEST); + } + + /** + * Validate individual's id - check whether id is null or not and if valid, + * validates idType and UIN/VID. + * + * @param id the id + * @param idType the id type + * @param errors the errors + * @param idFieldName the id field name + */ + public void validateIdvId(String id, String idType, Errors errors, String idFieldName) { + if (id == null || StringUtils.isEmpty(id.trim())) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, MISSING_INPUT_PARAMETER + IDV_ID); + errors.rejectValue(idFieldName, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { IDV_ID }, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } else { + validateIdtypeUinVid(id, idType, errors, idFieldName); + } + } + + /** + * Validate txn id - check whether it is of length 10 and alphanumeric. + * + * @param txnID the txn ID + * @param errors the errors + * @param paramName the param name + */ + protected void validateTxnId(String txnID, Errors errors, String paramName) { + if (StringUtils.isEmpty(txnID)) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + MISSING_INPUT_PARAMETER + TRANSACTION_ID + paramName); + errors.rejectValue(TRANSACTION_ID, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { paramName }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } else if (!A_Z0_9_10.matcher(txnID).matches()) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "INVALID_INPUT_PARAMETER - txnID - value -> " + txnID + paramName); + errors.rejectValue(TRANSACTION_ID, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { paramName }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + + /** + * Validate req time. + * + * @param reqTime the req time + * @param errors the errors + * @param paramName the param name + */ + protected void validateReqTime(String reqTime, Errors errors, String paramName) { + validateReqTime(reqTime, errors, paramName, this::requestTimeParser); + } + + /** + * Validate req time. + * + * @param reqTime the req time + * @param errors the errors + * @param paramName the param name + * @param dateTimeParser the date time parser + */ + protected void validateReqTime(String reqTime, Errors errors, String paramName, FunctionWithThrowable dateTimeParser) { + validateReqTime(reqTime, errors, paramName, REQ_TIME, dateTimeParser); + } + + /** + * Validate req time. + * + * @param reqTime the req time + * @param errors the errors + * @param paramName the param name + * @param fieldName the field name + * @param parser the parser + */ + private void validateReqTime(String reqTime, Errors errors, String paramName, String fieldName, FunctionWithThrowable parser) { + + if (StringUtils.isEmpty(reqTime)) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + MISSING_INPUT_PARAMETER + paramName); + errors.rejectValue(fieldName, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { paramName }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } else { + checkFutureReqTime(reqTime, errors, paramName, fieldName, parser); + } + } + + /** + * Check future req time. + * + * @param reqTime the req time + * @param errors the errors + * @param paramName the param name + * @param fieldName the field name + * @param dateTimeParser the date time parser + */ + private void checkFutureReqTime(String reqTime, Errors errors, String paramName, String fieldName, FunctionWithThrowable dateTimeParser) { + Date reqDateAndTime = null; + try { + reqDateAndTime = dateTimeParser.apply(reqTime); + } catch (ParseException e) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "ParseException : Invalid Date\n" + ExceptionUtils.getStackTrace(e)); + errors.rejectValue(fieldName, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { paramName }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + + Date plusAdjustmentTime = getCurrentTimePlusAdjutsmentTime(); + + if (reqDateAndTime != null && DateUtils.after(reqDateAndTime, plusAdjustmentTime)) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, "Invalid Date"); + Long reqDateMaxTimeLong = EnvUtil.getAuthRequestReceivedTimeAllowedInSeconds(); + String message; + if (paramName == null) { + message = IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(); + } else { + message = String.format("%s. Attribute: %s", IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(), paramName); + } + errors.rejectValue(IdAuthCommonConstants.REQ_TIME, + IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorCode(), + new Object[] { reqDateMaxTimeLong }, + message); + } + } + + /** + * Gets the current time plus adjutsment time. + * + * @return the current time plus adjutsment time + */ + private Date getCurrentTimePlusAdjutsmentTime() { + return getAdjustmentTime(LocalDateTime.now(), LocalDateTime::plusSeconds); + } + + /** + * Gets the adjustment time. + * + * @param originalLdt the original ldt + * @param adjustmentFunc the adjustment func + * @return the adjustment time + */ + private Date getAdjustmentTime(LocalDateTime originalLdt, BiFunction adjustmentFunc) { + long adjustmentSeconds = EnvUtil.getRequestTimeAdjustmentSeconds(); + LocalDateTime ldt = adjustmentFunc.apply(originalLdt, adjustmentSeconds); + Date plusAdjustmentTime = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); + return plusAdjustmentTime; + } + + /** + * Validate request timed out. + * + * @param reqTime the req time + * @param errors the errors + */ + protected void validateRequestTimedOut(String reqTime, Errors errors) { + validateRequestTimedOut(reqTime, errors, this::requestTimeParser, null); + } + + /** + * Validate request timed out. + * + * @param reqTime the req time + * @param errors the errors + * @param dateTimeParser the date time parser + * @param paramName the param name + */ + protected void validateRequestTimedOut(String reqTime, Errors errors, FunctionWithThrowable dateTimeParser, String paramName) { + try { + Instant reqTimeInstance = dateTimeParser.apply(reqTime).toInstant(); + Instant now = Instant.now(); + mosipLogger.debug(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + VALIDATE_REQUEST_TIMED_OUT, + "reqTimeInstance" + reqTimeInstance.toString() + " -- current time : " + now.toString()); + Long reqDateMaxTimeLong = EnvUtil.getAuthRequestReceivedTimeAllowedInSeconds(); + Long adjustmentSeconds = EnvUtil.getRequestTimeAdjustmentSeconds(); + Instant maxAllowedEarlyInstant = now.minus(reqDateMaxTimeLong + adjustmentSeconds, ChronoUnit.SECONDS); + if (reqTimeInstance.isBefore(maxAllowedEarlyInstant)) { + mosipLogger.debug(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + VALIDATE_REQUEST_TIMED_OUT, + "Time difference in sec : " + Duration.between(reqTimeInstance, now).toSeconds()); + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + VALIDATE_REQUEST_TIMED_OUT, + "INVALID_AUTH_REQUEST_TIMESTAMP -- " + + String.format(IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(), + Duration.between(reqTimeInstance, now).toSeconds() - reqDateMaxTimeLong)); + String message; + if (paramName == null) { + message = IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(); + } else { + message = String.format("%s. Attribute: %s", IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorMessage(), paramName); + } + errors.rejectValue(IdAuthCommonConstants.REQ_TIME, + IdAuthenticationErrorConstants.INVALID_TIMESTAMP.getErrorCode(), + new Object[] { reqDateMaxTimeLong }, + message); + } + } catch (DateTimeParseException | ParseException e) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + VALIDATE_REQUEST_TIMED_OUT, + IdAuthCommonConstants.INVALID_INPUT_PARAMETER + IdAuthCommonConstants.REQ_TIME); + errors.rejectValue(IdAuthCommonConstants.REQ_TIME, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { IdAuthCommonConstants.REQ_TIME }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + + } + + /** + * Validate UIN, VID. + * + * @param id the id + * @param idTypeOrAlias the id type + * @param errors the errors + * @param idFieldName the id field name + */ + private void validateIdtypeUinVid(String id, String idTypeOrAlias, Errors errors, String idFieldName) { + Set allowedIdTypeSet = getAllowedIdTypes(); + // Checks for null IdType + if (StringUtils.isEmpty(idTypeOrAlias)) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + MISSING_INPUT_PARAMETER + IDV_ID_TYPE); + errors.rejectValue(idFieldName, IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { IDV_ID_TYPE }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } // checks IdType is Allowed or Not + else if (allowedIdTypeSet.contains(IdType.getIDTypeStrOrSameStr(idTypeOrAlias))) { + Optional idTypeOpt = IdType.getIDType(idTypeOrAlias); + if(idTypeOpt.isPresent()) { + IdType idType = idTypeOpt.get(); + //If UIN alias is configured only that is allowed + if (idType.getAliasOrType().equals(idTypeOrAlias)) { + if (idType == IdType.UIN) { + try { + idValidator.validateUIN(id); + } catch (IdAuthenticationBusinessException e) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "InvalidIDException - " + e); + errors.rejectValue(idFieldName, IdAuthenticationErrorConstants.INVALID_UIN.getErrorCode(), + IdAuthenticationErrorConstants.INVALID_UIN.getErrorMessage()); + + } + } else if (idType == IdType.VID) { + try { + idValidator.validateVID(id); + } catch (IdAuthenticationBusinessException e) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "InvalidIDException - " + e); + errors.rejectValue(idFieldName, IdAuthenticationErrorConstants.INVALID_VID.getErrorCode(), + IdAuthenticationErrorConstants.INVALID_VID.getErrorMessage()); + } + } + } else { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "INCORRECT_IDTYPE - " + idTypeOrAlias); + errors.rejectValue(IDV_ID_TYPE, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { IDV_ID_TYPE }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } else { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "INCORRECT_IDTYPE - " + idTypeOrAlias); + errors.rejectValue(IDV_ID_TYPE, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { IDV_ID_TYPE }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } else { + // Checks idType is valid or invalid.If Valid and not configured + // IDENTITYTYPE_NOT_ALLOWED error is thrown else INVALID_INPUT_PARAMETER will be + // thrown. + if (IdType.getIDType(idTypeOrAlias) + .filter(idType -> idType.getAliasOrType().equals(idTypeOrAlias)).isPresent()) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "NOT ALLOWED IDENTITY TYPE - " + idTypeOrAlias); + errors.rejectValue(IDV_ID_TYPE, IdAuthenticationErrorConstants.IDENTITYTYPE_NOT_ALLOWED.getErrorCode(), + new Object[] { idTypeOrAlias }, + IdAuthenticationErrorConstants.IDENTITYTYPE_NOT_ALLOWED.getErrorMessage()); + } else { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "INCORRECT_IDTYPE - " + idTypeOrAlias); + errors.rejectValue(IDV_ID_TYPE, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { IDV_ID_TYPE }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + } + + /** + * Gets the allowed id types. + * + * @return the allowed id types + */ + protected Set getAllowedIdTypes() { + return Stream.of(getAllowedIdTypesConfigVal().split(",")) + .map(String::trim) + .filter(str -> !str.isEmpty()) + .collect(Collectors.toSet()); + } + + /** + * Gets the allowed auth types. + * + * @return the allowed auth types + */ + protected Set getAllowedAuthTypes() { + return getAllowedAuthTypes(getAllowedAuthTypeProperty()); + } + + /** + * Extract auth info. + * + * @param configKey the config key + * @return the sets the + */ + private Set getAllowedAuthTypes(String configKey) { + return Stream.of(configKey.split(",")) + .map(String::trim) + .filter(str -> !str.isEmpty()) + .collect(Collectors.toSet()); + } + + /** + * Gets the allowed auth type property. + * + * @return the allowedAuthType + */ + protected String getAllowedAuthTypeProperty() { + return EnvUtil.getAllowedAuthType(); + } + + /** + * Gets the allowed id types config key. + * + * @return the allowed id types config key + */ + protected String getAllowedIdTypesConfigVal() { + return EnvUtil.getAllowedIdTypes(); + } + + /** + * Validates the ConsentRequest on request. + * + * @param consentValue the consent value + * @param errors the errors + */ + protected void validateConsentReq(boolean consentValue, Errors errors) { + if (!consentValue) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + "consentObtained - " + consentValue); + errors.rejectValue(CONSENT_OBTAINED, IdAuthenticationErrorConstants.CONSENT_NOT_AVAILABLE.getErrorCode(), + String.format(IdAuthenticationErrorConstants.CONSENT_NOT_AVAILABLE.getErrorMessage(), + CONSENT_OBTAINED)); + } + } + + /** + * Validate txn id. + * + * @param transactionID the transaction ID + * @param requestTransactionID the request transaction ID + * @param errors the errors + */ + protected void validateTxnId(String transactionID, String requestTransactionID, Errors errors) { + if (!StringUtils.isEmpty(requestTransactionID) && !StringUtils.isEmpty(transactionID) + && !transactionID.equals(requestTransactionID)) { + errors.rejectValue(TRANSACTION_ID, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { TRANSACTION_ID }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + + /** + * Request time parser. + * + * @param reqTime the req time + * @return the date + * @throws ParseException the parse exception + */ + protected Date requestTimeParser(String reqTime) throws ParseException { + // SimpleDateFormat.parse(String) only matches a leading prefix of the input and + // silently ignores trailing characters (e.g. "2024-01-01T10:00:00.000Z111111" + // would otherwise parse successfully). Parsing with a ParsePosition lets us + // confirm the whole string was consumed and reject any leftover characters. + String pattern = EnvUtil.getDateTimePattern(); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern); + simpleDateFormat.setLenient(false); + ParsePosition parsePosition = new ParsePosition(0); + Date parsedDate = simpleDateFormat.parse(reqTime, parsePosition); + if (parsedDate == null || parsePosition.getIndex() != reqTime.length()) { + throw new ParseException(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), REQ_TIME)); + } + return parsedDate; + } + +} diff --git a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/controller/KycAuthController.java b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/controller/KycAuthController.java index d06569a8b16..ee0b4288be6 100644 --- a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/controller/KycAuthController.java +++ b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/controller/KycAuthController.java @@ -1,5 +1,6 @@ package io.mosip.authentication.service.kyc.controller; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.INDIVIDUAL_ID; import static io.mosip.authentication.core.constant.IdAuthConfigKeyConstants.AUTHENTICATION_ERROR_EVENTING_ENABLED; import java.util.Map; @@ -148,6 +149,15 @@ private void initEKycBinder(WebDataBinder binder) { private void initKycExchangeBinder(WebDataBinder binder) { binder.setValidator(kycExchangeValidator); } + + /** + * + * @param binder the binder + */ + @InitBinder("kycExchangeRequestDTOV2") + private void initKycExchangeV2Binder(WebDataBinder binder) { + binder.setValidator(kycExchangeValidator); + } @PostConstruct public void init() { @@ -281,7 +291,7 @@ public KycAuthResponseDTO processKycAuth(@Validated @RequestBody KycAuthRequestD String idType = Objects.nonNull(authRequestDTO.getIndividualIdType()) ? authRequestDTO.getIndividualIdType() : idTypeUtil.getIdType(authRequestDTO.getIndividualId()).getType(); authRequestDTO.setIndividualIdType(idType); - authRequestValidator.validateIdvId(authRequestDTO.getIndividualId(), idType, errors); + authRequestValidator.validateIdvId(authRequestDTO.getIndividualId(), idType, errors, INDIVIDUAL_ID); if(AuthTypeUtil.isBio(authRequestDTO)) { kycReqValidator.validateDeviceDetails(authRequestDTO, errors); } @@ -363,7 +373,7 @@ public KycExchangeResponseDTO processKycExchange(@Validated @RequestBody KycExch String idType = Objects.nonNull(kycExchangeRequestDTO.getIndividualIdType()) ? kycExchangeRequestDTO.getIndividualIdType() : idTypeUtil.getIdType(kycExchangeRequestDTO.getIndividualId()).getType(); kycExchangeRequestDTO.setIndividualIdType(idType); - kycExchangeValidator.validateIdvId(kycExchangeRequestDTO.getIndividualId(), idType, errors); + kycExchangeValidator.validateIdvId(kycExchangeRequestDTO.getIndividualId(), idType, errors, INDIVIDUAL_ID); DataValidationUtil.validate(errors); Map metadata = kycExchangeRequestDTO.getMetadata(); @@ -431,7 +441,7 @@ public KycAuthResponseDTOV2 processKycAuthV2(@Validated @RequestBody KycAuthRequ String idType = Objects.nonNull(authRequestDTO.getIndividualIdType()) ? authRequestDTO.getIndividualIdType() : idTypeUtil.getIdType(authRequestDTO.getIndividualId()).getType(); authRequestDTO.setIndividualIdType(idType); - authRequestValidator.validateIdvId(authRequestDTO.getIndividualId(), idType, errors); + authRequestValidator.validateIdvId(authRequestDTO.getIndividualId(), idType, errors, INDIVIDUAL_ID); if(AuthTypeUtil.isBio(authRequestDTO)) { kycReqValidator.validateDeviceDetails(authRequestDTO, errors); } @@ -514,7 +524,7 @@ public KycExchangeResponseDTO processKycExchangeV2(@Validated @RequestBody KycEx String idType = Objects.nonNull(kycExchangeRequestDTOV2.getIndividualIdType()) ? kycExchangeRequestDTOV2.getIndividualIdType() : idTypeUtil.getIdType(kycExchangeRequestDTOV2.getIndividualId()).getType(); kycExchangeRequestDTOV2.setIndividualIdType(idType); - kycExchangeValidator.validateIdvId(kycExchangeRequestDTOV2.getIndividualId(), idType, errors); + kycExchangeValidator.validateIdvId(kycExchangeRequestDTOV2.getIndividualId(), idType, errors, INDIVIDUAL_ID); DataValidationUtil.validate(errors); Map metadata = kycExchangeRequestDTOV2.getMetadata(); diff --git a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/facade/KycFacadeImpl.java b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/facade/KycFacadeImpl.java index e026f4ac56e..d0013cc5e8f 100644 --- a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/facade/KycFacadeImpl.java +++ b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/facade/KycFacadeImpl.java @@ -578,8 +578,9 @@ public KycExchangeResponseDTO processKycExchangeV2(KycExchangeRequestDTOV2 kycEx IdAuthenticationErrorConstants.PARTNER_POLICY_NOT_FOUND.getErrorMessage()); } - List unVerifiedConsentClaims = kycExchangeRequestDTOV2.getUnVerifiedConsentedClaims() - .keySet().stream().collect(Collectors.toList()); + List unVerifiedConsentClaims = kycExchangeRequestDTOV2.getUnVerifiedConsentedClaims() == null + ? new ArrayList<>() + : new ArrayList<>(kycExchangeRequestDTOV2.getUnVerifiedConsentedClaims().keySet()); mosipLogger.info(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "processKycExchangeV2", "UnVerifiedConsentClaims List:" + unVerifiedConsentClaims); List verifiedConsentClaims = exchangeDataAttributesUtil.getVerifiedClaimsList( diff --git a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/validator/KycExchangeRequestValidator.java b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/validator/KycExchangeRequestValidator.java index bcd69d94748..0db33048fe6 100644 --- a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/validator/KycExchangeRequestValidator.java +++ b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/validator/KycExchangeRequestValidator.java @@ -11,7 +11,9 @@ import io.mosip.authentication.common.service.validator.BaseAuthRequestValidator; import io.mosip.authentication.core.constant.IdAuthCommonConstants; import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants; +import io.mosip.authentication.core.indauth.dto.BaseRequestDTO; import io.mosip.authentication.core.indauth.dto.KycExchangeRequestDTO; +import io.mosip.authentication.core.indauth.dto.KycExchangeRequestDTOV2; import io.mosip.authentication.core.logger.IdaLogger; import io.mosip.kernel.core.logger.spi.Logger; import io.mosip.kernel.core.util.StringUtils; @@ -39,26 +41,29 @@ public class KycExchangeRequestValidator extends AuthRequestValidator { */ @Override public boolean supports(Class clazz) { - return KycExchangeRequestDTO.class.equals(clazz); + return KycExchangeRequestDTO.class.equals(clazz) || KycExchangeRequestDTOV2.class.equals(clazz); } /* * (non-Javadoc) - * + * * @see io.mosip.authentication.service.impl.indauth.validator. * BaseAuthRequestValidator#validate(java.lang.Object, * org.springframework.validation.Errors) */ @Override public void validate(Object target, Errors errors) { - KycExchangeRequestDTO kycExchangeRequestDTO = (KycExchangeRequestDTO) target; + // target is either KycExchangeRequestDTO (v1) or KycExchangeRequestDTOV2 - they + // don't share a common type for kycToken, so BaseRequestDTO covers the common + // fields and getKycToken(target) branches for the rest. + BaseRequestDTO kycExchangeRequestDTO = target instanceof BaseRequestDTO ? (BaseRequestDTO) target : null; if (kycExchangeRequestDTO != null) { if (!errors.hasErrors()) { validateReqTime(kycExchangeRequestDTO.getRequestTime(), errors, IdAuthCommonConstants.REQ_TIME); } if (!errors.hasErrors()) { - validateKycToken(kycExchangeRequestDTO.getKycToken(), errors, IdAuthCommonConstants.KYC_TOKEN); + validateKycToken(getKycToken(target), errors, IdAuthCommonConstants.KYC_TOKEN); } // commented below validation because end user can provide nil consent. @@ -69,7 +74,7 @@ public void validate(Object target, Errors errors) { if (!errors.hasErrors()) { validateTxnId(kycExchangeRequestDTO.getTransactionID(), errors, IdAuthCommonConstants.TRANSACTION_ID); } - + } else { mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), IdAuthCommonConstants.VALIDATE, IdAuthCommonConstants.INVALID_INPUT_PARAMETER + IdAuthCommonConstants.REQUEST); @@ -79,6 +84,15 @@ public void validate(Object target, Errors errors) { } + private String getKycToken(Object target) { + if (target instanceof KycExchangeRequestDTO) { + return ((KycExchangeRequestDTO) target).getKycToken(); + } else if (target instanceof KycExchangeRequestDTOV2) { + return ((KycExchangeRequestDTOV2) target).getKycToken(); + } + return null; + } + private void validateKycToken(String kycToken, Errors errors, String paramName) { if (kycToken == null || StringUtils.isEmpty(kycToken.trim())) {