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

io.mosip.authentication.common.service.util.BioMatcherUtil Maven / Gradle / Ivy

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

import static io.mosip.authentication.core.constant.IdAuthCommonConstants.BDB_DEAULT_PROCESSED_LEVEL;
import static io.mosip.authentication.core.constant.IdAuthConfigKeyConstants.IDA_BDB_PROCESSED_LEVEL;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;
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.Value;
import org.springframework.stereotype.Component;

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.logger.IdaLogger;
import io.mosip.authentication.core.spi.indauth.match.IdInfoFetcher;
import io.mosip.authentication.core.spi.indauth.match.IdMapping;
import io.mosip.kernel.biometrics.constant.BiometricFunction;
import io.mosip.kernel.biometrics.constant.BiometricType;
import io.mosip.kernel.biometrics.constant.ProcessedLevelType;
import io.mosip.kernel.biometrics.constant.PurposeType;
import io.mosip.kernel.biometrics.entities.BDBInfo;
import io.mosip.kernel.biometrics.entities.BIR;
import io.mosip.kernel.biometrics.entities.BIR.BIRBuilder;
import io.mosip.kernel.biometrics.entities.RegistryIDType;
import io.mosip.kernel.biosdk.provider.factory.BioAPIFactory;
import io.mosip.kernel.biosdk.provider.spi.iBioProviderApi;
import io.mosip.kernel.core.bioapi.exception.BiometricException;
import io.mosip.kernel.core.cbeffutil.constant.CbeffConstant;
import io.mosip.kernel.core.exception.ExceptionUtils;
import io.mosip.kernel.core.logger.spi.Logger;
import io.mosip.authentication.core.util.CryptoUtil;
import io.mosip.kernel.core.util.DateUtils;
import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * The Class BioMatcherUtil is the utility class to match biometrics, that uses the Bio SDK provider.
 * 
 * @author Loganathan Sekar
 */
@Component
public class BioMatcherUtil {
	
	/** The logger. */
	private static Logger logger = IdaLogger.getLogger(BioMatcherUtil.class);
	
	/** The id info fetcher. */
	@Autowired
	private IdInfoFetcher idInfoFetcher;
	
	/** The bio api factory. */
	@Autowired
	private BioAPIFactory bioApiFactory;
	
	@Value("${" + IDA_BDB_PROCESSED_LEVEL + ":" + BDB_DEAULT_PROCESSED_LEVEL + "}")
	private String bdbProcessedLevel;

	/**
	 * Match function.
	 *
	 * @param probe the probe
	 * @param gallery the gallery
	 * @param properties the properties
	 * @return the double
	 * @throws IdAuthenticationBusinessException the id authentication business exception
	 */
	public double match(Map probe, Map gallery,
			Map properties) throws IdAuthenticationBusinessException {
		logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "Inside match method");
		
		IdMapping[] idMappings = (IdMapping[]) properties.get(IdMapping.class.getSimpleName()); 
		BIR[][] objArrays = getBirValues(probe, gallery, idMappings);
		BIR[] reqInfoObj = objArrays[0];
		BIR[] entityBIR = objArrays[1];
		
		Map> reqBirByType =  getBirByType(reqInfoObj);
		logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "Sample BIR Type count: " + reqBirByType.size());
		logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "Sample BIR Types: " + reqBirByType.keySet().stream().map(BiometricType::name).collect(Collectors.joining(",")));
		
		Map> entityBirByType = getBirByType(entityBIR);
		logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "Gallery BIR Type count: " + entityBirByType.size());
		logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "Gallery BIR Types: " + entityBirByType.keySet().stream().map(BiometricType::name).collect(Collectors.joining(",")));

		
		boolean res = !reqBirByType.isEmpty();
		for (BiometricType modality : Arrays.asList(BiometricType.FINGER, BiometricType.IRIS, BiometricType.FACE)) {
			if(reqBirByType.containsKey(modality)) {
				logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "Matching for " + modality.name());
				try {
					iBioProviderApi bioProvider = bioApiFactory.getBioProvider(modality,
							BiometricFunction.MATCH);
					logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "bioProvider - " + bioProvider.getClass().getCanonicalName());
					List sample = reqBirByType.get(modality);
					List record = entityBirByType.get(modality);
					if(sample != null) {
						logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "sample birs list is not null");
						if(record != null) {
							logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "record birs list is not null");
							Map flags = new HashMap<>();
							flags.put("uniqueRefID", UUID.randomUUID().toString());
							flags.put("timestamp", DateUtils.getUTCCurrentDateTimeString());
							res =  bioProvider.verify(sample, record, modality, flags);
							logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "match response : " + res + " for " + modality);
							if(!res) {
								break;
							}
						} else {
							logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "record birs list is null");
							throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.BIOMETRIC_MISSING.getErrorCode(), 
									String.format(IdAuthenticationErrorConstants.BIOMETRIC_MISSING.getErrorMessage(), modality));
						}
					} else {
						logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "sample birs list is null");
					}
				} catch (BiometricException e) {
					logger.error(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction",
							String.format("%s: %s", e.getClass().getSimpleName(), ExceptionUtils.getStackTrace(e)));
					throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS_BIO, e);
				}
			}
		}
		
		logger.debug(IdAuthCommonConstants.SESSION_ID, "IDA", "matchFunction", "Match Result: " + res);
	
		return res ? (double)  100 : (double) 0;
	}

	/**
	 * Gets the bir by type.
	 *
	 * @param reqInfoObj the req info obj
	 * @return the bir by type
	 * @throws IdAuthenticationBusinessException 
	 */
	private Map> getBirByType(BIR[] reqInfoObj) throws IdAuthenticationBusinessException {
		 return	Stream.of(reqInfoObj)
				.filter(bir -> 
						Optional.ofNullable(bir.getBdbInfo())
								.map(BDBInfo::getType)
								.filter(list -> !list.isEmpty() && list.get(0) != null)
								.isPresent()
						)
				 .collect(Collectors.groupingBy(bir -> 
					BiometricType.fromValue(
							bir.getBdbInfo()
								.getType()
								.get(0)
								.value())));
	}

	/**
	 * Gets the bir values.
	 *
	 * @param reqInfo the req info
	 * @param entityInfo the entity info
	 * @param idMappings the id mappings
	 * @return the bir values
	 * @throws IdAuthenticationBusinessException 
	 */
	private BIR[][] getBirValues(Map reqInfo, Map entityInfo, IdMapping[] idMappings) throws IdAuthenticationBusinessException {
		BIR[] reqInfoObj;
		BIR[] entityInfoObj;
	
		int index = 0;
		if (reqInfo.keySet().stream().noneMatch(key -> key.startsWith(IdAuthCommonConstants.UNKNOWN_BIO))) {
			reqInfoObj = new BIR[reqInfo.size()];
			entityInfoObj = new BIR[reqInfo.size()];
	
			for (Map.Entry e : reqInfo.entrySet()) {
				String key = e.getKey();
				
				reqInfoObj[index] = getBir(e.getValue(), getType(key, idMappings));
				entityInfoObj[index] = getBir(entityInfo.get(key), getType(key, idMappings));
				index++;
			}
		} else {
			List reqMapexceptions = new ArrayList<>();
			Function, ? extends BIR> birMapper = e -> {
				try {
					return getBir(e.getValue(), getType(e.getKey(), idMappings));
				} catch (IdAuthenticationBusinessException e1) {
					reqMapexceptions.add(e1);
					return null;
				}
			};
			reqInfoObj = reqInfo.entrySet().stream()
							.map(birMapper)
							.toArray(s -> new BIR[s]);
			if(!reqMapexceptions.isEmpty()) {
				throw reqMapexceptions.get(0);
			}
			
			List entityMapexceptions = new ArrayList<>();

			entityInfoObj = entityInfo.entrySet()
								.stream()
								.map(birMapper)
								.toArray(s -> new BIR[s]);
			
			if(!entityMapexceptions.isEmpty()) {
				throw entityMapexceptions.get(0);
			}
		}
	
		return new BIR[][] { reqInfoObj, entityInfoObj };
	}

	/**
	 * Gets the type.
	 *
	 * @param idName the id name
	 * @param idMappings the id mappings
	 * @return the type
	 * @throws IdAuthenticationBusinessException 
	 */
	private BioInfo getType(String idName, IdMapping[] idMappings) throws IdAuthenticationBusinessException {
		//Note: Finger minutiea type not handled based on the requirement
		String typeForIdName = idInfoFetcher.getTypeForIdName(idName, idMappings).orElse("");
		long type = 0L;
		BiometricType singleType = null;
		if(typeForIdName.equalsIgnoreCase(BiometricType.FINGER.value())) {
			type = CbeffConstant.FORMAT_TYPE_FINGER;
			singleType = BiometricType.FINGER;
		} else if(typeForIdName.equalsIgnoreCase(BiometricType.IRIS.value())) {
			type = CbeffConstant.FORMAT_TYPE_IRIS;
			singleType = BiometricType.IRIS;
		} else if(typeForIdName.equalsIgnoreCase(BiometricType.FACE.value())) {
			type = CbeffConstant.FORMAT_TYPE_FACE;
			singleType = BiometricType.FACE;
		} else {
			 logger.error(IdAuthCommonConstants.SESSION_ID, "IDA", "getType",
						"Found invalid type: " + typeForIdName);
			 throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS);
		}
		String[] subTypes = Arrays.stream(idName.split(" "))
				.filter(str -> !str.isEmpty())
				.toArray(s -> new String[s]);
		return new BioInfo(String.valueOf(type), singleType, subTypes);
	}

	/**
	 * To create BIRType based on requested input.
	 *
	 * @param info            the info
	 * @param type the type
	 * @return the bir
	 */
	private BIR getBir(Object info, BioInfo type) {
		BIRBuilder birBuilder = new BIRBuilder();
		if (info instanceof String) {
			RegistryIDType format = new RegistryIDType();
			format.setOrganization(String.valueOf(CbeffConstant.FORMAT_OWNER));
			format.setType(type.getType());
			BDBInfo bdbInfo = new BDBInfo.BDBInfoBuilder()
					.withType(Collections.singletonList(type.getSingleType()))
					.withSubtype(Arrays.asList(type.getSubTypes()))
					.withLevel(ProcessedLevelType.fromValue(bdbProcessedLevel))
					.withFormat(format)
					.withPurpose(PurposeType.VERIFY).build();
			String reqInfoStr = (String) info;
			byte[] decodedrefInfo = decodeValue(reqInfoStr);
			birBuilder.withBdb(decodedrefInfo);
			birBuilder.withBdbInfo(bdbInfo);
		}
		return birBuilder.build();
	}

	/**
	 * Decode value.
	 *
	 * @param value
	 *            the value
	 * @return the byte[]
	 */
	private static byte[] decodeValue(String value) {
		return CryptoUtil.decodeBase64(value);
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Data
	
	/**
	 * Instantiates a new bio info.
	 *
	 * @param type the type
	 * @param singleType the single type
	 * @param subTypes the sub types
	 */
	@AllArgsConstructor
	private static class BioInfo {
		
		/** The type. */
		private String type;
		
		/** The single type. */
		private BiometricType singleType;
		
		/** The sub types. */
		private String[] subTypes;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy