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

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

package io.mosip.authentication.common.service.impl;

import static io.mosip.authentication.core.constant.IdAuthConfigKeyConstants.IDA_AUTH_PARTNER_ID;
import static io.mosip.authentication.core.constant.IdAuthConfigKeyConstants.IDA_ZERO_KNOWLEDGE_UNENCRYPTED_CREDENTIAL_ATTRIBUTES;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.hibernate.exception.JDBCConnectionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionException;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.mosip.authentication.common.service.entity.AutnTxn;
import io.mosip.authentication.common.service.entity.IdentityEntity;
import io.mosip.authentication.common.service.repository.AutnTxnRepository;
import io.mosip.authentication.common.service.repository.IdentityCacheRepository;
import io.mosip.authentication.common.service.transaction.manager.IdAuthSecurityManager;
import io.mosip.authentication.core.constant.IdAuthCommonConstants;
import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants;
import io.mosip.authentication.core.exception.IdAuthenticationBusinessException;
import io.mosip.authentication.core.indauth.dto.IdType;
import io.mosip.authentication.core.logger.IdaLogger;
import io.mosip.authentication.core.spi.id.service.IdService;
import io.mosip.kernel.core.exception.ExceptionUtils;
import io.mosip.kernel.core.logger.spi.Logger;
import io.mosip.kernel.core.util.DateUtils;

/**
 * The class validates the UIN and VID.
 *
 * @author Arun Bose
 * @author Rakesh Roshan
 */
@Service
public class IdServiceImpl implements IdService {

	private static final String TOKEN = "TOKEN";

	private static final String BIOMETRICS = "biometrics";

	private static final String DEMOGRAPHICS = "demographics";

	/** The logger. */
	private static Logger logger = IdaLogger.getLogger(IdServiceImpl.class);

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

	@Autowired
	private ObjectMapper mapper;

	@Autowired
	private IdentityCacheRepository identityRepo;

	@Autowired
	private IdAuthSecurityManager securityManager;

	@Value("${" + IDA_ZERO_KNOWLEDGE_UNENCRYPTED_CREDENTIAL_ATTRIBUTES + ":#{null}" + "}")
	private String zkUnEncryptedCredAttribs;

	@Value("${"+ IDA_AUTH_PARTNER_ID  +"}")
	private String authPartherId;

	/*
	 * To get Identity data from IDRepo based on UIN
	 *
	 * @see
	 * org.mosip.auth.core.spi.idauth.service.IdAuthService#validateUIN(java.lang.
	 * String)
	 */
	@Override
	public Map getIdByUin(String uin, boolean isBio, Set filterAttributes) throws IdAuthenticationBusinessException {
		return getIdentity(uin, isBio, filterAttributes);
	}

	/*
	 * To get Identity data from IDRepo based on VID
	 *
	 * @see
	 * org.mosip.auth.core.spi.idauth.service.IdAuthService#validateVID(java.lang.
	 * String)
	 */
	@Override
	public Map getIdByVid(String vid, boolean isBio, Set filterAttributes) throws IdAuthenticationBusinessException {
		return getIdentity(vid, isBio, IdType.VID, filterAttributes);
	}

	/**
	 * Process the IdType and validates the Idtype and upon validation reference Id
	 * is returned in AuthRequestDTO.
	 *
	 * @param idvIdType idType
	 * @param idvId     id-number
	 * @param isBio the is bio
	 * @return map map
	 * @throws IdAuthenticationBusinessException the id authentication business
	 *                                           exception
	 */
	@Override
	public Map processIdType(String idvIdType, String idvId, boolean isBio, boolean markVidConsumed, Set filterAttributes)
			throws IdAuthenticationBusinessException {
		Map idResDTO = null;
		if (idvIdType.equals(IdType.UIN.getType())) {
			try {
				idResDTO = getIdByUin(idvId, isBio, filterAttributes);
			} catch (IdAuthenticationBusinessException e) {
				logger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), e.getErrorCode(), e.getErrorText());
				throw e;
			}
		} else if(idvIdType.equals(IdType.VID.getType())) {
			try {
				idResDTO = getIdByVid(idvId, isBio, filterAttributes);
			} catch (IdAuthenticationBusinessException e) {
				logger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), e.getErrorCode(), e.getErrorText());
				throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.INVALID_VID, e);
			}

			if(markVidConsumed) {
				updateVIDstatus(idvId);
			}
		}
		return idResDTO;
	}

	/**
	 * Store entry in Auth_txn table for all authentications.
	 *
	 * @param authTxn the auth txn
	 * @throws IdAuthenticationBusinessException the id authentication business
	 *                                           exception
	 */
	public void saveAutnTxn(AutnTxn authTxn) throws IdAuthenticationBusinessException {
		autntxnrepository.saveAndFlush(authTxn);
	}

	/**
	 * Gets the demo data.
	 *
	 * @param identity the identity
	 * @return the demo data
	 */
	@SuppressWarnings("unchecked")
	public Map getDemoData(Map identity) {
		return Optional.ofNullable(identity.get("response"))
				.filter(obj -> obj instanceof Map)
				.map(obj -> ((Map)obj).get("identity"))
				.filter(obj -> obj instanceof Map)
				.map(obj -> (Map) obj)
				.orElseGet(Collections::emptyMap);
	}

	public Map getIdentity(String id, boolean isBio, Set filterAttributes) throws IdAuthenticationBusinessException {
		return getIdentity(id, isBio, IdType.UIN, filterAttributes);
	}

	/**
	 * Fetch data from Id Repo based on Individual's UIN / VID value and all UIN.
	 *
	 * @param id
	 *            the uin
	 * @param isBio
	 *            the is bio
	 * @return the idenity
	 * @throws IdAuthenticationBusinessException
	 *             the id authentication business exception
	 */
	@SuppressWarnings("unchecked")
	public Map getIdentity(String id, boolean isBio, IdType idType, Set filterAttributes) throws IdAuthenticationBusinessException {
		String hashedId;
		try {
			hashedId = securityManager.hash(id);
		} catch (IdAuthenticationBusinessException e) {
			throw new IdAuthenticationBusinessException(
					IdAuthenticationErrorConstants.ID_NOT_AVAILABLE.getErrorCode(),
					String.format(IdAuthenticationErrorConstants.ID_NOT_AVAILABLE.getErrorMessage(),
							idType.getType()));
		}

		try {
			IdentityEntity entity = null;
			if (!identityRepo.existsById(hashedId)) {
				logger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "getIdentity",
						"Id not found in DB");
				throw new IdAuthenticationBusinessException(
						IdAuthenticationErrorConstants.ID_NOT_AVAILABLE.getErrorCode(),
						String.format(IdAuthenticationErrorConstants.ID_NOT_AVAILABLE.getErrorMessage(),
								idType.getType()));
			}

			if (isBio) {
				entity = identityRepo.getOne(hashedId);
			} else {
				Object[] data = identityRepo.findDemoDataById(hashedId).get(0);
				entity = new IdentityEntity();
				entity.setId(String.valueOf(data[0]));
				entity.setDemographicData((byte[]) data[1]);
				entity.setExpiryTimestamp(Objects.nonNull(data[2]) ? LocalDateTime.parse(String.valueOf(data[2])) : null);
				entity.setTransactionLimit(Objects.nonNull(data[3]) ? Integer.parseInt(String.valueOf(data[3])) : null);
				entity.setToken(String.valueOf(data[4]));
			}

			if (Objects.nonNull(entity.getExpiryTimestamp())
					&& DateUtils.before(entity.getExpiryTimestamp(), DateUtils.getUTCCurrentDateTime())) {
				logger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "getIdentity",
						idType.getType() + " expired/deactivated/revoked/blocked");
				IdAuthenticationErrorConstants errorConstant;
				if (idType == IdType.UIN) {
					errorConstant = IdAuthenticationErrorConstants.UIN_DEACTIVATED_BLOCKED;
				} else {
					errorConstant = IdAuthenticationErrorConstants.VID_EXPIRED_DEACTIVATED_REVOKED;
				}
				throw new IdAuthenticationBusinessException(errorConstant);
			}

			Map responseMap = new LinkedHashMap<>();

			Map demoDataMap = mapper.readValue(entity.getDemographicData(), Map.class);
			Set filterAttributesInLowercase = filterAttributes.isEmpty() ? Set.of()
					: filterAttributes.stream().map(String::toLowerCase).collect(Collectors.toSet());

			if (!filterAttributesInLowercase.isEmpty()) {
				Map demoDataMapPostFilter = demoDataMap.entrySet().stream()
						.filter(demo -> filterAttributesInLowercase.contains(demo.getKey().toLowerCase()))
						.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
				responseMap.put(DEMOGRAPHICS, decryptConfiguredAttributes(id, demoDataMapPostFilter));
			}

			if (entity.getBiometricData() != null) {
				Map bioDataMap = mapper.readValue(entity.getBiometricData(), Map.class);
				if (!filterAttributesInLowercase.isEmpty()) {
					Map bioDataMapPostFilter = bioDataMap.entrySet().stream()
							.filter(bio -> filterAttributesInLowercase.contains(bio.getKey().toLowerCase()))
							.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
					responseMap.put(BIOMETRICS, decryptConfiguredAttributes(id, bioDataMapPostFilter));
				}
			}
			responseMap.put(TOKEN, entity.getToken());
			return responseMap;
		} catch (IOException | DataAccessException | TransactionException | JDBCConnectionException e) {
			logger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "getIdentity",
					ExceptionUtils.getStackTrace(e));
			throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS, e);
		}
	}

	/**
	 * Decrypt the attributes as per configuration.
	 * @param id
	 * @param dataMap
	 * @return
	 * @throws IdAuthenticationBusinessException
	 */
	private Map decryptConfiguredAttributes(String id, Map dataMap) throws IdAuthenticationBusinessException {
		List zkUnEncryptedAttributes = getZkUnEncryptedAttributes()
				.stream().map(String::toLowerCase).collect(Collectors.toList());
		Map> partitionedMap = dataMap.entrySet()
				.stream()
				.collect(Collectors.partitioningBy(entry ->
								!zkUnEncryptedAttributes.contains(entry.getKey().toLowerCase()),
						Collectors.toMap(Entry::getKey, Entry::getValue)));
		Map dataToDecrypt = partitionedMap.get(true);
		Map plainData = partitionedMap.get(false);
		Map decryptedData = dataToDecrypt.isEmpty() ? Map.of()
				: securityManager.zkDecrypt(id, dataToDecrypt);
		Map finalDataStr = new LinkedHashMap<>();
		finalDataStr.putAll(plainData);
		finalDataStr.putAll(decryptedData);
		return finalDataStr.entrySet().stream().collect(Collectors.toMap(entry -> (String) entry.getKey(),
				entry -> {
					Object valObject = entry.getValue();
					if (valObject instanceof String) {
						String val = (String) valObject;
						if (val.trim().startsWith("[") || val.trim().startsWith("{")) {
							try {
								return mapper.readValue(val.getBytes(), Object.class);
							} catch (IOException e) {
								logger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(),
										"decryptConfiguredAttributes", ExceptionUtils.getStackTrace(e));
								return val;
							}
						} else {
							return val;
						}
					} else {
						return valObject;
					}
				}));
	}

	/**
	 * Get the list of attributes not to decrypt from config. Returns empty if no config is there
	 * @return
	 */
	private List getZkUnEncryptedAttributes() {
		return Optional.ofNullable(zkUnEncryptedCredAttribs).stream()
				.flatMap(str -> Stream.of(str.split(",")))
				.filter(str -> !str.isEmpty())
				.collect(Collectors.toList());
	}

	/**
	 * Update VID dstatus.
	 *
	 * @param vid
	 *            the vid
	 * @throws IdAuthenticationBusinessException
	 *             the id authentication business exception
	 */
	private void updateVIDstatus(String vid) throws IdAuthenticationBusinessException {
		try {
			vid = securityManager.hash(vid);
			// Assumption : If transactionLimit is null, id is considered as Perpetual VID
			// If transactionLimit is nonNull, id is considered as Temporary VID

			//get entity
			Optional entityOpt = identityRepo.findById(vid);
			if(entityOpt.isPresent()) {
				IdentityEntity entity =entityOpt.get();
				Integer transactionLimit = entity.getTransactionLimit();
				if (identityRepo.existsById(vid)
						&& Objects.nonNull(transactionLimit)){
					int newTransactionLimit = transactionLimit-1;
					if (newTransactionLimit>0) {
						entity.setTransactionLimit(newTransactionLimit);
						identityRepo.save(entity);
					} else {
						identityRepo.deleteById(vid);
					}
				}
			}

		} catch (DataAccessException | TransactionException | JDBCConnectionException e) {
			logger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "getIdentity",
					ExceptionUtils.getStackTrace(e));
			throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS, e);
		}
	}

	@Override
	public String getToken(Map idResDTO) {
		return (String) idResDTO.get(TOKEN);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy