diff options
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.java | 246 |
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); + } + +} |