/* * 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.connector.builder; import java.util.Date; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.springframework.stereotype.Service; import com.google.common.collect.Streams; import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; import at.asitplus.eidas.specific.modules.auth.idaustria.IdAustriaAuthConstants; import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions; import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions; import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions.EidIdentityStatusLevelValues; import at.gv.egiz.eaaf.core.api.idp.IAuthData; import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration; import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer; import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException; import at.gv.egiz.eaaf.core.exceptions.EaafBuilderException; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.data.Triple; import at.gv.egiz.eaaf.core.impl.idp.AuthenticationData; import at.gv.egiz.eaaf.core.impl.idp.EidAuthenticationData; import at.gv.egiz.eaaf.core.impl.idp.auth.builder.AbstractAuthenticationDataBuilder; import at.gv.egiz.eaaf.core.impl.idp.auth.data.EidAuthProcessDataWrapper; import lombok.extern.slf4j.Slf4j; @Service("AuthenticationDataBuilder") @Slf4j public class AuthenticationDataBuilder extends AbstractAuthenticationDataBuilder { private static final String ERROR_B11 = "builder.11"; @Override protected IAuthData buildDeprecatedAuthData(IRequest pendingReq) throws EaafException { final EidAuthProcessDataWrapper authProcessData = pendingReq.getSessionData(EidAuthProcessDataWrapper.class); final EidAuthenticationData authData = new EidAuthenticationData(); // set basis infos super.generateDeprecatedBasicAuthData(authData, pendingReq, authProcessData); // set specific informations authData.setSsoSessionValidTo( new Date(new Date().getTime() + MsEidasNodeConstants.DEFAULT_PVP_ASSERTION_VALIDITY * 60 * 1000)); authData.setEidStatus(authProcessData.isTestIdentity() ? EidIdentityStatusLevelValues.TESTIDENTITY : EidIdentityStatusLevelValues.IDENTITY); return authData; } @Override protected void buildServiceSpecificAuthenticationData(IAuthData authData, IRequest pendingReq) throws EaafException { if (authData instanceof EidAuthenticationData) { ((EidAuthenticationData) authData).setGenericData( ExtendedPvpAttributeDefinitions.EID_PII_TRANSACTION_ID_NAME, pendingReq.getUniquePiiTransactionIdentifier()); log.trace("Inject piiTransactionId: {} into AuthData", pendingReq.getUniquePiiTransactionIdentifier()); // set specific informations ((EidAuthenticationData) authData).setSsoSessionValidTo( new Date(new Date().getTime() + MsEidasNodeConstants.DEFAULT_PVP_ASSERTION_VALIDITY * 60 * 1000)); // set E-ID status-level final EidAuthProcessDataWrapper authProcessData = pendingReq.getSessionData(EidAuthProcessDataWrapper.class); ((EidAuthenticationData) authData).setEidStatus(authProcessData.isTestIdentity() ? EidIdentityStatusLevelValues.TESTIDENTITY : EidIdentityStatusLevelValues.IDENTITY); // handle mandate informations buildMandateInformation((EidAuthenticationData) authData, pendingReq, authProcessData); } else { throw new RuntimeException("Can not inject PiiTransactionId because AuthData is of unknown type: " + authData.getClass().getName()); } } @Override protected IAuthData getAuthDataInstance(IRequest arg0) throws EaafException { return new EidAuthenticationData(); } @Override protected Pair buildOAspecificbPK(IRequest pendingReq, AuthenticationData authData) throws EaafBuilderException { return super.buildOAspecificbPK(pendingReq, authData); } @Override protected Pair getEncryptedBpkFromPvpAttribute(IAuthProcessDataContainer arg0, AuthenticationData arg1, ISpConfiguration arg2) throws EaafBuilderException { return null; } @Override protected Pair getbaseIdFromSzr(AuthenticationData arg0, String arg1, String arg2) { return null; } private void buildMandateInformation(EidAuthenticationData authData, IRequest pendingReq, EidAuthProcessDataWrapper authProcessData) throws EaafAuthenticationException, EaafBuilderException, EaafStorageException { authData.setUseMandate(authProcessData.isMandateUsed()); if (authProcessData.isMandateUsed()) { log.debug("Build mandate-releated authentication data ... "); if (authProcessData.isForeigner()) { buildMandateInformationForEidasIncoming(); } else { buildMandateInformationForEidasOutgoing(authData, pendingReq, authProcessData); } // inject mandate information into authdata final Set mandateAttributes = Streams.concat( IdAustriaAuthConstants.DEFAULT_REQUIRED_MANDATE_NAT_PVP_ATTRIBUTES.stream(), IdAustriaAuthConstants.DEFAULT_REQUIRED_MANDATE_JUR_PVP_ATTRIBUTES.stream()) .map(el -> el.getFirst()) .collect(Collectors.toSet()); authProcessData.getGenericSessionDataStream() .filter(el -> mandateAttributes.contains(el.getKey())) .forEach(el -> { try { authData.setGenericData(el.getKey(), el.getValue()); } catch (final EaafStorageException e) { log.error("Can not store attribute: {} into session.", el.getKey(), e); throw new RuntimeException(e); } }); } } private void buildMandateInformationForEidasIncoming() { log.debug("Find eIDAS incoming process. Generated mandate-information for ID-Austria system ... "); // TODO: implement IDA specific processing of foreign mandate } private void buildMandateInformationForEidasOutgoing(EidAuthenticationData authData, IRequest pendingReq, EidAuthProcessDataWrapper authProcessData) throws EaafAuthenticationException, EaafBuilderException, EaafStorageException { log.debug("Find eIDAS outgoing process. Generated mandate-information for other country ... "); if (authProcessData.getGenericDataFromSession( PvpAttributeDefinitions.MANDATE_NAT_PER_BPK_NAME) != null) { final Optional> missingAttribute = IdAustriaAuthConstants.DEFAULT_REQUIRED_MANDATE_NAT_PVP_ATTRIBUTES.stream() .filter(el -> authProcessData.getGenericDataFromSession(el.getFirst()) == null) .findFirst(); if (missingAttribute.isPresent()) { log.error("ID-Austria response contains not all attributes for nat. person mandator. Missing: {}", missingAttribute.get().getFirst()); throw new EaafAuthenticationException(ERROR_B11, new Object[] { "Nat. person mandate" }); } else { log.trace("Find nat. person mandate. Mandate can be used as it is "); authData.setGenericData(MsProxyServiceConstants.ATTR_EIDAS_NAT_MANDATOR_PERSONAL_IDENTIFIER, extractBpkFromResponse(authProcessData.getGenericDataFromSession( PvpAttributeDefinitions.MANDATE_NAT_PER_BPK_NAME, String.class))); } } else { final Optional> missingAttribute = IdAustriaAuthConstants.DEFAULT_REQUIRED_MANDATE_JUR_PVP_ATTRIBUTES.stream() .filter(el -> authProcessData.getGenericDataFromSession(el.getFirst()) == null) .findFirst(); if (missingAttribute.isPresent()) { log.error("ID-Austria response contains not all attributes for legal. person mandator. Missing: {}", missingAttribute.get().getFirst()); throw new EaafAuthenticationException(ERROR_B11, new Object[] { "Legal. person mandate" }); } else { log.trace( "Find jur. person mandate. Generate eIDAS identifier from legal-person sourcePin and type ... "); final String sourcePin = authProcessData.getGenericDataFromSession( PvpAttributeDefinitions.MANDATE_LEG_PER_SOURCE_PIN_NAME, String.class); final String sourcePinType = authProcessData.getGenericDataFromSession( PvpAttributeDefinitions.MANDATE_LEG_PER_SOURCE_PIN_TYPE_NAME, String.class); // build leagl-person identifier for eIDAS out-going final String[] splittedTarget = pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier().split("\\+"); StringBuilder sb = new StringBuilder(); sb.append(splittedTarget[1]) .append("/") .append(splittedTarget[2]) .append("/") .append(sourcePinType) .append("+") .append(sourcePin); log.debug("Use legal-person eIDAS identifer: {} from baseId: {} and baseIdType: {}", sb.toString(), sourcePin, sourcePinType); authData.setGenericData(MsProxyServiceConstants.ATTR_EIDAS_JUR_MANDATOR_PERSONAL_IDENTIFIER, sb.toString()); } } } private String extractBpkFromResponse(String pvpBpkAttrValue) { final String[] split = pvpBpkAttrValue.split(":", 2); if (split.length == 2) { return split[1]; } else { log.warn("PVP bPK attribute: {} has wrong format. Use it as it is.", pvpBpkAttrValue); return pvpBpkAttrValue; } } }