All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mosip.authentication.common.service.impl.OTPServiceImpl Maven / Gradle / Ivy

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 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;

/**
 * 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";

	/** The id auth service. */
	@Autowired
	private IdService idAuthService;

	/** The autntxnrepository. */
	@Autowired
	private AutnTxnRepository autntxnrepository;

	/** 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);

			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 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);
			
			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.
	 *
	 * @param otpRequestDto the otp request dto
	 * @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())) {
			maskedResponseDTO.setMaskedMobile(MaskUtil.maskMobile(phone));
		} else if (value.equalsIgnoreCase(NotificationType.EMAIL.getChannel())) {
			maskedResponseDTO.setMaskedEmail(MaskUtil.maskEmail(email));
		}

	}

	/**
	 * 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;

	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy