aboutsummaryrefslogtreecommitdiff
path: root/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java
diff options
context:
space:
mode:
Diffstat (limited to 'modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java')
-rw-r--r--modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java246
1 files changed, 246 insertions, 0 deletions
diff --git a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java
new file mode 100644
index 00000000..96aa9c51
--- /dev/null
+++ b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2020 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 static at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants.CONTEXT_FLAG_ADVANCED_MATCHING_FAILED;
+import static at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants.CONTEXT_FLAG_ADVANCED_MATCHING_FAILED_REASON;
+import static at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants.TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK;
+
+import java.util.Map;
+import java.util.Objects;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Component;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterOperationStatus;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterStatusResults;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import eu.eidas.auth.commons.light.ILightResponse;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Searches registers (ERnP and ZMR) after alternative eIDAS authn, before adding person to SZR.
+ * Input:
+ * <ul>
+ * <li>{@link Constants#DATA_FULL_EIDAS_RESPONSE_ALTERNATIVE} data from the alternative eIDAS authn</li>
+ * <li>{@link Constants#DATA_SIMPLE_EIDAS} data from the initial eIDAS authn</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#DATA_PERSON_MATCH_RESULT} results after second search in registers with MDS</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link GenerateOtherLoginMethodGuiTask} if no results in registers were found for this user</li>
+ * <li>{@link CreateIdentityLinkTask} if search in register returned one match, user is uniquely identified</li>
+ * </ul>
+ *
+ * @author amarsalek
+ * @author ckollmann
+ * @author tlenz
+ */
+@Slf4j
+@Component("AlternativeSearchTask")
+@SuppressWarnings("PMD.TooManyStaticImports")
+public class AlternativeSearchTask extends AbstractAuthServletTask {
+
+ private static final String MSG_PROP_25 = "module.eidasauth.matching.25";
+
+ private final RegisterSearchService registerSearchService;
+ private final ICcSpecificEidProcessingService eidPostProcessor;
+
+ /**
+ * Constructor.
+ *
+ * @param registerSearchService Service for register search access
+ * @param eidPostProcessor Country-Specific post processing of attributes
+ */
+ public AlternativeSearchTask(RegisterSearchService registerSearchService,
+ ICcSpecificEidProcessingService eidPostProcessor) {
+ this.registerSearchService = registerSearchService;
+ this.eidPostProcessor = eidPostProcessor;
+ }
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ final SimpleEidasData altEidasData = convertEidasAttrToSimpleData();
+ final SimpleEidasData initialEidasData = MatchingTaskUtils.getInitialEidasData(pendingReq);
+ final RegisterStatusResults intermediateMatchingState =
+ MatchingTaskUtils.getIntermediateMatchingResult(pendingReq);
+
+ //pre-validation of eIDAS data
+ preVerifyAlternativeEidasData(altEidasData, initialEidasData, intermediateMatchingState);
+
+ //perform register search operation based on alterantive eIDAS data
+ step11RegisterSearchWithPersonIdentifier(executionContext, altEidasData,
+ intermediateMatchingState, initialEidasData);
+
+ } catch (WorkflowException e) {
+ throw new TaskExecutionException(pendingReq, "Initial search failed", e);
+
+ } catch (final Exception e) {
+ log.error("Initial search failed", e);
+ throw new TaskExecutionException(pendingReq, "Initial search failed with a generic error", e);
+
+ }
+ }
+
+ /**
+ * Pre-validation of eIDAS information.
+ *
+ * <p>Check if country-code and MDS (givenName, familyName, dateOfBirth) matches.</p>
+ *
+ * @param altEidasData eIDAS data from alternative authentication
+ * @param initialEidasData eIDAS data from initial authentication
+ * @param intermediateMatchingState Intermediate matching result
+ * @throws WorkflowException In case of a validation error
+ */
+ private void preVerifyAlternativeEidasData(SimpleEidasData altEidasData, SimpleEidasData initialEidasData,
+ RegisterStatusResults intermediateMatchingState) throws WorkflowException {
+ if (initialEidasData == null) {
+ throw new WorkflowException("step11", "No initial eIDAS authn data", true);
+
+ }
+
+ if (intermediateMatchingState == null) {
+ throw new WorkflowException("step11", "No intermediate matching-state", true);
+
+ }
+
+ if (!Objects.equals(altEidasData.getCitizenCountryCode(), initialEidasData.getCitizenCountryCode())) {
+ throw new WorkflowException("step11", "Country Code of alternative eIDAS authn not matching", true);
+
+ }
+
+ if (!altEidasData.equalsMds(initialEidasData)) {
+ throw new WorkflowException("step11", "MDS of alternative eIDAS authn does not match initial authn", true);
+
+ }
+ }
+
+ private void step11RegisterSearchWithPersonIdentifier(
+ ExecutionContext executionContext, SimpleEidasData altEidasData,
+ RegisterStatusResults intermediateMatchingState, SimpleEidasData initialEidasData)
+ throws WorkflowException, EaafStorageException {
+ try {
+ log.trace("Starting step11RegisterSearchWithPersonIdentifier");
+ RegisterStatusResults altSearchResult = registerSearchService.searchWithPersonIdentifier(
+ intermediateMatchingState.getOperationStatus(), altEidasData);
+
+ int resultCount = altSearchResult.getResultCount();
+ if (resultCount == 0) {
+ step12CountrySpecificSearch(executionContext, intermediateMatchingState, initialEidasData,
+ altSearchResult.getOperationStatus(), altEidasData);
+
+ } else if (resultCount == 1) {
+ log.debug("step11RegisterSearchWithPersonIdentifier find single result. Starting KITT operation ... ");
+ RegisterStatusResults matchtedResult = registerSearchService.step7bKittProcess(
+ intermediateMatchingState, initialEidasData, altSearchResult, altEidasData);
+
+ log.debug("KITT operation finished. Finalize matching process ... ");
+ foundMatchFinalizeTask(matchtedResult, altEidasData);
+
+ } else {
+ throw new WorkflowException("step11RegisterSearchWithPersonIdentifier",
+ "More than one entry with unique personal-identifier", true);
+
+ }
+ } catch (WorkflowException e) {
+ log.warn("Workflow error during matching step: {}. Reason: {}", e.getProcessStepName(), e.getErrorReason());
+ throw e;
+
+ }
+ }
+
+ private void step12CountrySpecificSearch(ExecutionContext executionContext,
+ RegisterStatusResults intermediateMatchingState,
+ SimpleEidasData initialEidasData,
+ RegisterOperationStatus registerOperationStatus,
+ SimpleEidasData altEidasData)
+ throws EaafStorageException, WorkflowException {
+ log.trace("Starting 'step12CountrySpecificSearch' ... ");
+ RegisterStatusResults ccAltSearchResult = registerSearchService.searchWithCountrySpecifics(
+ registerOperationStatus, altEidasData);
+
+ if (ccAltSearchResult.getResultCount() == 0) {
+ log.trace("'step12CountrySpecificSearch' ends with no result. Forward to GUI based matching step ... ");
+ log.debug("Forward to GUI based matching steps ... ");
+ executionContext.put(TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK, true);
+ executionContext.put(CONTEXT_FLAG_ADVANCED_MATCHING_FAILED_REASON, MSG_PROP_25);
+ executionContext.put(CONTEXT_FLAG_ADVANCED_MATCHING_FAILED, true);
+
+ } else if (ccAltSearchResult.getResultCount() == 1) {
+ log.debug("'step12CountrySpecificSearch' find single result. Starting KITT operation ... ");
+ RegisterStatusResults matchtedResult = registerSearchService.step7bKittProcess(
+ intermediateMatchingState, initialEidasData, ccAltSearchResult, altEidasData);
+
+ log.debug("KITT operation finished. Finalize matching process ... ");
+ foundMatchFinalizeTask(matchtedResult, altEidasData);
+
+ } else {
+ throw new WorkflowException("step12CountrySpecificSearch",
+ "More than one entry with unique country-specific information", true);
+
+ }
+ }
+
+ private void foundMatchFinalizeTask(RegisterStatusResults searchResult, SimpleEidasData eidasData)
+ throws WorkflowException, EaafStorageException {
+ MatchedPersonResult result = MatchedPersonResult.generateFormMatchingResult(
+ searchResult.getResult(), eidasData.getCitizenCountryCode());
+ MatchingTaskUtils.storeFinalMatchingResult(pendingReq, result);
+
+ //remove intermediate matching-state
+ MatchingTaskUtils.storeIntermediateMatchingResult(pendingReq, null);
+
+ }
+
+ @NotNull
+ private SimpleEidasData convertEidasAttrToSimpleData()
+ throws EidasAttributeException, EidPostProcessingException {
+ final ILightResponse eidasResponse = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq)
+ .getGenericDataFromSession(Constants.DATA_FULL_EIDAS_RESPONSE_ALTERNATIVE, ILightResponse.class);
+ Map<String, Object> simpleMap = MatchingTaskUtils.convertEidasAttrToSimpleMap(
+ eidasResponse.getAttributes().getAttributeMap(), log);
+ return eidPostProcessor.postProcess(simpleMap);
+ }
+
+}