package at.asitplus.eidas.specific.modules.auth.idaustria.tasks; import java.security.NoSuchAlgorithmException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.opensaml.messaging.encoder.MessageEncodingException; import org.opensaml.saml.saml2.core.Attribute; import org.opensaml.saml.saml2.metadata.EntityDescriptor; import org.springframework.beans.factory.annotation.Autowired; import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; import at.asitplus.eidas.specific.modules.auth.idaustria.IdAustriaAuthConstants; import at.asitplus.eidas.specific.modules.auth.idaustria.config.IdAustriaAuthRequestBuilderConfiguration; import at.asitplus.eidas.specific.modules.auth.idaustria.utils.IdAustriaAuthCredentialProvider; import at.asitplus.eidas.specific.modules.auth.idaustria.utils.IdAustriaAuthMetadataProvider; import at.asitplus.eidas.specific.modules.auth.idaustria.utils.Utils; import at.asitplus.eidas.specific.modules.msproxyservice.protocol.ProxyServicePendingRequest; 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.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; import at.gv.egiz.eaaf.core.impl.utils.Random; import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EaafRequestedAttribute; import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PvpAttributeBuilder; import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; import at.gv.egiz.eaaf.modules.pvp2.sp.exception.AuthnRequestBuildException; import at.gv.egiz.eaaf.modules.pvp2.sp.impl.PvpAuthnRequestBuilder; import lombok.extern.slf4j.Slf4j; import net.shibboleth.utilities.java.support.resolver.ResolverException; import net.shibboleth.utilities.java.support.security.impl.SecureRandomIdentifierGenerationStrategy; /** * eIDAS Authentication task that generates PVP2 S-Profile request to central * Austrian MS-Connector. * * @author tlenz * */ @Slf4j public class RequestIdAustriaSystemTask extends AbstractAuthServletTask { private static final String ERROR_PVP_02 = "sp.pvp2.02"; private static final String ERROR_PVP_13 = "sp.pvp2.13"; private static final String ERROR_MSG_1 = "IDA System {0} has no valid metadata or metadata is not found"; private static final String ERROR_MSG_4 = "Build PVP2.1 AuthnRequest to connect 'ms-specific eIDAS node' FAILED."; @Autowired PvpAuthnRequestBuilder authnReqBuilder; @Autowired IdAustriaAuthCredentialProvider credential; @Autowired IdAustriaAuthMetadataProvider metadataService; @Autowired ITransactionStorage transactionStorage; @Override public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response) throws TaskExecutionException { try { //revisionsLogger.logEvent(pendingReq, EidasAuthEventConstants.AUTHPROCESS_EIDAS_AT_CONNECTOR_SELECTED); // get entityID for central ID Austria system final String idAustriaEntityID = Utils.getIdAustriaEntityId(pendingReq.getServiceProviderConfiguration(), authConfig); if (StringUtils.isEmpty(idAustriaEntityID)) { log.info("ID Austria authentication not possible -> NO EntityID for central central ID Austria System FOUND!"); throw new EaafConfigurationException(IdAustriaAuthConstants.ERRORTYPE_00, new Object[] { IdAustriaAuthConstants.CONFIG_PROPS_IDAUSTRIA_ENTITYID }); } // load IDP SAML2 entitydescriptor final EntityDescriptor entityDesc = metadataService.getEntityDescriptor(idAustriaEntityID); if (entityDesc == null) { throw new EaafConfigurationException(IdAustriaAuthConstants.ERRORTYPE_05, new Object[] { idAustriaEntityID, MessageFormat.format(ERROR_MSG_1, idAustriaEntityID) }); } // setup AuthnRequestBuilder configuration final IdAustriaAuthRequestBuilderConfiguration authnReqConfig = new IdAustriaAuthRequestBuilderConfiguration(); final SecureRandomIdentifierGenerationStrategy gen = new SecureRandomIdentifierGenerationStrategy(); // set basic infos authnReqConfig.setRequestId(gen.generateIdentifier()); authnReqConfig.setIdpEntity(entityDesc); authnReqConfig.setPassive(false); authnReqConfig.setSignCred(credential.getMessageSigningCredential()); authnReqConfig.setSpEntityID(pendingReq.getAuthUrl() + IdAustriaAuthConstants.ENDPOINT_METADATA); // set eIDAS Proxy-Service specific information for ID Austria system authnReqConfig.setRequestedAttributes(buildRequestedAttributes(pendingReq)); /*build relayState for session synchronization, because SAML2 only allows RelayState with 80 characters * but encrypted PendingRequestId is much longer. */ String relayState = Random.nextProcessReferenceValue(); transactionStorage.put(relayState, pendingReq.getPendingRequestId(), -1); // build and transmit AuthnRequest authnReqBuilder.buildAuthnRequest(pendingReq, authnReqConfig, relayState, response); log.info("Requsting ID Austria with SAML2 requestId: {}", authnReqConfig.getRequestID()); revisionsLogger.logEvent(pendingReq, IdAustriaAuthConstants.AUTHPROCESS_EIDAS_AT_PROXYSERVICE_REQUESTED, authnReqConfig.getRequestID()); } catch (final EaafException e) { throw new TaskExecutionException(pendingReq, e.getMessage(), e); } catch (final ResolverException e) { throw new TaskExecutionException(pendingReq, ERROR_MSG_4, new AuthnRequestBuildException(ERROR_PVP_02, new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING }, e)); } catch (MessageEncodingException | NoSuchAlgorithmException | SecurityException e) { throw new TaskExecutionException(pendingReq, e.getMessage(), new AuthnRequestBuildException(ERROR_PVP_13, new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING }, e)); } catch (final Exception e) { throw new TaskExecutionException(pendingReq, e.getMessage(), e); } } private String selectHighestLoa(List requiredLoA) { //TODO: implement LoA selection return requiredLoA.get(0); } private List buildRequestedAttributes(IRequest pendingReq) { final List attributs = new ArrayList<>(); //build attribute that contains the unique identifier of the eIDAS-Connector injectAttribute(attributs, ExtendedPvpAttributeDefinitions.EIDAS_CONNECTOR_UNIQUEID_NAME, pendingReq.getServiceProviderConfiguration().getUniqueIdentifier()); // build EID sector for identification attribute injectAttribute(attributs, PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()); // set requested LoA as attribute injectAttribute(attributs, PvpAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME, selectHighestLoa(pendingReq.getServiceProviderConfiguration().getRequiredLoA())); // set list of IDA attributes as attribute injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_REQUIRED_ATTRIBUTES_NAME, StringUtils.join( pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).getRequestedAttributes(), ",")); //set ProviderName if available String providerName = ((ProxyServicePendingRequest)pendingReq).getEidasRequest().getProviderName(); if (StringUtils.isNotEmpty(providerName)) { injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_FRIENDLYNAME_NAME, providerName); } //set ProviderName if available String requesterId = ((ProxyServicePendingRequest)pendingReq).getEidasRequest().getRequesterId(); if (StringUtils.isNotEmpty(requesterId)) { injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_UNIQUEID_NAME, requesterId); } //set mandate profiles List mandateProfiles = pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).getMandateProfiles(); if (mandateProfiles != null && !mandateProfiles.isEmpty()) { log.debug("Set mandate-profiles attribute into ID-Austria request"); injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_USED_MANDATE_PROFILES_NAME, StringUtils.join(mandateProfiles, ",")); } // inject mandate mode attribute injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_USED_MANDATE_TYPE_NAME, pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).getMandateMode().getMode()); return attributs; } private void injectAttribute(List attributs, String attributeName, String attributeValue) { final Attribute requesterIdAttr = PvpAttributeBuilder.buildEmptyAttribute(attributeName); final EaafRequestedAttribute requesterIdReqAttr = Saml2Utils.generateReqAuthnAttributeSimple( requesterIdAttr, true, attributeValue); attributs.add(requesterIdReqAttr); } }