/* * Copyright 2018 A-SIT Plus GmbH * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "License"); * You may not use this work except in compliance with the License. * You may obtain a copy of the License at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ package at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks; import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MergedRegisterSearchResult; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; import at.asitplus.eidas.specific.modules.auth.eidas.v2.ernb.IErnbClient; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.ManualFixNecessaryException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.ICountrySpecificDetailSearchProcessor; import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils; import at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr.IZmrClient; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.data.Triple; import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import eu.eidas.auth.commons.attribute.AttributeDefinition; import eu.eidas.auth.commons.attribute.AttributeValue; import eu.eidas.auth.commons.light.ILightResponse; import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Task that searches ErnB and ZMR before adding person to SZR. * * @author tlenz */ @Slf4j @Component("InitialSearchTask") public class InitialSearchTask extends AbstractAuthServletTask { private List handlers = new ArrayList<>(); // @Autowired // private ApplicationContext context; private IErnbClient ernbClient; private IZmrClient zmrClient; /* * (non-Javadoc) * * @see at.gv.egovernment.moa.id.process.springweb.MoaIdTask#execute(at.gv. * egovernment.moa.id.process.api.ExecutionContext, * javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse) */ @Override public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response) throws TaskExecutionException { try { final AuthProcessDataWrapper authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class); final ILightResponse eidasResponse = authProcessData .getGenericDataFromSession(Constants.DATA_FULL_EIDAS_RESPONSE, ILightResponse.class); final Map simpleAttrMap = convertEidasAttrToSimpleMap( eidasResponse.getAttributes().getAttributeMap()); // post-process eIDAS attributes final SimpleEidasData eidData = convertSimpleMapToSimpleData(simpleAttrMap); String bpK = step2(eidData); authProcessData.setGenericDataToSession(Constants.DATA_RESULT_MATCHING_BPK, bpK); } catch (final Exception e) { log.error("Initial search FAILED.", e); throw new TaskExecutionException(pendingReq, "Initial search FAILED.", e); } } private String step2(SimpleEidasData eidData) throws TaskExecutionException { String personIdentifier = eidData.getPseudonym(); //search in register(step 2) MergedRegisterSearchResult result = searchInZmrAndErnp(personIdentifier); if (result.getResultCount() == 0) { return step5(result, eidData); } else if (result.getResultCount() == 1) { return step3(result, eidData); } //else if (result.getResultCount() > 1) { throw new TaskExecutionException(pendingReq, "Initial search - Kitt Process necessary.", new ManualFixNecessaryException(personIdentifier)); // } // return null; } private SimpleEidasData convertSimpleMapToSimpleData(Map eidasAttrMap) throws EidasAttributeException, EidPostProcessingException { SimpleEidasData simpleEidasData = new SimpleEidasData(); final Object eIdentifierObj = eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); final Triple eIdentifier = EidasResponseUtils.parseEidasPersonalIdentifier((String) eIdentifierObj); simpleEidasData.setCitizenCountryCode(eIdentifier.getFirst()); // MDS attributes simpleEidasData.setPseudonym(EidasResponseUtils.processPseudonym( eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER))); simpleEidasData.setFamilyName(EidasResponseUtils.processFamilyName( eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTFAMILYNAME))); simpleEidasData.setGivenName(EidasResponseUtils.processGivenName( eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTGIVENNAME))); simpleEidasData.setDateOfBirth(EidasResponseUtils.processDateOfBirthToString( eidasAttrMap.get(Constants.eIDAS_ATTR_DATEOFBIRTH))); // additional attributes simpleEidasData.setPlaceOfBirth(EidasResponseUtils.processPlaceOfBirth( eidasAttrMap.get(Constants.eIDAS_ATTR_PLACEOFBIRTH))); simpleEidasData.setBirthName(EidasResponseUtils.processBirthName( eidasAttrMap.get(Constants.eIDAS_ATTR_BIRTHNAME))); simpleEidasData.setAddress(EidasResponseUtils.processAddress( eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTADDRESS))); if (eidasAttrMap.containsKey(Constants.eIDAS_ATTR_TAXREFERENCE)) { simpleEidasData.setTaxNumber(EidasResponseUtils.processTaxReference( eidasAttrMap.get(Constants.eIDAS_ATTR_TAXREFERENCE))); } //TODO other additional attributes return simpleEidasData; } private String step3(MergedRegisterSearchResult result, SimpleEidasData eidData) throws TaskExecutionException { //check if data from eidas authentication matches with data from register log.debug("Compare " + result + " with " + eidData); //TODO check if data matches try { if (eidData.equalsRegisterData(result)) { //TODO return result.getBpk(); } else { return step4(result, eidData); } } catch (WorkflowException e) { throw new TaskExecutionException(pendingReq, "Initial search - Kitt Process necessary.", e); } } private String step4(MergedRegisterSearchResult result, SimpleEidasData eidData) throws WorkflowException { log.debug("Update " + result + " with " + eidData); //TODO return result.getBpk(); } private String step5(MergedRegisterSearchResult result, SimpleEidasData eidData) throws TaskExecutionException { String citizenCountry = eidData.getCitizenCountryCode(); ICountrySpecificDetailSearchProcessor foundHandler = null; for (final ICountrySpecificDetailSearchProcessor el : handlers) { //5 check if country specific search is possible if (el.canHandle(citizenCountry, eidData)) { log.debug("Found suitable country specific search handler for " + citizenCountry + " by using: " + el.getName()); foundHandler = el; break; } } if (foundHandler == null) { //MDS search return step8(result, eidData); } else { //country specific search return step6(foundHandler, result, eidData); } } private String step6(ICountrySpecificDetailSearchProcessor countrySpecificDetailSearchProcessor, MergedRegisterSearchResult initialSearchResult, SimpleEidasData eidData) throws TaskExecutionException { //6 country specific search MergedRegisterSearchResult countrySpecificDetailSearchResult = countrySpecificDetailSearchProcessor.search(eidData); switch (countrySpecificDetailSearchResult.getResultCount()) { case 0: return step8(initialSearchResult, eidData); case 1: return step7a(initialSearchResult, countrySpecificDetailSearchResult, eidData); default://should not happen throw new TaskExecutionException(pendingReq, "Detail search - Kitt Process necessary.", new ManualFixNecessaryException(eidData)); } } private String step7a(MergedRegisterSearchResult initialSearchResult, MergedRegisterSearchResult countrySpecificDetailSearchResult, SimpleEidasData eidData) throws TaskExecutionException { //TODO automerge log.debug("Automerge " + initialSearchResult + " with " + eidData + " " + countrySpecificDetailSearchResult); try { if (initialSearchResult.getResultCount() != 0) { throw new WorkflowException("initialSearchResult.getResultCount() != 0"); } if (countrySpecificDetailSearchResult.getResultCount() != 1) { throw new WorkflowException("countrySpecificDetailSearchResult.getResultCount() != 1"); } if (countrySpecificDetailSearchResult.getResultsZmr().size() == 1) { //update ZMR zmrClient.update(countrySpecificDetailSearchResult.getResultsZmr().get(0), eidData); } if (countrySpecificDetailSearchResult.getResultsErnb().size() == 1) { //update ErnB ernbClient.update(countrySpecificDetailSearchResult.getResultsErnb().get(0), eidData); } String bpK = countrySpecificDetailSearchResult.getBpk(); return bpK; } catch (WorkflowException e) { throw new TaskExecutionException(pendingReq, "Step7a failed.", e); } } private String step8(MergedRegisterSearchResult initialSearchResult, SimpleEidasData eidData) { MergedRegisterSearchResult mdsSearchResult = new MergedRegisterSearchResult(); ArrayList resultsZmr = zmrClient.searchWithMds(eidData.getGivenName(), eidData.getFamilyName(), eidData.getDateOfBirth()); mdsSearchResult.setResultsZmr(resultsZmr); ArrayList resultsErnb = ernbClient.searchWithMds(eidData.getGivenName(), eidData.getFamilyName(), eidData.getDateOfBirth()); mdsSearchResult.setResultsErnb(resultsErnb); log.debug("Automerge " + initialSearchResult + " with " + eidData + " " + mdsSearchResult); //TODO return "105"; } private MergedRegisterSearchResult searchInZmrAndErnp(String personIdentifier) { MergedRegisterSearchResult initialSearchResult = new MergedRegisterSearchResult(); ArrayList resultsZmr = zmrClient.searchWithPersonIdentifer(personIdentifier); initialSearchResult.setResultsZmr(resultsZmr); ArrayList resultsErnb = ernbClient.searchWithPersonIdentifer(personIdentifier); initialSearchResult.setResultsErnb(resultsErnb); return initialSearchResult; } private Map convertEidasAttrToSimpleMap( ImmutableMap, ImmutableSet>> attributeMap) { final Map result = new HashMap<>(); for (final AttributeDefinition el : attributeMap.keySet()) { final Class parameterizedType = el.getParameterizedType(); if (DateTime.class.equals(parameterizedType)) { final DateTime attribute = EidasResponseUtils.translateDateAttribute(el, attributeMap.get(el).asList()); if (attribute != null) { result.put(el.getFriendlyName(), attribute); log.trace("Find attr '" + el.getFriendlyName() + "' with value: " + attribute.toString()); } else { log.info("Ignore empty 'DateTime' attribute"); } } else if (PostalAddress.class.equals(parameterizedType)) { final PostalAddress addressAttribute = EidasResponseUtils .translateAddressAttribute(el, attributeMap.get(el).asList()); if (addressAttribute != null) { result.put(el.getFriendlyName(), addressAttribute); log.trace("Find attr '" + el.getFriendlyName() + "' with value: " + addressAttribute.toString()); } else { log.info("Ignore empty 'PostalAddress' attribute"); } } else { final List natPersonIdObj = EidasResponseUtils .translateStringListAttribute(el, attributeMap.get(el).asList()); final String stringAttr = natPersonIdObj.get(0); if (StringUtils.isNotEmpty(stringAttr)) { result.put(el.getFriendlyName(), stringAttr); log.trace("Find attr '" + el.getFriendlyName() + "' with value: " + stringAttr); } else { log.info("Ignore empty 'String' attribute"); } } } log.debug("Receive #" + result.size() + " attributes with names: " + result.keySet().toString()); return result; } //just for testing //TODO is there a nicer solution? @Autowired public void setErnbClient(IErnbClient ernbClient) { this.ernbClient = ernbClient; } @Autowired public void setZmrClient(IZmrClient zmrClient) { this.zmrClient = zmrClient; } @Autowired public void setHandlers(List handlers) { this.handlers = handlers; log.info("# " + handlers.size() + " country specific detail search services are registrated"); } }