From 9e41c8ea1b5b26c777f4a5e7a2e16dcf37d3ccac Mon Sep 17 00:00:00 2001 From: Mahammed Taheer Date: Wed, 31 Jan 2024 22:29:31 +0530 Subject: [PATCH] [MOSIP-28837] [MOSIP-31341] added validation for auth inputs and corrected response. Signed-off-by: Mahammed Taheer --- .../builder/AuthStatusInfoBuilder.java | 606 ++--- .../service/config/IDAMappingConfig.java | 2 +- .../service/impl/IdInfoFetcherImpl.java | 4 +- .../common/service/impl/OTPServiceImpl.java | 824 +++--- .../notification/NotificationServiceImpl.java | 751 +++--- .../util/KeyBindedTokenMatcherUtil.java | 7 +- .../validator/AuthRequestValidator.java | 1305 +++++----- .../validator/BaseAuthRequestValidator.java | 2245 +++++++++-------- .../IdAuthenticationErrorConstants.java | 5 +- .../kyc/controller/KycAuthController.java | 2 +- 10 files changed, 2938 insertions(+), 2813 deletions(-) diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/builder/AuthStatusInfoBuilder.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/builder/AuthStatusInfoBuilder.java index ee69a663cdd..3c3ae83fe42 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/builder/AuthStatusInfoBuilder.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/builder/AuthStatusInfoBuilder.java @@ -1,288 +1,318 @@ -package io.mosip.authentication.common.service.builder; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import io.mosip.authentication.common.service.config.IDAMappingConfig; -import io.mosip.authentication.common.service.impl.match.BioAuthType; -import io.mosip.authentication.common.service.impl.match.DemoAuthType; -import io.mosip.authentication.common.service.impl.match.DemoMatchType; -import io.mosip.authentication.common.service.impl.match.IdaIdMapping; -import io.mosip.authentication.common.service.impl.match.PinAuthType; -import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants; -import io.mosip.authentication.core.indauth.dto.ActionableAuthError; -import io.mosip.authentication.core.indauth.dto.AuthError; -import io.mosip.authentication.core.indauth.dto.AuthStatusInfo; -import io.mosip.authentication.core.spi.indauth.match.AuthType; -import io.mosip.authentication.core.spi.indauth.match.MatchInput; -import io.mosip.authentication.core.spi.indauth.match.MatchOutput; -import io.mosip.authentication.core.spi.indauth.match.MatchType.Category; - -/** - * The builder class of AuthStatusInfo. - * - * @author Loganathan Sekar - */ - -public class AuthStatusInfoBuilder { - - private static final String ADDRESS_LINE_ITEMS = "address line item(s)"; - - /** The built flag. */ - private boolean built; - - /** The auth status info. */ - private AuthStatusInfo authStatusInfo; - - /** - * Instantiates a new AuthStatusInfoBuilder. - */ - private AuthStatusInfoBuilder() { - authStatusInfo = new AuthStatusInfo(); - } - - /** - * Gets new instance of AuthStatusInfo. - * - * @return the auth status info builder - */ - public static AuthStatusInfoBuilder newInstance() { - return new AuthStatusInfoBuilder(); - } - - /** - * Sets the status. - * - * @param status the status - * @return the auth status info builder - */ - public AuthStatusInfoBuilder setStatus(boolean status) { - assertNotBuilt(); - authStatusInfo.setStatus(status); - return this; - } - - /** - * Builds the status info. - * - * @param matched the demo matched - * @param listMatchInputs the list match inputs - * @param listMatchOutputs the list match outputs - * @param authTypes the auth types - * @param idMappingConfig the id mapping config - * @return the auth status info - */ - public static AuthStatusInfo buildStatusInfo(boolean matched, List listMatchInputs, - List listMatchOutputs, AuthType[] authTypes, IDAMappingConfig idMappingConfig) { - AuthStatusInfoBuilder statusInfoBuilder = AuthStatusInfoBuilder.newInstance(); - statusInfoBuilder.setStatus(matched); - prepareErrorList(listMatchOutputs, statusInfoBuilder, idMappingConfig); - return statusInfoBuilder.build(); - } - - /** - * Builds the usage data bits. - * - * @param listMatchOutputs the list match outputs - * @param statusInfoBuilder the status info builder - * @param idaMappingConfig the ida mapping config - */ - /** - * prepares the list of errors if the authentication status got failed - * - * @param listMatchOutputs the list match outputs - * @param statusInfoBuilder the status info builder - */ - private static void prepareErrorList(List listMatchOutputs, AuthStatusInfoBuilder statusInfoBuilder, - IDAMappingConfig idaMappingConfig) { - listMatchOutputs.forEach((MatchOutput matchOutput) -> { - if (!matchOutput.isMatched()) { - prepareErrorList(matchOutput, statusInfoBuilder, idaMappingConfig); - } - }); - } - - /** - * @param matchOutput - * @param statusInfoBuilder - */ - private static void prepareErrorList(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder, - IDAMappingConfig idMappingConfig) { - - if (matchOutput != null && !matchOutput.isMatched()) { - String category = matchOutput.getMatchType().getCategory().getType(); - if (category.equalsIgnoreCase(Category.BIO.getType())) { - constructBioError(matchOutput, statusInfoBuilder); - } else if (category.equalsIgnoreCase(Category.SPIN.getType())) { - constructPinError(matchOutput, statusInfoBuilder); - } else if (category.equalsIgnoreCase(Category.DEMO.getType())) { - constructDemoError(matchOutput, statusInfoBuilder, idMappingConfig); - } else if (category.equalsIgnoreCase(Category.OTP.getType())) { - constructOTPError(matchOutput, statusInfoBuilder); - } - } - } - - private static void constructDemoError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder, - IDAMappingConfig idMappingConfig) { - boolean multiLanguage = matchOutput.getMatchType().isMultiLanguage() && matchOutput.getLanguage() != null; - - Optional authTypeForMatchType; - AuthType[] authTypes; - authTypes = DemoAuthType.values(); - authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); - if (authTypeForMatchType.isPresent()) { - AuthError errors = null; - String idName = matchOutput.getIdName(); - - //If name mapping contains the id Name the error message will not be checked for full address mapping condition - List nameMapping = IdaIdMapping.NAME.getMappingFunction().apply(idMappingConfig, - matchOutput.getMatchType()); - String idNameForErrorMessage; - if(nameMapping.contains(idName)) { - idNameForErrorMessage = idName; - } else { - // For Address line items, check if Full address mapping contains the id Name - // the error message will be called as address line item(s) - List fullAddressMappings = IdaIdMapping.FULLADDRESS.getMappingFunction().apply(idMappingConfig, - matchOutput.getMatchType()); - if (fullAddressMappings.contains(idName)) { - idNameForErrorMessage = ADDRESS_LINE_ITEMS; - } else { - idNameForErrorMessage = idName; - } - } - //Need special handling for age since it is mapped to Date of Birth , but error should say about age only. - if(matchOutput.getMatchType().equals(DemoMatchType.AGE)) { - idNameForErrorMessage = IdaIdMapping.AGE.getIdname(); - } - - if (!multiLanguage) { - errors = createActionableAuthError(IdAuthenticationErrorConstants.DEMO_DATA_MISMATCH, idNameForErrorMessage); - } else { - errors = createActionableAuthError(IdAuthenticationErrorConstants.DEMOGRAPHIC_DATA_MISMATCH_LANG, idNameForErrorMessage, - matchOutput.getLanguage()); - } - - statusInfoBuilder.addErrors(errors); - } - } - - private static void constructOTPError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder) { - Optional authTypeForMatchType; - AuthType[] authTypes; - authTypes = PinAuthType.values(); - authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); - - if (authTypeForMatchType.isPresent()) { - AuthError errors = createActionableAuthError(IdAuthenticationErrorConstants.INVALID_OTP, ""); - statusInfoBuilder.addErrors(errors); - } - } - - /** - * Construct pin error. - * - * @param matchOutput the match output - * @param statusInfoBuilder the status info builder - */ - private static void constructPinError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder) { - Optional authTypeForMatchType; - AuthType authType; - AuthType[] authTypes; - authTypes = PinAuthType.values(); - authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); - - if (authTypeForMatchType.isPresent()) { - authType = authTypeForMatchType.get(); - if (authType.getDisplayName().equals(PinAuthType.SPIN.getDisplayName())) { - AuthError errors = createActionableAuthError(IdAuthenticationErrorConstants.PIN_MISMATCH, ""); - statusInfoBuilder.addErrors(errors); - } - } - } - - /** - * Construct bio error. - * - * @param matchOutput the match output - * @param statusInfoBuilder the status info builder - */ - private static void constructBioError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder) { - Optional authTypeForMatchType; - AuthType[] authTypes; - authTypes = BioAuthType.values(); - authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); - if (authTypeForMatchType.isPresent()) { - AuthType authType = authTypeForMatchType.get(); - String type; - if(!authType.equals(BioAuthType.MULTI_MODAL)){ - type = " - " + authType.getType(); - } else { - type = ""; - } - - AuthError errors = createActionableAuthError(IdAuthenticationErrorConstants.BIO_MISMATCH, - type); - statusInfoBuilder.addErrors(errors); - } - } - - /** - * Construct Actionable Auth errors. - * - * @param idAuthenticationErrorConstants - * @param paramName - * @return - */ - private static AuthError createActionableAuthError(IdAuthenticationErrorConstants idAuthenticationErrorConstants, - Object... params) { - String errorCode = idAuthenticationErrorConstants.getErrorCode(); - String errorMessage = String.format(idAuthenticationErrorConstants.getErrorMessage(), params); - String actionMessage; - if (idAuthenticationErrorConstants.getActionMessage() != null) { - actionMessage = String.format(idAuthenticationErrorConstants.getActionMessage(), params); - } else { - actionMessage = null; - } - return new ActionableAuthError(errorCode, errorMessage, actionMessage); - } - - /** - * Adds the errors to the AuthStatusInfo. - * - * @param errors the errors - * @return the auth status info builder - */ - public AuthStatusInfoBuilder addErrors(AuthError... errors) { - assertNotBuilt(); - if (authStatusInfo.getErr() == null) { - authStatusInfo.setErr(new ArrayList<>()); - } - - authStatusInfo.getErr().addAll(Arrays.asList(errors)); - return this; - } - - /** - * Builds the AuthStatusInfo. - * - * @return the AuthStatusInfo instance - */ - public AuthStatusInfo build() { - assertNotBuilt(); - built = true; - return authStatusInfo; - } - - /** - * Assert that AuthStatusInfo is not built. - */ - private void assertNotBuilt() { - if (built) { - throw new IllegalStateException(); - } - } -} +package io.mosip.authentication.common.service.builder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import io.mosip.authentication.common.service.config.IDAMappingConfig; +import io.mosip.authentication.common.service.impl.match.BioAuthType; +import io.mosip.authentication.common.service.impl.match.DemoAuthType; +import io.mosip.authentication.common.service.impl.match.DemoMatchType; +import io.mosip.authentication.common.service.impl.match.IdaIdMapping; +import io.mosip.authentication.common.service.impl.match.KeyBindedTokenAuthType; +import io.mosip.authentication.common.service.impl.match.PasswordAuthType; +import io.mosip.authentication.common.service.impl.match.PinAuthType; +import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants; +import io.mosip.authentication.core.indauth.dto.ActionableAuthError; +import io.mosip.authentication.core.indauth.dto.AuthError; +import io.mosip.authentication.core.indauth.dto.AuthStatusInfo; +import io.mosip.authentication.core.spi.indauth.match.AuthType; +import io.mosip.authentication.core.spi.indauth.match.MatchInput; +import io.mosip.authentication.core.spi.indauth.match.MatchOutput; +import io.mosip.authentication.core.spi.indauth.match.MatchType.Category; + +/** + * The builder class of AuthStatusInfo. + * + * @author Loganathan Sekar + */ + +public class AuthStatusInfoBuilder { + + private static final String ADDRESS_LINE_ITEMS = "address line item(s)"; + + /** The built flag. */ + private boolean built; + + /** The auth status info. */ + private AuthStatusInfo authStatusInfo; + + /** + * Instantiates a new AuthStatusInfoBuilder. + */ + private AuthStatusInfoBuilder() { + authStatusInfo = new AuthStatusInfo(); + } + + /** + * Gets new instance of AuthStatusInfo. + * + * @return the auth status info builder + */ + public static AuthStatusInfoBuilder newInstance() { + return new AuthStatusInfoBuilder(); + } + + /** + * Sets the status. + * + * @param status the status + * @return the auth status info builder + */ + public AuthStatusInfoBuilder setStatus(boolean status) { + assertNotBuilt(); + authStatusInfo.setStatus(status); + return this; + } + + /** + * Builds the status info. + * + * @param matched the demo matched + * @param listMatchInputs the list match inputs + * @param listMatchOutputs the list match outputs + * @param authTypes the auth types + * @param idMappingConfig the id mapping config + * @return the auth status info + */ + public static AuthStatusInfo buildStatusInfo(boolean matched, List listMatchInputs, + List listMatchOutputs, AuthType[] authTypes, IDAMappingConfig idMappingConfig) { + AuthStatusInfoBuilder statusInfoBuilder = AuthStatusInfoBuilder.newInstance(); + statusInfoBuilder.setStatus(matched); + prepareErrorList(listMatchOutputs, statusInfoBuilder, idMappingConfig); + return statusInfoBuilder.build(); + } + + /** + * Builds the usage data bits. + * + * @param listMatchOutputs the list match outputs + * @param statusInfoBuilder the status info builder + * @param idaMappingConfig the ida mapping config + */ + /** + * prepares the list of errors if the authentication status got failed + * + * @param listMatchOutputs the list match outputs + * @param statusInfoBuilder the status info builder + */ + private static void prepareErrorList(List listMatchOutputs, AuthStatusInfoBuilder statusInfoBuilder, + IDAMappingConfig idaMappingConfig) { + listMatchOutputs.forEach((MatchOutput matchOutput) -> { + if (!matchOutput.isMatched()) { + prepareErrorList(matchOutput, statusInfoBuilder, idaMappingConfig); + } + }); + } + + /** + * @param matchOutput + * @param statusInfoBuilder + */ + private static void prepareErrorList(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder, + IDAMappingConfig idMappingConfig) { + + if (matchOutput != null && !matchOutput.isMatched()) { + String category = matchOutput.getMatchType().getCategory().getType(); + if (category.equalsIgnoreCase(Category.BIO.getType())) { + constructBioError(matchOutput, statusInfoBuilder); + } else if (category.equalsIgnoreCase(Category.SPIN.getType())) { + constructPinError(matchOutput, statusInfoBuilder); + } else if (category.equalsIgnoreCase(Category.DEMO.getType())) { + constructDemoError(matchOutput, statusInfoBuilder, idMappingConfig); + } else if (category.equalsIgnoreCase(Category.OTP.getType())) { + constructOTPError(matchOutput, statusInfoBuilder); + } else if (category.equalsIgnoreCase(Category.PWD.getType())) { + constructPWDError(matchOutput, statusInfoBuilder); + } else if (category.equalsIgnoreCase(Category.KBT.getType())) { + constructKBTError(matchOutput, statusInfoBuilder); + } + } + } + + private static void constructDemoError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder, + IDAMappingConfig idMappingConfig) { + boolean multiLanguage = matchOutput.getMatchType().isMultiLanguage() && matchOutput.getLanguage() != null; + + Optional authTypeForMatchType; + AuthType[] authTypes; + authTypes = DemoAuthType.values(); + authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); + if (authTypeForMatchType.isPresent()) { + AuthError errors = null; + String idName = matchOutput.getIdName(); + + //If name mapping contains the id Name the error message will not be checked for full address mapping condition + List nameMapping = IdaIdMapping.NAME.getMappingFunction().apply(idMappingConfig, + matchOutput.getMatchType()); + String idNameForErrorMessage; + if(nameMapping.contains(idName)) { + idNameForErrorMessage = idName; + } else { + // For Address line items, check if Full address mapping contains the id Name + // the error message will be called as address line item(s) + List fullAddressMappings = IdaIdMapping.FULLADDRESS.getMappingFunction().apply(idMappingConfig, + matchOutput.getMatchType()); + if (fullAddressMappings.contains(idName)) { + idNameForErrorMessage = ADDRESS_LINE_ITEMS; + } else { + idNameForErrorMessage = idName; + } + } + //Need special handling for age since it is mapped to Date of Birth , but error should say about age only. + if(matchOutput.getMatchType().equals(DemoMatchType.AGE)) { + idNameForErrorMessage = IdaIdMapping.AGE.getIdname(); + } + + if (!multiLanguage) { + errors = createActionableAuthError(IdAuthenticationErrorConstants.DEMO_DATA_MISMATCH, idNameForErrorMessage); + } else { + errors = createActionableAuthError(IdAuthenticationErrorConstants.DEMOGRAPHIC_DATA_MISMATCH_LANG, idNameForErrorMessage, + matchOutput.getLanguage()); + } + + statusInfoBuilder.addErrors(errors); + } + } + + private static void constructOTPError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder) { + Optional authTypeForMatchType; + AuthType[] authTypes; + authTypes = PinAuthType.values(); + authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); + + if (authTypeForMatchType.isPresent()) { + AuthError errors = createActionableAuthError(IdAuthenticationErrorConstants.INVALID_OTP, ""); + statusInfoBuilder.addErrors(errors); + } + } + + private static void constructPWDError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder) { + Optional authTypeForMatchType; + AuthType[] authTypes; + authTypes = PasswordAuthType.values(); + authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); + + if (authTypeForMatchType.isPresent()) { + AuthError errors = createActionableAuthError(IdAuthenticationErrorConstants.PASSWORD_MISMATCH, ""); + statusInfoBuilder.addErrors(errors); + } + } + + private static void constructKBTError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder) { + Optional authTypeForMatchType; + AuthType[] authTypes; + authTypes = KeyBindedTokenAuthType.values(); + authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); + + if (authTypeForMatchType.isPresent()) { + AuthError errors = createActionableAuthError(IdAuthenticationErrorConstants.ERROR_TOKEN_VERIFICATION, ""); + statusInfoBuilder.addErrors(errors); + } + } + + /** + * Construct pin error. + * + * @param matchOutput the match output + * @param statusInfoBuilder the status info builder + */ + private static void constructPinError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder) { + Optional authTypeForMatchType; + AuthType authType; + AuthType[] authTypes; + authTypes = PinAuthType.values(); + authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); + + if (authTypeForMatchType.isPresent()) { + authType = authTypeForMatchType.get(); + if (authType.getDisplayName().equals(PinAuthType.SPIN.getDisplayName())) { + AuthError errors = createActionableAuthError(IdAuthenticationErrorConstants.PIN_MISMATCH, ""); + statusInfoBuilder.addErrors(errors); + } + } + } + + /** + * Construct bio error. + * + * @param matchOutput the match output + * @param statusInfoBuilder the status info builder + */ + private static void constructBioError(MatchOutput matchOutput, AuthStatusInfoBuilder statusInfoBuilder) { + Optional authTypeForMatchType; + AuthType[] authTypes; + authTypes = BioAuthType.values(); + authTypeForMatchType = AuthType.getAuthTypeForMatchType(matchOutput.getMatchType(), authTypes); + if (authTypeForMatchType.isPresent()) { + AuthType authType = authTypeForMatchType.get(); + String type; + if(!authType.equals(BioAuthType.MULTI_MODAL)){ + type = " - " + authType.getType(); + } else { + type = ""; + } + + AuthError errors = createActionableAuthError(IdAuthenticationErrorConstants.BIO_MISMATCH, + type); + statusInfoBuilder.addErrors(errors); + } + } + + /** + * Construct Actionable Auth errors. + * + * @param idAuthenticationErrorConstants + * @param paramName + * @return + */ + private static AuthError createActionableAuthError(IdAuthenticationErrorConstants idAuthenticationErrorConstants, + Object... params) { + String errorCode = idAuthenticationErrorConstants.getErrorCode(); + String errorMessage = String.format(idAuthenticationErrorConstants.getErrorMessage(), params); + String actionMessage; + if (idAuthenticationErrorConstants.getActionMessage() != null) { + actionMessage = String.format(idAuthenticationErrorConstants.getActionMessage(), params); + } else { + actionMessage = null; + } + return new ActionableAuthError(errorCode, errorMessage, actionMessage); + } + + /** + * Adds the errors to the AuthStatusInfo. + * + * @param errors the errors + * @return the auth status info builder + */ + public AuthStatusInfoBuilder addErrors(AuthError... errors) { + assertNotBuilt(); + if (authStatusInfo.getErr() == null) { + authStatusInfo.setErr(new ArrayList<>()); + } + + authStatusInfo.getErr().addAll(Arrays.asList(errors)); + return this; + } + + /** + * Builds the AuthStatusInfo. + * + * @return the AuthStatusInfo instance + */ + public AuthStatusInfo build() { + assertNotBuilt(); + built = true; + return authStatusInfo; + } + + /** + * Assert that AuthStatusInfo is not built. + */ + private void assertNotBuilt() { + if (built) { + throw new IllegalStateException(); + } + } +} diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/config/IDAMappingConfig.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/config/IDAMappingConfig.java index 3117ec4c3fc..59d3ca494a9 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/config/IDAMappingConfig.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/config/IDAMappingConfig.java @@ -8,6 +8,7 @@ import org.springframework.context.annotation.PropertySource; import io.mosip.authentication.common.service.factory.IDAMappingFactory; +import io.mosip.authentication.core.indauth.dto.KeyBindedTokenDTO; import io.mosip.authentication.core.spi.indauth.match.MappingConfig; import lombok.Data; @@ -128,5 +129,4 @@ public class IDAMappingConfig implements MappingConfig { /** The password. */ private List password; - } diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/IdInfoFetcherImpl.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/IdInfoFetcherImpl.java index cdebdf68e2c..89ecc0d8b1c 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/IdInfoFetcherImpl.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/IdInfoFetcherImpl.java @@ -571,7 +571,9 @@ public List getUserPreferredLanguages(Map> if (userPreferredLangAttribute != null) { List identityInfoList = idInfo.get(userPreferredLangAttribute); if (identityInfoList != null) { - return identityInfoList.stream().map(IdentityInfoDTO::getValue).collect(Collectors.toList()); + return identityInfoList.stream().map(info -> info.getValue().split(",")) + .flatMap(java.util.Arrays::stream) + .collect(Collectors.toList()); } return Collections.emptyList(); } diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/OTPServiceImpl.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/OTPServiceImpl.java index 57fc40ffed2..345367b2ee1 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/OTPServiceImpl.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/OTPServiceImpl.java @@ -1,412 +1,414 @@ -package io.mosip.authentication.common.service.impl; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import io.mosip.authentication.authfilter.exception.IdAuthenticationFilterException; -import io.mosip.authentication.common.service.entity.AuthtypeLock; -import io.mosip.authentication.common.service.repository.AuthLockRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; - -import io.mosip.authentication.common.manager.IdAuthFraudAnalysisEventManager; -import io.mosip.authentication.common.service.builder.AuthTransactionBuilder; -import io.mosip.authentication.common.service.entity.AutnTxn; -import io.mosip.authentication.common.service.helper.IdInfoHelper; -import io.mosip.authentication.common.service.impl.match.DemoMatchType; -import io.mosip.authentication.common.service.integration.OTPManager; -import io.mosip.authentication.common.service.integration.TokenIdManager; -import io.mosip.authentication.common.service.repository.AutnTxnRepository; -import io.mosip.authentication.common.service.repository.IdaUinHashSaltRepo; -import io.mosip.authentication.common.service.transaction.manager.IdAuthSecurityManager; -import io.mosip.authentication.common.service.util.EnvUtil; -import io.mosip.authentication.common.service.util.IdaRequestResponsConsumerUtil; -import io.mosip.authentication.core.constant.IdAuthCommonConstants; -import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants; -import io.mosip.authentication.core.constant.RequestType; -import io.mosip.authentication.core.dto.ObjectWithMetadata; -import io.mosip.authentication.core.exception.IDDataValidationException; -import io.mosip.authentication.core.exception.IdAuthenticationBusinessException; -import io.mosip.authentication.core.indauth.dto.IdType; -import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; -import io.mosip.authentication.core.indauth.dto.NotificationType; -import io.mosip.authentication.core.logger.IdaLogger; -import io.mosip.authentication.core.otp.dto.MaskedResponseDTO; -import io.mosip.authentication.core.otp.dto.OtpRequestDTO; -import io.mosip.authentication.core.otp.dto.OtpResponseDTO; -import io.mosip.authentication.core.partner.dto.PartnerDTO; -import io.mosip.authentication.core.spi.id.service.IdService; -import io.mosip.authentication.core.spi.indauth.match.IdInfoFetcher; -import io.mosip.authentication.core.spi.otp.service.OTPService; -import io.mosip.authentication.core.spi.partner.service.PartnerService; -import io.mosip.authentication.core.util.LanguageComparator; -import io.mosip.authentication.core.util.MaskUtil; -import io.mosip.kernel.core.exception.ParseException; -import io.mosip.kernel.core.logger.spi.Logger; -import io.mosip.kernel.core.util.DateUtils; -import io.mosip.kernel.core.util.StringUtils; - -/** - * Service implementation of OtpTriggerService. - * - * @author Rakesh Roshan - * @author Dinesh Karuppiah.T - */ -@Service -public class OTPServiceImpl implements OTPService { - - /** The Constant NAME. */ - private static final String NAME = "name"; - private static final String OTP = "otp"; - private static final String PHONE = "PHONE"; - private static final String EMAIL = "EMAIL"; - private static final String OTP_SMS = "otp-sms"; - private static final String OTP_EMAIL = "otp-email"; - - - /** The id auth service. */ - @Autowired - private IdService idAuthService; - - /** The autntxnrepository. */ - @Autowired - private AutnTxnRepository autntxnrepository; - - /** The auth lock repository. */ - @Autowired - AuthLockRepository authLockRepository; - - /** The env. */ - @Autowired - private EnvUtil env; - - @Autowired - private IdInfoHelper idInfoHelper; - - @Autowired - private IdInfoFetcher idInfoFetcher; - - /** The otp manager. */ - @Autowired - private OTPManager otpManager; - - /** The TokenId manager */ - @Autowired - private TokenIdManager tokenIdManager; - - @Autowired - private IdaUinHashSaltRepo uinHashSaltRepo; - - @Autowired - private IdAuthSecurityManager securityManager; - - @Autowired - private PartnerService partnerService; - - @Autowired - private IdAuthFraudAnalysisEventManager fraudEventManager; - - @Autowired - @Qualifier("NotificationLangComparator") - private LanguageComparator languageComparator; - - /** The mosip logger. */ - private static Logger mosipLogger = IdaLogger.getLogger(OTPServiceImpl.class); - - /** - * Generate OTP, store the OTP request details for success/failure. And send OTP - * notification by sms(on mobile)/mail(on email-id). - * - * @param otpRequestDto the otp request dto - * @return otpResponseDTO - * @throws IdAuthenticationBusinessException the id authentication business - * exception - */ - @Override - public OtpResponseDTO generateOtp(OtpRequestDTO otpRequestDto, String partnerId, ObjectWithMetadata requestWithMetadata) - throws IdAuthenticationBusinessException { - boolean isInternal = partnerId != null && partnerId.equalsIgnoreCase(IdAuthCommonConstants.INTERNAL); - boolean status; - String token = null; - try { - String individualIdType = IdType.getIDTypeStrOrDefault(otpRequestDto.getIndividualIdType()); - String individualId = otpRequestDto.getIndividualId(); - - Map idResDTO = idAuthService.processIdType(individualIdType, individualId, false, false, - idInfoHelper.getDefaultFilterAttributes()); - - token = idAuthService.getToken(idResDTO); - - validateAllowedOtpChannles(token, otpRequestDto.getOtpChannel()); - - OtpResponseDTO otpResponseDTO = doGenerateOTP(otpRequestDto, partnerId, isInternal, token, individualIdType, idResDTO); - IdaRequestResponsConsumerUtil.setIdVersionToResponse(requestWithMetadata, otpResponseDTO); - - status = otpResponseDTO.getErrors() == null || otpResponseDTO.getErrors().isEmpty(); - saveToTxnTable(otpRequestDto, isInternal, status, partnerId, token, otpResponseDTO, requestWithMetadata); - - return otpResponseDTO; - - } catch(IdAuthenticationBusinessException e) { - status = false; - //FIXME check if for this condition auth transaction is stored, then remove below code - //saveToTxnTable(otpRequestDto, isInternal, status, partnerId, token, null, null); - throw e; - } - - - } - - private void validateAllowedOtpChannles(String token, List otpChannel) throws IdAuthenticationFilterException { - - if(containsChannel(otpChannel, OTP)) { - checkAuthLock(token, OTP); - } - else if(containsChannel(otpChannel, PHONE)) { - checkAuthLock(token, OTP_SMS); - } - else if(containsChannel(otpChannel, EMAIL)) { - checkAuthLock(token, OTP_EMAIL); - } - } - - private static boolean containsChannel(List otpChannel, String channel) { - return otpChannel.stream().anyMatch(channelItem -> channel.equalsIgnoreCase(channelItem)); - } - - private void checkAuthLock(String token, String authTypeCode) throws IdAuthenticationFilterException { - List authTypeLocks = authLockRepository.findByTokenAndAuthtypecode(token, authTypeCode); - for(AuthtypeLock authtypeLock : authTypeLocks) { - if(authtypeLock.getStatuscode().equalsIgnoreCase("true")){ - throw new IdAuthenticationFilterException( - IdAuthenticationErrorConstants.AUTH_TYPE_LOCKED.getErrorCode(), - String.format(IdAuthenticationErrorConstants.AUTH_TYPE_LOCKED.getErrorMessage(), - authTypeCode)); - } - } - } - - private void saveToTxnTable(OtpRequestDTO otpRequestDto, boolean isInternal, boolean status, String partnerId, String token, OtpResponseDTO otpResponseDTO, ObjectWithMetadata requestWithMetadata) - throws IdAuthenticationBusinessException { - if (token != null) { - boolean authTokenRequired = !isInternal - && EnvUtil.getAuthTokenRequired(); - String authTokenId = authTokenRequired ? tokenIdManager.generateTokenId(token, partnerId) : null; - saveTxn(otpRequestDto, token, authTokenId, status, partnerId, isInternal, otpResponseDTO, requestWithMetadata); - } - } - - private OtpResponseDTO doGenerateOTP(OtpRequestDTO otpRequestDto, String partnerId, boolean isInternal, String token, String individualIdType, Map idResDTO) - throws IdAuthenticationBusinessException, IDDataValidationException { - String individualId = otpRequestDto.getIndividualId(); - String requestTime = otpRequestDto.getRequestTime(); - OtpResponseDTO otpResponseDTO = new OtpResponseDTO(); - - if (isOtpFlooded(token, requestTime)) { - throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.OTP_REQUEST_FLOODED); - } else { - String transactionId = otpRequestDto.getTransactionID(); - otpResponseDTO.setId(otpRequestDto.getId()); - otpResponseDTO.setTransactionID(transactionId); - - Map> idInfo = IdInfoFetcher.getIdInfo(idResDTO); - Map valueMap = new HashMap<>(); - - List templateLanguages = getTemplateLanguages(idInfo); - for (String lang : templateLanguages) { - valueMap.put(NAME + "_" + lang, getName(lang, idInfo)); - } - - String email = getEmail(idInfo); - String phoneNumber = getPhoneNumber(idInfo); - valueMap.put(IdAuthCommonConstants.PHONE_NUMBER, phoneNumber); - valueMap.put(IdAuthCommonConstants.EMAIL, email); - - List otpChannel = otpRequestDto.getOtpChannel(); - if (StringUtils.isBlank(phoneNumber) && containsChannel(otpChannel, PHONE) && !containsChannel(otpChannel, EMAIL)) { - throw new IdAuthenticationBusinessException( - IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorCode(), - IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorMessage() - + ". Phone Number is not found in identity data."); - } - - if (StringUtils.isBlank(email) && containsChannel(otpChannel, EMAIL) && !containsChannel(otpChannel, PHONE)) { - throw new IdAuthenticationBusinessException( - IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorCode(), - IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorMessage() - + ". Email ID is not found in identity data."); - } - - if(StringUtils.isBlank(phoneNumber) && StringUtils.isBlank(email) && (containsChannel(otpChannel, PHONE) && containsChannel(otpChannel, EMAIL))) { - throw new IdAuthenticationBusinessException( - IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorCode(), - IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorMessage() - + ". Both Phone Number and Email ID are not found in identity data."); - } - - boolean isOtpGenerated = otpManager.sendOtp(otpRequestDto, individualId, individualIdType, valueMap, - templateLanguages); - - if (isOtpGenerated) { - otpResponseDTO.setErrors(null); - String responseTime = IdaRequestResponsConsumerUtil.getResponseTime(otpRequestDto.getRequestTime(), - EnvUtil.getDateTimePattern()); - otpResponseDTO.setResponseTime(responseTime); - MaskedResponseDTO maskedResponseDTO = new MaskedResponseDTO(); - List otpChannels = otpRequestDto.getOtpChannel(); - for (String channel : otpChannels) { - processChannel(channel, phoneNumber, email, maskedResponseDTO); - } - otpResponseDTO.setResponse(maskedResponseDTO); - - mosipLogger.info(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), this.getClass().getName(), - " is OTP generated: " + isOtpGenerated); - } else { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), - this.getClass().getName(), "OTP Generation failed"); - throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.OTP_GENERATION_FAILED); - } - - } - return otpResponseDTO; - } - - /** - * Audit txn. - * - * @param otpRequestDto the otp request dto - * @param token the uin - * @param authTokenId the auth token id - * @param status the status - * @param otpResponseDTO - * @param requestWithMetadata - * @throws IdAuthenticationBusinessException the id authentication business - * exception - */ - private void saveTxn(OtpRequestDTO otpRequestDto, String token, String authTokenId, boolean status, String partnerId, boolean isInternal, OtpResponseDTO otpResponseDTO, ObjectWithMetadata requestWithMetadata) - throws IdAuthenticationBusinessException { - Optional partner = isInternal ? Optional.empty() : partnerService.getPartner(partnerId, otpRequestDto.getMetadata()); - AutnTxn authTxn = AuthTransactionBuilder.newInstance() - .withRequest(otpRequestDto) - .addRequestType(RequestType.OTP_REQUEST) - .withAuthToken(authTokenId) - .withStatus(status) - .withToken(token) - .withPartner(partner) - .withInternal(isInternal) - .build(env,uinHashSaltRepo,securityManager); - fraudEventManager.analyseEvent(authTxn); - if(requestWithMetadata != null) { - requestWithMetadata.setMetadata(Map.of(AutnTxn.class.getSimpleName(), authTxn)); - } else { - idAuthService.saveAutnTxn(authTxn); - } - } - - private String getName(String language, Map> idInfo) - throws IdAuthenticationBusinessException { - return idInfoHelper.getEntityInfoAsString(DemoMatchType.NAME, language, idInfo); - - } - - /** - * Validate the number of request for OTP generation. Limit for the number of - * request for OTP is should not exceed 3 in 60sec. - * - * @return true, if is otp flooded - * @throws IdAuthenticationBusinessException - */ - private boolean isOtpFlooded(String token, String requestTime) throws IdAuthenticationBusinessException { - boolean isOtpFlooded = false; - LocalDateTime reqTime; - try { - String strUTCDate = DateUtils.getUTCTimeFromDate( - DateUtils.parseToDate(requestTime, EnvUtil.getDateTimePattern())); - reqTime = LocalDateTime.parse(strUTCDate, - DateTimeFormatter.ofPattern(EnvUtil.getDateTimePattern())); - - } catch (ParseException e) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), e.getClass().getName(), - e.getMessage()); - throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS, e); - } - int addMinutes = EnvUtil.getOtpRequestFloodingDuration(); - LocalDateTime addMinutesInOtpRequestDTimes = reqTime.minus(addMinutes, ChronoUnit.MINUTES); - int maxCount = EnvUtil.getOtpRequestFloodingMaxCount(); - if (autntxnrepository.countRequestDTime(reqTime, addMinutesInOtpRequestDTimes, token) > maxCount) { - isOtpFlooded = true; - } - return isOtpFlooded; - } - - private void processChannel(String value, String phone, String email, MaskedResponseDTO maskedResponseDTO) throws IdAuthenticationBusinessException { - if (value.equalsIgnoreCase(NotificationType.SMS.getChannel())) { - if(phone != null && !phone.isEmpty()) { - maskedResponseDTO.setMaskedMobile(MaskUtil.maskMobile(phone)); - } else { - mosipLogger.warn("Phone Number is not available in identity data. But PHONE channel is requested for OTP."); - } - } else if (value.equalsIgnoreCase(NotificationType.EMAIL.getChannel())) { - if(email != null && !email.isEmpty()) { - maskedResponseDTO.setMaskedEmail(MaskUtil.maskEmail(email)); - } else { - mosipLogger.warn("Email ID is not available in identity data. But email channel is requested for OTP."); - } - } - - } - - /** - * Get Mail. - * - * @param idInfo List of IdentityInfoDTO - * @return mail - * @throws IdAuthenticationBusinessException - */ - private String getEmail(Map> idInfo) throws IdAuthenticationBusinessException { - return idInfoHelper.getEntityInfoAsString(DemoMatchType.EMAIL, idInfo); - } - - /** - * Get Mobile number. - * - * @param idInfo List of IdentityInfoDTO - * @return Mobile number - * @throws IdAuthenticationBusinessException - */ - private String getPhoneNumber(Map> idInfo) throws IdAuthenticationBusinessException { - return idInfoHelper.getEntityInfoAsString(DemoMatchType.PHONE, idInfo); - } - - /** - * This method gets the template languages in following order. - * 1. Gets user preferred languages if not - * 2. Gets default template languages from configuration if not - * 3. Gets the data capture languages - * @param idInfo - * @return - * @throws IdAuthenticationBusinessException - */ - private List getTemplateLanguages(Map> idInfo) - throws IdAuthenticationBusinessException { - List userPreferredLangs = idInfoFetcher.getUserPreferredLanguages(idInfo); - List defaultTemplateLanguges = userPreferredLangs.isEmpty() - ? idInfoFetcher.getTemplatesDefaultLanguageCodes() - : userPreferredLangs; - if (defaultTemplateLanguges.isEmpty()) { - List dataCaptureLanguages = idInfoHelper.getDataCapturedLanguages(DemoMatchType.NAME, idInfo); - Collections.sort(dataCaptureLanguages, languageComparator); - return dataCaptureLanguages; - } - - return defaultTemplateLanguges; - - } +package io.mosip.authentication.common.service.impl; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import io.mosip.authentication.authfilter.exception.IdAuthenticationFilterException; +import io.mosip.authentication.common.service.entity.AuthtypeLock; +import io.mosip.authentication.common.service.repository.AuthLockRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import io.mosip.authentication.common.manager.IdAuthFraudAnalysisEventManager; +import io.mosip.authentication.common.service.builder.AuthTransactionBuilder; +import io.mosip.authentication.common.service.entity.AutnTxn; +import io.mosip.authentication.common.service.helper.IdInfoHelper; +import io.mosip.authentication.common.service.impl.match.DemoMatchType; +import io.mosip.authentication.common.service.integration.OTPManager; +import io.mosip.authentication.common.service.integration.TokenIdManager; +import io.mosip.authentication.common.service.repository.AutnTxnRepository; +import io.mosip.authentication.common.service.repository.IdaUinHashSaltRepo; +import io.mosip.authentication.common.service.transaction.manager.IdAuthSecurityManager; +import io.mosip.authentication.common.service.util.EnvUtil; +import io.mosip.authentication.common.service.util.IdaRequestResponsConsumerUtil; +import io.mosip.authentication.core.constant.IdAuthCommonConstants; +import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants; +import io.mosip.authentication.core.constant.RequestType; +import io.mosip.authentication.core.dto.ObjectWithMetadata; +import io.mosip.authentication.core.exception.IDDataValidationException; +import io.mosip.authentication.core.exception.IdAuthenticationBusinessException; +import io.mosip.authentication.core.indauth.dto.IdType; +import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; +import io.mosip.authentication.core.indauth.dto.NotificationType; +import io.mosip.authentication.core.logger.IdaLogger; +import io.mosip.authentication.core.otp.dto.MaskedResponseDTO; +import io.mosip.authentication.core.otp.dto.OtpRequestDTO; +import io.mosip.authentication.core.otp.dto.OtpResponseDTO; +import io.mosip.authentication.core.partner.dto.PartnerDTO; +import io.mosip.authentication.core.spi.id.service.IdService; +import io.mosip.authentication.core.spi.indauth.match.IdInfoFetcher; +import io.mosip.authentication.core.spi.otp.service.OTPService; +import io.mosip.authentication.core.spi.partner.service.PartnerService; +import io.mosip.authentication.core.util.LanguageComparator; +import io.mosip.authentication.core.util.MaskUtil; +import io.mosip.kernel.core.exception.ParseException; +import io.mosip.kernel.core.logger.spi.Logger; +import io.mosip.kernel.core.util.DateUtils; +import io.mosip.kernel.core.util.StringUtils; + +/** + * Service implementation of OtpTriggerService. + * + * @author Rakesh Roshan + * @author Dinesh Karuppiah.T + */ +@Service +public class OTPServiceImpl implements OTPService { + + /** The Constant NAME. */ + private static final String NAME = "name"; + private static final String OTP = "otp"; + private static final String PHONE = "PHONE"; + private static final String EMAIL = "EMAIL"; + private static final String OTP_SMS = "otp-sms"; + private static final String OTP_EMAIL = "otp-email"; + + + /** The id auth service. */ + @Autowired + private IdService idAuthService; + + /** The autntxnrepository. */ + @Autowired + private AutnTxnRepository autntxnrepository; + + /** The auth lock repository. */ + @Autowired + AuthLockRepository authLockRepository; + + /** The env. */ + @Autowired + private EnvUtil env; + + @Autowired + private IdInfoHelper idInfoHelper; + + @Autowired + private IdInfoFetcher idInfoFetcher; + + /** The otp manager. */ + @Autowired + private OTPManager otpManager; + + /** The TokenId manager */ + @Autowired + private TokenIdManager tokenIdManager; + + @Autowired + private IdaUinHashSaltRepo uinHashSaltRepo; + + @Autowired + private IdAuthSecurityManager securityManager; + + @Autowired + private PartnerService partnerService; + + @Autowired + private IdAuthFraudAnalysisEventManager fraudEventManager; + + @Autowired + @Qualifier("NotificationLangComparator") + private LanguageComparator languageComparator; + + /** The mosip logger. */ + private static Logger mosipLogger = IdaLogger.getLogger(OTPServiceImpl.class); + + /** + * Generate OTP, store the OTP request details for success/failure. And send OTP + * notification by sms(on mobile)/mail(on email-id). + * + * @param otpRequestDto the otp request dto + * @return otpResponseDTO + * @throws IdAuthenticationBusinessException the id authentication business + * exception + */ + @Override + public OtpResponseDTO generateOtp(OtpRequestDTO otpRequestDto, String partnerId, ObjectWithMetadata requestWithMetadata) + throws IdAuthenticationBusinessException { + boolean isInternal = partnerId != null && partnerId.equalsIgnoreCase(IdAuthCommonConstants.INTERNAL); + boolean status; + String token = null; + try { + String individualIdType = IdType.getIDTypeStrOrDefault(otpRequestDto.getIndividualIdType()); + String individualId = otpRequestDto.getIndividualId(); + + Map idResDTO = idAuthService.processIdType(individualIdType, individualId, false, false, + idInfoHelper.getDefaultFilterAttributes()); + + token = idAuthService.getToken(idResDTO); + + validateAllowedOtpChannles(token, otpRequestDto.getOtpChannel()); + + OtpResponseDTO otpResponseDTO = doGenerateOTP(otpRequestDto, partnerId, isInternal, token, individualIdType, idResDTO); + IdaRequestResponsConsumerUtil.setIdVersionToResponse(requestWithMetadata, otpResponseDTO); + + status = otpResponseDTO.getErrors() == null || otpResponseDTO.getErrors().isEmpty(); + saveToTxnTable(otpRequestDto, isInternal, status, partnerId, token, otpResponseDTO, requestWithMetadata); + + return otpResponseDTO; + + } catch(IdAuthenticationBusinessException e) { + status = false; + //FIXME check if for this condition auth transaction is stored, then remove below code + //saveToTxnTable(otpRequestDto, isInternal, status, partnerId, token, null, null); + throw e; + } + + + } + + private void validateAllowedOtpChannles(String token, List otpChannel) throws IdAuthenticationFilterException { + + if(containsChannel(otpChannel, OTP)) { + checkAuthLock(token, OTP); + } + else if(containsChannel(otpChannel, PHONE)) { + checkAuthLock(token, OTP_SMS); + } + else if(containsChannel(otpChannel, EMAIL)) { + checkAuthLock(token, OTP_EMAIL); + } + } + + private static boolean containsChannel(List otpChannel, String channel) { + return otpChannel.stream().anyMatch(channelItem -> channel.equalsIgnoreCase(channelItem)); + } + + private void checkAuthLock(String token, String authTypeCode) throws IdAuthenticationFilterException { + List authTypeLocks = authLockRepository.findByTokenAndAuthtypecode(token, authTypeCode); + for(AuthtypeLock authtypeLock : authTypeLocks) { + if(authtypeLock.getStatuscode().equalsIgnoreCase("true")){ + throw new IdAuthenticationFilterException( + IdAuthenticationErrorConstants.AUTH_TYPE_LOCKED.getErrorCode(), + String.format(IdAuthenticationErrorConstants.AUTH_TYPE_LOCKED.getErrorMessage(), + authTypeCode)); + } + } + } + + private void saveToTxnTable(OtpRequestDTO otpRequestDto, boolean isInternal, boolean status, String partnerId, String token, OtpResponseDTO otpResponseDTO, ObjectWithMetadata requestWithMetadata) + throws IdAuthenticationBusinessException { + if (token != null) { + boolean authTokenRequired = !isInternal + && EnvUtil.getAuthTokenRequired(); + String authTokenId = authTokenRequired ? tokenIdManager.generateTokenId(token, partnerId) : null; + saveTxn(otpRequestDto, token, authTokenId, status, partnerId, isInternal, otpResponseDTO, requestWithMetadata); + } + } + + private OtpResponseDTO doGenerateOTP(OtpRequestDTO otpRequestDto, String partnerId, boolean isInternal, String token, String individualIdType, Map idResDTO) + throws IdAuthenticationBusinessException, IDDataValidationException { + String individualId = otpRequestDto.getIndividualId(); + String requestTime = otpRequestDto.getRequestTime(); + OtpResponseDTO otpResponseDTO = new OtpResponseDTO(); + + if (isOtpFlooded(token, requestTime)) { + throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.OTP_REQUEST_FLOODED); + } else { + String transactionId = otpRequestDto.getTransactionID(); + otpResponseDTO.setId(otpRequestDto.getId()); + otpResponseDTO.setTransactionID(transactionId); + + Map> idInfo = IdInfoFetcher.getIdInfo(idResDTO); + Map valueMap = new HashMap<>(); + + List templateLanguages = getTemplateLanguages(idInfo); + for (String lang : templateLanguages) { + valueMap.put(NAME + "_" + lang, getName(lang, idInfo)); + } + + String email = getEmail(idInfo); + String phoneNumber = getPhoneNumber(idInfo); + valueMap.put(IdAuthCommonConstants.PHONE_NUMBER, phoneNumber); + valueMap.put(IdAuthCommonConstants.EMAIL, email); + + List otpChannel = otpRequestDto.getOtpChannel(); + if (StringUtils.isBlank(phoneNumber) && containsChannel(otpChannel, PHONE) && !containsChannel(otpChannel, EMAIL)) { + throw new IdAuthenticationBusinessException( + IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorCode(), + IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorMessage() + + ". Phone Number is not found in identity data."); + } + + if (StringUtils.isBlank(email) && containsChannel(otpChannel, EMAIL) && !containsChannel(otpChannel, PHONE)) { + throw new IdAuthenticationBusinessException( + IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorCode(), + IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorMessage() + + ". Email ID is not found in identity data."); + } + + if(StringUtils.isBlank(phoneNumber) && StringUtils.isBlank(email) && (containsChannel(otpChannel, PHONE) && containsChannel(otpChannel, EMAIL))) { + throw new IdAuthenticationBusinessException( + IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorCode(), + IdAuthenticationErrorConstants.OTP_GENERATION_FAILED.getErrorMessage() + + ". Both Phone Number and Email ID are not found in identity data."); + } + + boolean isOtpGenerated = otpManager.sendOtp(otpRequestDto, individualId, individualIdType, valueMap, + templateLanguages); + + if (isOtpGenerated) { + otpResponseDTO.setErrors(null); + String responseTime = IdaRequestResponsConsumerUtil.getResponseTime(otpRequestDto.getRequestTime(), + EnvUtil.getDateTimePattern()); + otpResponseDTO.setResponseTime(responseTime); + MaskedResponseDTO maskedResponseDTO = new MaskedResponseDTO(); + List otpChannels = otpRequestDto.getOtpChannel(); + for (String channel : otpChannels) { + processChannel(channel, phoneNumber, email, maskedResponseDTO); + } + otpResponseDTO.setResponse(maskedResponseDTO); + + mosipLogger.info(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), this.getClass().getName(), + " is OTP generated: " + isOtpGenerated); + } else { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), + this.getClass().getName(), "OTP Generation failed"); + throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.OTP_GENERATION_FAILED); + } + + } + return otpResponseDTO; + } + + /** + * Audit txn. + * + * @param otpRequestDto the otp request dto + * @param token the uin + * @param authTokenId the auth token id + * @param status the status + * @param otpResponseDTO + * @param requestWithMetadata + * @throws IdAuthenticationBusinessException the id authentication business + * exception + */ + private void saveTxn(OtpRequestDTO otpRequestDto, String token, String authTokenId, boolean status, String partnerId, boolean isInternal, OtpResponseDTO otpResponseDTO, ObjectWithMetadata requestWithMetadata) + throws IdAuthenticationBusinessException { + Optional partner = isInternal ? Optional.empty() : partnerService.getPartner(partnerId, otpRequestDto.getMetadata()); + AutnTxn authTxn = AuthTransactionBuilder.newInstance() + .withRequest(otpRequestDto) + .addRequestType(RequestType.OTP_REQUEST) + .withAuthToken(authTokenId) + .withStatus(status) + .withToken(token) + .withPartner(partner) + .withInternal(isInternal) + .build(env,uinHashSaltRepo,securityManager); + fraudEventManager.analyseEvent(authTxn); + if(requestWithMetadata != null) { + requestWithMetadata.setMetadata(Map.of(AutnTxn.class.getSimpleName(), authTxn)); + } else { + idAuthService.saveAutnTxn(authTxn); + } + } + + private String getName(String language, Map> idInfo) + throws IdAuthenticationBusinessException { + return idInfoHelper.getEntityInfoAsString(DemoMatchType.NAME, language, idInfo); + + } + + /** + * Validate the number of request for OTP generation. Limit for the number of + * request for OTP is should not exceed 3 in 60sec. + * + * @return true, if is otp flooded + * @throws IdAuthenticationBusinessException + */ + private boolean isOtpFlooded(String token, String requestTime) throws IdAuthenticationBusinessException { + boolean isOtpFlooded = false; + LocalDateTime reqTime; + try { + String strUTCDate = DateUtils.getUTCTimeFromDate( + DateUtils.parseToDate(requestTime, EnvUtil.getDateTimePattern())); + reqTime = LocalDateTime.parse(strUTCDate, + DateTimeFormatter.ofPattern(EnvUtil.getDateTimePattern())); + + } catch (ParseException e) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), e.getClass().getName(), + e.getMessage()); + throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS, e); + } + int addMinutes = EnvUtil.getOtpRequestFloodingDuration(); + LocalDateTime addMinutesInOtpRequestDTimes = reqTime.minus(addMinutes, ChronoUnit.MINUTES); + int maxCount = EnvUtil.getOtpRequestFloodingMaxCount(); + if (autntxnrepository.countRequestDTime(reqTime, addMinutesInOtpRequestDTimes, token) >= maxCount) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getName(), this.getClass().getName(), + " OTP requested Flooded: " + reqTime + "," + addMinutesInOtpRequestDTimes + "," + maxCount); + isOtpFlooded = true; + } + return isOtpFlooded; + } + + private void processChannel(String value, String phone, String email, MaskedResponseDTO maskedResponseDTO) throws IdAuthenticationBusinessException { + if (value.equalsIgnoreCase(NotificationType.SMS.getChannel())) { + if(phone != null && !phone.isEmpty()) { + maskedResponseDTO.setMaskedMobile(MaskUtil.maskMobile(phone)); + } else { + mosipLogger.warn("Phone Number is not available in identity data. But PHONE channel is requested for OTP."); + } + } else if (value.equalsIgnoreCase(NotificationType.EMAIL.getChannel())) { + if(email != null && !email.isEmpty()) { + maskedResponseDTO.setMaskedEmail(MaskUtil.maskEmail(email)); + } else { + mosipLogger.warn("Email ID is not available in identity data. But email channel is requested for OTP."); + } + } + + } + + /** + * Get Mail. + * + * @param idInfo List of IdentityInfoDTO + * @return mail + * @throws IdAuthenticationBusinessException + */ + private String getEmail(Map> idInfo) throws IdAuthenticationBusinessException { + return idInfoHelper.getEntityInfoAsString(DemoMatchType.EMAIL, idInfo); + } + + /** + * Get Mobile number. + * + * @param idInfo List of IdentityInfoDTO + * @return Mobile number + * @throws IdAuthenticationBusinessException + */ + private String getPhoneNumber(Map> idInfo) throws IdAuthenticationBusinessException { + return idInfoHelper.getEntityInfoAsString(DemoMatchType.PHONE, idInfo); + } + + /** + * This method gets the template languages in following order. + * 1. Gets user preferred languages if not + * 2. Gets default template languages from configuration if not + * 3. Gets the data capture languages + * @param idInfo + * @return + * @throws IdAuthenticationBusinessException + */ + private List getTemplateLanguages(Map> idInfo) + throws IdAuthenticationBusinessException { + List userPreferredLangs = idInfoFetcher.getUserPreferredLanguages(idInfo); + List defaultTemplateLanguges = userPreferredLangs.isEmpty() + ? idInfoFetcher.getTemplatesDefaultLanguageCodes() + : userPreferredLangs; + if (defaultTemplateLanguges.isEmpty()) { + List dataCaptureLanguages = idInfoHelper.getDataCapturedLanguages(DemoMatchType.NAME, idInfo); + Collections.sort(dataCaptureLanguages, languageComparator); + return dataCaptureLanguages; + } + + return defaultTemplateLanguges; + + } } \ No newline at end of file diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/notification/NotificationServiceImpl.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/notification/NotificationServiceImpl.java index d31efda3ff2..349a64e236c 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/notification/NotificationServiceImpl.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/notification/NotificationServiceImpl.java @@ -1,375 +1,378 @@ -package io.mosip.authentication.common.service.impl.notification; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; - -import io.mosip.authentication.common.service.helper.IdInfoHelper; -import io.mosip.authentication.common.service.impl.match.BioAuthType; -import io.mosip.authentication.common.service.impl.match.DemoAuthType; -import io.mosip.authentication.common.service.impl.match.DemoMatchType; -import io.mosip.authentication.common.service.impl.match.PinAuthType; -import io.mosip.authentication.common.service.integration.IdTemplateManager; -import io.mosip.authentication.common.service.integration.NotificationManager; -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.AuthRequestDTO; -import io.mosip.authentication.core.indauth.dto.AuthResponseDTO; -import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; -import io.mosip.authentication.core.indauth.dto.NotificationType; -import io.mosip.authentication.core.indauth.dto.SenderType; -import io.mosip.authentication.core.spi.indauth.match.AuthType; -import io.mosip.authentication.core.spi.indauth.match.IdInfoFetcher; -import io.mosip.authentication.core.spi.notification.service.NotificationService; -import io.mosip.authentication.core.util.LanguageComparator; -import io.mosip.authentication.core.util.MaskUtil; -import io.mosip.kernel.core.util.DateUtils; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuples; - -/*** - * - * Service class to notify users via SMS or Email notification. - * - * @author Dinesh Karuppiah.T - */ -@Service -public class NotificationServiceImpl implements NotificationService { - - /** The Constant AUTH_TYPE. */ - private static final String AUTH_TYPE = "authType"; - /** The Constant NAME. */ - private static final String NAME = "name"; - /** The Constant TIME. */ - private static final String TIME = "time"; - /** The Constant DATE. */ - private static final String DATE = "date"; - - /** The demo auth service. */ - @Autowired - private IdInfoHelper infoHelper; - - @Autowired - private IdInfoFetcher idInfoFetcher; - - /** ID Template manager */ - @Autowired - private IdTemplateManager idTemplateManager; - - @Autowired - private NotificationManager notificationManager; - - @Autowired - @Qualifier("NotificationLangComparator") - private LanguageComparator languageComparator; - - public void sendAuthNotification(AuthRequestDTO authRequestDTO, String idvid, AuthResponseDTO authResponseDTO, - Map> idInfo, boolean isAuth) throws IdAuthenticationBusinessException { - - Map values = new HashMap<>(); - List templateLanguages = getTemplateLanguages(idInfo); - - for (String lang : templateLanguages) { - values.put(NAME + "_" + lang, infoHelper.getEntityInfoAsString(DemoMatchType.NAME, lang, idInfo)); - } - Tuple2 dateAndTime = getDateAndTime(DateUtils.parseToLocalDateTime(authResponseDTO.getResponseTime())); - values.put(DATE, dateAndTime.getT1()); - values.put(TIME, dateAndTime.getT2()); - String maskedUin = ""; - String charCount = EnvUtil.getUinMaskingCharCount(); - if (charCount != null && !charCount.isEmpty()) { - maskedUin = MaskUtil.generateMaskValue(idvid, Integer.parseInt(charCount)); - } - values.put("idvid", maskedUin); - String idvidType = authRequestDTO.getIndividualIdType(); - values.put("idvidType", idvidType); - - // TODO add for all auth types - String authTypeStr = Stream - .of(Stream.of(DemoAuthType.values()), Stream.of(BioAuthType.values()), - Stream.of(PinAuthType.values())) - .flatMap(Function.identity()) - .filter(authType -> authType.isAuthTypeEnabled(authRequestDTO, idInfoFetcher)) - .peek(System.out::println) - .map(authType -> authType.getDisplayName(authRequestDTO, idInfoFetcher)).distinct().collect(Collectors.joining(",")); - values.put(AUTH_TYPE, authTypeStr); - if (authResponseDTO.getResponse().isAuthStatus()) { - values.put(IdAuthCommonConstants.STATUS, "Passed"); - } else { - values.put(IdAuthCommonConstants.STATUS, "Failed"); - } - - String phoneNumber = null; - String email = null; - phoneNumber = infoHelper.getEntityInfoAsString(DemoMatchType.PHONE, idInfo); - email = infoHelper.getEntityInfoAsString(DemoMatchType.EMAIL, idInfo); - String notificationType = null; - if (isAuth) { - notificationType = EnvUtil.getNotificationType(); - } else { - // For internal auth no notification is done - notificationType = NotificationType.NONE.getName(); - } - - sendNotification(values, email, phoneNumber, SenderType.AUTH, notificationType, templateLanguages); - } - - public void sendOTPNotification(String idvid, String idvidType, Map valueMap, - List templateLanguages, String otp, String notificationProperty, LocalDateTime otpGenerationTime) - throws IdAuthenticationBusinessException { - Map otpTemplateValues = getOtpTemplateValues(idvid, idvidType, valueMap, otpGenerationTime); - otpTemplateValues.put("otp", otp); - this.sendNotification(otpTemplateValues, valueMap.get(IdAuthCommonConstants.EMAIL), - valueMap.get(IdAuthCommonConstants.PHONE_NUMBER), SenderType.OTP, notificationProperty, - templateLanguages); - } - - /* - * Send Otp Notification - * - */ - private Map getOtpTemplateValues(String idvid, String idvidType, Map valueMap, - LocalDateTime otpGenerationTime) { - - Tuple2 dateAndTime = getDateAndTime(otpGenerationTime); - String date = dateAndTime.getT1(); - String time = dateAndTime.getT2(); - - String maskedUin = null; - Map values = new HashMap<>(); - String charCount = EnvUtil.getUinMaskingCharCount(); - if (charCount != null) { - maskedUin = MaskUtil.generateMaskValue(idvid, Integer.parseInt(charCount)); - } - values.put("idvid", maskedUin); - values.put("idvidType", idvidType); - Integer timeInSeconds = EnvUtil.getOtpExpiryTime(); - int timeInMinutes = (timeInSeconds % 3600) / 60; - values.put("validTime", String.valueOf(timeInMinutes)); - values.put(DATE, date); - values.put(TIME, time); - values.putAll(valueMap); - values.remove(IdAuthCommonConstants.PHONE_NUMBER); - values.remove(IdAuthCommonConstants.EMAIL); - return values; - } - - /** - * Gets the date and time. - * - * @param requestTime the request time - * @param pattern the pattern - * @return the date and time - */ - private Tuple2 getDateAndTime(LocalDateTime timestamp) { - ZonedDateTime dateTime = ZonedDateTime.of(timestamp, ZoneId.of("UTC")).withZoneSameInstant(getZone()); - String date = dateTime.format(DateTimeFormatter.ofPattern(EnvUtil.getNotificationDateFormat())); - String time = dateTime.format(DateTimeFormatter.ofPattern(EnvUtil.getNotificationTimeFormat())); - return Tuples.of(date, time); - } - - private ZoneId getZone() { - return ZoneId.of(EnvUtil.getNotificationTimeZone()); - } - - /** - * Method to Send Notification to the Individual via SMS / E-Mail - * - * @param notificationtype - specifies notification type - * @param values - list of values to send notification - * @param emailId - sender E-Mail ID - * @param phoneNumber - sender Phone Number - * @param sender - to specify the sender type - * @param notificationProperty - * @throws IdAuthenticationBusinessException - */ - - public void sendNotification(Map values, String emailId, String phoneNumber, SenderType sender, - String notificationProperty, List templateLanguages) throws IdAuthenticationBusinessException { - String notificationtypeconfig = notificationProperty; - String notificationMobileNo = phoneNumber; - Set notificationtype = new HashSet<>(); - - if (isNotNullorEmpty(notificationtypeconfig) - && !notificationtypeconfig.equalsIgnoreCase(NotificationType.NONE.getName())) { - if (notificationtypeconfig.contains("|")) { - String value[] = notificationtypeconfig.split("\\|"); - for (int i = 0; i < 2; i++) { - String nvalue = ""; - nvalue = value[i]; - processNotification(emailId, notificationMobileNo, notificationtype, nvalue); - } - } else { - processNotification(emailId, notificationMobileNo, notificationtype, notificationtypeconfig); - } - - } - - if (notificationtype.contains(NotificationType.SMS)) { - invokeSmsNotification(values, sender, notificationMobileNo, templateLanguages); - - } - if (notificationtype.contains(NotificationType.EMAIL)) { - invokeEmailNotification(values, emailId, sender, templateLanguages); - - } - - } - - /** - * Reads notification type from property and set the notification type - * - * @param emailId - email id of Individual - * @param phoneNumber - Phone Number of Individual - * @param notificationtype - Notification type - * @param notificationtypeconfig - Notification type from the configuration - */ - - private void processNotification(String emailId, String phoneNumber, Set notificationtype, - String notificationtypeconfig) { - String type = notificationtypeconfig; - if (type.equalsIgnoreCase(NotificationType.SMS.getName())) { - if (isNotNullorEmpty(phoneNumber)) { - notificationtype.add(NotificationType.SMS); - } else { - if (isNotNullorEmpty(emailId)) { - notificationtype.add(NotificationType.EMAIL); - } - } - } - - if (type.equalsIgnoreCase(NotificationType.EMAIL.getName())) { - if (isNotNullorEmpty(emailId)) { - notificationtype.add(NotificationType.EMAIL); - } else { - if (isNotNullorEmpty(phoneNumber)) { - notificationtype.add(NotificationType.SMS); - } - } - } - } - - private boolean isNotNullorEmpty(String value) { - return value != null && !value.isEmpty() && value.trim().length() > 0; - } - - /** - * To apply Templates for Email or SMS Notifications - * - * @param values - content for Template - * @param templateName - Template name to fetch - * @return - * @throws IdAuthenticationBusinessException - */ - private String applyTemplate(Map values, String templateName, List templateLanguages) - throws IdAuthenticationBusinessException { - try { - Objects.requireNonNull(templateName); - return idTemplateManager.applyTemplate(templateName, values, templateLanguages); - } catch (IOException e) { - // FIXME change the error code - throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS, e); - } - } - - /** - * Sms notification. - * - * @param values the values - * @param sender the sender - * @param contentTemplate the content template - * @param notificationMobileNo the notification mobile no - * @throws IdAuthenticationBusinessException the id authentication business - * exception - */ - private void invokeSmsNotification(Map values, SenderType sender, String notificationMobileNo, List templateLanguages) - throws IdAuthenticationBusinessException { - String authSmsTemplate = EnvUtil.getAuthSmsTemplate(); - String otpSmsTemplate = EnvUtil.getOtpSmsTemplate(); - String contentTemplate = ""; - if (sender == SenderType.AUTH && authSmsTemplate != null) { - contentTemplate = authSmsTemplate; - } else if (sender == SenderType.OTP && otpSmsTemplate != null) { - contentTemplate = otpSmsTemplate; - } - - String smsTemplate = applyTemplate(values, contentTemplate, templateLanguages); - notificationManager.sendSmsNotification(notificationMobileNo, smsTemplate); - } - - /** - * Email notification. - * - * @param values the values - * @param emailId the email id - * @param sender the sender - * @param contentTemplate the content template - * @param subjectTemplate the subject template - * @throws IdAuthenticationBusinessException the id authentication business - * exception - */ - private void invokeEmailNotification(Map values, String emailId, SenderType sender, List templateLanguages) - throws IdAuthenticationBusinessException { - String otpContentTemaplate = EnvUtil.getOtpContentTemplate(); - String authEmailSubjectTemplate = EnvUtil.getAuthEmailSubjectTemplate(); - String authEmailContentTemplate = EnvUtil.getAuthEmailContentTemplate(); - String otpSubjectTemplate = EnvUtil.getOtpSubjectTemplate(); - - String contentTemplate = ""; - String subjectTemplate = ""; - if (sender == SenderType.AUTH && authEmailSubjectTemplate != null && authEmailContentTemplate != null) { - subjectTemplate = authEmailSubjectTemplate; - contentTemplate = authEmailContentTemplate; - } else if (sender == SenderType.OTP && otpSubjectTemplate != null && otpContentTemaplate != null) { - subjectTemplate = otpSubjectTemplate; - contentTemplate = otpContentTemaplate; - } - - String mailSubject = applyTemplate(values, subjectTemplate, templateLanguages); - String mailContent = applyTemplate(values, contentTemplate, templateLanguages); - notificationManager.sendEmailNotification(emailId, mailSubject, mailContent); - } - - /** - * This method gets the template languages in following order. - * 1. Gets user preferred languages if not - * 2. Gets default template languages from configuration if not - * 3. Gets the data capture languages - * @param idInfo - * @return - * @throws IdAuthenticationBusinessException - */ - private List getTemplateLanguages(Map> idInfo) - throws IdAuthenticationBusinessException { - List userPreferredLangs = idInfoFetcher.getUserPreferredLanguages(idInfo); - List defaultTemplateLanguges = userPreferredLangs.isEmpty() - ? idInfoFetcher.getTemplatesDefaultLanguageCodes() - : userPreferredLangs; - if (defaultTemplateLanguges.isEmpty()) { - List dataCaptureLanguages = infoHelper.getDataCapturedLanguages(DemoMatchType.NAME, idInfo); - Collections.sort(dataCaptureLanguages, languageComparator); - return dataCaptureLanguages; - } - - return defaultTemplateLanguges; - - } +package io.mosip.authentication.common.service.impl.notification; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import io.mosip.authentication.common.service.helper.IdInfoHelper; +import io.mosip.authentication.common.service.impl.match.BioAuthType; +import io.mosip.authentication.common.service.impl.match.DemoAuthType; +import io.mosip.authentication.common.service.impl.match.DemoMatchType; +import io.mosip.authentication.common.service.impl.match.KeyBindedTokenAuthType; +import io.mosip.authentication.common.service.impl.match.PasswordAuthType; +import io.mosip.authentication.common.service.impl.match.PinAuthType; +import io.mosip.authentication.common.service.integration.IdTemplateManager; +import io.mosip.authentication.common.service.integration.NotificationManager; +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.AuthRequestDTO; +import io.mosip.authentication.core.indauth.dto.AuthResponseDTO; +import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; +import io.mosip.authentication.core.indauth.dto.NotificationType; +import io.mosip.authentication.core.indauth.dto.SenderType; +import io.mosip.authentication.core.spi.indauth.match.AuthType; +import io.mosip.authentication.core.spi.indauth.match.IdInfoFetcher; +import io.mosip.authentication.core.spi.notification.service.NotificationService; +import io.mosip.authentication.core.util.LanguageComparator; +import io.mosip.authentication.core.util.MaskUtil; +import io.mosip.kernel.core.util.DateUtils; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuples; + +/*** + * + * Service class to notify users via SMS or Email notification. + * + * @author Dinesh Karuppiah.T + */ +@Service +public class NotificationServiceImpl implements NotificationService { + + /** The Constant AUTH_TYPE. */ + private static final String AUTH_TYPE = "authType"; + /** The Constant NAME. */ + private static final String NAME = "name"; + /** The Constant TIME. */ + private static final String TIME = "time"; + /** The Constant DATE. */ + private static final String DATE = "date"; + + /** The demo auth service. */ + @Autowired + private IdInfoHelper infoHelper; + + @Autowired + private IdInfoFetcher idInfoFetcher; + + /** ID Template manager */ + @Autowired + private IdTemplateManager idTemplateManager; + + @Autowired + private NotificationManager notificationManager; + + @Autowired + @Qualifier("NotificationLangComparator") + private LanguageComparator languageComparator; + + public void sendAuthNotification(AuthRequestDTO authRequestDTO, String idvid, AuthResponseDTO authResponseDTO, + Map> idInfo, boolean isAuth) throws IdAuthenticationBusinessException { + + Map values = new HashMap<>(); + List templateLanguages = getTemplateLanguages(idInfo); + + for (String lang : templateLanguages) { + values.put(NAME + "_" + lang, infoHelper.getEntityInfoAsString(DemoMatchType.NAME, lang, idInfo)); + } + Tuple2 dateAndTime = getDateAndTime(DateUtils.parseToLocalDateTime(authResponseDTO.getResponseTime())); + values.put(DATE, dateAndTime.getT1()); + values.put(TIME, dateAndTime.getT2()); + String maskedUin = ""; + String charCount = EnvUtil.getUinMaskingCharCount(); + if (charCount != null && !charCount.isEmpty()) { + maskedUin = MaskUtil.generateMaskValue(idvid, Integer.parseInt(charCount)); + } + values.put("idvid", maskedUin); + String idvidType = authRequestDTO.getIndividualIdType(); + values.put("idvidType", idvidType); + + // TODO add for all auth types + String authTypeStr = Stream + .of(Stream.of(DemoAuthType.values()), Stream.of(BioAuthType.values()), + Stream.of(PinAuthType.values()), Stream.of(PasswordAuthType.values()), + Stream.of(KeyBindedTokenAuthType.values())) + .flatMap(Function.identity()) + .filter(authType -> authType.isAuthTypeEnabled(authRequestDTO, idInfoFetcher)) + .peek(System.out::println) + .map(authType -> authType.getDisplayName(authRequestDTO, idInfoFetcher)).distinct().collect(Collectors.joining(",")); + values.put(AUTH_TYPE, authTypeStr); + if (authResponseDTO.getResponse().isAuthStatus()) { + values.put(IdAuthCommonConstants.STATUS, "Passed"); + } else { + values.put(IdAuthCommonConstants.STATUS, "Failed"); + } + + String phoneNumber = null; + String email = null; + phoneNumber = infoHelper.getEntityInfoAsString(DemoMatchType.PHONE, idInfo); + email = infoHelper.getEntityInfoAsString(DemoMatchType.EMAIL, idInfo); + String notificationType = null; + if (isAuth) { + notificationType = EnvUtil.getNotificationType(); + } else { + // For internal auth no notification is done + notificationType = NotificationType.NONE.getName(); + } + + sendNotification(values, email, phoneNumber, SenderType.AUTH, notificationType, templateLanguages); + } + + public void sendOTPNotification(String idvid, String idvidType, Map valueMap, + List templateLanguages, String otp, String notificationProperty, LocalDateTime otpGenerationTime) + throws IdAuthenticationBusinessException { + Map otpTemplateValues = getOtpTemplateValues(idvid, idvidType, valueMap, otpGenerationTime); + otpTemplateValues.put("otp", otp); + this.sendNotification(otpTemplateValues, valueMap.get(IdAuthCommonConstants.EMAIL), + valueMap.get(IdAuthCommonConstants.PHONE_NUMBER), SenderType.OTP, notificationProperty, + templateLanguages); + } + + /* + * Send Otp Notification + * + */ + private Map getOtpTemplateValues(String idvid, String idvidType, Map valueMap, + LocalDateTime otpGenerationTime) { + + Tuple2 dateAndTime = getDateAndTime(otpGenerationTime); + String date = dateAndTime.getT1(); + String time = dateAndTime.getT2(); + + String maskedUin = null; + Map values = new HashMap<>(); + String charCount = EnvUtil.getUinMaskingCharCount(); + if (charCount != null) { + maskedUin = MaskUtil.generateMaskValue(idvid, Integer.parseInt(charCount)); + } + values.put("idvid", maskedUin); + values.put("idvidType", idvidType); + Integer timeInSeconds = EnvUtil.getOtpExpiryTime(); + int timeInMinutes = (timeInSeconds % 3600) / 60; + values.put("validTime", String.valueOf(timeInMinutes)); + values.put(DATE, date); + values.put(TIME, time); + values.putAll(valueMap); + values.remove(IdAuthCommonConstants.PHONE_NUMBER); + values.remove(IdAuthCommonConstants.EMAIL); + return values; + } + + /** + * Gets the date and time. + * + * @param requestTime the request time + * @param pattern the pattern + * @return the date and time + */ + private Tuple2 getDateAndTime(LocalDateTime timestamp) { + ZonedDateTime dateTime = ZonedDateTime.of(timestamp, ZoneId.of("UTC")).withZoneSameInstant(getZone()); + String date = dateTime.format(DateTimeFormatter.ofPattern(EnvUtil.getNotificationDateFormat())); + String time = dateTime.format(DateTimeFormatter.ofPattern(EnvUtil.getNotificationTimeFormat())); + return Tuples.of(date, time); + } + + private ZoneId getZone() { + return ZoneId.of(EnvUtil.getNotificationTimeZone()); + } + + /** + * Method to Send Notification to the Individual via SMS / E-Mail + * + * @param notificationtype - specifies notification type + * @param values - list of values to send notification + * @param emailId - sender E-Mail ID + * @param phoneNumber - sender Phone Number + * @param sender - to specify the sender type + * @param notificationProperty + * @throws IdAuthenticationBusinessException + */ + + public void sendNotification(Map values, String emailId, String phoneNumber, SenderType sender, + String notificationProperty, List templateLanguages) throws IdAuthenticationBusinessException { + String notificationtypeconfig = notificationProperty; + String notificationMobileNo = phoneNumber; + Set notificationtype = new HashSet<>(); + + if (isNotNullorEmpty(notificationtypeconfig) + && !notificationtypeconfig.equalsIgnoreCase(NotificationType.NONE.getName())) { + if (notificationtypeconfig.contains("|")) { + String value[] = notificationtypeconfig.split("\\|"); + for (int i = 0; i < 2; i++) { + String nvalue = ""; + nvalue = value[i]; + processNotification(emailId, notificationMobileNo, notificationtype, nvalue); + } + } else { + processNotification(emailId, notificationMobileNo, notificationtype, notificationtypeconfig); + } + + } + + if (notificationtype.contains(NotificationType.SMS)) { + invokeSmsNotification(values, sender, notificationMobileNo, templateLanguages); + + } + if (notificationtype.contains(NotificationType.EMAIL)) { + invokeEmailNotification(values, emailId, sender, templateLanguages); + + } + + } + + /** + * Reads notification type from property and set the notification type + * + * @param emailId - email id of Individual + * @param phoneNumber - Phone Number of Individual + * @param notificationtype - Notification type + * @param notificationtypeconfig - Notification type from the configuration + */ + + private void processNotification(String emailId, String phoneNumber, Set notificationtype, + String notificationtypeconfig) { + String type = notificationtypeconfig; + if (type.equalsIgnoreCase(NotificationType.SMS.getName())) { + if (isNotNullorEmpty(phoneNumber)) { + notificationtype.add(NotificationType.SMS); + } else { + if (isNotNullorEmpty(emailId)) { + notificationtype.add(NotificationType.EMAIL); + } + } + } + + if (type.equalsIgnoreCase(NotificationType.EMAIL.getName())) { + if (isNotNullorEmpty(emailId)) { + notificationtype.add(NotificationType.EMAIL); + } else { + if (isNotNullorEmpty(phoneNumber)) { + notificationtype.add(NotificationType.SMS); + } + } + } + } + + private boolean isNotNullorEmpty(String value) { + return value != null && !value.isEmpty() && value.trim().length() > 0; + } + + /** + * To apply Templates for Email or SMS Notifications + * + * @param values - content for Template + * @param templateName - Template name to fetch + * @return + * @throws IdAuthenticationBusinessException + */ + private String applyTemplate(Map values, String templateName, List templateLanguages) + throws IdAuthenticationBusinessException { + try { + Objects.requireNonNull(templateName); + return idTemplateManager.applyTemplate(templateName, values, templateLanguages); + } catch (IOException e) { + // FIXME change the error code + throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS, e); + } + } + + /** + * Sms notification. + * + * @param values the values + * @param sender the sender + * @param contentTemplate the content template + * @param notificationMobileNo the notification mobile no + * @throws IdAuthenticationBusinessException the id authentication business + * exception + */ + private void invokeSmsNotification(Map values, SenderType sender, String notificationMobileNo, List templateLanguages) + throws IdAuthenticationBusinessException { + String authSmsTemplate = EnvUtil.getAuthSmsTemplate(); + String otpSmsTemplate = EnvUtil.getOtpSmsTemplate(); + String contentTemplate = ""; + if (sender == SenderType.AUTH && authSmsTemplate != null) { + contentTemplate = authSmsTemplate; + } else if (sender == SenderType.OTP && otpSmsTemplate != null) { + contentTemplate = otpSmsTemplate; + } + + String smsTemplate = applyTemplate(values, contentTemplate, templateLanguages); + notificationManager.sendSmsNotification(notificationMobileNo, smsTemplate); + } + + /** + * Email notification. + * + * @param values the values + * @param emailId the email id + * @param sender the sender + * @param contentTemplate the content template + * @param subjectTemplate the subject template + * @throws IdAuthenticationBusinessException the id authentication business + * exception + */ + private void invokeEmailNotification(Map values, String emailId, SenderType sender, List templateLanguages) + throws IdAuthenticationBusinessException { + String otpContentTemaplate = EnvUtil.getOtpContentTemplate(); + String authEmailSubjectTemplate = EnvUtil.getAuthEmailSubjectTemplate(); + String authEmailContentTemplate = EnvUtil.getAuthEmailContentTemplate(); + String otpSubjectTemplate = EnvUtil.getOtpSubjectTemplate(); + + String contentTemplate = ""; + String subjectTemplate = ""; + if (sender == SenderType.AUTH && authEmailSubjectTemplate != null && authEmailContentTemplate != null) { + subjectTemplate = authEmailSubjectTemplate; + contentTemplate = authEmailContentTemplate; + } else if (sender == SenderType.OTP && otpSubjectTemplate != null && otpContentTemaplate != null) { + subjectTemplate = otpSubjectTemplate; + contentTemplate = otpContentTemaplate; + } + + String mailSubject = applyTemplate(values, subjectTemplate, templateLanguages); + String mailContent = applyTemplate(values, contentTemplate, templateLanguages); + notificationManager.sendEmailNotification(emailId, mailSubject, mailContent); + } + + /** + * This method gets the template languages in following order. + * 1. Gets user preferred languages if not + * 2. Gets default template languages from configuration if not + * 3. Gets the data capture languages + * @param idInfo + * @return + * @throws IdAuthenticationBusinessException + */ + private List getTemplateLanguages(Map> idInfo) + throws IdAuthenticationBusinessException { + List userPreferredLangs = idInfoFetcher.getUserPreferredLanguages(idInfo); + List defaultTemplateLanguges = userPreferredLangs.isEmpty() + ? idInfoFetcher.getTemplatesDefaultLanguageCodes() + : userPreferredLangs; + if (defaultTemplateLanguges.isEmpty()) { + List dataCaptureLanguages = infoHelper.getDataCapturedLanguages(DemoMatchType.NAME, idInfo); + Collections.sort(dataCaptureLanguages, languageComparator); + return dataCaptureLanguages; + } + + return defaultTemplateLanguges; + + } } \ No newline at end of file diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/util/KeyBindedTokenMatcherUtil.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/util/KeyBindedTokenMatcherUtil.java index cf3ff8f905c..41ee0afca45 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/util/KeyBindedTokenMatcherUtil.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/util/KeyBindedTokenMatcherUtil.java @@ -157,10 +157,11 @@ private boolean verifyWLAAsJWT(String individualId, JWT jwt, String certificateD return true; } catch (BadJOSEException | JOSEException e) { mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "", - "Failed to verify WLA token", e); - throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.ERROR_TOKEN_VERIFICATION.getErrorCode(), - String.format(IdAuthenticationErrorConstants.ERROR_TOKEN_VERIFICATION.getErrorMessage(), e.getMessage())); + "Failed to verify WLA token" + e.getMessage(), e); + /* throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.ERROR_TOKEN_VERIFICATION.getErrorCode(), + String.format(IdAuthenticationErrorConstants.ERROR_TOKEN_VERIFICATION.getErrorMessage())); */ } + return false; } private boolean isIatWithinAllowedTime(Date issuedDateTime) { diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/AuthRequestValidator.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/AuthRequestValidator.java index e3e801d7b4a..02b630f5cca 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/AuthRequestValidator.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/AuthRequestValidator.java @@ -1,653 +1,654 @@ -package io.mosip.authentication.common.service.validator; - -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.BIO_PATH; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.REQUEST; -import static io.mosip.authentication.core.constant.IdAuthCommonConstants.SESSION_ID; - -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import javax.annotation.PostConstruct; - -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; -import org.springframework.validation.Errors; - -import io.mosip.authentication.common.service.util.AuthTypeUtil; -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.indauth.dto.AuthRequestDTO; -import io.mosip.authentication.core.indauth.dto.BioIdentityInfoDTO; -import io.mosip.authentication.core.indauth.dto.DataDTO; -import io.mosip.authentication.core.indauth.dto.DigitalId; -import io.mosip.authentication.core.indauth.dto.RequestDTO; -import io.mosip.authentication.core.logger.IdaLogger; -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; - -/** - * - * This class validates the parameters for Authorization Request. The class - * {@code AuthRequestValidator} validates AuthRequestDTO - * - * @author Manoj SP - * @author Rakesh Roshan - * - */ -@Component -@Primary -public class AuthRequestValidator extends BaseAuthRequestValidator { - - private static final String DATE_TIME = "dateTime"; - - private static final String DATA_TIMESTAMP = "data/timestamp"; - - /** The Constant DIGITAL_ID. */ - private static final String DIGITAL_ID = "data/digitalId/"; - - /** The Constant FINGERPRINT_COUNT. */ - private static final int FINGERPRINT_COUNT = 10; - - /** The Constant REQUEST_REQUEST_TIME. */ - private static final String REQUEST_REQUEST_TIME = "request/timestamp"; - - /** The mosip logger. */ - private static Logger mosipLogger = IdaLogger.getLogger(AuthRequestValidator.class); - - /** - * Allowed environments - */ - private List allowedEnvironments; - - /** - * Allowed domainUris - */ - private List allowedDomainUris; - - @PostConstruct - public void initialize() { - allowedEnvironments = Arrays.stream(EnvUtil.getAllowedEnv().split((","))) - .map(String::trim).collect(Collectors.toList()); - allowedDomainUris = Arrays.stream(EnvUtil.getAllowedDomainUri().split((","))) - .map(String::trim).collect(Collectors.toList()); - } - - /** - * Supports. - * - * @param clazz the clazz - * @return true, if successful - */ - /* - * (non-Javadoc) - * - * @see io.mosip.authentication.service.impl.indauth.validator. - * BaseAuthRequestValidator#supports(java.lang.Class) - */ - @Override - public boolean supports(Class clazz) { - return AuthRequestDTO.class.equals(clazz); - } - - /** - * Validate. - * - * @param target the target - * @param errors the errors - */ - /* - * (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) { - - AuthRequestDTO authRequestDto = (AuthRequestDTO) target; - - if (authRequestDto != null) { - if (!errors.hasErrors()) { - validateConsentReq(authRequestDto.isConsentObtained(), errors); - } - - if (!errors.hasErrors()) { - validateReqTime(authRequestDto.getRequestTime(), errors, IdAuthCommonConstants.REQ_TIME); - // Validation for Time Stamp in the RequestDTO. - validateReqTime(authRequestDto.getRequest().getTimestamp(), errors, REQUEST_REQUEST_TIME); - } - - if (!errors.hasErrors()) { - validateDomainURI(authRequestDto, errors); - } - - if (!errors.hasErrors()) { - validateEnv(authRequestDto, errors); - } - - if (!errors.hasErrors()) { - validateTxnId(authRequestDto.getTransactionID(), errors, IdAuthCommonConstants.TRANSACTION_ID); - } - if (!errors.hasErrors()) { - validateAllowedAuthTypes(authRequestDto, errors); - } - - if (!errors.hasErrors()) { - validateBiometrics(authRequestDto.getRequest().getBiometrics(), authRequestDto.getTransactionID(), errors); - } - - if (!errors.hasErrors()) { - super.validate(target, errors); - - if (!errors.hasErrors()) { - checkAuthRequest(authRequestDto, errors); - } - } - - if (!errors.hasErrors()) { - validateAuthType(authRequestDto, errors); - } - - } else { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), IdAuthCommonConstants.VALIDATE, - IdAuthCommonConstants.INVALID_INPUT_PARAMETER + REQUEST); - errors.rejectValue(REQUEST ,IdAuthenticationErrorConstants.UNABLE_TO_PROCESS.getErrorCode(), - IdAuthenticationErrorConstants.UNABLE_TO_PROCESS.getErrorMessage()); - } - } - - /** - * Validate biometric timestamps. - * - * @param biometrics the biometrics - * @param authTxnId - * @param errors the errors - */ - protected void validateBiometrics(List biometrics, String authTxnId, Errors errors) { - if (biometrics != null) { - for (int i = 0; i < biometrics.size(); i++) { - BioIdentityInfoDTO bioIdentityInfoDTO = biometrics.get(i); - if (bioIdentityInfoDTO.getData() == null) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, i, IdAuthCommonConstants.DATA) }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } else { - validateBioTxnId(authTxnId, errors, i, bioIdentityInfoDTO.getData().getTransactionId()); - validateBiometricTimestampAndDigitalIdTimestamp(biometrics.size() - 1, errors, i, - bioIdentityInfoDTO.getData()); - validateSuccessiveBioSegmentTimestamp(biometrics, errors, i, bioIdentityInfoDTO); - } - } - } - } - - private void validateSuccessiveBioSegmentTimestamp(List biometrics, Errors errors, int index, - BioIdentityInfoDTO bioIdentityInfoDTO) { - if (!errors.hasErrors() && index != 0) { - LocalDateTime currentIndexDateTime = DateUtils.parseDateToLocalDateTime( - this.biometricTimestampParser(bioIdentityInfoDTO.getData().getTimestamp())); - LocalDateTime previousIndexDateTime = DateUtils.parseDateToLocalDateTime( - this.biometricTimestampParser((biometrics.get(index - 1).getData().getTimestamp()))); - long bioTimestampDiffInSeconds = Duration.between(previousIndexDateTime, currentIndexDateTime).toSeconds(); - - Long allowedTimeDiffInSeconds = EnvUtil.getBioSegmentTimeDiffAllowed(); - if (bioTimestampDiffInSeconds < 0 || bioTimestampDiffInSeconds > allowedTimeDiffInSeconds) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - IdAuthenticationErrorConstants.INVALID_BIO_TIMESTAMP); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_BIO_TIMESTAMP.getErrorCode(), new Object[] { allowedTimeDiffInSeconds }, - IdAuthenticationErrorConstants.INVALID_BIO_TIMESTAMP.getErrorMessage()); - } - validateSuccessiveDigitalIdTimestamp(biometrics, errors, index, bioIdentityInfoDTO, allowedTimeDiffInSeconds); - } - } - - protected void validateSuccessiveDigitalIdTimestamp(List biometrics, Errors errors, int index, - BioIdentityInfoDTO bioIdentityInfoDTO, Long allowedTimeDiffInSeconds) { - LocalDateTime currentIndexDateTime = DateUtils.parseDateToLocalDateTime( - this.biometricTimestampParser(bioIdentityInfoDTO.getData().getDigitalId().getDateTime())); - LocalDateTime previousIndexDateTime = DateUtils.parseDateToLocalDateTime( - this.biometricTimestampParser(biometrics.get(index - 1).getData().getDigitalId().getDateTime())); - long digitalIdTimestampDiffInSeconds = Duration.between(previousIndexDateTime, currentIndexDateTime).toSeconds(); - if (digitalIdTimestampDiffInSeconds < 0 || digitalIdTimestampDiffInSeconds > allowedTimeDiffInSeconds) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - IdAuthenticationErrorConstants.INVALID_BIO_DIGITALID_TIMESTAMP); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_BIO_DIGITALID_TIMESTAMP.getErrorCode(), new Object[] { allowedTimeDiffInSeconds }, - IdAuthenticationErrorConstants.INVALID_BIO_DIGITALID_TIMESTAMP.getErrorMessage()); - } - } - - private void validateBioTxnId(String authTxnId, Errors errors, int index, String bioTxnId) { - // authTxnId validation is already done at this point - if (Objects.isNull(bioTxnId)) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, IdAuthCommonConstants.BIO_TXN_ID_PATH) }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - - } else - if(!authTxnId.contentEquals(bioTxnId)) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, IdAuthCommonConstants.BIO_TXN_ID_PATH) }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - - private void validateBiometricTimestampAndDigitalIdTimestamp(int biometricSize, Errors errors, int index, - DataDTO dataDTO) { - - String paramName = String.format(BIO_PATH, index, DATA_TIMESTAMP); - if (index == biometricSize) { - // validating future datetime check and other checks on last segment of bio and - // digitalId - validateReqTime(dataDTO.getTimestamp(), errors, paramName, this::biometricTimestampParser); - - if (!errors.hasErrors()) { - validateDigitalIdTimestamp(dataDTO.getDigitalId(), errors, String.format(BIO_PATH, index, DIGITAL_ID)); - } - } else { - // validating null check on bio timestamps and digitialId timestamps except last - // segment - nullCheckOnBioTimestampAndDigitalIdTimestamp(errors, index, dataDTO, paramName); - } - } - - private void nullCheckOnBioTimestampAndDigitalIdTimestamp(Errors errors, int i, DataDTO dataDTO, String paramName) { - if (StringUtils.isEmpty(dataDTO.getTimestamp())) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, - MISSING_INPUT_PARAMETER + paramName); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), new Object[] { paramName }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - // null check only on digitalId and digitalId timestamp - nullCheckDigitalIdAndTimestamp(dataDTO.getDigitalId(), errors, String.format(BIO_PATH, i, DIGITAL_ID)); - } - - /** - * Validate digital id timestamp. - * - * @param digitalId the digital id - * @param errors the errors - * @param field the field - */ - protected void validateDigitalIdTimestamp(DigitalId digitalId, Errors errors, String field) { - final String dateTimeField = field + DATE_TIME; - if (nullCheckDigitalIdAndTimestamp(digitalId, errors, field)) { - validateReqTime(digitalId.getDateTime(), errors, dateTimeField, this::biometricTimestampParser); - } - - } - - protected boolean nullCheckDigitalIdAndTimestamp(DigitalId digitalId, Errors errors, String field) { - if (digitalId != null) { - if (digitalId.getDateTime() == null) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), new Object[] { field + DATE_TIME }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - return false; - } - } else { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), new Object[] { field }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - return false; - } - return true; - } - - /** - * Validate domain URI and env. - * - * @param authRequestDto the auth request dto - * @param errors the errors - */ - private void validateDomainURI(AuthRequestDTO authRequestDto, Errors errors) { - - // It is error if domain URI in request is not null but in biometrics it is null - if(authRequestDto.getDomainUri() != null) { - String nullBioDomainUris = ""; - if(authRequestDto.getRequest().getBiometrics() != null) { - nullBioDomainUris = IntStream.range(0, authRequestDto.getRequest().getBiometrics().size()) - .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) - && authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri() == null) - .mapToObj(String::valueOf).collect(Collectors.joining(",")); - } - - if (!nullBioDomainUris.isEmpty()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "bio domain uri is null"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), - "request/biometrics/" + nullBioDomainUris + "/data/domainUri")); - } - } - - // It is error if domain URI in biometrics is not null and null in the request - if (authRequestDto.getDomainUri() == null && (authRequestDto.getRequest().getBiometrics() != null && - authRequestDto.getRequest().getBiometrics().stream().filter(bio -> Objects.nonNull(bio.getData())) - .anyMatch(bio -> bio.getData().getDomainUri() != null))) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "request domainUri is null"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), - "request/domainUri")); - - } - - if(authRequestDto.getDomainUri() != null && authRequestDto.getRequest().getBiometrics() != null) { - // Both are not null and they both are not equal - String requestAndBioDomainUrisNotSame = IntStream - .range(0, authRequestDto.getRequest().getBiometrics().size()) - .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) - && authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri() != null - && !authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri() - .contentEquals(authRequestDto.getDomainUri())) - .mapToObj(String::valueOf).collect(Collectors.joining(",")); - if(!requestAndBioDomainUrisNotSame.isEmpty()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "request domainUri is no matching against bio domainUri"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INPUT_MISMATCH.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INPUT_MISMATCH.getErrorMessage(), - "request/biometrics/" + requestAndBioDomainUrisNotSame + "/data/domainUri", "request/domainUri")); - } - } - - if(authRequestDto.getRequest().getBiometrics() != null) { - // bio domain uri is not null and not matching with configurations - String notMatchingBioDomainsUris = IntStream.range(0, authRequestDto.getRequest().getBiometrics().size()) - .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) - && authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri() != null - && !isValuesContainsIgnoreCase(allowedDomainUris, - authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri())) - .mapToObj(String::valueOf).collect(Collectors.joining(",")); - if (!notMatchingBioDomainsUris.isEmpty()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "bio domain uri is not matching with configured domain uris"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), - "request/biometrics/" + notMatchingBioDomainsUris + "/data/domainUri")); - - } - } - - // request domain uri is not null and not matching with configurations - if (authRequestDto.getDomainUri() != null - && !isValuesContainsIgnoreCase(allowedDomainUris, authRequestDto.getDomainUri())) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, - "request domain uri is not matching with configured domain uris"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), - "request/domainUri")); - } - - } - - /** - * Validate domain URI and env. - * - * @param authRequestDto the auth request dto - * @param errors the errors - */ - private void validateEnv(AuthRequestDTO authRequestDto, Errors errors) { - - if(authRequestDto.getEnv() != null) { - String nullBioEnvUris = ""; - if(authRequestDto.getRequest().getBiometrics() != null) { - nullBioEnvUris = IntStream.range(0, authRequestDto.getRequest().getBiometrics().size()) - .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) - && authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv() == null) - .mapToObj(String::valueOf).collect(Collectors.joining(",")); - } - - if (!nullBioEnvUris.isEmpty()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "bio env is null"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), - "request/biometrics/" + nullBioEnvUris + "/data/env")); - } - } - - // It is error if env in biometrics is not null and null in the request - if (authRequestDto.getEnv() == null && (authRequestDto.getRequest().getBiometrics() != null && - authRequestDto.getRequest().getBiometrics().stream().filter(bio -> Objects.nonNull(bio.getData())) - .anyMatch(bio -> bio.getData().getEnv() != null))) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "request env is null"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), - "request/env")); - - } - - if(authRequestDto.getEnv() != null && authRequestDto.getRequest().getBiometrics() != null) { - // Both are not null and they both are not equal - String requestAndBioEnvNotSame = IntStream - .range(0, authRequestDto.getRequest().getBiometrics().size()) - .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) - && authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv() != null - && !authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv() - .contentEquals(authRequestDto.getEnv())) - .mapToObj(String::valueOf).collect(Collectors.joining(",")); - if(!requestAndBioEnvNotSame.isEmpty()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "request env is no matching against bio env"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INPUT_MISMATCH.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INPUT_MISMATCH.getErrorMessage(), - "request/biometrics/" + requestAndBioEnvNotSame + "/data/env", "request/env")); - } - } - - if(authRequestDto.getRequest().getBiometrics() != null) { - // bio env is not null and not matching with configurations - String notMatchingBioEnvss = IntStream.range(0, authRequestDto.getRequest().getBiometrics().size()) - .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) - && authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv() != null - && !isValuesContainsIgnoreCase(allowedEnvironments, - authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv())) - .mapToObj(String::valueOf).collect(Collectors.joining(",")); - if (!notMatchingBioEnvss.isEmpty()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "bio env is not matching with configured environments"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), - "request/biometrics/" + notMatchingBioEnvss + "/data/env")); - - } - } - - // request env is not null and not matching with configurations - if (authRequestDto.getEnv() != null - && !isValuesContainsIgnoreCase(allowedEnvironments, authRequestDto.getEnv())) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, - "request env is not matching with configured environments"); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), - "request/env")); - } - - } - - /** - * Validate req time. - * - * @param reqTime the req time - * @param errors the errors - * @param paramName the param name - */ - @Override - protected void validateReqTime(String reqTime, Errors errors, String paramName) { - super.validateReqTime(reqTime, errors, paramName); - if (!errors.hasErrors()) { - validateRequestTimedOut(reqTime, errors); - } - } - - /** - * 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) { - super.validateReqTime(reqTime, errors, paramName, dateTimeParser); - if (!errors.hasErrors()) { - validateRequestTimedOut(reqTime, errors, dateTimeParser, paramName); - } - } - - /** - * Check auth request. - * - * @param authRequest the auth request - * @param errors the errors - */ - private void checkAuthRequest(AuthRequestDTO authRequest, Errors errors) { - if (AuthTypeUtil.isDemo(authRequest)) { - checkDemoAuth(authRequest, errors); - } - } - - /** - * Gets the max finger count. - * - * @return the max finger count - */ - @Override - protected int getMaxFingerCount() { - return FINGERPRINT_COUNT; - } - - /** - * Validate device details. - * - * @param authRequest the auth request - * @param errors the errors - */ - public void validateDeviceDetails(AuthRequestDTO authRequest, Errors errors) { - List bioData = Optional.ofNullable(authRequest.getRequest()).map(RequestDTO::getBiometrics) - .map(List::stream).orElseGet(Stream::empty).map(BioIdentityInfoDTO::getData) - .collect(Collectors.toList()); - - IntStream.range(0, bioData.size()).forEach(index -> { - if (StringUtils.isEmpty(bioData.get(index).getDeviceCode())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, "deviceCode") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - if (StringUtils.isEmpty(bioData.get(index).getDeviceServiceVersion())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, "deviceServiceVersion") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - if (Objects.isNull(bioData.get(index).getDigitalId())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, "digitalId") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } else { - if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getSerialNo())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, "digitalId/serialNo") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getMake())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "make") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getModel())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "model") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getType())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "type") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getDeviceSubType())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "deviceSubType") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getDp())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "deviceProvider") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getDpId())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "deviceProviderId") }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - - } - }); - } - - /** - * Biometric timestamp parser. - * - * @param timestamp the timestamp - * @return the date - * @throws ParseException the parse exception - */ - private Date biometricTimestampParser(String timestamp) throws ParseException { - try { - // First try parsing with biometric timestamp format - return DateUtils.parseToDate(timestamp, EnvUtil.getBiometricDateTimePattern()); - } catch (ParseException e) { - mosipLogger.debug( - "error parsing timestamp with biomerics date time pattern: {}, so paring with request time pattern", - e.getMessage()); - // Try parsing with request time stamp format - return this.requestTimeParser(timestamp); - } - } - - /** - * Checks the list of Strings contains given string or not by ignoring the case - * - * @param values - * @param value - * @return - */ - private boolean isValuesContainsIgnoreCase(List values, String value) { - if (value != null) { - return values.stream().anyMatch(value::equalsIgnoreCase); - } - return false; - } +package io.mosip.authentication.common.service.validator; + +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.BIO_PATH; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.REQUEST; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.SESSION_ID; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import javax.annotation.PostConstruct; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; + +import io.mosip.authentication.common.service.util.AuthTypeUtil; +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.indauth.dto.AuthRequestDTO; +import io.mosip.authentication.core.indauth.dto.BioIdentityInfoDTO; +import io.mosip.authentication.core.indauth.dto.DataDTO; +import io.mosip.authentication.core.indauth.dto.DigitalId; +import io.mosip.authentication.core.indauth.dto.KycAuthRequestDTO; +import io.mosip.authentication.core.indauth.dto.RequestDTO; +import io.mosip.authentication.core.logger.IdaLogger; +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; + +/** + * + * This class validates the parameters for Authorization Request. The class + * {@code AuthRequestValidator} validates AuthRequestDTO + * + * @author Manoj SP + * @author Rakesh Roshan + * + */ +@Component +@Primary +public class AuthRequestValidator extends BaseAuthRequestValidator { + + private static final String DATE_TIME = "dateTime"; + + private static final String DATA_TIMESTAMP = "data/timestamp"; + + /** The Constant DIGITAL_ID. */ + private static final String DIGITAL_ID = "data/digitalId/"; + + /** The Constant FINGERPRINT_COUNT. */ + private static final int FINGERPRINT_COUNT = 10; + + /** The Constant REQUEST_REQUEST_TIME. */ + private static final String REQUEST_REQUEST_TIME = "request/timestamp"; + + /** The mosip logger. */ + private static Logger mosipLogger = IdaLogger.getLogger(AuthRequestValidator.class); + + /** + * Allowed environments + */ + private List allowedEnvironments; + + /** + * Allowed domainUris + */ + private List allowedDomainUris; + + @PostConstruct + public void initialize() { + allowedEnvironments = Arrays.stream(EnvUtil.getAllowedEnv().split((","))) + .map(String::trim).collect(Collectors.toList()); + allowedDomainUris = Arrays.stream(EnvUtil.getAllowedDomainUri().split((","))) + .map(String::trim).collect(Collectors.toList()); + } + + /** + * Supports. + * + * @param clazz the clazz + * @return true, if successful + */ + /* + * (non-Javadoc) + * + * @see io.mosip.authentication.service.impl.indauth.validator. + * BaseAuthRequestValidator#supports(java.lang.Class) + */ + @Override + public boolean supports(Class clazz) { + return AuthRequestDTO.class.equals(clazz) || KycAuthRequestDTO.class.equals(clazz); + } + + /** + * Validate. + * + * @param target the target + * @param errors the errors + */ + /* + * (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) { + + AuthRequestDTO authRequestDto = (AuthRequestDTO) target; + + if (authRequestDto != null) { + if (!errors.hasErrors()) { + validateConsentReq(authRequestDto.isConsentObtained(), errors); + } + + if (!errors.hasErrors()) { + validateReqTime(authRequestDto.getRequestTime(), errors, IdAuthCommonConstants.REQ_TIME); + // Validation for Time Stamp in the RequestDTO. + validateReqTime(authRequestDto.getRequest().getTimestamp(), errors, REQUEST_REQUEST_TIME); + } + + if (!errors.hasErrors()) { + validateDomainURI(authRequestDto, errors); + } + + if (!errors.hasErrors()) { + validateEnv(authRequestDto, errors); + } + + if (!errors.hasErrors()) { + validateTxnId(authRequestDto.getTransactionID(), errors, IdAuthCommonConstants.TRANSACTION_ID); + } + if (!errors.hasErrors()) { + validateAllowedAuthTypes(authRequestDto, errors); + } + + if (!errors.hasErrors()) { + validateBiometrics(authRequestDto.getRequest().getBiometrics(), authRequestDto.getTransactionID(), errors); + } + + if (!errors.hasErrors()) { + super.validate(target, errors); + + if (!errors.hasErrors()) { + checkAuthRequest(authRequestDto, errors); + } + } + + if (!errors.hasErrors()) { + validateAuthType(authRequestDto, errors); + } + + } else { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), IdAuthCommonConstants.VALIDATE, + IdAuthCommonConstants.INVALID_INPUT_PARAMETER + REQUEST); + errors.rejectValue(REQUEST ,IdAuthenticationErrorConstants.UNABLE_TO_PROCESS.getErrorCode(), + IdAuthenticationErrorConstants.UNABLE_TO_PROCESS.getErrorMessage()); + } + } + + /** + * Validate biometric timestamps. + * + * @param biometrics the biometrics + * @param authTxnId + * @param errors the errors + */ + protected void validateBiometrics(List biometrics, String authTxnId, Errors errors) { + if (biometrics != null) { + for (int i = 0; i < biometrics.size(); i++) { + BioIdentityInfoDTO bioIdentityInfoDTO = biometrics.get(i); + if (bioIdentityInfoDTO.getData() == null) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, i, IdAuthCommonConstants.DATA) }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } else { + validateBioTxnId(authTxnId, errors, i, bioIdentityInfoDTO.getData().getTransactionId()); + validateBiometricTimestampAndDigitalIdTimestamp(biometrics.size() - 1, errors, i, + bioIdentityInfoDTO.getData()); + validateSuccessiveBioSegmentTimestamp(biometrics, errors, i, bioIdentityInfoDTO); + } + } + } + } + + private void validateSuccessiveBioSegmentTimestamp(List biometrics, Errors errors, int index, + BioIdentityInfoDTO bioIdentityInfoDTO) { + if (!errors.hasErrors() && index != 0) { + LocalDateTime currentIndexDateTime = DateUtils.parseDateToLocalDateTime( + this.biometricTimestampParser(bioIdentityInfoDTO.getData().getTimestamp())); + LocalDateTime previousIndexDateTime = DateUtils.parseDateToLocalDateTime( + this.biometricTimestampParser((biometrics.get(index - 1).getData().getTimestamp()))); + long bioTimestampDiffInSeconds = Duration.between(previousIndexDateTime, currentIndexDateTime).toSeconds(); + + Long allowedTimeDiffInSeconds = EnvUtil.getBioSegmentTimeDiffAllowed(); + if (bioTimestampDiffInSeconds < 0 || bioTimestampDiffInSeconds > allowedTimeDiffInSeconds) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + IdAuthenticationErrorConstants.INVALID_BIO_TIMESTAMP); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_BIO_TIMESTAMP.getErrorCode(), new Object[] { allowedTimeDiffInSeconds }, + IdAuthenticationErrorConstants.INVALID_BIO_TIMESTAMP.getErrorMessage()); + } + validateSuccessiveDigitalIdTimestamp(biometrics, errors, index, bioIdentityInfoDTO, allowedTimeDiffInSeconds); + } + } + + protected void validateSuccessiveDigitalIdTimestamp(List biometrics, Errors errors, int index, + BioIdentityInfoDTO bioIdentityInfoDTO, Long allowedTimeDiffInSeconds) { + LocalDateTime currentIndexDateTime = DateUtils.parseDateToLocalDateTime( + this.biometricTimestampParser(bioIdentityInfoDTO.getData().getDigitalId().getDateTime())); + LocalDateTime previousIndexDateTime = DateUtils.parseDateToLocalDateTime( + this.biometricTimestampParser(biometrics.get(index - 1).getData().getDigitalId().getDateTime())); + long digitalIdTimestampDiffInSeconds = Duration.between(previousIndexDateTime, currentIndexDateTime).toSeconds(); + if (digitalIdTimestampDiffInSeconds < 0 || digitalIdTimestampDiffInSeconds > allowedTimeDiffInSeconds) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + IdAuthenticationErrorConstants.INVALID_BIO_DIGITALID_TIMESTAMP); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_BIO_DIGITALID_TIMESTAMP.getErrorCode(), new Object[] { allowedTimeDiffInSeconds }, + IdAuthenticationErrorConstants.INVALID_BIO_DIGITALID_TIMESTAMP.getErrorMessage()); + } + } + + private void validateBioTxnId(String authTxnId, Errors errors, int index, String bioTxnId) { + // authTxnId validation is already done at this point + if (Objects.isNull(bioTxnId)) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, IdAuthCommonConstants.BIO_TXN_ID_PATH) }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + + } else + if(!authTxnId.contentEquals(bioTxnId)) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, IdAuthCommonConstants.BIO_TXN_ID_PATH) }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + + private void validateBiometricTimestampAndDigitalIdTimestamp(int biometricSize, Errors errors, int index, + DataDTO dataDTO) { + + String paramName = String.format(BIO_PATH, index, DATA_TIMESTAMP); + if (index == biometricSize) { + // validating future datetime check and other checks on last segment of bio and + // digitalId + validateReqTime(dataDTO.getTimestamp(), errors, paramName, this::biometricTimestampParser); + + if (!errors.hasErrors()) { + validateDigitalIdTimestamp(dataDTO.getDigitalId(), errors, String.format(BIO_PATH, index, DIGITAL_ID)); + } + } else { + // validating null check on bio timestamps and digitialId timestamps except last + // segment + nullCheckOnBioTimestampAndDigitalIdTimestamp(errors, index, dataDTO, paramName); + } + } + + private void nullCheckOnBioTimestampAndDigitalIdTimestamp(Errors errors, int i, DataDTO dataDTO, String paramName) { + if (StringUtils.isEmpty(dataDTO.getTimestamp())) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), VALIDATE, + MISSING_INPUT_PARAMETER + paramName); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), new Object[] { paramName }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + // null check only on digitalId and digitalId timestamp + nullCheckDigitalIdAndTimestamp(dataDTO.getDigitalId(), errors, String.format(BIO_PATH, i, DIGITAL_ID)); + } + + /** + * Validate digital id timestamp. + * + * @param digitalId the digital id + * @param errors the errors + * @param field the field + */ + protected void validateDigitalIdTimestamp(DigitalId digitalId, Errors errors, String field) { + final String dateTimeField = field + DATE_TIME; + if (nullCheckDigitalIdAndTimestamp(digitalId, errors, field)) { + validateReqTime(digitalId.getDateTime(), errors, dateTimeField, this::biometricTimestampParser); + } + + } + + protected boolean nullCheckDigitalIdAndTimestamp(DigitalId digitalId, Errors errors, String field) { + if (digitalId != null) { + if (digitalId.getDateTime() == null) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), new Object[] { field + DATE_TIME }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + return false; + } + } else { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), new Object[] { field }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + return false; + } + return true; + } + + /** + * Validate domain URI and env. + * + * @param authRequestDto the auth request dto + * @param errors the errors + */ + private void validateDomainURI(AuthRequestDTO authRequestDto, Errors errors) { + + // It is error if domain URI in request is not null but in biometrics it is null + if(authRequestDto.getDomainUri() != null) { + String nullBioDomainUris = ""; + if(authRequestDto.getRequest().getBiometrics() != null) { + nullBioDomainUris = IntStream.range(0, authRequestDto.getRequest().getBiometrics().size()) + .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) + && authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri() == null) + .mapToObj(String::valueOf).collect(Collectors.joining(",")); + } + + if (!nullBioDomainUris.isEmpty()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "bio domain uri is null"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), + "request/biometrics/" + nullBioDomainUris + "/data/domainUri")); + } + } + + // It is error if domain URI in biometrics is not null and null in the request + if (authRequestDto.getDomainUri() == null && (authRequestDto.getRequest().getBiometrics() != null && + authRequestDto.getRequest().getBiometrics().stream().filter(bio -> Objects.nonNull(bio.getData())) + .anyMatch(bio -> bio.getData().getDomainUri() != null))) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "request domainUri is null"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), + "request/domainUri")); + + } + + if(authRequestDto.getDomainUri() != null && authRequestDto.getRequest().getBiometrics() != null) { + // Both are not null and they both are not equal + String requestAndBioDomainUrisNotSame = IntStream + .range(0, authRequestDto.getRequest().getBiometrics().size()) + .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) + && authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri() != null + && !authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri() + .contentEquals(authRequestDto.getDomainUri())) + .mapToObj(String::valueOf).collect(Collectors.joining(",")); + if(!requestAndBioDomainUrisNotSame.isEmpty()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "request domainUri is no matching against bio domainUri"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INPUT_MISMATCH.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INPUT_MISMATCH.getErrorMessage(), + "request/biometrics/" + requestAndBioDomainUrisNotSame + "/data/domainUri", "request/domainUri")); + } + } + + if(authRequestDto.getRequest().getBiometrics() != null) { + // bio domain uri is not null and not matching with configurations + String notMatchingBioDomainsUris = IntStream.range(0, authRequestDto.getRequest().getBiometrics().size()) + .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) + && authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri() != null + && !isValuesContainsIgnoreCase(allowedDomainUris, + authRequestDto.getRequest().getBiometrics().get(i).getData().getDomainUri())) + .mapToObj(String::valueOf).collect(Collectors.joining(",")); + if (!notMatchingBioDomainsUris.isEmpty()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "bio domain uri is not matching with configured domain uris"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), + "request/biometrics/" + notMatchingBioDomainsUris + "/data/domainUri")); + + } + } + + // request domain uri is not null and not matching with configurations + if (authRequestDto.getDomainUri() != null + && !isValuesContainsIgnoreCase(allowedDomainUris, authRequestDto.getDomainUri())) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, + "request domain uri is not matching with configured domain uris"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), + "request/domainUri")); + } + + } + + /** + * Validate domain URI and env. + * + * @param authRequestDto the auth request dto + * @param errors the errors + */ + private void validateEnv(AuthRequestDTO authRequestDto, Errors errors) { + + if(authRequestDto.getEnv() != null) { + String nullBioEnvUris = ""; + if(authRequestDto.getRequest().getBiometrics() != null) { + nullBioEnvUris = IntStream.range(0, authRequestDto.getRequest().getBiometrics().size()) + .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) + && authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv() == null) + .mapToObj(String::valueOf).collect(Collectors.joining(",")); + } + + if (!nullBioEnvUris.isEmpty()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "bio env is null"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), + "request/biometrics/" + nullBioEnvUris + "/data/env")); + } + } + + // It is error if env in biometrics is not null and null in the request + if (authRequestDto.getEnv() == null && (authRequestDto.getRequest().getBiometrics() != null && + authRequestDto.getRequest().getBiometrics().stream().filter(bio -> Objects.nonNull(bio.getData())) + .anyMatch(bio -> bio.getData().getEnv() != null))) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "request env is null"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), + "request/env")); + + } + + if(authRequestDto.getEnv() != null && authRequestDto.getRequest().getBiometrics() != null) { + // Both are not null and they both are not equal + String requestAndBioEnvNotSame = IntStream + .range(0, authRequestDto.getRequest().getBiometrics().size()) + .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) + && authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv() != null + && !authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv() + .contentEquals(authRequestDto.getEnv())) + .mapToObj(String::valueOf).collect(Collectors.joining(",")); + if(!requestAndBioEnvNotSame.isEmpty()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "request env is no matching against bio env"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INPUT_MISMATCH.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INPUT_MISMATCH.getErrorMessage(), + "request/biometrics/" + requestAndBioEnvNotSame + "/data/env", "request/env")); + } + } + + if(authRequestDto.getRequest().getBiometrics() != null) { + // bio env is not null and not matching with configurations + String notMatchingBioEnvss = IntStream.range(0, authRequestDto.getRequest().getBiometrics().size()) + .filter(i -> Objects.nonNull(authRequestDto.getRequest().getBiometrics().get(i).getData()) + && authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv() != null + && !isValuesContainsIgnoreCase(allowedEnvironments, + authRequestDto.getRequest().getBiometrics().get(i).getData().getEnv())) + .mapToObj(String::valueOf).collect(Collectors.joining(",")); + if (!notMatchingBioEnvss.isEmpty()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "bio env is not matching with configured environments"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), + "request/biometrics/" + notMatchingBioEnvss + "/data/env")); + + } + } + + // request env is not null and not matching with configurations + if (authRequestDto.getEnv() != null + && !isValuesContainsIgnoreCase(allowedEnvironments, authRequestDto.getEnv())) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, + "request env is not matching with configured environments"); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + String.format(IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage(), + "request/env")); + } + + } + + /** + * Validate req time. + * + * @param reqTime the req time + * @param errors the errors + * @param paramName the param name + */ + @Override + protected void validateReqTime(String reqTime, Errors errors, String paramName) { + super.validateReqTime(reqTime, errors, paramName); + if (!errors.hasErrors()) { + validateRequestTimedOut(reqTime, errors); + } + } + + /** + * 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) { + super.validateReqTime(reqTime, errors, paramName, dateTimeParser); + if (!errors.hasErrors()) { + validateRequestTimedOut(reqTime, errors, dateTimeParser, paramName); + } + } + + /** + * Check auth request. + * + * @param authRequest the auth request + * @param errors the errors + */ + private void checkAuthRequest(AuthRequestDTO authRequest, Errors errors) { + if (AuthTypeUtil.isDemo(authRequest)) { + checkDemoAuth(authRequest, errors); + } + } + + /** + * Gets the max finger count. + * + * @return the max finger count + */ + @Override + protected int getMaxFingerCount() { + return FINGERPRINT_COUNT; + } + + /** + * Validate device details. + * + * @param authRequest the auth request + * @param errors the errors + */ + public void validateDeviceDetails(AuthRequestDTO authRequest, Errors errors) { + List bioData = Optional.ofNullable(authRequest.getRequest()).map(RequestDTO::getBiometrics) + .map(List::stream).orElseGet(Stream::empty).map(BioIdentityInfoDTO::getData) + .collect(Collectors.toList()); + + IntStream.range(0, bioData.size()).forEach(index -> { + if (StringUtils.isEmpty(bioData.get(index).getDeviceCode())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, "deviceCode") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + if (StringUtils.isEmpty(bioData.get(index).getDeviceServiceVersion())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, "deviceServiceVersion") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + if (Objects.isNull(bioData.get(index).getDigitalId())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, "digitalId") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } else { + if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getSerialNo())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, "digitalId/serialNo") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getMake())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "make") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getModel())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "model") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getType())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "type") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getDeviceSubType())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "deviceSubType") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getDp())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "deviceProvider") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + if (StringUtils.isEmpty(bioData.get(index).getDigitalId().getDpId())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(BIO_PATH, index, DIGITAL_ID + "deviceProviderId") }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + + } + }); + } + + /** + * Biometric timestamp parser. + * + * @param timestamp the timestamp + * @return the date + * @throws ParseException the parse exception + */ + private Date biometricTimestampParser(String timestamp) throws ParseException { + try { + // First try parsing with biometric timestamp format + return DateUtils.parseToDate(timestamp, EnvUtil.getBiometricDateTimePattern()); + } catch (ParseException e) { + mosipLogger.debug( + "error parsing timestamp with biomerics date time pattern: {}, so paring with request time pattern", + e.getMessage()); + // Try parsing with request time stamp format + return this.requestTimeParser(timestamp); + } + } + + /** + * Checks the list of Strings contains given string or not by ignoring the case + * + * @param values + * @param value + * @return + */ + private boolean isValuesContainsIgnoreCase(List values, String value) { + if (value != null) { + return values.stream().anyMatch(value::equalsIgnoreCase); + } + return false; + } } \ No newline at end of file diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/BaseAuthRequestValidator.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/BaseAuthRequestValidator.java index 7d2078f7067..0296e90dc8b 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/BaseAuthRequestValidator.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/BaseAuthRequestValidator.java @@ -1,1081 +1,1166 @@ -package io.mosip.authentication.common.service.validator; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.OptionalInt; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.validation.Errors; - -import io.mosip.authentication.common.service.helper.IdInfoHelper; -import io.mosip.authentication.common.service.impl.match.BioAuthType; -import io.mosip.authentication.common.service.impl.match.BioMatchType; -import io.mosip.authentication.common.service.impl.match.DOBType; -import io.mosip.authentication.common.service.impl.match.DemoAuthType; -import io.mosip.authentication.common.service.impl.match.DemoMatchType; -import io.mosip.authentication.common.service.impl.match.PinMatchType; -import io.mosip.authentication.common.service.util.AuthTypeUtil; -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.indauth.dto.AuthRequestDTO; -import io.mosip.authentication.core.indauth.dto.BaseAuthRequestDTO; -import io.mosip.authentication.core.indauth.dto.BioIdentityInfoDTO; -import io.mosip.authentication.core.indauth.dto.DataDTO; -import io.mosip.authentication.core.indauth.dto.IdentityDTO; -import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; -import io.mosip.authentication.core.indauth.dto.RequestDTO; -import io.mosip.authentication.core.logger.IdaLogger; -import io.mosip.authentication.core.spi.indauth.match.AuthType; -import io.mosip.authentication.core.spi.indauth.match.IdInfoFetcher; -import io.mosip.authentication.core.spi.indauth.match.IdMapping; -import io.mosip.authentication.core.spi.indauth.match.MatchType; -import io.mosip.authentication.core.spi.indauth.match.MatchType.Category; -import io.mosip.kernel.core.logger.spi.Logger; -import io.mosip.kernel.core.pinvalidator.exception.InvalidPinException; -import io.mosip.kernel.core.util.StringUtils; -import io.mosip.kernel.pinvalidator.impl.PinValidatorImpl; - -/** - * The Class BaseAuthRequestValidator. - * - * @author Manoj SP - * @author Prem Kumar - * @author RakeshRoshan - * - */ -@Component -public abstract class BaseAuthRequestValidator extends IdAuthValidator { - - /** The Constant OTP2. */ - private static final String OTP2 = "OTP"; - - /** The Constant PIN. */ - private static final String PIN = "PIN"; - - /** The Final Constant For PIN_VALUE. */ - private static final String PIN_VALUE = "pinValue"; - - /** The mosip logger. */ - private static Logger mosipLogger = IdaLogger.getLogger(BaseAuthRequestValidator.class); - - /** The Constant iris. */ - private static final String IRIS = "iris"; - - /** The Constant face. */ - private static final String FACE = "face"; - - /** The id info helper. */ - @Autowired - protected IdInfoHelper idInfoHelper; - - /** The id info helper. */ - @Autowired - protected IdInfoFetcher idInfoFetcher; - - /** The pin validator. */ - @Autowired - private PinValidatorImpl pinValidator; - - /** The Constant REQUEST. */ - private static final String REQUEST = "request"; - - /** The Constant SESSION_ID. */ - private static final String SESSION_ID = "SESSION_ID"; - - /** The Constant IRIS_COUNT. */ - private static final int IRIS_COUNT = 2; - - /** - * Supports. - * - * @param clazz the clazz - * @return true, if successful - */ - /* - * (non-Javadoc) - * - * @see org.springframework.validation.Validator#supports(java.lang.Class) - */ - @Override - public boolean supports(Class clazz) { - return BaseAuthRequestDTO.class.isAssignableFrom(clazz); - } - - /** - * Validate. - * - * @param req the req - * @param errors the errors - */ - /* - * (non-Javadoc) - * - * @see org.springframework.validation.Validator#validate(java.lang.Object, - * org.springframework.validation.Errors) - */ - @Override - public void validate(Object req, Errors errors) { - BaseAuthRequestDTO baseAuthRequestDTO = (BaseAuthRequestDTO) req; - - if (baseAuthRequestDTO != null) { - validateId(baseAuthRequestDTO.getId(), errors); - } - - } - - /** - * validates the Static Pin Details. - * - * @param authRequestDTO - * the auth request DTO - * @param errors - * the errors - */ - protected void validateAdditionalFactorsDetails(AuthRequestDTO authRequestDTO, Errors errors) { - - if ((AuthTypeUtil.isPin(authRequestDTO) && isMatchtypeEnabled(PinMatchType.SPIN))) { - - Optional pinOpt = Optional.ofNullable(authRequestDTO.getRequest()).map(RequestDTO::getStaticPin); - - if (!pinOpt.isPresent()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Missing pinval in the request"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), new Object[] { PIN }, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); - } else { - try { - pinValidator.validatePin(pinOpt.get()); - } catch (InvalidPinException e) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), "validateStaticPin", - "INVALID_INPUT_PARAMETER - pinValue - value -> " + pinOpt.get()); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { PIN_VALUE }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - - } - } else if ((AuthTypeUtil.isOtp(authRequestDTO) && isMatchtypeEnabled(PinMatchType.OTP))) { - Optional otp = Optional.ofNullable(authRequestDTO.getRequest()).map(RequestDTO::getOtp); - - if (!otp.isPresent()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Missing OTP value in the request"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), - new Object[] { Category.OTP.getType() }, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); - } else { - try { - pinValidator.validatePin(otp.get()); - } catch (InvalidPinException e) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), "validateOtpValue", - "INVALID_INPUT_PARAMETER - OtppinValue - value -> " + otp.get()); - errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { OTP2 }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - } - } - - /** - * Validate Biometric details i.e validating fingers,iris,face and device - * information. - * - * @param authRequestDTO - * the auth request DTO - * @param errors - * the errors - * @param allowedAuthType - * the allowed auth type - */ - protected void validateBioMetadataDetails(AuthRequestDTO authRequestDTO, Errors errors, - Set allowedAuthType) { - if (authRequestDTO.getRequest() != null) { - List bioInfo = authRequestDTO.getRequest().getBiometrics(); - - if (bioInfo != null && !bioInfo.isEmpty()) { - OptionalInt nullDataIndex = IntStream.range(0, bioInfo.size()) - .filter(index -> bioInfo.get(index).getData() == null).findAny(); - if (nullDataIndex.isPresent()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "missing biometric request"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), - new Object[] { Category.BIO.getType() + "/*/data" }, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); - } else { - List bioData = bioInfo.stream().map(BioIdentityInfoDTO::getData) - .collect(Collectors.toList()); - validateBioType(bioData, errors, allowedAuthType); - validateBioData(bioData, errors); - validateCount(authRequestDTO, errors, bioData); - } - } - } - } - - /** - * Validate count. - * - * @param authRequestDTO - * the auth request DTO - * @param errors - * the errors - * @param bioData - * the bio data - */ - private void validateCount(AuthRequestDTO authRequestDTO, Errors errors, List bioData) { - if (!errors.hasErrors()) { - BioAuthType[] fingerTypes; - if(EnvUtil.getIsFmrEnabled()) { - fingerTypes = new BioAuthType[] {BioAuthType.FGR_IMG, BioAuthType.FGR_MIN, BioAuthType.FGR_IMG_COMPOSITE, BioAuthType.FGR_MIN_COMPOSITE}; - } else { - fingerTypes = new BioAuthType[] {BioAuthType.FGR_IMG, BioAuthType.FGR_IMG_COMPOSITE}; - } - if (isAuthtypeEnabled(fingerTypes)) { - validateFinger(authRequestDTO, bioData, errors); - } - - if (isAuthtypeEnabled(BioAuthType.IRIS_IMG, BioAuthType.IRIS_COMP_IMG)) { - validateIris(authRequestDTO, bioData, errors); - } - if (isMatchtypeEnabled(BioMatchType.FACE)) { - validateFace(authRequestDTO, bioData, errors); - } - } - } - - /** - * Validate bio data. - * - * @param bioData - * the bio data - * @param errors - * the errors - */ - private void validateBioData(List bioData, Errors errors) { - List filterdBioData = bioData.stream() - .filter(dataDto -> dataDto.getBioValue() == null || dataDto.getBioValue().isEmpty()) - .collect(Collectors.toList()); - filterdBioData.forEach(bioInfo -> { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { "bioValue for bioType - " + bioInfo.getBioType() + " " + "& bioSubType - " - + bioInfo.getBioSubType() }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - }); - - } - - /** - * Validates the BioType value. - * - * @param bioInfos - * the bio infos - * @param errors - * the errors - * @param allowedAuthTypesFromConfig - * the allowed auth types from config - */ - private void validateBioType(List bioInfos, Errors errors, Set allowedAuthTypesFromConfig) { - BioAuthType[] authTypes = BioAuthType.values(); - Set availableAuthTypeInfos = new HashSet<>(); - for (BioAuthType authType : authTypes) { - availableAuthTypeInfos.add(authType.getConfigNameValue().toLowerCase()); - } - Set allowedAvailableAuthTypes = allowedAuthTypesFromConfig.stream().filter(authTypeFromConfig -> { - String authType = authTypeFromConfig.toLowerCase(); - boolean contains = (authType.equalsIgnoreCase(MatchType.Category.DEMO.getType()) - || authType.equalsIgnoreCase(MatchType.Category.OTP.getType())) ? true - : availableAuthTypeInfos.contains(authType); - // TODO handle invalid bio authtype cases - if (!contains) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Invalid bio type config: " + authTypeFromConfig); - } - return contains; - }).map(BioAuthType::getTypeForConfigNameValue).filter(Optional::isPresent).map(Optional::get) - .collect(Collectors.toSet()); - - for (int i = 0; i < bioInfos.size(); i++) { - DataDTO bioInfo = bioInfos.get(i); - String bioType = bioInfo.getBioType(); - if (StringUtils.isEmpty(bioType)) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(IdAuthCommonConstants.BIO_TYPE_INPUT_PARAM, i) }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } else if (allowedAvailableAuthTypes.stream().noneMatch(authType -> authType.equalsIgnoreCase(bioType))) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), - new Object[] { MatchType.Category.BIO.getType() + "-" + bioType }, - IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); - } else { - validateBioType(errors, allowedAvailableAuthTypes, bioInfo, i); - } - } - - } - - /** - * Validate bio type. - * - * @param errors the errors - * @param availableAuthTypeInfos the available auth type infos - * @param bioInfo the bio info - * @param bioIndex the bio index - */ - private void validateBioType(Errors errors, Set availableAuthTypeInfos, DataDTO bioInfo, int bioIndex) { - String bioType = bioInfo.getBioType(); - if (availableAuthTypeInfos.stream().noneMatch(authType -> authType.equalsIgnoreCase(bioType))) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(IdAuthCommonConstants.BIO_TYPE_INPUT_PARAM, bioIndex) + " - " + bioType }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } else { - String bioSubType = bioInfo.getBioSubType(); - if(!BioAuthType.FACE_IMG.getType().equalsIgnoreCase(bioType)) { - if (bioSubType != null && !bioSubType.isEmpty()) { - // Valid bio type - Optional bioAuthTypeOpt = BioAuthType.getSingleBioAuthTypeForType(bioType); - if (bioAuthTypeOpt.isPresent()) { - BioAuthType bioAuthType = bioAuthTypeOpt.get(); - Set associatedMatchTypes = bioAuthType.getAssociatedMatchTypes(); - boolean invalidBioType = associatedMatchTypes.stream() - .filter(matchType -> matchType instanceof BioMatchType) - .map(matchType -> (BioMatchType) matchType).map(BioMatchType::getIdMapping) - .map(IdMapping::getSubType).distinct() - .noneMatch(idName -> idName.equalsIgnoreCase(bioSubType)); - if (invalidBioType) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(IdAuthCommonConstants.BIO_SUB_TYPE_INPUT_PARAM, bioIndex) + " - " + bioSubType }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - - } - } else { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { String.format(IdAuthCommonConstants.BIO_SUB_TYPE_INPUT_PARAM, bioIndex) }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - } - } - } - } - - /** - * Validate fingers. - * - * @param authRequestDTO - * the auth request DTO - * @param bioInfo - * the bio info - * @param errors - * the errors - */ - private void validateFinger(AuthRequestDTO authRequestDTO, List bioInfo, Errors errors) { - if (EnvUtil.getIsFmrEnabled() && isAvailableBioType(bioInfo, BioAuthType.FGR_MIN)) { - validateFingerRequestCount(authRequestDTO, errors, BioAuthType.FGR_MIN.getType()); - } - if (isAvailableBioType(bioInfo, BioAuthType.FGR_IMG)) { - validateFingerRequestCount(authRequestDTO, errors, BioAuthType.FGR_IMG.getType()); - } - } - - /** - * Validates the Iris parameters present in thr request. - * - * @param authRequestDTO - * the auth request DTO - * @param bioInfo - * the bio info - * @param errors - * the errors - */ - private void validateIris(AuthRequestDTO authRequestDTO, List bioInfo, Errors errors) { - if (isAvailableBioType(bioInfo, BioAuthType.IRIS_IMG)) { - validateIrisRequestCount(authRequestDTO, errors); - validateMultiIrisValue(authRequestDTO, errors); - } - } - - /** - * Validation for MultiIris Values present in the request. - * - * @param authRequestDTO - * the auth request DTO - * @param errors - * the errors - */ - private void validateMultiIrisValue(AuthRequestDTO authRequestDTO, Errors errors) { - if (isDuplicateBioValue(authRequestDTO, BioAuthType.IRIS_IMG.getType(), getMaxIrisCount())) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Duplicate IRIS in request"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.DUPLICATE_IRIS.getErrorCode(), - String.format(IdAuthenticationErrorConstants.DUPLICATE_IRIS.getErrorMessage(), - IdAuthCommonConstants.REQUEST)); - } - } - - /** - * Checks if is duplicate bio value. - * - * @param authRequestDTO - * the auth request DTO - * @param type - * the type - * @param maxCount - * the max count - * @return true, if is duplicate bio value - */ - private boolean isDuplicateBioValue(AuthRequestDTO authRequestDTO, String type, int maxCount) { - Map countsMap = getBioValueCounts(authRequestDTO, type); - return hasDuplicate(countsMap, maxCount); - } - - /** - * Checks for duplicate. - * - * @param countsMap - * the counts map - * @param maxCount - * the max count - * @return true, if successful - */ - private boolean hasDuplicate(Map countsMap, int maxCount) { - return countsMap.entrySet().stream() - .anyMatch(entry -> (entry.getKey().equalsIgnoreCase(IdAuthCommonConstants.UNKNOWN_BIO) - && entry.getValue() > maxCount) - || (!entry.getKey().equalsIgnoreCase(IdAuthCommonConstants.UNKNOWN_BIO) - && entry.getValue() > 1)); - } - - /** - * Gets the bio subtype counts. - * - * @param authRequestDTO - * the auth request DTO - * @param type - * the type - * @return the bio subtype counts - */ - private Map getBioSubtypeCounts(AuthRequestDTO authRequestDTO, String type) { - return getBioSubtypeCount(getBioIds(authRequestDTO, type)); - } - - /** - * Gets the bio value counts. - * - * @param authRequestDTO - * the auth request DTO - * @param type - * the type - * @return the bio value counts - */ - private Map getBioValueCounts(AuthRequestDTO authRequestDTO, String type) { - return getBioValuesCount(getBioIds(authRequestDTO, type)); - } - - /** - * Gets the bio ids. - * - * @param authRequestDTO - * the auth request DTO - * @param type - * the type - * @return the bio ids - */ - private List getBioIds(AuthRequestDTO authRequestDTO, String type) { - List identity = Optional.ofNullable(authRequestDTO.getRequest()) - .map(RequestDTO::getBiometrics).orElseGet(Collections::emptyList); - if (!identity.isEmpty()) { - return identity.stream().filter(Objects::nonNull) - .filter(bioId -> bioId.getData().getBioType().equalsIgnoreCase(type)).collect(Collectors.toList()); - } - return Collections.emptyList(); - } - - /** - * Validate Face. - * - * @param authRequestDTO - * the auth request DTO - * @param bioInfo - * the bio info - * @param errors - * the errors - */ - private void validateFace(AuthRequestDTO authRequestDTO, List bioInfo, Errors errors) { - - if (isAvailableBioType(bioInfo, BioAuthType.FACE_IMG)) { - validateFaceBioType(authRequestDTO, errors); - } - } - - /** - * Validate face bio type. - * - * @param authRequestDTO - * the auth request DTO - * @param errors - * the errors - */ - private void validateFaceBioType(AuthRequestDTO authRequestDTO, Errors errors) { - List listBioIdentity = getBioIds(authRequestDTO, BioAuthType.FACE_IMG.getType()); - if (listBioIdentity.size() > 1) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Face : face count is more than 1."); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.FACE_EXCEEDING.getErrorCode(), new Object[] { FACE }, - IdAuthenticationErrorConstants.FACE_EXCEEDING.getErrorMessage()); - } - } - - /** - * check any IdentityInfoDto data available or not. - * - * @param authRequestDTO - * the auth request DTO - * @param functions - * the functions - * @return true, if successful - */ - @SuppressWarnings("unchecked") - boolean checkAnyIdInfoAvailable(AuthRequestDTO authRequestDTO, - Function>... functions) { - return Stream.>>of(functions).anyMatch(func -> Optional - .ofNullable(authRequestDTO.getRequest()).map(RequestDTO::getDemographics).map(func) - .filter(list -> list != null && !list.isEmpty() - && list.stream().allMatch(idDto -> idDto.getValue() != null && !idDto.getValue().isEmpty())) - .isPresent()); - } - - /** - * If DemoAuthType is Bio, then validate bioinfo is available or not. - * - * @param bioInfoList - * the bio info list - * @param bioType - * the bio type - * @return true, if is available bio type - */ - private boolean isAvailableBioType(List bioInfoList, BioAuthType bioType) { - return bioInfoList.parallelStream().anyMatch(bio -> bio.getBioType() != null && !bio.getBioType().isEmpty() - && bio.getBioType().equals(bioType.getType())); - } - - /** - * If DemoAuthType is Bio, Then check duplicate request of finger and number - * finger of request should not exceed to 10. - * - * @param authRequestDTO - * the auth request DTO - * @param errors - * the errors - * @param bioType - * the bio type - */ - private void validateFingerRequestCount(AuthRequestDTO authRequestDTO, Errors errors, String bioType) { - Map fingerSubtypesCountsMap = getBioSubtypeCounts(authRequestDTO, bioType); - boolean anyInfoIsMoreThanOne = hasDuplicate(fingerSubtypesCountsMap, getMaxFingerCount()); - Map fingerValuesCountsMap = getBioValueCounts(authRequestDTO, bioType); - boolean anyValueIsMoreThanOne = hasDuplicate(fingerValuesCountsMap, getMaxFingerCount()); - - if (anyInfoIsMoreThanOne || anyValueIsMoreThanOne) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Duplicate fingers"); - errors.reject(IdAuthenticationErrorConstants.DUPLICATE_FINGER.getErrorCode(), - IdAuthenticationErrorConstants.DUPLICATE_FINGER.getErrorMessage()); - } - - validateMaxFingerCount(errors, fingerSubtypesCountsMap); - } - - /** - * Validate max finger count. - * - * @param errors - * the errors - * @param fingerSubtypesCountsMap - * the finger subtypes counts map - */ - private void validateMaxFingerCount(Errors errors, Map fingerSubtypesCountsMap) { - long fingerCountExceeding = fingerSubtypesCountsMap.values().stream().mapToLong(l -> l).sum(); - int maxFingerCount = getMaxFingerCount(); - if (fingerCountExceeding > maxFingerCount) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "finger count is exceeding to " + maxFingerCount); - errors.reject(IdAuthenticationErrorConstants.FINGER_EXCEEDING.getErrorCode(), - String.format(IdAuthenticationErrorConstants.FINGER_EXCEEDING.getErrorMessage(), maxFingerCount)); - } - } - - /** - * Gets the max finger count. - * - * @return the max finger count - */ - protected abstract int getMaxFingerCount(); - - /** - * Gets the bio subtype count. - * - * @param idendityInfoList - * the idendity info list - * @return the bio subtype count - */ - private Map getBioSubtypeCount(List idendityInfoList) { - return idendityInfoList.stream().map(BioIdentityInfoDTO::getData) - .collect(Collectors.groupingBy(DataDTO::getBioSubType, Collectors.counting())); - - } - - /** - * Gets the bio values count. - * - * @param idendityInfoList - * the idendity info list - * @return the bio values count - */ - private Map getBioValuesCount(List idendityInfoList) { - return idendityInfoList.stream().map(BioIdentityInfoDTO::getData) - .collect(Collectors.groupingBy(DataDTO::getBioValue, Collectors.counting())); - - } - - /** - * validate Iris request count. left and right eye should not exceed 1 and total - * iris should not exceed 2. - * - * @param authRequestDTO - * the auth request DTO - * @param errors - * the errors - */ - private void validateIrisRequestCount(AuthRequestDTO authRequestDTO, Errors errors) { - Map irisSubtypeCounts = getBioSubtypeCounts(authRequestDTO, BioAuthType.IRIS_IMG.getType()); - if (irisSubtypeCounts.entrySet().stream().anyMatch( - map -> (map.getKey().equalsIgnoreCase(IdAuthCommonConstants.UNKNOWN_BIO) && map.getValue() > 2) - || (!map.getKey().equalsIgnoreCase(IdAuthCommonConstants.UNKNOWN_BIO) && map.getValue() > 1))) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Iris : either left eye or right eye count is more than 1."); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.IRIS_EXCEEDING.getErrorCode(), new Object[] { IRIS }, - IdAuthenticationErrorConstants.IRIS_EXCEEDING.getErrorMessage()); - } - - } - - /** - * Check demo auth. - * - * @param authRequest - * the auth request - * @param errors - * the errors - */ - protected void checkDemoAuth(AuthRequestDTO authRequest, Errors errors) { - AuthType[] authTypes = DemoAuthType.values(); - Set availableAuthTypeInfos = new HashSet<>(); - boolean hasMatch = false; - for (AuthType authType : authTypes) { - if (authType.isAuthTypeEnabled(authRequest, idInfoFetcher)) { - Set associatedMatchTypes = authType.getAssociatedMatchTypes(); - for (MatchType matchType : associatedMatchTypes) { - if (isMatchtypeEnabled(matchType)) { - if(!matchType.equals(DemoMatchType.DYNAMIC)) { - List identityInfos = matchType.getIdentityInfoList(authRequest.getRequest()); - hasMatch = checkIdentityInfoAndLanguageDetails(errors, availableAuthTypeInfos, hasMatch, authType, matchType, - identityInfos); - } else { - Set dynamicAttributeNames = new HashSet<>(idInfoFetcher.getMappingConfig().getDynamicAttributes().keySet()); - Optional.ofNullable(authRequest.getRequest()) - .map(RequestDTO::getDemographics) - .map(IdentityDTO::getMetadata) - .map(Map::keySet) - .ifPresent(dynamicAttributeNames::addAll); - for(String idName : dynamicAttributeNames) { - Map> identityInfosMap = idInfoFetcher.getIdentityInfo(matchType, idName, authRequest.getRequest()); - for(List identityInfos : identityInfosMap.values()) { - hasMatch = checkIdentityInfoAndLanguageDetails(errors, availableAuthTypeInfos, hasMatch, authType, matchType, - identityInfos); - } - } - } - } - - } - } - } - - if (!hasMatch) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Missing IdentityInfoDTO"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), - new Object[] { Category.DEMO.getType() }, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); - } else { - checkOtherValues(authRequest, errors, availableAuthTypeInfos); - } - } - - /** - * Check identity info and language details. - * - * @param errors the errors - * @param availableAuthTypeInfos the available auth type infos - * @param hasMatch the has match - * @param authType the auth type - * @param matchType the match type - * @param identityInfos the identity infos - * @return true, if successful - */ - private boolean checkIdentityInfoAndLanguageDetails(Errors errors, Set availableAuthTypeInfos, boolean hasMatch, AuthType authType, - MatchType matchType, List identityInfos) { - if (identityInfos != null && !identityInfos.isEmpty()) { - availableAuthTypeInfos.add(authType.getType()); - hasMatch = true; - checkIdentityInfoValue(identityInfos, errors); - checkLangaugeDetails(matchType, identityInfos, errors); - } - return hasMatch; - } - - /** - * Check identity info value. - * - * @param identityInfos - * the identity infos - * @param errors - * the errors - */ - private void checkIdentityInfoValue(List identityInfos, Errors errors) { - for (IdentityInfoDTO identityInfoDTO : identityInfos) { - if (Objects.isNull(identityInfoDTO.getValue())) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "IdentityInfoDTO is invalid"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), - new Object[] { Category.DEMO.getType() }, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); - } - - } - - } - - /** - * Check other values. - * - * @param authRequest - * the auth request - * @param errors - * the errors - * @param availableAuthTypeInfos - * the available auth type infos - */ - private void checkOtherValues(AuthRequestDTO authRequest, Errors errors, Set availableAuthTypeInfos) { - - if (isMatchtypeEnabled(DemoMatchType.DOBTYPE)) { - checkDOBType(authRequest, errors); - } - - if (isMatchtypeEnabled(DemoMatchType.AGE)) { - checkAge(authRequest, errors); - } - - if (isAuthtypeEnabled(DemoAuthType.ADDRESS, DemoAuthType.FULL_ADDRESS)) { - validateAdAndFullAd(availableAuthTypeInfos, errors); - } - - } - - /** - * Checks if is matchtype enabled. - * - * @param matchType - * the match type - * @return true, if is matchtype enabled - */ - private boolean isMatchtypeEnabled(MatchType matchType) { - return idInfoHelper.isMatchtypeEnabled(matchType); - } - - /** - * Checks if is authtype enabled. - * - * @param authTypes - * the auth types - * @return true, if is authtype enabled - */ - private boolean isAuthtypeEnabled(AuthType... authTypes) { - return Stream.of(authTypes).anyMatch( - authType -> authType.getAssociatedMatchTypes().stream().anyMatch(idInfoHelper::isMatchtypeEnabled)); - } - - /** - * Validate ad and full ad. - * - * @param availableAuthTypeInfos - * the available auth type infos - * @param errors - * the errors - */ - private void validateAdAndFullAd(Set availableAuthTypeInfos, Errors errors) { - if (availableAuthTypeInfos.contains(DemoAuthType.ADDRESS.getType()) - && availableAuthTypeInfos.contains(DemoAuthType.FULL_ADDRESS.getType())) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Ad and FAD are enabled"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), - new Object[] { Category.DEMO.getType() }, - IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); - } - } - - /** - * Check DOB type. - * - * @param authRequest - * the auth request - * @param errors - * the errors - */ - private void checkDOBType(AuthRequestDTO authRequest, Errors errors) { - List dobTypeList = DemoMatchType.DOBTYPE.getIdentityInfoList(authRequest.getRequest()); - if (dobTypeList != null) { - for (IdentityInfoDTO identityInfoDTO : dobTypeList) { - if (!DOBType.isTypePresent(identityInfoDTO.getValue())) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Demographic data – DOBType(pi) did not match"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { "DOBType" }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - } - - } - - /** - * Check age. - * - * @param authRequest - * the auth request - * @param errors - * the errors - */ - private void checkAge(AuthRequestDTO authRequest, Errors errors) { - List ageList = DemoMatchType.AGE.getIdentityInfoList(authRequest.getRequest()); - if (ageList != null) { - for (IdentityInfoDTO identityInfoDTO : ageList) { - try { - Integer.parseInt(identityInfoDTO.getValue()); - } catch (NumberFormatException e) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.VALIDATE, "Demographic data – Age(pi) did not match"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { "age" }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - } - } - - /** - * Check langauge details. - * - * @param demoMatchType - * the demo match type - * @param identityInfos - * the identity infos - * @param errors - * the errors - */ - private void checkLangaugeDetails(MatchType demoMatchType, List identityInfos, Errors errors) { - //Dynamic attributes validations are skipping here - //will be done in match input building stage(MatchInputBuilder) - if (!demoMatchType.isDynamic() && demoMatchType.isMultiLanguage() && identityInfos.stream().anyMatch( - identityInfo -> (identityInfo.getLanguage() == null || identityInfo.getLanguage().isEmpty()))) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.MISSING_INPUT_PARAMETER, "LanguageCode cannot be null"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), - new Object[] { "LanguageCode" }, - IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); - - } - - if (!demoMatchType.isDynamic() && !errors.hasErrors() && demoMatchType.isMultiLanguage()) { - Map langCount = identityInfos.stream() - .collect(Collectors.groupingBy(IdentityInfoDTO::getLanguage, Collectors.counting())); - - langCount.keySet().forEach(langCode -> validateLangCode(langCode, errors, REQUEST)); - - for (long value : langCount.values()) { - if (value > 1) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.INVALID_INPUT_PARAMETER, "Invalid or Multiple language code"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { "LanguageCode" }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - - if (langCount.keySet().size() > 1 && !demoMatchType.isMultiLanguage()) { - mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), - IdAuthCommonConstants.INVALID_INPUT_PARAMETER, "Invalid or Multiple language code"); - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), - new Object[] { "LanguageCode" }, - IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); - } - } - } - - /** - * Validates the AuthType. - * - * @param authType - * the auth type - * @param errors - * the errors - */ - protected void validateAuthType(AuthRequestDTO authRequestDto, Errors errors) { - if (!(AuthTypeUtil.isDemo(authRequestDto) - || AuthTypeUtil.isBio(authRequestDto) - || AuthTypeUtil.isOtp(authRequestDto) - || AuthTypeUtil.isPin(authRequestDto))) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.NO_AUTHENTICATION_TYPE_SELECTED_IN_REQUEST.getErrorCode(), - IdAuthenticationErrorConstants.NO_AUTHENTICATION_TYPE_SELECTED_IN_REQUEST.getErrorMessage()); - } - } - - /** - * Method to validate auth type. - * - * @param requestDTO - * the request DTO - * @param errors - * the errors - */ - protected void validateAllowedAuthTypes(AuthRequestDTO requestDTO, Errors errors) { - Set allowedAuthType = getAllowedAuthTypes(); - validateAuthType(requestDTO, errors, allowedAuthType); - } - - /** - * Validate auth type. - * - * @param requestDTO - * the request DTO - * @param errors - * the errors - * @param authTypeDTO - * the auth type DTO - * @param allowedAuthType - * the allowed auth type - */ - private void validateAuthType(AuthRequestDTO requestDTO, Errors errors, - Set allowedAuthType) { - checkAllowedAuthType(requestDTO, errors, allowedAuthType); - validateBioMetadataDetails(requestDTO, errors, allowedAuthType); - } - - /** - * Check allowed auth type. - * - * @param requestDTO - * the request DTO - * @param errors - * the errors - * @param authTypeDTO - * the auth type DTO - * @param allowedAuthType - * the allowed auth type - */ - private void checkAllowedAuthType(AuthRequestDTO requestDTO, Errors errors, - Set allowedAuthType) { - if (AuthTypeUtil.isDemo(requestDTO)) { - if (allowedAuthType.contains(MatchType.Category.DEMO.getType())) { - checkDemoAuth(requestDTO, errors); - } else { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), - new Object[] { MatchType.Category.DEMO.getType() }, - IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); - } - } - - boolean isOtp = AuthTypeUtil.isOtp(requestDTO); - if (isOtp && !allowedAuthType.contains(MatchType.Category.OTP.getType())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), - new Object[] { MatchType.Category.OTP.getType() }, - IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); - } - - boolean isPin = AuthTypeUtil.isPin(requestDTO); - if (isPin && !allowedAuthType.contains(MatchType.Category.SPIN.getType())) { - errors.rejectValue(IdAuthCommonConstants.REQUEST, - IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), - new Object[] { MatchType.Category.SPIN.getType() }, - IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); - } - - if ((isOtp || isPin) && !errors.hasErrors()) { - validateAdditionalFactorsDetails(requestDTO, errors); - } - } - - /** - * validates langauges - * request. - * - * @param langCode the lang code - * @param errors the errors - * @param field the field - */ - protected void validateLangCode(String langCode, Errors errors, String field) { - if (Objects.nonNull(langCode)) { - if (!idInfoFetcher.getSystemSupportedLanguageCodes().contains(langCode)) { - mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), IdAuthCommonConstants.VALIDATE, - IdAuthCommonConstants.INVALID_INPUT_PARAMETER + field + " : " + langCode); - errors.rejectValue(field, IdAuthenticationErrorConstants.UNSUPPORTED_LANGUAGE.getErrorCode(), - new Object[] { field.concat(" : " + langCode) }, - IdAuthenticationErrorConstants.UNSUPPORTED_LANGUAGE.getErrorMessage()); - } - } - - } - - /** - * Gets the max iris count. - * - * @return the max iris count - */ - protected int getMaxIrisCount() { - return IRIS_COUNT; - } - +package io.mosip.authentication.common.service.validator; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.validation.Errors; + +import io.mosip.authentication.common.service.helper.IdInfoHelper; +import io.mosip.authentication.common.service.impl.match.BioAuthType; +import io.mosip.authentication.common.service.impl.match.BioMatchType; +import io.mosip.authentication.common.service.impl.match.DOBType; +import io.mosip.authentication.common.service.impl.match.DemoAuthType; +import io.mosip.authentication.common.service.impl.match.DemoMatchType; +import io.mosip.authentication.common.service.impl.match.KeyBindedTokenAuthType; +import io.mosip.authentication.common.service.impl.match.KeyBindedTokenMatchType; +import io.mosip.authentication.common.service.impl.match.PasswordMatchType; +import io.mosip.authentication.common.service.impl.match.PinMatchType; +import io.mosip.authentication.common.service.util.AuthTypeUtil; +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.indauth.dto.AuthRequestDTO; +import io.mosip.authentication.core.indauth.dto.BaseAuthRequestDTO; +import io.mosip.authentication.core.indauth.dto.BioIdentityInfoDTO; +import io.mosip.authentication.core.indauth.dto.DataDTO; +import io.mosip.authentication.core.indauth.dto.IdentityDTO; +import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; +import io.mosip.authentication.core.indauth.dto.KeyBindedTokenDTO; +import io.mosip.authentication.core.indauth.dto.KycAuthRequestDTO; +import io.mosip.authentication.core.indauth.dto.KycRequestDTO; +import io.mosip.authentication.core.indauth.dto.RequestDTO; +import io.mosip.authentication.core.logger.IdaLogger; +import io.mosip.authentication.core.spi.indauth.match.AuthType; +import io.mosip.authentication.core.spi.indauth.match.IdInfoFetcher; +import io.mosip.authentication.core.spi.indauth.match.IdMapping; +import io.mosip.authentication.core.spi.indauth.match.MatchType; +import io.mosip.authentication.core.spi.indauth.match.MatchType.Category; +import io.mosip.kernel.core.logger.spi.Logger; +import io.mosip.kernel.core.pinvalidator.exception.InvalidPinException; +import io.mosip.kernel.core.util.StringUtils; +import io.mosip.kernel.pinvalidator.impl.PinValidatorImpl; + +/** + * The Class BaseAuthRequestValidator. + * + * @author Manoj SP + * @author Prem Kumar + * @author RakeshRoshan + * + */ +@Component +public abstract class BaseAuthRequestValidator extends IdAuthValidator { + + /** The Constant OTP2. */ + private static final String OTP2 = "OTP"; + + /** The Constant PIN. */ + private static final String PIN = "PIN"; + + /** The Final Constant For PIN_VALUE. */ + private static final String PIN_VALUE = "pinValue"; + + /** The mosip logger. */ + private static Logger mosipLogger = IdaLogger.getLogger(BaseAuthRequestValidator.class); + + /** The Constant iris. */ + private static final String IRIS = "iris"; + + /** The Constant face. */ + private static final String FACE = "face"; + + /** The id info helper. */ + @Autowired + protected IdInfoHelper idInfoHelper; + + /** The id info helper. */ + @Autowired + protected IdInfoFetcher idInfoFetcher; + + /** The pin validator. */ + @Autowired + private PinValidatorImpl pinValidator; + + /** The Constant REQUEST. */ + private static final String REQUEST = "request"; + + /** The Constant SESSION_ID. */ + private static final String SESSION_ID = "SESSION_ID"; + + /** The Constant IRIS_COUNT. */ + private static final int IRIS_COUNT = 2; + + /** + * Supports. + * + * @param clazz the clazz + * @return true, if successful + */ + /* + * (non-Javadoc) + * + * @see org.springframework.validation.Validator#supports(java.lang.Class) + */ + @Override + public boolean supports(Class clazz) { + return BaseAuthRequestDTO.class.isAssignableFrom(clazz); + } + + /** + * Validate. + * + * @param req the req + * @param errors the errors + */ + /* + * (non-Javadoc) + * + * @see org.springframework.validation.Validator#validate(java.lang.Object, + * org.springframework.validation.Errors) + */ + @Override + public void validate(Object req, Errors errors) { + BaseAuthRequestDTO baseAuthRequestDTO = (BaseAuthRequestDTO) req; + + if (baseAuthRequestDTO != null) { + validateId(baseAuthRequestDTO.getId(), errors); + } + + } + + /** + * validates the Static Pin Details. + * + * @param authRequestDTO + * the auth request DTO + * @param errors + * the errors + */ + protected void validateAdditionalFactorsDetails(AuthRequestDTO authRequestDTO, Errors errors) { + + if ((AuthTypeUtil.isPin(authRequestDTO) && isMatchtypeEnabled(PinMatchType.SPIN))) { + + Optional pinOpt = Optional.ofNullable(authRequestDTO.getRequest()).map(RequestDTO::getStaticPin); + + if (!pinOpt.isPresent()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Missing pinval in the request"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), new Object[] { PIN }, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); + } else { + try { + pinValidator.validatePin(pinOpt.get()); + } catch (InvalidPinException e) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), "validateStaticPin", + "INVALID_INPUT_PARAMETER - pinValue - value -> " + pinOpt.get()); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { PIN_VALUE }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + + } + } else if ((AuthTypeUtil.isOtp(authRequestDTO) && isMatchtypeEnabled(PinMatchType.OTP))) { + Optional otp = Optional.ofNullable(authRequestDTO.getRequest()).map(RequestDTO::getOtp); + + if (!otp.isPresent()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Missing OTP value in the request"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), + new Object[] { Category.OTP.getType() }, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); + } else { + try { + pinValidator.validatePin(otp.get()); + } catch (InvalidPinException e) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), "validateOtpValue", + "INVALID_INPUT_PARAMETER - OtppinValue - value -> " + otp.get()); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { OTP2 }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + } + } + + private void validatePasswordDetails(AuthRequestDTO authRequestDTO, Errors errors) { + + if (isMatchtypeEnabled(PasswordMatchType.PASSWORD)) { + KycAuthRequestDTO kycAuthRequestDTO = (KycAuthRequestDTO) authRequestDTO; + Optional passwordOpt = Optional.ofNullable(kycAuthRequestDTO.getRequest()).map(KycRequestDTO::getPassword); + if (!passwordOpt.isPresent()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Missing Password value in the request"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), + new Object[] { Category.PWD.getType() }, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); + } else { + if (passwordOpt.get().isBlank()) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), "validatePasswordDetails", + "INVALID_INPUT_PARAMETER - Pwd value -> " + passwordOpt.get()); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { "PWD" }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + } + } + + private void validateKBTDetails(AuthRequestDTO authRequestDTO, Errors errors) { + if(authRequestDTO instanceof KycAuthRequestDTO) { + KycAuthRequestDTO kycAuthRequestDTO = (KycAuthRequestDTO)authRequestDTO; + boolean isKbt = CollectionUtils.isEmpty(kycAuthRequestDTO.getRequest().getKeyBindedTokens()); + if (!isKbt) { + KeyBindedTokenDTO kbtDto = kycAuthRequestDTO.getRequest().getKeyBindedTokens().get(0); + if (Objects.isNull(kbtDto.getFormat()) || kbtDto.getFormat().isBlank()) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), "validateKBTDetails", + "INVALID_INPUT_PARAMETER - KBT value -> " + kbtDto.getFormat()); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { "KeyBindedTokens.Format" }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + if (Objects.isNull(kbtDto.getToken()) || kbtDto.getToken().isBlank()) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), "validateKBTDetails", + "INVALID_INPUT_PARAMETER - KBT value -> " + kbtDto.getToken()); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { "KeyBindedTokens.Token" }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + if (Objects.isNull(kbtDto.getType()) || kbtDto.getType().isBlank()) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), "validateKBTDetails", + "INVALID_INPUT_PARAMETER - KBT value -> " + kbtDto.getType()); + errors.rejectValue(REQUEST, IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { "KeyBindedTokens.Type" }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + } + } + + /** + * Validate Biometric details i.e validating fingers,iris,face and device + * information. + * + * @param authRequestDTO + * the auth request DTO + * @param errors + * the errors + * @param allowedAuthType + * the allowed auth type + */ + protected void validateBioMetadataDetails(AuthRequestDTO authRequestDTO, Errors errors, + Set allowedAuthType) { + if (authRequestDTO.getRequest() != null) { + List bioInfo = authRequestDTO.getRequest().getBiometrics(); + + if (bioInfo != null && !bioInfo.isEmpty()) { + OptionalInt nullDataIndex = IntStream.range(0, bioInfo.size()) + .filter(index -> bioInfo.get(index).getData() == null).findAny(); + if (nullDataIndex.isPresent()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "missing biometric request"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), + new Object[] { Category.BIO.getType() + "/*/data" }, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); + } else { + List bioData = bioInfo.stream().map(BioIdentityInfoDTO::getData) + .collect(Collectors.toList()); + validateBioType(bioData, errors, allowedAuthType); + validateBioData(bioData, errors); + validateCount(authRequestDTO, errors, bioData); + } + } + } + } + + /** + * Validate count. + * + * @param authRequestDTO + * the auth request DTO + * @param errors + * the errors + * @param bioData + * the bio data + */ + private void validateCount(AuthRequestDTO authRequestDTO, Errors errors, List bioData) { + if (!errors.hasErrors()) { + BioAuthType[] fingerTypes; + if(EnvUtil.getIsFmrEnabled()) { + fingerTypes = new BioAuthType[] {BioAuthType.FGR_IMG, BioAuthType.FGR_MIN, BioAuthType.FGR_IMG_COMPOSITE, BioAuthType.FGR_MIN_COMPOSITE}; + } else { + fingerTypes = new BioAuthType[] {BioAuthType.FGR_IMG, BioAuthType.FGR_IMG_COMPOSITE}; + } + if (isAuthtypeEnabled(fingerTypes)) { + validateFinger(authRequestDTO, bioData, errors); + } + + if (isAuthtypeEnabled(BioAuthType.IRIS_IMG, BioAuthType.IRIS_COMP_IMG)) { + validateIris(authRequestDTO, bioData, errors); + } + if (isMatchtypeEnabled(BioMatchType.FACE)) { + validateFace(authRequestDTO, bioData, errors); + } + } + } + + /** + * Validate bio data. + * + * @param bioData + * the bio data + * @param errors + * the errors + */ + private void validateBioData(List bioData, Errors errors) { + List filterdBioData = bioData.stream() + .filter(dataDto -> dataDto.getBioValue() == null || dataDto.getBioValue().isEmpty()) + .collect(Collectors.toList()); + filterdBioData.forEach(bioInfo -> { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { "bioValue for bioType - " + bioInfo.getBioType() + " " + "& bioSubType - " + + bioInfo.getBioSubType() }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + }); + + } + + /** + * Validates the BioType value. + * + * @param bioInfos + * the bio infos + * @param errors + * the errors + * @param allowedAuthTypesFromConfig + * the allowed auth types from config + */ + private void validateBioType(List bioInfos, Errors errors, Set allowedAuthTypesFromConfig) { + BioAuthType[] authTypes = BioAuthType.values(); + Set availableAuthTypeInfos = new HashSet<>(); + for (BioAuthType authType : authTypes) { + availableAuthTypeInfos.add(authType.getConfigNameValue().toLowerCase()); + } + Set allowedAvailableAuthTypes = allowedAuthTypesFromConfig.stream().filter(authTypeFromConfig -> { + String authType = authTypeFromConfig.toLowerCase(); + boolean contains = (authType.equalsIgnoreCase(MatchType.Category.DEMO.getType()) + || authType.equalsIgnoreCase(MatchType.Category.OTP.getType())) ? true + : availableAuthTypeInfos.contains(authType); + // TODO handle invalid bio authtype cases + if (!contains) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Invalid bio type config: " + authTypeFromConfig); + } + return contains; + }).map(BioAuthType::getTypeForConfigNameValue).filter(Optional::isPresent).map(Optional::get) + .collect(Collectors.toSet()); + + for (int i = 0; i < bioInfos.size(); i++) { + DataDTO bioInfo = bioInfos.get(i); + String bioType = bioInfo.getBioType(); + if (StringUtils.isEmpty(bioType)) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(IdAuthCommonConstants.BIO_TYPE_INPUT_PARAM, i) }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } else if (allowedAvailableAuthTypes.stream().noneMatch(authType -> authType.equalsIgnoreCase(bioType))) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), + new Object[] { MatchType.Category.BIO.getType() + "-" + bioType }, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); + } else { + validateBioType(errors, allowedAvailableAuthTypes, bioInfo, i); + } + } + + } + + /** + * Validate bio type. + * + * @param errors the errors + * @param availableAuthTypeInfos the available auth type infos + * @param bioInfo the bio info + * @param bioIndex the bio index + */ + private void validateBioType(Errors errors, Set availableAuthTypeInfos, DataDTO bioInfo, int bioIndex) { + String bioType = bioInfo.getBioType(); + if (availableAuthTypeInfos.stream().noneMatch(authType -> authType.equalsIgnoreCase(bioType))) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(IdAuthCommonConstants.BIO_TYPE_INPUT_PARAM, bioIndex) + " - " + bioType }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } else { + String bioSubType = bioInfo.getBioSubType(); + if(!BioAuthType.FACE_IMG.getType().equalsIgnoreCase(bioType)) { + if (bioSubType != null && !bioSubType.isEmpty()) { + // Valid bio type + Optional bioAuthTypeOpt = BioAuthType.getSingleBioAuthTypeForType(bioType); + if (bioAuthTypeOpt.isPresent()) { + BioAuthType bioAuthType = bioAuthTypeOpt.get(); + Set associatedMatchTypes = bioAuthType.getAssociatedMatchTypes(); + boolean invalidBioType = associatedMatchTypes.stream() + .filter(matchType -> matchType instanceof BioMatchType) + .map(matchType -> (BioMatchType) matchType).map(BioMatchType::getIdMapping) + .map(IdMapping::getSubType).distinct() + .noneMatch(idName -> idName.equalsIgnoreCase(bioSubType)); + if (invalidBioType) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(IdAuthCommonConstants.BIO_SUB_TYPE_INPUT_PARAM, bioIndex) + " - " + bioSubType }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + + } + } else { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { String.format(IdAuthCommonConstants.BIO_SUB_TYPE_INPUT_PARAM, bioIndex) }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + } + } + } + } + + /** + * Validate fingers. + * + * @param authRequestDTO + * the auth request DTO + * @param bioInfo + * the bio info + * @param errors + * the errors + */ + private void validateFinger(AuthRequestDTO authRequestDTO, List bioInfo, Errors errors) { + if (EnvUtil.getIsFmrEnabled() && isAvailableBioType(bioInfo, BioAuthType.FGR_MIN)) { + validateFingerRequestCount(authRequestDTO, errors, BioAuthType.FGR_MIN.getType()); + } + if (isAvailableBioType(bioInfo, BioAuthType.FGR_IMG)) { + validateFingerRequestCount(authRequestDTO, errors, BioAuthType.FGR_IMG.getType()); + } + } + + /** + * Validates the Iris parameters present in thr request. + * + * @param authRequestDTO + * the auth request DTO + * @param bioInfo + * the bio info + * @param errors + * the errors + */ + private void validateIris(AuthRequestDTO authRequestDTO, List bioInfo, Errors errors) { + if (isAvailableBioType(bioInfo, BioAuthType.IRIS_IMG)) { + validateIrisRequestCount(authRequestDTO, errors); + validateMultiIrisValue(authRequestDTO, errors); + } + } + + /** + * Validation for MultiIris Values present in the request. + * + * @param authRequestDTO + * the auth request DTO + * @param errors + * the errors + */ + private void validateMultiIrisValue(AuthRequestDTO authRequestDTO, Errors errors) { + if (isDuplicateBioValue(authRequestDTO, BioAuthType.IRIS_IMG.getType(), getMaxIrisCount())) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Duplicate IRIS in request"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.DUPLICATE_IRIS.getErrorCode(), + String.format(IdAuthenticationErrorConstants.DUPLICATE_IRIS.getErrorMessage(), + IdAuthCommonConstants.REQUEST)); + } + } + + /** + * Checks if is duplicate bio value. + * + * @param authRequestDTO + * the auth request DTO + * @param type + * the type + * @param maxCount + * the max count + * @return true, if is duplicate bio value + */ + private boolean isDuplicateBioValue(AuthRequestDTO authRequestDTO, String type, int maxCount) { + Map countsMap = getBioValueCounts(authRequestDTO, type); + return hasDuplicate(countsMap, maxCount); + } + + /** + * Checks for duplicate. + * + * @param countsMap + * the counts map + * @param maxCount + * the max count + * @return true, if successful + */ + private boolean hasDuplicate(Map countsMap, int maxCount) { + return countsMap.entrySet().stream() + .anyMatch(entry -> (entry.getKey().equalsIgnoreCase(IdAuthCommonConstants.UNKNOWN_BIO) + && entry.getValue() > maxCount) + || (!entry.getKey().equalsIgnoreCase(IdAuthCommonConstants.UNKNOWN_BIO) + && entry.getValue() > 1)); + } + + /** + * Gets the bio subtype counts. + * + * @param authRequestDTO + * the auth request DTO + * @param type + * the type + * @return the bio subtype counts + */ + private Map getBioSubtypeCounts(AuthRequestDTO authRequestDTO, String type) { + return getBioSubtypeCount(getBioIds(authRequestDTO, type)); + } + + /** + * Gets the bio value counts. + * + * @param authRequestDTO + * the auth request DTO + * @param type + * the type + * @return the bio value counts + */ + private Map getBioValueCounts(AuthRequestDTO authRequestDTO, String type) { + return getBioValuesCount(getBioIds(authRequestDTO, type)); + } + + /** + * Gets the bio ids. + * + * @param authRequestDTO + * the auth request DTO + * @param type + * the type + * @return the bio ids + */ + private List getBioIds(AuthRequestDTO authRequestDTO, String type) { + List identity = Optional.ofNullable(authRequestDTO.getRequest()) + .map(RequestDTO::getBiometrics).orElseGet(Collections::emptyList); + if (!identity.isEmpty()) { + return identity.stream().filter(Objects::nonNull) + .filter(bioId -> bioId.getData().getBioType().equalsIgnoreCase(type)).collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + /** + * Validate Face. + * + * @param authRequestDTO + * the auth request DTO + * @param bioInfo + * the bio info + * @param errors + * the errors + */ + private void validateFace(AuthRequestDTO authRequestDTO, List bioInfo, Errors errors) { + + if (isAvailableBioType(bioInfo, BioAuthType.FACE_IMG)) { + validateFaceBioType(authRequestDTO, errors); + } + } + + /** + * Validate face bio type. + * + * @param authRequestDTO + * the auth request DTO + * @param errors + * the errors + */ + private void validateFaceBioType(AuthRequestDTO authRequestDTO, Errors errors) { + List listBioIdentity = getBioIds(authRequestDTO, BioAuthType.FACE_IMG.getType()); + if (listBioIdentity.size() > 1) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Face : face count is more than 1."); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.FACE_EXCEEDING.getErrorCode(), new Object[] { FACE }, + IdAuthenticationErrorConstants.FACE_EXCEEDING.getErrorMessage()); + } + } + + /** + * check any IdentityInfoDto data available or not. + * + * @param authRequestDTO + * the auth request DTO + * @param functions + * the functions + * @return true, if successful + */ + @SuppressWarnings("unchecked") + boolean checkAnyIdInfoAvailable(AuthRequestDTO authRequestDTO, + Function>... functions) { + return Stream.>>of(functions).anyMatch(func -> Optional + .ofNullable(authRequestDTO.getRequest()).map(RequestDTO::getDemographics).map(func) + .filter(list -> list != null && !list.isEmpty() + && list.stream().allMatch(idDto -> idDto.getValue() != null && !idDto.getValue().isEmpty())) + .isPresent()); + } + + /** + * If DemoAuthType is Bio, then validate bioinfo is available or not. + * + * @param bioInfoList + * the bio info list + * @param bioType + * the bio type + * @return true, if is available bio type + */ + private boolean isAvailableBioType(List bioInfoList, BioAuthType bioType) { + return bioInfoList.parallelStream().anyMatch(bio -> bio.getBioType() != null && !bio.getBioType().isEmpty() + && bio.getBioType().equals(bioType.getType())); + } + + /** + * If DemoAuthType is Bio, Then check duplicate request of finger and number + * finger of request should not exceed to 10. + * + * @param authRequestDTO + * the auth request DTO + * @param errors + * the errors + * @param bioType + * the bio type + */ + private void validateFingerRequestCount(AuthRequestDTO authRequestDTO, Errors errors, String bioType) { + Map fingerSubtypesCountsMap = getBioSubtypeCounts(authRequestDTO, bioType); + boolean anyInfoIsMoreThanOne = hasDuplicate(fingerSubtypesCountsMap, getMaxFingerCount()); + Map fingerValuesCountsMap = getBioValueCounts(authRequestDTO, bioType); + boolean anyValueIsMoreThanOne = hasDuplicate(fingerValuesCountsMap, getMaxFingerCount()); + + if (anyInfoIsMoreThanOne || anyValueIsMoreThanOne) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Duplicate fingers"); + errors.reject(IdAuthenticationErrorConstants.DUPLICATE_FINGER.getErrorCode(), + IdAuthenticationErrorConstants.DUPLICATE_FINGER.getErrorMessage()); + } + + validateMaxFingerCount(errors, fingerSubtypesCountsMap); + } + + /** + * Validate max finger count. + * + * @param errors + * the errors + * @param fingerSubtypesCountsMap + * the finger subtypes counts map + */ + private void validateMaxFingerCount(Errors errors, Map fingerSubtypesCountsMap) { + long fingerCountExceeding = fingerSubtypesCountsMap.values().stream().mapToLong(l -> l).sum(); + int maxFingerCount = getMaxFingerCount(); + if (fingerCountExceeding > maxFingerCount) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "finger count is exceeding to " + maxFingerCount); + errors.reject(IdAuthenticationErrorConstants.FINGER_EXCEEDING.getErrorCode(), + String.format(IdAuthenticationErrorConstants.FINGER_EXCEEDING.getErrorMessage(), maxFingerCount)); + } + } + + /** + * Gets the max finger count. + * + * @return the max finger count + */ + protected abstract int getMaxFingerCount(); + + /** + * Gets the bio subtype count. + * + * @param idendityInfoList + * the idendity info list + * @return the bio subtype count + */ + private Map getBioSubtypeCount(List idendityInfoList) { + return idendityInfoList.stream().map(BioIdentityInfoDTO::getData) + .collect(Collectors.groupingBy(DataDTO::getBioSubType, Collectors.counting())); + + } + + /** + * Gets the bio values count. + * + * @param idendityInfoList + * the idendity info list + * @return the bio values count + */ + private Map getBioValuesCount(List idendityInfoList) { + return idendityInfoList.stream().map(BioIdentityInfoDTO::getData) + .collect(Collectors.groupingBy(DataDTO::getBioValue, Collectors.counting())); + + } + + /** + * validate Iris request count. left and right eye should not exceed 1 and total + * iris should not exceed 2. + * + * @param authRequestDTO + * the auth request DTO + * @param errors + * the errors + */ + private void validateIrisRequestCount(AuthRequestDTO authRequestDTO, Errors errors) { + Map irisSubtypeCounts = getBioSubtypeCounts(authRequestDTO, BioAuthType.IRIS_IMG.getType()); + if (irisSubtypeCounts.entrySet().stream().anyMatch( + map -> (map.getKey().equalsIgnoreCase(IdAuthCommonConstants.UNKNOWN_BIO) && map.getValue() > 2) + || (!map.getKey().equalsIgnoreCase(IdAuthCommonConstants.UNKNOWN_BIO) && map.getValue() > 1))) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Iris : either left eye or right eye count is more than 1."); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.IRIS_EXCEEDING.getErrorCode(), new Object[] { IRIS }, + IdAuthenticationErrorConstants.IRIS_EXCEEDING.getErrorMessage()); + } + + } + + /** + * Check demo auth. + * + * @param authRequest + * the auth request + * @param errors + * the errors + */ + protected void checkDemoAuth(AuthRequestDTO authRequest, Errors errors) { + AuthType[] authTypes = DemoAuthType.values(); + Set availableAuthTypeInfos = new HashSet<>(); + boolean hasMatch = false; + for (AuthType authType : authTypes) { + if (authType.isAuthTypeEnabled(authRequest, idInfoFetcher)) { + Set associatedMatchTypes = authType.getAssociatedMatchTypes(); + for (MatchType matchType : associatedMatchTypes) { + if (isMatchtypeEnabled(matchType)) { + if(!matchType.equals(DemoMatchType.DYNAMIC)) { + List identityInfos = matchType.getIdentityInfoList(authRequest.getRequest()); + hasMatch = checkIdentityInfoAndLanguageDetails(errors, availableAuthTypeInfos, hasMatch, authType, matchType, + identityInfos); + } else { + Set dynamicAttributeNames = new HashSet<>(idInfoFetcher.getMappingConfig().getDynamicAttributes().keySet()); + Optional.ofNullable(authRequest.getRequest()) + .map(RequestDTO::getDemographics) + .map(IdentityDTO::getMetadata) + .map(Map::keySet) + .ifPresent(dynamicAttributeNames::addAll); + for(String idName : dynamicAttributeNames) { + Map> identityInfosMap = idInfoFetcher.getIdentityInfo(matchType, idName, authRequest.getRequest()); + for(List identityInfos : identityInfosMap.values()) { + hasMatch = checkIdentityInfoAndLanguageDetails(errors, availableAuthTypeInfos, hasMatch, authType, matchType, + identityInfos); + } + } + } + } + + } + } + } + + if (!hasMatch) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Missing IdentityInfoDTO"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), + new Object[] { Category.DEMO.getType() }, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); + } else { + checkOtherValues(authRequest, errors, availableAuthTypeInfos); + } + } + + /** + * Check identity info and language details. + * + * @param errors the errors + * @param availableAuthTypeInfos the available auth type infos + * @param hasMatch the has match + * @param authType the auth type + * @param matchType the match type + * @param identityInfos the identity infos + * @return true, if successful + */ + private boolean checkIdentityInfoAndLanguageDetails(Errors errors, Set availableAuthTypeInfos, boolean hasMatch, AuthType authType, + MatchType matchType, List identityInfos) { + if (identityInfos != null && !identityInfos.isEmpty()) { + availableAuthTypeInfos.add(authType.getType()); + hasMatch = true; + checkIdentityInfoValue(identityInfos, errors); + checkLangaugeDetails(matchType, identityInfos, errors); + } + return hasMatch; + } + + /** + * Check identity info value. + * + * @param identityInfos + * the identity infos + * @param errors + * the errors + */ + private void checkIdentityInfoValue(List identityInfos, Errors errors) { + for (IdentityInfoDTO identityInfoDTO : identityInfos) { + if (Objects.isNull(identityInfoDTO.getValue())) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "IdentityInfoDTO is invalid"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), + new Object[] { Category.DEMO.getType() }, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); + } + + } + + } + + /** + * Check other values. + * + * @param authRequest + * the auth request + * @param errors + * the errors + * @param availableAuthTypeInfos + * the available auth type infos + */ + private void checkOtherValues(AuthRequestDTO authRequest, Errors errors, Set availableAuthTypeInfos) { + + if (isMatchtypeEnabled(DemoMatchType.DOBTYPE)) { + checkDOBType(authRequest, errors); + } + + if (isMatchtypeEnabled(DemoMatchType.AGE)) { + checkAge(authRequest, errors); + } + + if (isAuthtypeEnabled(DemoAuthType.ADDRESS, DemoAuthType.FULL_ADDRESS)) { + validateAdAndFullAd(availableAuthTypeInfos, errors); + } + + } + + /** + * Checks if is matchtype enabled. + * + * @param matchType + * the match type + * @return true, if is matchtype enabled + */ + private boolean isMatchtypeEnabled(MatchType matchType) { + return idInfoHelper.isMatchtypeEnabled(matchType); + } + + /** + * Checks if is authtype enabled. + * + * @param authTypes + * the auth types + * @return true, if is authtype enabled + */ + private boolean isAuthtypeEnabled(AuthType... authTypes) { + return Stream.of(authTypes).anyMatch( + authType -> authType.getAssociatedMatchTypes().stream().anyMatch(idInfoHelper::isMatchtypeEnabled)); + } + + /** + * Validate ad and full ad. + * + * @param availableAuthTypeInfos + * the available auth type infos + * @param errors + * the errors + */ + private void validateAdAndFullAd(Set availableAuthTypeInfos, Errors errors) { + if (availableAuthTypeInfos.contains(DemoAuthType.ADDRESS.getType()) + && availableAuthTypeInfos.contains(DemoAuthType.FULL_ADDRESS.getType())) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Ad and FAD are enabled"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorCode(), + new Object[] { Category.DEMO.getType() }, + IdAuthenticationErrorConstants.MISSING_AUTHTYPE.getErrorMessage()); + } + } + + /** + * Check DOB type. + * + * @param authRequest + * the auth request + * @param errors + * the errors + */ + private void checkDOBType(AuthRequestDTO authRequest, Errors errors) { + List dobTypeList = DemoMatchType.DOBTYPE.getIdentityInfoList(authRequest.getRequest()); + if (dobTypeList != null) { + for (IdentityInfoDTO identityInfoDTO : dobTypeList) { + if (!DOBType.isTypePresent(identityInfoDTO.getValue())) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Demographic data – DOBType(pi) did not match"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { "DOBType" }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + } + + } + + /** + * Check age. + * + * @param authRequest + * the auth request + * @param errors + * the errors + */ + private void checkAge(AuthRequestDTO authRequest, Errors errors) { + List ageList = DemoMatchType.AGE.getIdentityInfoList(authRequest.getRequest()); + if (ageList != null) { + for (IdentityInfoDTO identityInfoDTO : ageList) { + try { + Integer.parseInt(identityInfoDTO.getValue()); + } catch (NumberFormatException e) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.VALIDATE, "Demographic data – Age(pi) did not match"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { "age" }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + } + } + + /** + * Check langauge details. + * + * @param demoMatchType + * the demo match type + * @param identityInfos + * the identity infos + * @param errors + * the errors + */ + private void checkLangaugeDetails(MatchType demoMatchType, List identityInfos, Errors errors) { + //Dynamic attributes validations are skipping here + //will be done in match input building stage(MatchInputBuilder) + if (!demoMatchType.isDynamic() && demoMatchType.isMultiLanguage() && identityInfos.stream().anyMatch( + identityInfo -> (identityInfo.getLanguage() == null || identityInfo.getLanguage().isEmpty()))) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.MISSING_INPUT_PARAMETER, "LanguageCode cannot be null"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorCode(), + new Object[] { "LanguageCode" }, + IdAuthenticationErrorConstants.MISSING_INPUT_PARAMETER.getErrorMessage()); + + } + + if (!demoMatchType.isDynamic() && !errors.hasErrors() && demoMatchType.isMultiLanguage()) { + Map langCount = identityInfos.stream() + .collect(Collectors.groupingBy(IdentityInfoDTO::getLanguage, Collectors.counting())); + + langCount.keySet().forEach(langCode -> validateLangCode(langCode, errors, REQUEST)); + + for (long value : langCount.values()) { + if (value > 1) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.INVALID_INPUT_PARAMETER, "Invalid or Multiple language code"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { "LanguageCode" }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + + if (langCount.keySet().size() > 1 && !demoMatchType.isMultiLanguage()) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + IdAuthCommonConstants.INVALID_INPUT_PARAMETER, "Invalid or Multiple language code"); + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorCode(), + new Object[] { "LanguageCode" }, + IdAuthenticationErrorConstants.INVALID_INPUT_PARAMETER.getErrorMessage()); + } + } + } + + /** + * Validates the AuthType. + * + * @param authType + * the auth type + * @param errors + * the errors + */ + protected void validateAuthType(AuthRequestDTO authRequestDto, Errors errors) { + if (!(AuthTypeUtil.isDemo(authRequestDto) + || AuthTypeUtil.isBio(authRequestDto) + || AuthTypeUtil.isOtp(authRequestDto) + || AuthTypeUtil.isPin(authRequestDto) + || AuthTypeUtil.isPassword(authRequestDto) + || AuthTypeUtil.isKeyBindedToken(authRequestDto))) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.NO_AUTHENTICATION_TYPE_SELECTED_IN_REQUEST.getErrorCode(), + IdAuthenticationErrorConstants.NO_AUTHENTICATION_TYPE_SELECTED_IN_REQUEST.getErrorMessage()); + } + } + + /** + * Method to validate auth type. + * + * @param requestDTO + * the request DTO + * @param errors + * the errors + */ + protected void validateAllowedAuthTypes(AuthRequestDTO requestDTO, Errors errors) { + Set allowedAuthType = getAllowedAuthTypes(); + validateAuthType(requestDTO, errors, allowedAuthType); + } + + /** + * Validate auth type. + * + * @param requestDTO + * the request DTO + * @param errors + * the errors + * @param authTypeDTO + * the auth type DTO + * @param allowedAuthType + * the allowed auth type + */ + private void validateAuthType(AuthRequestDTO requestDTO, Errors errors, + Set allowedAuthType) { + checkAllowedAuthType(requestDTO, errors, allowedAuthType); + validateBioMetadataDetails(requestDTO, errors, allowedAuthType); + } + + /** + * Check allowed auth type. + * + * @param requestDTO + * the request DTO + * @param errors + * the errors + * @param authTypeDTO + * the auth type DTO + * @param allowedAuthType + * the allowed auth type + */ + private void checkAllowedAuthType(AuthRequestDTO requestDTO, Errors errors, + Set allowedAuthType) { + if (AuthTypeUtil.isDemo(requestDTO)) { + if (allowedAuthType.contains(MatchType.Category.DEMO.getType())) { + checkDemoAuth(requestDTO, errors); + } else { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), + new Object[] { MatchType.Category.DEMO.getType() }, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); + } + } + + boolean isOtp = AuthTypeUtil.isOtp(requestDTO); + if (isOtp && !allowedAuthType.contains(MatchType.Category.OTP.getType())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), + new Object[] { MatchType.Category.OTP.getType() }, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); + } + + boolean isPin = AuthTypeUtil.isPin(requestDTO); + if (isPin && !allowedAuthType.contains(MatchType.Category.SPIN.getType())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), + new Object[] { MatchType.Category.SPIN.getType() }, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); + } + + boolean isKeyBindedToken = AuthTypeUtil.isKeyBindedToken(requestDTO); + if (isKeyBindedToken && !allowedAuthType.contains(MatchType.Category.KBT.getType())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), + new Object[] { MatchType.Category.KBT.getType() }, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); + } + + boolean isPassword = AuthTypeUtil.isPassword(requestDTO); + if (isPassword && !allowedAuthType.contains(MatchType.Category.PWD.getType())) { + errors.rejectValue(IdAuthCommonConstants.REQUEST, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorCode(), + new Object[] { MatchType.Category.PWD.getType() }, + IdAuthenticationErrorConstants.AUTH_TYPE_NOT_SUPPORTED.getErrorMessage()); + } + + if ((isOtp || isPin) && !errors.hasErrors()) { + validateAdditionalFactorsDetails(requestDTO, errors); + } + if(isPassword && !errors.hasErrors()) { + validatePasswordDetails(requestDTO, errors); + } + if (!errors.hasErrors()) { + validateKBTDetails(requestDTO, errors); + } + } + + /** + * validates langauges + * request. + * + * @param langCode the lang code + * @param errors the errors + * @param field the field + */ + protected void validateLangCode(String langCode, Errors errors, String field) { + if (Objects.nonNull(langCode)) { + if (!idInfoFetcher.getSystemSupportedLanguageCodes().contains(langCode)) { + mosipLogger.error(SESSION_ID, this.getClass().getSimpleName(), IdAuthCommonConstants.VALIDATE, + IdAuthCommonConstants.INVALID_INPUT_PARAMETER + field + " : " + langCode); + errors.rejectValue(field, IdAuthenticationErrorConstants.UNSUPPORTED_LANGUAGE.getErrorCode(), + new Object[] { field.concat(" : " + langCode) }, + IdAuthenticationErrorConstants.UNSUPPORTED_LANGUAGE.getErrorMessage()); + } + } + } + + /** + * Gets the max iris count. + * + * @return the max iris count + */ + protected int getMaxIrisCount() { + return IRIS_COUNT; + } + } \ No newline at end of file diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/IdAuthenticationErrorConstants.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/IdAuthenticationErrorConstants.java index 1ac830d0b01..7985dd48e67 100644 --- a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/IdAuthenticationErrorConstants.java +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/IdAuthenticationErrorConstants.java @@ -96,7 +96,8 @@ public enum IdAuthenticationErrorConstants { BINDED_KEY_NOT_FOUND("IDA-KBT-001", "Certificate not found for the input x5t#S256: %s and authtype: %s"), BINDED_TOKEN_EXPIRED("IDA-KBT-002", "Signed token issued at (iat) is not in allowed time range."), - ERROR_TOKEN_VERIFICATION("IDA-KBT-003", "Error verifying key binded token. Error: %s"), + ERROR_TOKEN_VERIFICATION("IDA-KBT-003", "Binded Token verification failed.", + "Please retry token generation with correct Key."), INVALID_ENCRYPT_EKYC_RESPONSE("IDA-EKA-001", "Unable to encrypt eKYC response"), @@ -217,7 +218,7 @@ public enum IdAuthenticationErrorConstants { VCI_NOT_SUPPORTED_ERROR("IDA-VCI-004", "Error VCI not supported."), LDP_VC_GENERATION_FAILED("IDA-VCI-005", "Ldp VC generation Failed."), - PASSWORD_MISMATCH("IDA-PSD-001", "Password value did not match", "Please re-enter your password"), + PASSWORD_MISMATCH("IDA-PSD-001", "Password value did not match", "Please re-enter your correct password"), PASSWORD_MISSING("IDA-PSD-002", "For the input VID/UIN - No Password found in DB.", "Please use UIN/VID with Password Auth."); 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 3d53a90474d..a8ffe0862f0 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 @@ -107,7 +107,7 @@ public class KycAuthController { * * @param binder the binder */ - @InitBinder("authRequestDTO") + @InitBinder("kycAuthRequestDTO") private void initKycAuthRequestBinder(WebDataBinder binder) { binder.setValidator(authRequestValidator); }