diff options
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.java | 285 |
1 files changed, 152 insertions, 133 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 index 9900fa98..74525e65 100644 --- 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 @@ -19,10 +19,11 @@ * 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.io.IOException; import java.util.UUID; import javax.servlet.ServletException; @@ -30,6 +31,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @@ -39,6 +41,7 @@ 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.EidPostProcessingException; 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.gui.ISpringMvcGuiFormBuilder; @@ -46,6 +49,8 @@ import at.gv.egiz.eaaf.core.api.idp.IConfiguration; 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.GuiBuildException; 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; @@ -58,146 +63,163 @@ import eu.eidas.specificcommunication.exception.SpecificCommunicationException; import eu.eidas.specificcommunication.protocol.SpecificCommunicationService; import lombok.extern.slf4j.Slf4j; + /** - * Authentication-process task that generates the Authn. Request to eIDAS Node. - * - * @author tlenz + * Generates the authn request to the eIDAS Node. This is the first task in the process. + * Input: + * <ul> + * <li>none</li> + * </ul> + * Output: + * <ul> + * <li>none</li> + * </ul> + * Transitions: + * <ul> + * <li>{@link at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.ReceiveAuthnResponseTask} + * to read the response from the eIDAS Node</li> + * </ul> * + * @author tlenz + * @author ckollmann */ @Slf4j -@Component("ConnecteIDASNodeTask") +@Component("GenerateAuthnRequestTask") public class GenerateAuthnRequestTask extends AbstractAuthServletTask { + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired IConfiguration basicConfig; + @Autowired ApplicationContext context; + + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired ITransactionStorage transactionStore; + + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired ISpringMvcGuiFormBuilder guiBuilder; + @Autowired ICcSpecificEidProcessingService ccSpecificProcessing; @Override - public void execute(ExecutionContext executionContext, - HttpServletRequest request, HttpServletResponse response) + public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response) throws TaskExecutionException { - try { - // 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()); - - // set nameIDFormat - authnRequestBuilder.nameIdFormat( - authConfig.getBasicConfiguration(Constants.CONFIG_PROP_EIDAS_NODE_NAMEIDFORMAT)); - - // set citizen country code for foreign uses - authnRequestBuilder.citizenCountryCode(citizenCountryCode); - - //set Issuer - 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); - + final String citizenCountryCode = extractCitizenCountryCode(executionContext); + final String environment = (String) executionContext.get(MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT); + final String issuer = loadIssuerFromConfig(); + final LightRequest lightAuthnReq = buildEidasAuthnRequest(citizenCountryCode, issuer); - // 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()); - + workaroundRelayState(lightAuthnReq); + final String forwardUrl = selectForwardUrl(environment); + + String configValue = basicConfig.getBasicConfiguration( + Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_METHOD, Constants.FORWARD_METHOD_GET); + boolean useHttpRedirect = configValue.equals(Constants.FORWARD_METHOD_GET); + if (useHttpRedirect) { + sendRedirect(response, tokenBase64, forwardUrl); } 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"); - + sendPost(request, response, tokenBase64, forwardUrl); } 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); + } + } + + @NotNull + private String extractCitizenCountryCode(ExecutionContext executionContext) throws EidasSAuthenticationException { + final String result = (String) executionContext.get(MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY); + // illegal state; task should not have been executed without a selected country + if (StringUtils.isEmpty(result)) { + 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: {}", result); + revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.COUNTRY_SELECTED, result); + return result; + } + + @NotNull + private String loadIssuerFromConfig() throws EaafConfigurationException { + final String result = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_ENTITYID); + if (StringUtils.isEmpty(result)) { + 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}); + } + return result; + } + @NotNull + private LightRequest buildEidasAuthnRequest(String citizenCountryCode, String issuer) + throws EidPostProcessingException { + final LightRequest.Builder builder = LightRequest.builder(); + builder.id(UUID.randomUUID().toString()); + + // set nameIDFormat + builder.nameIdFormat( + authConfig.getBasicConfiguration(Constants.CONFIG_PROP_EIDAS_NODE_NAMEIDFORMAT)); + + builder.citizenCountryCode(citizenCountryCode); + builder.issuer(issuer); + // Add country-specific information into eIDAS request + ccSpecificProcessing.preProcess(citizenCountryCode, pendingReq, builder); + return builder.build(); + } + + private BinaryLightToken putRequestInCommunicationCache(ILightRequest lightRequest) + throws ServletException { + final BinaryLightToken binaryLightToken; + try { + String beanName = SpecificCommunicationDefinitionBeanNames.SPECIFIC_CONNECTOR_COMMUNICATION_SERVICE.toString(); + final SpecificCommunicationService service = (SpecificCommunicationService) context.getBean(beanName); + binaryLightToken = service.putRequest(lightRequest); + } catch (final SpecificCommunicationException e) { + log.error("Unable to process specific request"); + throw new ServletException(e); } + return binaryLightToken; + } + + /** + * Workaround, because eIDAS node ref. impl. does not return relayState + */ + private void workaroundRelayState(LightRequest lightAuthnReq) throws EaafException { + 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); + } + } + + @NotNull + private String selectForwardUrl(String environment) throws EaafConfigurationException { + String result = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL); + if (StringUtils.isNotEmpty(environment)) { + result = selectedForwardUrlForEnvironment(environment); + } + if (StringUtils.isEmpty(result)) { + 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: {} selected to forward eIDAS request", result); + return result; } /** @@ -205,51 +227,48 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { * <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 + * Impl nodes are required to support production, testing, or QS stages for one * country by using one ms-specific eIDAS connector - * + * * @param environment Environment selector from CountrySlection page - * @return + * @return the URL from the configuration */ private String selectedForwardUrlForEnvironment(String environment) { - log.trace("Starting endpoint selection process for environment: " + 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)) { + } 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)) { + } 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"); + log.info("Environment selector: {} is not supported", environment); 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); - - } + private void sendRedirect(HttpServletResponse response, String tokenBase64, String forwardUrl) throws IOException { + log.debug("Use http-redirect for eIDAS node forwarding ... "); + final UriComponentsBuilder redirectUrl = UriComponentsBuilder.fromHttpUrl(forwardUrl); + redirectUrl.queryParam(EidasParameterKeys.TOKEN.toString(), tokenBase64); + response.sendRedirect(redirectUrl.build().encode().toString()); + + } - return binaryLightToken; + private void sendPost(HttpServletRequest request, HttpServletResponse response, String tokenBase64, String forwardUrl) + throws GuiBuildException { + 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); + String token = EidasParameterKeys.TOKEN.toString(); + config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_NAME, token); + config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_VALUE, tokenBase64); + guiBuilder.build(request, response, config, "Forward to eIDASNode form"); } } |