/* * 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 java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.Nullable; import org.jose4j.lang.JoseException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.w3c.dom.Element; import com.fasterxml.jackson.core.JsonProcessingException; import at.asitplus.eidas.specific.core.MsConnectorEventCodes; import at.asitplus.eidas.specific.core.MsEidasNodeConstants; import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.szr.SzrClient; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.SzrCommunicationException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.AuthBlockSigningService; import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils; 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.EaafStorageException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.builder.BpkBuilder; import at.gv.egiz.eaaf.core.impl.data.Pair; 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 lombok.Data; import lombok.extern.slf4j.Slf4j; import szrservices.IdentityLinkType; /** * Task that creates the IdentityLink for an eIDAS authenticated person. * Input: * * Output: * * Transitions: * * @author tlenz */ @Slf4j @Component("CreateIdentityLinkTask") public class CreateIdentityLinkTask extends AbstractAuthServletTask { @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired private IConfiguration basicConfig; @Autowired private SzrClient szrClient; @Autowired private AuthBlockSigningService authBlockSigner; private static final String EID_STATUS = "urn:eidgvat:eid.status.eidas"; /* * (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 { /*TODO: needs more re-factoring if we finalize CreateNewErnpEntryTask and we know how add entries into ERnP * Maybe, we can fully replace eidData by matchedPersonData, * because matchedPersonData holds the result after a successful matching process. * * Currently, we only add a work-around to operate without new ERnP implementation. */ final SimpleEidasData eidData = MatchingTaskUtils.getInitialEidasData(pendingReq); MatchedPersonResult matchedPersonData = MatchingTaskUtils.getFinalMatchingResult(pendingReq); // write log information based on current configuration writeMdsLogInformation(eidData); //request SZR based on IDL or E-ID mode if (pendingReq.getServiceProviderConfiguration() .isConfigurationValue(MsEidasNodeConstants.PROP_CONFIG_SP_NEW_EID_MODE, false)) { executeEidMode(eidData, matchedPersonData); } else { executeIdlMode(eidData, matchedPersonData); } storeGenericInfoToSession(eidData); requestStoreage.storePendingRequest(pendingReq); } catch (final EidasAttributeException e) { throw new TaskExecutionException(pendingReq, "Minimum required eIDAS attributeset not found.", e); } catch (final EaafException e) { throw new TaskExecutionException(pendingReq, "IdentityLink generation for foreign person FAILED.", e); } catch (final Exception e) { log.error("IdentityLink generation for foreign person FAILED.", e); throw new TaskExecutionException(pendingReq, "IdentityLink generation for foreign person FAILED.", e); } } private void storeGenericInfoToSession(SimpleEidasData eidData) throws EaafStorageException { AuthProcessDataWrapper authProcessData = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq); authProcessData.setForeigner(true); authProcessData.setGenericDataToSession(PvpAttributeDefinitions.EID_ISSUING_NATION_NAME, eidData.getCitizenCountryCode()); } private void executeIdlMode(SimpleEidasData eidData, MatchedPersonResult matchedPersonData) throws EaafException { //request SZR SzrResultHolder idlResult = requestSzrForIdentityLink(matchedPersonData); //write revision-Log entry for personal-identifier mapping writeExtendedRevisionLogEntry(eidData, eidData.getPersonalIdentifier()); //check result-data and write revision-log based on current state checkStateAndWriteRevisionLog(idlResult); //inject personal-data into session AuthProcessDataWrapper authProcessDataWrapper = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq); authProcessDataWrapper.setIdentityLink(idlResult.getIdentityLink()); authProcessDataWrapper.setEidProcess(false); // set bPK and bPKType into auth session authProcessDataWrapper.setGenericDataToSession(PvpAttributeDefinitions.BPK_NAME, extendBpkByPrefix( idlResult.getBpK(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier())); authProcessDataWrapper.setGenericDataToSession(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, pendingReq.getServiceProviderConfiguration() .getAreaSpecificTargetIdentifier()); } private void executeEidMode(SimpleEidasData eidData, MatchedPersonResult matchedPersonData) throws JsonProcessingException, EaafException, JoseException { // get encrypted baseId log.debug("Requesting encrypted baseId by already matched person information ... "); String vsz = szrClient.getEncryptedStammzahl(matchedPersonData); //write revision-Log entry and extended infos personal-identifier mapping revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_VSZ_RECEIVED); writeExtendedRevisionLogEntry(eidData, eidData.getPersonalIdentifier()); // get eIDAS bind String signedEidasBind = szrClient .getEidasBind(vsz, authBlockSigner.getBase64EncodedPublicKey(), EID_STATUS, matchedPersonData); revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_EIDASBIND_RECEIVED); AuthProcessDataWrapper authProcessDataWrapper = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq); authProcessDataWrapper.setGenericDataToSession(MsEidasNodeConstants.AUTH_DATA_EIDAS_BIND, signedEidasBind); //get signed AuthBlock String jwsSignature = authBlockSigner.buildSignedAuthBlock(pendingReq); revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.TECH_AUCHBLOCK_CREATED); authProcessDataWrapper.setGenericDataToSession(MsEidasNodeConstants.AUTH_DATA_SZR_AUTHBLOCK, jwsSignature); //inject personal-data into session authProcessDataWrapper.setEidProcess(true); } private void writeExtendedRevisionLogEntry(SimpleEidasData eidData, String personalIdentifier) { // write ERnP input-data into revision-log if (basicConfig.getBasicConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_SZRCLIENT_WORKAROUND_REVISIONLOGDATASTORE_ACTIVE, false)) { revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_ERNB_EIDAS_RAW_ID, personalIdentifier); revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_ERNB_EIDAS_ERNB_ID, eidData.getPseudonym()); } } private SzrResultHolder requestSzrForIdentityLink(MatchedPersonResult matchedPersonData) throws EaafException { //request IdentityLink from SZR log.debug("Requesting encrypted baseId by already matched person information ... "); IdentityLinkType result = szrClient.getIdentityLinkInRawMode(matchedPersonData); final Element idlFromSzr = (Element) result.getAssertion(); final IIdentityLink identityLink = new SimpleIdentityLinkAssertionParser(idlFromSzr).parseIdentityLink(); // get bPK from SZR String bpk = null; String targetId = pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier(); boolean debugUseSzrForBpk = basicConfig .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USESRZFORBPKGENERATION, true); if (debugUseSzrForBpk) { String vkz = basicConfig .getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ, "no VKZ defined"); List bpkList = szrClient.getBpk(matchedPersonData, targetId, vkz); if (!bpkList.isEmpty()) { bpk = bpkList.get(0); } } else { log.debug("Calculating bPK from baseId ... "); String idValue = identityLink.getIdentificationValue(); String idType = identityLink.getIdentificationType(); final Pair bpkCalc = BpkBuilder.generateAreaSpecificPersonIdentifier(idValue, idType, targetId); bpk = bpkCalc.getFirst(); } return new SzrResultHolder(identityLink, bpk); } private void checkStateAndWriteRevisionLog(SzrResultHolder idlResult) throws SzrCommunicationException { // write some infos into revision log if (idlResult.getIdentityLink() == null) { log.error("ERnB did not return an identity link."); throw new SzrCommunicationException("ernb.00", null); } String assertionId = idlResult.getIdentityLink().getSamlAssertion() .getAttribute(SimpleIdentityLinkAssertionParser.ASSERTIONID); revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_IDL_RECEIVED, assertionId); if (idlResult.getBpK() == 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"); } private String extendBpkByPrefix(String bpk, String type) { String bpkType = getBpkType(type); 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; } } @Nullable private String getBpkType(String type) { if (type.startsWith(EaafConstants.URN_PREFIX_WBPK)) { return type.substring(EaafConstants.URN_PREFIX_WBPK.length()); } else if (type.startsWith(EaafConstants.URN_PREFIX_CDID)) { return type.substring(EaafConstants.URN_PREFIX_CDID.length()); } else if (type.startsWith(EaafConstants.URN_PREFIX_EIDAS)) { return type.substring(EaafConstants.URN_PREFIX_EIDAS.length()); } else { return null; } } /** * write MDS into technical log and revision log. */ private void writeMdsLogInformation(SimpleEidasData eidData) { boolean writeMdsInTechLog = basicConfig .getBasicConfigurationBoolean(MsEidasNodeConstants.PROP_CONFIG_TECHNICALLOG_WRITE_MDS_INTO_TECH_LOG, false); if (writeMdsInTechLog) { log.info("eIDAS Auth. for user: " + eidData.getGivenName() + " " + eidData.getFamilyName() + " " + eidData .getDateOfBirth() + " " + "from " + eidData.getCitizenCountryCode()); } boolean writeMdsInRevLog = basicConfig .getBasicConfigurationBoolean(MsEidasNodeConstants.PROP_CONFIG_REVISIONLOG_WRITE_MDS_INTO_REVISION_LOG, false); if (writeMdsInRevLog) { revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.RESPONSE_FROM_EIDAS_MDSDATA, "{" + eidData.getGivenName() + "," + eidData.getFamilyName() + "," + eidData .getDateOfBirth() + "," + eidData.getCitizenCountryCode() + "}"); } } @Data private static class SzrResultHolder { final IIdentityLink identityLink; final String bpK; } }