/******************************************************************************* * 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.authmodule_eIDASv2.tasks; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.w3c.dom.Element; import org.w3c.dom.Node; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import at.asitplus.eidas.specific.connector.MSConnectorEventCodes; import at.asitplus.eidas.specific.connector.MSeIDASNodeConstants; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.Constants; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.DAO.ERnBeIDData; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.exception.SZRCommunicationException; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.exception.eIDASAttributeException; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.service.IeIDPostProcessingService; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.szr.SZRClient; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.utils.eIDASResponseUtils; import at.gv.e_government.reference.namespace.persondata._20020228.AlternativeNameType; import at.gv.e_government.reference.namespace.persondata._20020228.PersonNameType; import at.gv.e_government.reference.namespace.persondata._20020228.PhysicalPersonType; import at.gv.egiz.eaaf.core.api.data.EAAFConstants; import at.gv.egiz.eaaf.core.api.data.PVPAttributeDefinitions; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.exceptions.EAAFException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.idp.auth.builder.BPKBuilder; import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; import at.gv.egiz.eaaf.core.impl.idp.auth.data.SimpleIdentityLinkAssertionParser; import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; import at.gv.egiz.eaaf.core.impl.utils.DOMUtils; import at.gv.egiz.eaaf.core.impl.utils.XPathUtils; 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 szrservices.IdentityLinkType; import szrservices.PersonInfoType; import szrservices.TravelDocumentType; /** * @author tlenz * */ @Component("CreateIdentityLinkTask") public class CreateIdentityLinkTask extends AbstractAuthServletTask { private static final Logger log = LoggerFactory.getLogger(CreateIdentityLinkTask.class); @Autowired private IConfiguration basicConfig; @Autowired private SZRClient szrClient; @Autowired private IeIDPostProcessingService eIDPostProcessor; /* (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{ AuthProcessDataWrapper authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class); ILightResponse eIDASResponse = authProcessData.getGenericDataFromSession( Constants.DATA_FULL_EIDAS_RESPONSE, ILightResponse.class); Map simpleAttrMap = converteIDASAttrToSimpleMap(eIDASResponse.getAttributes().getAttributeMap()); IIdentityLink identityLink = null; String bPK = null; //post-process eIDAS attributes ERnBeIDData eIDData = eIDPostProcessor.postProcess(simpleAttrMap); //write MDS into technical log and revision log writeMDSLogInformation(eIDData); //connect SZR-Gateway if(basicConfig.getBasicMOAIDConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USEDUMMY, false)) { log.warn("SZR-Dummy IS ACTIVE! IdentityLink is NOT VALID!!!!"); // create fake IdL // - fetch IdL template from resources InputStream s = CreateIdentityLinkTask.class.getResourceAsStream("/resources/xmldata/fakeIdL_IdL_template.xml"); Element idlTemplate = DOMUtils.parseXmlValidating(s); identityLink = new SimpleIdentityLinkAssertionParser(idlTemplate).parseIdentityLink(); // replace data Element idlassertion = identityLink.getSamlAssertion(); // - set fake baseID; Node prIdentification = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); prIdentification.getFirstChild().setNodeValue(eIDData.getPseudonym()); //build personal identifier which looks like a baseID // String fakeBaseID = new BPKBuilder().buildBPK(eIdentifier, "baseID"); // Logger.info("Map eIDAS eIdentifier:" + eIdentifier + " to fake baseID:" + fakeBaseID); // prIdentification.getFirstChild().setNodeValue(fakeBaseID); // - set last name Node prFamilyName = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_FAMILY_NAME_XPATH); prFamilyName.getFirstChild().setNodeValue(eIDData.getFamilyName()); // - set first name Node prGivenName = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_GIVEN_NAME_XPATH); prGivenName.getFirstChild().setNodeValue(eIDData.getGivenName()); // - set date of birth Node prDateOfBirth = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_DATE_OF_BIRTH_XPATH); prDateOfBirth.getFirstChild().setNodeValue(eIDData.getFormatedDateOfBirth()); identityLink = new SimpleIdentityLinkAssertionParser(idlassertion).parseIdentityLink(); Pair bPKCalc = new BPKBuilder().generateAreaSpecificPersonIdentifier( identityLink.getIdentificationValue(), identityLink.getIdentificationType(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()); bPK = bPKCalc.getFirst(); } else { //contact SZR Gateway log.debug("Starting connecting SZR Gateway"); PersonInfoType personInfo = new PersonInfoType(); PersonNameType personName = new PersonNameType(); PhysicalPersonType naturalPerson = new PhysicalPersonType(); TravelDocumentType eDocument = new TravelDocumentType(); naturalPerson.setName(personName ); personInfo.setPerson(naturalPerson ); personInfo.setTravelDocument(eDocument ); //person information personName.setFamilyName(eIDData.getFamilyName()); personName.setGivenName(eIDData.getGivenName()); naturalPerson.setDateOfBirth(eIDData.getFormatedDateOfBirth()); eDocument.setIssuingCountry(eIDData.getCitizenCountryCode()); eDocument.setDocumentNumber(eIDData.getPseudonym()); //eID document information eDocument.setDocumentType(basicConfig.getBasicConfiguration( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_EDOCUMENTTYPE, Constants.SZR_CONSTANTS_DEFAULT_DOCUMENT_TYPE)); //set PlaceOfBirth if available if (eIDData.getPlaceOfBirth() != null) { log.trace("Find 'PlaceOfBirth' attribute: " + eIDData.getPlaceOfBirth()); if (basicConfig.getBasicMOAIDConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETPLACEOFBIRTHIFAVAILABLE, true)) { naturalPerson.setPlaceOfBirth(eIDData.getPlaceOfBirth()); log.trace("Adding 'PlaceOfBirth' to ERnB request ... "); } } //set BirthName if available if (eIDData.getBirthName() != null) { log.trace("Find 'BirthName' attribute: " + eIDData.getBirthName()); if (basicConfig.getBasicMOAIDConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETBIRTHNAMEIFAVAILABLE, true)) { AlternativeNameType alternativeName = new AlternativeNameType(); naturalPerson.setAlternativeName(alternativeName ); alternativeName.setFamilyName(eIDData.getBirthName()); log.trace("Adding 'BirthName' to ERnB request ... "); } } IdentityLinkType result = szrClient.getIdentityLinkInRawMode(personInfo); Element idlFromSZR = (Element)result.getAssertion(); identityLink = new SimpleIdentityLinkAssertionParser(idlFromSZR).parseIdentityLink(); //write ERnB inputdata into revisionlog if (basicConfig.getBasicMOAIDConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_WORKAROUND_REVISIONLOGDATASTORE_ACTIVE, false)) { revisionsLogger.logEvent(pendingReq, MSConnectorEventCodes.SZR_ERNB_EIDAS_RAW_ID, (String)simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)); revisionsLogger.logEvent(pendingReq, MSConnectorEventCodes.SZR_ERNB_EIDAS_ERNB_ID, eIDData.getPseudonym()); } //get bPK from SZR if (basicConfig.getBasicMOAIDConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USESRZFORBPKGENERATION, true)) { bPK = szrClient.getBPK( personInfo, pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier(), basicConfig.getBasicConfiguration( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ, "no VKZ defined")); } else { log.debug("Calculating bPK from baseId ... "); Pair bPKCalc = new BPKBuilder().generateAreaSpecificPersonIdentifier( identityLink.getIdentificationValue(), identityLink.getIdentificationType(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()); bPK = bPKCalc.getFirst(); } } if (identityLink == null) { log.error("ERnB did not return an identity link."); throw new SZRCommunicationException("ernb.00", null); } revisionsLogger.logEvent(pendingReq, MSConnectorEventCodes.SZR_IDL_RECEIVED, identityLink.getSamlAssertion().getAttribute(SimpleIdentityLinkAssertionParser.ASSERTIONID)); if (bPK == null) { log.error("ERnB did not return a bPK for target: " + pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()); throw new SZRCommunicationException("ernb.01", null); } revisionsLogger.logEvent(pendingReq, MSConnectorEventCodes.SZR_BPK_RECEIVED); log.debug("ERnB communication was successfull"); authProcessData.setForeigner(true); authProcessData.setIdentityLink(identityLink); authProcessData.setGenericDataToSession( PVPAttributeDefinitions.EID_ISSUING_NATION_NAME, eIDASResponseUtils.parseEidasPersonalIdentifier((String) simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)).getFirst()); //set bPK and bPKType into auth session authProcessData.setGenericDataToSession( PVPAttributeDefinitions.BPK_NAME, extendBPKbyPrefix( bPK, pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()) ); authProcessData.setGenericDataToSession( PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()); //store pending-request requestStoreage.storePendingRequest(pendingReq); } catch (eIDASAttributeException e) { throw new TaskExecutionException(pendingReq, "Minimum required eIDAS attributeset not found.", e); } catch (EAAFException e) { throw new TaskExecutionException(pendingReq, "IdentityLink generation for foreign person FAILED.", e); } catch (Exception e) { log.error("IdentityLink generation for foreign person FAILED.", e); throw new TaskExecutionException(pendingReq, "IdentityLink generation for foreign person FAILED.", e); } } private String extendBPKbyPrefix(String bpk, String type) { String bPKType = null; if (type.startsWith(EAAFConstants.URN_PREFIX_WBPK)) bPKType = type.substring((EAAFConstants.URN_PREFIX_WBPK).length()); else if (type.startsWith(EAAFConstants.URN_PREFIX_CDID)) bPKType = type.substring((EAAFConstants.URN_PREFIX_CDID).length()); else if (type.startsWith(EAAFConstants.URN_PREFIX_EIDAS)) bPKType = type.substring((EAAFConstants.URN_PREFIX_EIDAS).length()); if (bPKType != null ) { log.trace("Authenticate user with bPK/wbPK " + bpk + " and Type=" + bPKType); return bPKType + ":" + bpk; } else { log.warn("Service Provider Target with: " + type + " is NOT supported. Set bPK as it is ..."); return bpk; } } private Map converteIDASAttrToSimpleMap( ImmutableMap, ImmutableSet>> attributeMap) { Map result = new HashMap(); for (AttributeDefinition el : attributeMap.keySet()) { final Class parameterizedType = el.getParameterizedType(); if ((DateTime.class).equals(parameterizedType)) { 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)) { 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 { List natPersonIdObj = eIDASResponseUtils.translateStringListAttribute(el, attributeMap.get(el).asList()); 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; } private void writeMDSLogInformation(ERnBeIDData eIDData) { //log MDS and country code into technical log if (basicConfig.getBasicMOAIDConfigurationBoolean( MSeIDASNodeConstants.PROP_CONFIG_TECHNICALLOG_WRITE_MDS_INTO_TECH_LOG, false)) log.info("eIDAS Auth. for user: " + eIDData.getGivenName() + " " + eIDData.getFamilyName() + " " + eIDData.getFormatedDateOfBirth() + " " + "from " + eIDData.getCitizenCountryCode()); //log MDS and country code into revision log if (basicConfig.getBasicMOAIDConfigurationBoolean( MSeIDASNodeConstants.PROP_CONFIG_REVISIONLOG_WRITE_MDS_INTO_REVISION_LOG, false)) revisionsLogger.logEvent(pendingReq, MSConnectorEventCodes.RESPONSE_FROM_EIDAS_MDSDATA, "{" + eIDData.getGivenName() + "," + eIDData.getFamilyName() + "," + eIDData.getFormatedDateOfBirth() + "," + eIDData.getCitizenCountryCode() + "}"); } }