aboutsummaryrefslogtreecommitdiff
path: root/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java
diff options
context:
space:
mode:
Diffstat (limited to 'eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java')
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java288
1 files changed, 288 insertions, 0 deletions
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java
new file mode 100644
index 00000000..da849c8f
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java
@@ -0,0 +1,288 @@
+/*
+ * 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.UUID;
+
+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 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.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService;
+import at.gv.egiz.eaaf.core.api.data.EaafConstants;
+import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder;
+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.light.ILightRequest;
+import eu.eidas.auth.commons.light.impl.LightRequest;
+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.SpecificCommunicationService;
+
+/**
+ * Authentication-process task that generates the Authn. Request to eIDAS Node.
+ *
+ * @author tlenz
+ *
+ */
+@Component("ConnecteIDASNodeTask")
+public class GenerateAuthnRequestTask extends AbstractAuthServletTask {
+ private static final Logger log = LoggerFactory.getLogger(GenerateAuthnRequestTask.class);
+
+ @Autowired
+ IConfiguration basicConfig;
+ @Autowired
+ ApplicationContext context;
+ @Autowired
+ ITransactionStorage transactionStore;
+ @Autowired
+ ISpringMvcGuiFormBuilder guiBuilder;
+ @Autowired
+ ICcSpecificEidProcessingService ccSpecificProcessing;
+
+ @Override
+ public void execute(ExecutionContext executionContext,
+ HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+
+ try {
+ // get service-provider configuration
+ final ISpConfiguration spConfig = pendingReq.getServiceProviderConfiguration();
+
+ // get target, environment and validate citizen countryCode
+ final String citizenCountryCode = (String) executionContext.get(
+ MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY);
+ final 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 EidasSAuthenticationException("eidas.03", new Object[] { "" });
+
+ }
+
+ // TODO: maybe add countryCode validation before request ref. impl. eIDAS node
+ log.info("Request eIDAS auth. for citizen of country: " + citizenCountryCode);
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.COUNTRY_SELECTED, citizenCountryCode);
+
+ // build eIDAS AuthnRequest
+ final LightRequest.Builder authnRequestBuilder = LightRequest.builder();
+ authnRequestBuilder.id(UUID.randomUUID().toString());
+
+ final 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 nameIDFormat
+ authnRequestBuilder.nameIdFormat(Constants.eIDAS_REQ_NAMEID_FORMAT);
+
+ // set citizen country code for foreign uses
+ authnRequestBuilder.citizenCountryCode(citizenCountryCode);
+
+ // set relay state
+ /*
+ * TODO: SecureToken PendingRequestId generates a validation exception in
+ * eIDASNode because eIDASNode implements limit on size for RelayState
+ * (80characaters)
+ */
+ // authnRequestBuilder.relayState(pendingReq.getPendingRequestId());
+
+ // Add country-specific informations into eIDAS request
+ ccSpecificProcessing.preProcess(citizenCountryCode, pendingReq, authnRequestBuilder);
+
+ // build request
+ final LightRequest lightAuthnReq = authnRequestBuilder.build();
+
+ // put request into Hazelcast cache
+ final BinaryLightToken token = putRequestInCommunicationCache(lightAuthnReq);
+ final String tokenBase64 = BinaryLightTokenHelper.encodeBinaryLightTokenBase64(token);
+
+ // Workaround, because eIDAS node ref. impl. does not return relayState
+ if (basicConfig.getBasicConfigurationBoolean(
+ 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[] {
+ environment == null ? Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL
+ : Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL + "." + environment
+ });
+
+ }
+ 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
+ final 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 ... ");
+ final StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration(
+ basicConfig,
+ pendingReq,
+ Constants.TEMPLATE_POST_FORWARD_NAME,
+ null,
+ resourceLoader);
+
+ config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_ENDPOINT, forwardUrl);
+ config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_NAME,
+ EidasParameterKeys.TOKEN.toString());
+ config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_VALUE,
+ tokenBase64);
+
+ guiBuilder.build(request, response, config, "Forward to eIDASNode form");
+
+ }
+
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.EIDAS_NODE_CONNECTED, lightAuthnReq.getId());
+
+ } catch (final EidasSAuthenticationException e) {
+ throw new TaskExecutionException(pendingReq, "eIDAS AuthnRequest generation FAILED.", e);
+
+ } catch (final 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 <br>
+ * <br>
+ * <b>Info: </b> 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 BinaryLightToken putRequestInCommunicationCache(ILightRequest lightRequest)
+ throws ServletException {
+ final BinaryLightToken binaryLightToken;
+ try {
+ final SpecificCommunicationService springManagedSpecificConnectorCommunicationService =
+ (SpecificCommunicationService) context.getBean(
+ SpecificCommunicationDefinitionBeanNames.SPECIFIC_CONNECTOR_COMMUNICATION_SERVICE.toString());
+
+ binaryLightToken = springManagedSpecificConnectorCommunicationService.putRequest(lightRequest);
+
+ } catch (final SpecificCommunicationException e) {
+ log.error("Unable to process specific request");
+ throw new ServletException(e);
+
+ }
+
+ return binaryLightToken;
+ }
+
+}