From 6d09f43225ba2e0f6d7b0583f843c858a1015807 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Thu, 26 Jul 2018 10:30:14 +0200 Subject: namespace refactoring --- .../tasks/GenerateAuthnRequestTask.java | 306 +++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/authmodule_eIDASv2/tasks/GenerateAuthnRequestTask.java (limited to 'eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/authmodule_eIDASv2/tasks/GenerateAuthnRequestTask.java') diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/authmodule_eIDASv2/tasks/GenerateAuthnRequestTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/authmodule_eIDASv2/tasks/GenerateAuthnRequestTask.java new file mode 100644 index 00000000..74c98de1 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/authmodule_eIDASv2/tasks/GenerateAuthnRequestTask.java @@ -0,0 +1,306 @@ +/******************************************************************************* + *******************************************************************************/ +package at.asitplus.eidas.specific.modules.authmodule_eIDASv2.tasks; + +import java.util.Map; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; + +import com.google.common.collect.ImmutableSortedSet; + +import at.asitplus.eidas.specific.connector.MSConnectorEventCodes; +import at.asitplus.eidas.specific.connector.MSeIDASNodeConstants; +import at.asitplus.eidas.specific.connector.gui.StaticGuiBuilderConfiguration; +import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.Constants; +import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.exception.eIDASAuthenticationException; +import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.service.eIDASAttributeRegistry; +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.gui.IGUIFormBuilder; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +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.TaskExecutionException; +import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; +import eu.eidas.auth.commons.EidasParameterKeys; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.light.ILightRequest; +import eu.eidas.auth.commons.light.impl.LightRequest; +import eu.eidas.auth.commons.protocol.eidas.SpType; +import eu.eidas.auth.commons.tx.BinaryLightToken; +import eu.eidas.specificcommunication.BinaryLightTokenHelper; +import eu.eidas.specificcommunication.SpecificCommunicationDefinitionBeanNames; +import eu.eidas.specificcommunication.exception.SpecificCommunicationException; +import eu.eidas.specificcommunication.protocol.impl.SpecificConnectorCommunicationServiceImpl; + +/** + * @author tlenz + * + */ +@Component("ConnecteIDASNodeTask") +public class GenerateAuthnRequestTask extends AbstractAuthServletTask { + private static final Logger log = LoggerFactory.getLogger(GenerateAuthnRequestTask.class); + + @Autowired IConfiguration basicConfig; + @Autowired eIDASAttributeRegistry attrRegistry; + @Autowired ApplicationContext context; + @Autowired ITransactionStorage transactionStore; + @Autowired IGUIFormBuilder guiBuilder; + + @Override + public void execute(ExecutionContext executionContext, + HttpServletRequest request, HttpServletResponse response) + throws TaskExecutionException { + + try{ + //get service-provider configuration + ISPConfiguration spConfig = pendingReq.getServiceProviderConfiguration(); + + // get target, environment and validate citizen countryCode + String citizenCountryCode = (String) executionContext.get(MSeIDASNodeConstants.REQ_PARAM_SELECTED_COUNTRY); + String environment = (String) executionContext.get(MSeIDASNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT); + + if (StringUtils.isEmpty(citizenCountryCode)) { + // illegal state; task should not have been executed without a selected country + throw new eIDASAuthenticationException("eidas.03", new Object[] { "" }); + + } + + //TODO: maybe add countryCode validation before request ref. impl. eIDAS node + log.debug("Request eIDAS auth. for citizen of country: " + citizenCountryCode); + revisionsLogger.logEvent(pendingReq, MSConnectorEventCodes.COUNTRY_SELECTED, citizenCountryCode); + + //build eIDAS AuthnRequest + LightRequest.Builder authnRequestBuilder = LightRequest.builder(); + authnRequestBuilder.id(UUID.randomUUID().toString()); + + + String issur = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_ENTITYID); + if (StringUtils.isEmpty(issur)) { + log.error("Found NO 'eIDAS node issuer' in configuration. Authentication NOT possible!"); + throw new EAAFConfigurationException("config.27", + new Object[] {"Application config containts NO " + Constants.CONIG_PROPS_EIDAS_NODE_ENTITYID }); + + } + authnRequestBuilder.issuer(issur); + + + //TODO: set matching mode if eIDAS ref. impl. support this method + + + //TODO: update if eIDAS ref. impl. supports exact matching for non-notified LoA schemes + String loa = EAAFConstants.EIDAS_LOA_HIGH; + if (spConfig.getRequiredLoA() != null) { + if (spConfig.getRequiredLoA().isEmpty()) + log.info("No eIDAS LoA requested. Use LoA HIGH as default"); + + else { + if (spConfig.getRequiredLoA().size() > 1 ) + log.info("Currently only ONE requested LoA is supported for service provider. Use first one ... "); + + loa = spConfig.getRequiredLoA().get(0); + + } + } + + log.debug("Request eIdAS node with LoA: " + loa); + authnRequestBuilder.levelOfAssurance(loa); + + //set correct SPType for requested target sector + String publicSectorTargetSelector = basicConfig.getBasicConfiguration( + Constants.CONIG_PROPS_EIDAS_NODE_PUBLICSECTOR_TARGETS, + Constants.POLICY_DEFAULT_ALLOWED_TARGETS); + Pattern p = Pattern.compile(publicSectorTargetSelector); + Matcher m = p.matcher(spConfig.getAreaSpecificTargetIdentifier()); + if (m.matches()) { + log.debug("Map " + spConfig.getAreaSpecificTargetIdentifier() + " to 'PublicSector'"); + authnRequestBuilder.spType(SpType.PUBLIC.getValue()); + + //TODO: only for eIDAS ref. node 2.0 and 2.1 because it need 'Providername' for any SPType + String providerName = pendingReq.getRawData(Constants.DATA_PROVIDERNAME, String.class); + if (StringUtils.isNotEmpty(providerName) + && basicConfig.getBasicMOAIDConfigurationBoolean( + Constants.CONIG_PROPS_EIDAS_NODE_WORKAROUND_ADD_ALWAYS_PROVIDERNAME, + false) + ) + authnRequestBuilder.providerName(providerName); + + } else { + log.debug("Map " + spConfig.getAreaSpecificTargetIdentifier() + " to 'PrivateSector'"); + authnRequestBuilder.spType(SpType.PRIVATE.getValue()); + + //TODO: switch to RequesterId in further version + //set provider name for private sector applications + String providerName = pendingReq.getRawData(Constants.DATA_PROVIDERNAME, String.class); + if (StringUtils.isNotEmpty(providerName)) + authnRequestBuilder.providerName(providerName); + + } + + //set nameIDFormat + authnRequestBuilder.nameIdFormat(Constants.eIDAS_REQ_NAMEID_FORMAT); + + //set citizen country code for foreign uses + authnRequestBuilder.citizenCountryCode(citizenCountryCode); + + //set relay state + authnRequestBuilder.relayState(pendingReq.getPendingRequestId()); + + //build and add requested attribute set + ImmutableAttributeMap reqAttrMap = translateToEidasAttributes(attrRegistry.getAttributeSetFromConfiguration()); + authnRequestBuilder.requestedAttributes(reqAttrMap); + + //build request + LightRequest lightAuthnReq = authnRequestBuilder.build(); + + //put request into cache + BinaryLightToken token = putRequestInCommunicationCache(lightAuthnReq); + final String tokenBase64 = BinaryLightTokenHelper.encodeBinaryLightTokenBase64(token); + + //Workaround, because eIDAS node ref. impl. does not return relayState + if (basicConfig.getBasicMOAIDConfigurationBoolean( + Constants.CONIG_PROPS_EIDAS_NODE_WORKAROUND_USEREQUESTIDASTRANSACTIONIDENTIFIER, + false)) { + log.trace("Put lightRequestId into transactionstore as session-handling backup"); + transactionStore.put(lightAuthnReq.getId(), pendingReq.getPendingRequestId(), -1); + + } + + //select forward URL regarding the selected environment + String forwardURL = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL); + if (StringUtils.isNotEmpty(environment)) + forwardURL = selectedForwardURLForEnvironment(environment); + + if (StringUtils.isEmpty(forwardURL)) { + log.warn("NO ForwardURL defined in configuration. Can NOT forward to eIDAS node! Process stops"); + throw new EAAFConfigurationException("config.08", new Object[] {Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL}); + + } + log.debug("ForwardURL: " + forwardURL + " selected to forward eIDAS request"); + + if (basicConfig.getBasicConfiguration( + Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_METHOD, + Constants.FORWARD_METHOD_GET + ).equals(Constants.FORWARD_METHOD_GET)) { + + log.debug("Use http-redirect for eIDAS node forwarding ... "); + //send redirect + UriComponentsBuilder redirectUrl = UriComponentsBuilder.fromHttpUrl(forwardURL); + redirectUrl.queryParam(EidasParameterKeys.TOKEN.toString(), tokenBase64); + response.sendRedirect(redirectUrl.build().encode().toString()); + + } else { + log.debug("Use http-post for eIDAS node forwarding ... "); + StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration( + basicConfig, + pendingReq, + Constants.TEMPLATE_POST_FORWARD_NAME, + null); + + config.putCustomParameter(Constants.TEMPLATE_POST_FORWARD_ENDPOINT, forwardURL); + config.putCustomParameter(Constants.TEMPLATE_POST_FORWARD_TOKEN_NAME, + EidasParameterKeys.TOKEN.toString()); + config.putCustomParameter(Constants.TEMPLATE_POST_FORWARD_TOKEN_VALUE, + tokenBase64); + + guiBuilder.build(response, config, "BKU-Selection form"); + + } + + revisionsLogger.logEvent(pendingReq, MSConnectorEventCodes.EIDAS_NODE_CONNECTED, lightAuthnReq.getId()); + + + } catch (eIDASAuthenticationException e) { + throw new TaskExecutionException(pendingReq, "eIDAS AuthnRequest generation FAILED.", e); + + } catch (Exception e) { + log.warn("eIDAS AuthnRequest generation FAILED.", e); + throw new TaskExecutionException(pendingReq, e.getMessage(), e); + + } + + } + + /** + * Select a forward URL from configuration for a specific environment + *

+ * Info: This method is needed, because eIDAS Ref. Impl only supports one countrycode on each instance. + * In consequence, more than one eIDAS Ref. Impl nodes are required to support producation, testing, or QS stages + * for one country by using one ms-specific eIDAS connector + * + * @param environment Environment selector from CountrySlection page + * @return + */ + private String selectedForwardURLForEnvironment(String environment) { + log.trace("Starting endpoint selection process for environment: " + environment + " ... "); + if (environment.equalsIgnoreCase(MSeIDASNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_PRODUCTION)) + return basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL); + + else if (environment.equalsIgnoreCase(MSeIDASNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_QS)) + return basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL + + "." + MSeIDASNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_QS); + + else if (environment.equalsIgnoreCase(MSeIDASNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_TESTING)) + return basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL + + "." + MSeIDASNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_TESTING); + + else if (environment.equalsIgnoreCase(MSeIDASNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_DEVELOPMENT)) + return basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL + + "." + MSeIDASNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_DEVELOPMENT); + + log.info("Environment selector: " + environment + " is not supported"); + return null; + + } + + private ImmutableAttributeMap translateToEidasAttributes(final Map requiredAttributes) { + ImmutableAttributeMap.Builder builder = ImmutableAttributeMap.builder(); + for (Map.Entry attribute : requiredAttributes.entrySet()) { + final String name = attribute.getKey(); + final ImmutableSortedSet> byFriendlyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName(name); + if (!byFriendlyName.isEmpty()) { + final AttributeDefinition attributeDefinition = byFriendlyName.first(); + builder.put(AttributeDefinition.builder(attributeDefinition).required(attribute.getValue()).build()); + + } else + log.warn("Can NOT request UNKNOWN attribute: " + attribute.getKey() + " Ignore it!"); + + } + + return builder.build(); + + } + + private BinaryLightToken putRequestInCommunicationCache(ILightRequest iLightRequest) throws ServletException { + final BinaryLightToken binaryLightToken; + try { + final SpecificConnectorCommunicationServiceImpl springManagedSpecificConnectorCommunicationService = + (SpecificConnectorCommunicationServiceImpl) context.getBean(SpecificCommunicationDefinitionBeanNames.SPECIFIC_CONNECTOR_COMMUNICATION_SERVICE.toString()); + + binaryLightToken = springManagedSpecificConnectorCommunicationService.putRequest(iLightRequest); + + } catch (SpecificCommunicationException e) { + log.error("Unable to process specific request"); + throw new ServletException(e); + + } + + return binaryLightToken; + } + +} -- cgit v1.2.3