/* * 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.test.tasks; import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Random; import java.util.stream.Collectors; import javax.xml.namespace.QName; import org.apache.commons.lang3.RandomStringUtils; import org.jetbrains.annotations.NotNull; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.ernp.ErnpRestClient.ErnpRegisterResult; import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.ernp.IErnpClient; import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.zmr.IZmrClient; import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.zmr.ZmrSoapClient; import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.zmr.ZmrSoapClient.ZmrRegisterResult; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.ErnpPersonRegisterResult; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; 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.EidPreProcessingException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.ZmrCommunicationException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.CountrySpecificDetailSearchProcessor; import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.GenericEidProcessor; import at.asitplus.eidas.specific.modules.auth.eidas.v2.log.statistic.DetailedMatchtingStatistic; 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.RegisterStatusResults; import at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.InitialSearchTask; import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils; import at.asitplus.eidas.specific.modules.core.eidas.EidasConstants; import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest; import at.gv.egiz.eaaf.core.api.IRequest; 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.data.AuthProcessDataWrapper; import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl; import at.gv.egiz.eaaf.core.impl.idp.process.ExecutionContextImpl; import eu.eidas.auth.commons.attribute.AttributeDefinition; import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; import eu.eidas.auth.commons.attribute.PersonType; import eu.eidas.auth.commons.light.impl.LightRequest; import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse; import lombok.SneakyThrows; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "/SpringTest-context_tasks_test.xml", "/SpringTest-context_basic_mapConfig.xml" }) @DirtiesContext(classMode = ClassMode.BEFORE_CLASS) public class InitialSearchTaskTest { private static final String EE = "EE"; private static final String DE = "DE"; private static final String EE_ST = EE + "/ST/"; private static final String DE_ST = DE + "/ST/"; @Mock private IZmrClient zmrClient; @Mock private IErnpClient ernpClient; @Autowired private List handlers; private RegisterSearchService registerSearchService; private final ICcSpecificEidProcessingService eidPostProcessor = createEidPostProcessor(); private InitialSearchTask task; final ExecutionContext executionContext = new ExecutionContextImpl(); private TestRequestImpl pendingReq; private final String randomBpk = RandomStringUtils.randomNumeric(6); private final String randomPseudonym = RandomStringUtils.randomNumeric(10); private final String randomPersonalIdentifier_DE = DE_ST + randomPseudonym; private final String randomPersonalIdentifier_EE = EE_ST + randomPseudonym; private final String randomFamilyName = randomAlphabetic(10); private final String randomGivenName = randomAlphabetic(10); private final String randomPlaceOfBirth = randomAlphabetic(10); private final String randomBirthName = randomAlphabetic(10); private final String randomBirthDate = "2011-01-" + (10 + new Random().nextInt(18)); /** * jUnit test set-up. */ @Before public void setUp() throws URISyntaxException, EaafStorageException { registerSearchService = new RegisterSearchService(handlers, zmrClient, ernpClient); task = new InitialSearchTask(registerSearchService, eidPostProcessor); MockHttpServletRequest httpReq = new MockHttpServletRequest("POST", "https://localhost/authhandler"); MockHttpServletResponse httpResp = new MockHttpServletResponse(); RequestContextHolder.resetRequestAttributes(); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(httpReq, httpResp)); final AuthenticationResponse response = buildDummyAuthResponseRandomPerson(); pendingReq = new TestRequestImpl(); pendingReq.getSessionData(AuthProcessDataWrapper.class) .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); } @NotNull private ICcSpecificEidProcessingService createEidPostProcessor() { return new ICcSpecificEidProcessingService() { private final GenericEidProcessor genericEidProcessor = new GenericEidProcessor(); @Override public SimpleEidasData postProcess(Map eidasAttrMap) throws EidPostProcessingException, EidasAttributeException { return genericEidProcessor.postProcess(eidasAttrMap); } @Override public void preProcess(String selectedCC, IRequest pendingReq, LightRequest.Builder authnRequestBuilder) throws EidPreProcessingException { genericEidProcessor.preProcess(pendingReq, authnRequestBuilder, selectedCC); } }; } /** * One match, but register update needed */ @Test @DirtiesContext public void singlePersonalIdMatchUpdateNecessary_Zmr() throws Exception { String oldGivenName = randomAlphabetic(10); String placeOfBirth = randomAlphabetic(10); RegisterResult firstZmrResult = randomRegisterResult(oldGivenName, randomBpk, placeOfBirth); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(zmrRegisterResult(firstZmrResult)); Mockito.when(zmrClient.searchCountrySpecific(any(), any(), any())) .thenThrow(new IllegalStateException("CountrySpecific search search should not be neccessary")); Mockito.when(zmrClient.searchWithMds(any(), any(), any(), any(), any())) .thenThrow(new IllegalStateException("MDS search should not be neccessary")); Mockito.when(zmrClient.update(any(), any(), any())) .thenReturn(zmrRegisterResult(firstZmrResult)); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchWithMds(any(), any(), any(), any())) .thenThrow(new IllegalStateException("MDS search should not be neccessary")); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute test task.execute(pendingReq, executionContext); // validate state //INFO: has to be the old givenName because ZMR allows no update of MDS information checkMatchingSuccessState(pendingReq, randomBpk, randomFamilyName, oldGivenName, randomBirthDate, DE); } /** * * One match, but register update needed. * * @throws EidasSAuthenticationException */ @Test @DirtiesContext public void singlePersonalIdMatchUpdateNecessary_Ernp() throws TaskExecutionException, EidasSAuthenticationException { Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(emptyZmrRegisterResult()); Mockito.when(zmrClient.searchCountrySpecific(any(), any(), any())) .thenThrow(new IllegalStateException("CountrySpecific search search should not be neccessary")); Mockito.when(zmrClient.searchWithMds(any(), any(), any(), any(), any())) .thenThrow(new IllegalStateException("MDS search should not be neccessary")); Mockito.when(zmrClient.update(any(), any(), any())) .thenThrow(new IllegalStateException("ZMR update should not be neccessary")); String oldRandomGivenName = randomAlphabetic(10); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(ernpRegisterResult(randomRegisterResult(oldRandomGivenName, randomBpk))); Mockito.when(ernpClient.searchCountrySpecific(any(), any())) .thenThrow(new IllegalStateException("CountrySpecific search search should not be neccessary")); Mockito.when(ernpClient.searchWithMds(any(), any(), any(), any())) .thenThrow(new IllegalStateException("MDS search should not be neccessary")); Mockito.when(ernpClient.update(any(), any())) .thenReturn(ernpRegisterResult(RegisterResult.builder() .bpk(randomBpk) .dateOfBirth(randomBirthDate) .givenName(randomGivenName) .familyName(randomFamilyName) .build())); // execute test task.execute(pendingReq, executionContext); // validate state checkMatchingSuccessState(pendingReq, randomBpk, randomFamilyName, randomGivenName, randomBirthDate, DE); } /** * Two matches by PersonalId found in ZMR * * @throws EidasSAuthenticationException */ @Test @DirtiesContext public void multiPersonalIdMatch_Zmr() throws EidasSAuthenticationException { String newRandomGivenName = randomAlphabetic(10); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Arrays.asList(randomRegisterResult(), randomRegisterResult(newRandomGivenName, randomBpk)), generateRandomProcessId())); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); // execute task TaskExecutionException exception = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq, executionContext)); // validate state assertTrue("Wrong exception", (exception.getOriginalException() instanceof WorkflowException)); assertTrue("Wrong flag 'manualFixNeeded'", ((WorkflowException) exception.getOriginalException()).isRequiresManualFix()); } /** * Two matches by PersonalId found in ZMR * * @throws EidasSAuthenticationException */ @Test @DirtiesContext public void withErrorFromZmr() throws EidasSAuthenticationException { Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenThrow(new ZmrCommunicationException("jUnit ZMR error", null)); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); // execute task TaskExecutionException exception = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq, executionContext)); // validate state assertTrue("Wrong exception", (exception.getOriginalException() instanceof WorkflowException)); assertFalse("Wrong flag 'manualFixNeeded'", ((WorkflowException) exception.getOriginalException()).isRequiresManualFix()); } /** * Two matches by PersonalId found in ErnP * * @throws EidasSAuthenticationException */ @Test @DirtiesContext public void multiPersonalIdMatch_Ernp() throws EidasSAuthenticationException { Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(emptyZmrRegisterResult()); String newRandomGivenName = randomAlphabetic(10); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(ernpRegisterResult( Arrays.asList(randomRegisterResult(), randomRegisterResult(newRandomGivenName, randomBpk)))); // execute task TaskExecutionException exception = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq, executionContext)); // validate state assertTrue("Wrong exception", (exception.getOriginalException() instanceof WorkflowException)); assertTrue("Wrong flag 'manualFixNeeded'", ((WorkflowException) exception.getOriginalException()).isRequiresManualFix()); } /** * Two matches by PersonalId found. * * * * @throws EidasSAuthenticationException */ @Test @DirtiesContext public void multiPersonalIdMatch_Zmr_ErnpKitt() throws EidasSAuthenticationException { Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Arrays.asList(randomRegisterResult(randomGivenName, randomBpk)), generateRandomProcessId())); String newRandomBpk = randomAlphabetic(10); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(ernpRegisterResult(Arrays.asList( new ErnpPersonRegisterResult(randomRegisterResult(randomGivenName, newRandomBpk), true)))); // execute task TaskExecutionException exception = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq, executionContext)); // validate state assertTrue("Wrong exception", (exception.getOriginalException() instanceof WorkflowException)); assertTrue("Wrong flag 'manualFixNeeded'", ((WorkflowException) exception.getOriginalException()).isRequiresManualFix()); } /** * Two matches by PersonalId found that are the same entity. * * * * @throws EidasSAuthenticationException */ @Test @DirtiesContext @SneakyThrows public void multiPersonalIdMatch_Zmr_ErnpKitt_Same_Enity() throws EidasSAuthenticationException { Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Arrays.asList( randomRegisterResult(randomGivenName, randomBpk)), generateRandomProcessId())); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(ernpRegisterResult(Arrays.asList( new ErnpPersonRegisterResult(randomRegisterResult(randomGivenName, randomBpk), true)))); // execute test task.execute(pendingReq, executionContext); // validate state checkMatchingSuccessState(pendingReq, randomBpk, randomFamilyName, randomGivenName, randomBirthDate, DE); } /** * Single matche by PersonalId found in ERnP, but it's a ZMR KITT entry. * * * @throws EidasSAuthenticationException */ @Test @DirtiesContext @SneakyThrows public void singlePersonalIdMatch_ErnpKitt_UpdateRequird() throws EidasSAuthenticationException { String oldGivenName = randomAlphabetic(10); String placeOfBirth = randomAlphabetic(10); RegisterResult firstErnpResult = randomRegisterResult(oldGivenName, randomBpk, placeOfBirth); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(emptyZmrRegisterResult()); Mockito.when(zmrClient.searchCountrySpecific(any(), any(), any())) .thenThrow(new IllegalStateException("CountrySpecific search search should not be neccessary")); Mockito.when(zmrClient.searchWithMds(any(), any(), any(), any(), any())) .thenThrow(new IllegalStateException("MDS search should not be neccessary")); Mockito.when(zmrClient.update(any(), any(), any())) .thenReturn(zmrRegisterResult(firstErnpResult)); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(ernpRegisterResult(Arrays.asList( new ErnpPersonRegisterResult(firstErnpResult, true)))); Mockito.when(ernpClient.searchWithMds(any(), any(), any(), any())) .thenThrow(new IllegalStateException("MDS search should not be neccessary")); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute test task.execute(pendingReq, executionContext); // validate state checkMatchingSuccessState(pendingReq, randomBpk, randomFamilyName, oldGivenName, randomBirthDate, DE); } /** * Two matches by PersonalId * * @throws EidasSAuthenticationException */ @Test @DirtiesContext public void multiPersonalIdMatch_ErnpAndZmr() throws EidasSAuthenticationException { Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(zmrRegisterResult(randomRegisterResult())); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(ernpRegisterResult(randomRegisterResult(RandomStringUtils.randomAlphanumeric(10)))); // execute task TaskExecutionException exception = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq, executionContext)); // validate state assertTrue("Wrong exception", (exception.getOriginalException() instanceof WorkflowException)); assertTrue("Wrong flag 'manualFixNeeded'", ((WorkflowException) exception.getOriginalException()).isRequiresManualFix()); } /** * Find to matches by PersonalId but they are the same person. */ @Test @DirtiesContext @SneakyThrows public void samePersonFromZmrAndErnp() { RegisterResult registerResult = randomRegisterResult(); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(zmrRegisterResult(registerResult)); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(ernpRegisterResult(registerResult)); // execute test task.execute(pendingReq, executionContext); // validate state checkMatchingSuccessState(pendingReq, randomBpk, randomFamilyName, randomGivenName, randomBirthDate, DE); } /** * One match by PersonalId, no register update needed */ @Test @DirtiesContext public void singlePersonalIdMatchNoUpdate_Ernp() throws Exception { Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(emptyZmrRegisterResult()); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(ernpRegisterResult(randomRegisterResult())); Mockito.when(zmrClient.update(any(), any(), any())) .thenThrow(new IllegalStateException("ZMR update should not be neccessary")); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute test task.execute(pendingReq, executionContext); // validate state checkMatchingSuccessState(pendingReq, randomBpk, randomFamilyName, randomGivenName, randomBirthDate, DE); } /** * One match by PersonalId, no register update needed */ @Test @DirtiesContext public void singlePersonalIdMatchNoUpdate_Zmr() throws Exception { Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(zmrRegisterResult(randomRegisterResult())); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(zmrClient.update(any(), any(), any())) .thenThrow(new IllegalStateException("ZMR update should not be neccessary")); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute test task.execute(pendingReq, executionContext); // validate state checkMatchingSuccessState(pendingReq, randomBpk, randomFamilyName, randomGivenName, randomBirthDate, DE); DetailedMatchtingStatistic entry = MatchingTaskUtils.getDetailedMatchingStatistic(pendingReq); assertNotNull("statisticLogEntry", entry); assertEquals("ZMR", 1, entry.getPersonalIdResult().getZmrResults()); assertEquals("ERnP", 0, entry.getPersonalIdResult().getErnpResults()); } /** * Find single person in ZMR by country specifics. */ @Test @DirtiesContext public void singlePersonFindWithCountySpecifics_Zmr() throws Exception { final AuthenticationResponse response = buildDummyAuthResponseDE(randomGivenName, randomFamilyName, randomPersonalIdentifier_DE, randomBirthDate, randomPlaceOfBirth, randomBirthName); TestRequestImpl pendingReq1 = new TestRequestImpl(); pendingReq1.getSessionData(AuthProcessDataWrapper.class) .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); BigInteger zmrProcessId = generateRandomProcessId(); RegisterResult zmrResult = RegisterResult.builder() .bpk(randomBpk) .pseudonym(Collections.singletonList(randomPseudonym)) .givenName(randomGivenName) .familyName(randomFamilyName) .dateOfBirth(randomBirthDate) .placeOfBirth(randomPlaceOfBirth) .birthName(randomBirthName) .build(); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.searchCountrySpecific(eq(zmrProcessId), any(PersonSuchenRequest.class), eq(DE))) .thenReturn(zmrRegisterResult(zmrResult, zmrProcessId)); RegisterResult randomRegisterResult = RegisterResult.builder() .bpk(randomBpk) .pseudonym(Arrays.asList(randomPseudonym, RandomStringUtils.randomAlphanumeric(10))) .givenName(randomGivenName) .familyName(randomFamilyName) .dateOfBirth(randomBirthDate) .placeOfBirth(randomPlaceOfBirth) .birthName(randomBirthName) .build(); Mockito.when(zmrClient.update(eq(zmrProcessId), eq(zmrResult), any())) .thenReturn(zmrRegisterResult(randomRegisterResult, zmrProcessId)); Mockito.when(zmrClient.searchWithMds(any(), any(), any(), any(), any())) .thenThrow(new IllegalStateException("MDS search should not be neccessary")); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchCountrySpecific(any(), eq(DE))) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchWithMds(any(), any(), any(), eq(DE))) .thenThrow(new IllegalStateException("ERnP MDS search should not be neccessary")); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); Mockito.when(ernpClient.add(any())) .thenThrow(new IllegalStateException("ERnP add-entity should not be neccessary")); // execute test task.execute(pendingReq1, executionContext); // validate state checkMatchingSuccessState(pendingReq1, randomBpk, randomFamilyName, randomGivenName, randomBirthDate, DE); } /** * Multiple matches found in ZMR by country specifics. */ @Test @DirtiesContext public void multiplePersonFindWithCountySpecifics_Zmr() throws Exception { String newRandomPseudonym = randomPersonalIdentifier_DE + RandomStringUtils.randomNumeric(2); String newRandomBpk = randomBpk + RandomStringUtils.randomNumeric(6); final AuthenticationResponse response = buildDummyAuthResponseDE(randomGivenName, randomFamilyName, randomPersonalIdentifier_DE, randomBirthDate, randomPlaceOfBirth, randomBirthName); TestRequestImpl pendingReq1 = new TestRequestImpl(); pendingReq1.getSessionData(AuthProcessDataWrapper.class) .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); BigInteger zmrProcessId = generateRandomProcessId(); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); RegisterResult randomResult1 = RegisterResult.builder() .bpk(randomBpk) .pseudonym(Collections.singletonList(randomPseudonym)) .givenName(randomGivenName) .familyName(randomFamilyName) .dateOfBirth(randomBirthDate) .placeOfBirth(randomPlaceOfBirth) .birthName(randomBirthName) .build(); RegisterResult randomResult2 = RegisterResult.builder() .bpk(newRandomBpk) .pseudonym(Collections.singletonList(newRandomPseudonym)) .givenName(randomGivenName) .familyName(randomFamilyName) .dateOfBirth(randomBirthDate) .placeOfBirth(randomPlaceOfBirth) .birthName(randomBirthName) .build(); Mockito.when(zmrClient.searchCountrySpecific(eq(zmrProcessId), any(PersonSuchenRequest.class), eq(DE))) .thenReturn(new ZmrRegisterResult(Arrays.asList(randomResult1, randomResult2), zmrProcessId)); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchCountrySpecific(any(), eq(DE))) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute task TaskExecutionException exception = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq1, executionContext)); // validate state assertTrue("Wrong exception", (exception.getOriginalException() instanceof WorkflowException)); assertTrue("Wrong flag 'manualFixNeeded'", ((WorkflowException) exception.getOriginalException()).isRequiresManualFix()); } /** * Multiple matches found in ZMR and ERnP by country specifics. */ @Test @DirtiesContext public void multiplePersonFindWithCountySpecifics_ZmrAndErnp() throws Exception { final AuthenticationResponse response = buildDummyAuthResponseDE(randomGivenName, randomFamilyName, randomPersonalIdentifier_DE, randomBirthDate, randomPlaceOfBirth, randomBirthName); TestRequestImpl pendingReq1 = new TestRequestImpl(); pendingReq1.getSessionData(AuthProcessDataWrapper.class) .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); BigInteger zmrProcessId = generateRandomProcessId(); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); RegisterResult randomResult1 = RegisterResult.builder() .bpk(randomBpk) .pseudonym(Collections.singletonList(randomPseudonym)) .givenName(randomGivenName) .familyName(randomFamilyName) .dateOfBirth(randomBirthDate) .placeOfBirth(randomPlaceOfBirth) .birthName(randomBirthName) .build(); Mockito.when(zmrClient.searchCountrySpecific(eq(zmrProcessId), any(PersonSuchenRequest.class), eq(DE))) .thenReturn(new ZmrRegisterResult(Arrays.asList(randomResult1), zmrProcessId)); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchCountrySpecific(any(), eq(DE))) .thenReturn(ernpRegisterResult(randomRegisterResult(RandomStringUtils.randomAlphanumeric(10)))); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute task TaskExecutionException exception = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq1, executionContext)); // validate state assertTrue("Wrong exception", (exception.getOriginalException() instanceof WorkflowException)); assertTrue("Wrong flag 'manualFixNeeded'", ((WorkflowException) exception.getOriginalException()).isRequiresManualFix()); } /** * Multiple matches found in ERnP by country specifics. */ @Test @DirtiesContext public void multiplePersonFindWithCountySpecifics_Ernp() throws Exception { final AuthenticationResponse response = buildDummyAuthResponseDE(randomGivenName, randomFamilyName, randomPersonalIdentifier_DE, randomBirthDate, randomPlaceOfBirth, randomBirthName); TestRequestImpl pendingReq1 = new TestRequestImpl(); pendingReq1.getSessionData(AuthProcessDataWrapper.class) .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); BigInteger zmrProcessId = generateRandomProcessId(); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.searchCountrySpecific(eq(zmrProcessId), any(PersonSuchenRequest.class), eq(DE))) .thenReturn(new ZmrRegisterResult(Arrays.asList(), zmrProcessId)); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchCountrySpecific(any(), eq(DE))) .thenReturn(ernpRegisterResult(Arrays.asList(randomRegisterResult(), randomRegisterResult()))); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute task TaskExecutionException exception = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq1, executionContext)); // validate state assertTrue("Wrong exception", (exception.getOriginalException() instanceof WorkflowException)); assertTrue("Wrong flag 'manualFixNeeded'", ((WorkflowException) exception.getOriginalException()).isRequiresManualFix()); } /** * NO match found in ZMR and ErnP with Initial and MDS search * * @throws EidasSAuthenticationException * @throws URISyntaxException * @throws EaafStorageException */ @Test @DirtiesContext public void noResultByAnySearch() throws TaskExecutionException, EidasSAuthenticationException, URISyntaxException, EaafStorageException { BigInteger zmrProcessId = generateRandomProcessId(); pendingReq.getSessionData(AuthProcessDataWrapper.class) .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, buildDummyAuthResponse(randomGivenName, randomFamilyName, randomPersonalIdentifier_EE, randomBirthDate)); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, EE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.searchWithMds(zmrProcessId, randomGivenName, randomFamilyName, randomBirthDate, EE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.update(any(), any(), any())) .thenThrow(new IllegalStateException("ZMR update should not be neccessary")); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, EE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchWithMds(randomGivenName, randomFamilyName, randomBirthDate, EE)) .thenReturn(new ErnpRegisterResult(Collections.emptyList())); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ZMR update should not be neccessary")); // execute task task.execute(pendingReq, executionContext); // validate state assertNotNull("find no eIDAS inbut data", MatchingTaskUtils.getInitialEidasData(pendingReq)); assertNull("Find intermediate matching data but matching should be finished", MatchingTaskUtils.getIntermediateMatchingResult(pendingReq)); assertNull("Find final matching data but no match sould be found", MatchingTaskUtils.getFinalMatchingResult(pendingReq)); Boolean transitionGUI = (Boolean) executionContext.get(Constants.TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK); Assert.assertNull("Wrong transition", transitionGUI); Boolean transitionErnb = (Boolean) executionContext.get(Constants.TRANSITION_TO_CREATE_NEW_ERNP_ENTRY_TASK); Assert.assertTrue("Wrong transition", transitionErnb); } /** * Find one match with MDS search in ERnP. */ @Test @DirtiesContext public void resultByMdsSearch_Ernb() throws TaskExecutionException, EidasSAuthenticationException, URISyntaxException, EaafStorageException { BigInteger zmrProcessId = generateRandomProcessId(); pendingReq.getSessionData(AuthProcessDataWrapper.class) .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, buildDummyAuthResponse(randomGivenName, randomFamilyName, randomPersonalIdentifier_EE, randomBirthDate)); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, EE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.searchWithMds(zmrProcessId, randomGivenName, randomFamilyName, randomBirthDate, EE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, EE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchWithMds(randomGivenName, randomFamilyName, randomBirthDate, EE)) .thenReturn(ernpRegisterResult(randomRegisterResult())); // execute test task.execute(pendingReq, executionContext); // validate state checkIntermediateResult(1); } /** * Find one match with MDS search in ZMR. */ @Test @DirtiesContext public void resultByMdsSearch_Zmr() throws TaskExecutionException, EidasSAuthenticationException { BigInteger zmrProcessId = generateRandomProcessId(); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.searchWithMds(zmrProcessId, randomGivenName, randomFamilyName, randomBirthDate, DE)) .thenReturn(zmrRegisterResult(randomRegisterResult(), zmrProcessId)); Mockito.when(zmrClient.update(any(), any(), any())) .thenThrow(new IllegalStateException("ZMR update should not be neccessary")); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)).thenReturn( emptyErnpRegisterResult()); Mockito.when(ernpClient.searchWithMds(randomGivenName, randomFamilyName, randomBirthDate, DE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute test task.execute(pendingReq, executionContext); // validate state checkIntermediateResult(1); } /** * Find matches with MDS search in ZMR and ERnP. */ @Test @DirtiesContext @SneakyThrows public void resultByMdsSearch_ZmrAndErnp() throws TaskExecutionException, EidasSAuthenticationException { BigInteger zmrProcessId = generateRandomProcessId(); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.searchWithMds(zmrProcessId, randomGivenName, randomFamilyName, randomBirthDate, DE)) .thenReturn(zmrRegisterResult(randomRegisterResult(), zmrProcessId)); Mockito.when(zmrClient.update(any(), any(), any())) .thenThrow(new IllegalStateException("ZMR update should not be neccessary")); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)).thenReturn( emptyErnpRegisterResult()); Mockito.when(ernpClient.searchWithMds(randomGivenName, randomFamilyName, randomBirthDate, DE)) .thenReturn(ernpRegisterResult(randomRegisterResult(RandomStringUtils.randomAlphanumeric(10)))); Mockito.when(ernpClient.update(any(), any())) .thenThrow(new IllegalStateException("ERnP update should not be neccessary")); // execute test task.execute(pendingReq, executionContext); // validate state checkIntermediateResult(2); DetailedMatchtingStatistic entry = MatchingTaskUtils.getDetailedMatchingStatistic(pendingReq); assertNotNull("statisticLogEntry", entry); assertEquals("ZMR", 0, entry.getPersonalIdResult().getZmrResults()); assertEquals("ERnP", 0, entry.getPersonalIdResult().getErnpResults()); assertEquals("ZMR", 1, entry.getMdsResult().getZmrResults()); assertEquals("ERnP", 1, entry.getMdsResult().getErnpResults()); } /** * resultByMdsSearch */ @Test @DirtiesContext public void multipleResultsByMdsSearch() throws TaskExecutionException, EidasSAuthenticationException { BigInteger zmrProcessId = generateRandomProcessId(); Mockito.when(zmrClient.searchWithPersonIdentifier(null, randomPseudonym, DE)) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.searchCountrySpecific(eq(zmrProcessId), any(PersonSuchenRequest.class), any(String.class))) .thenReturn(new ZmrRegisterResult(Collections.emptyList(), zmrProcessId)); Mockito.when(zmrClient.searchWithMds(zmrProcessId, randomGivenName, randomFamilyName, randomBirthDate, DE)) .thenReturn(zmrRegisterResult(randomRegisterResult(randomBpk + "2"), zmrProcessId)); Mockito.when(zmrClient.update(any(), any(), any())) .thenThrow(new IllegalStateException("ZMR update should not be neccessary")); Mockito.when(ernpClient.searchWithPersonIdentifier(randomPseudonym, DE)) .thenReturn(emptyErnpRegisterResult()); Mockito.when(ernpClient.searchWithMds(randomGivenName, randomFamilyName, randomBirthDate, DE)) .thenReturn(ernpRegisterResult(Arrays.asList(randomRegisterResult(), randomRegisterResult(randomBpk + "1")))); // execute test task.execute(pendingReq, executionContext); // validate state checkIntermediateResult(3); } @NotNull private ZmrSoapClient.ZmrRegisterResult emptyZmrRegisterResult() { return new ZmrRegisterResult(Collections.emptyList(), generateRandomProcessId()); } @NotNull private ErnpRegisterResult emptyErnpRegisterResult() { return new ErnpRegisterResult(Collections.emptyList()); } @NotNull private ZmrRegisterResult zmrRegisterResult(RegisterResult registerResult, BigInteger processId) { return new ZmrRegisterResult(Collections.singletonList(registerResult), processId); } @NotNull private ZmrRegisterResult zmrRegisterResult(RegisterResult registerResult) { return zmrRegisterResult(registerResult, generateRandomProcessId()); } @NotNull private ErnpRegisterResult ernpRegisterResult(RegisterResult registerResult) { return new ErnpRegisterResult(Collections.singletonList( new ErnpPersonRegisterResult(registerResult, false))); } @NotNull private ErnpRegisterResult ernpRegisterResult(List registerResult) { return new ErnpRegisterResult( registerResult.stream() .map(el -> { if (el instanceof ErnpPersonRegisterResult) { return (ErnpPersonRegisterResult)el; } else { return new ErnpPersonRegisterResult(el, false); } }) .collect(Collectors.toList())); } @NotNull private RegisterResult randomRegisterResult() { return randomRegisterResult(randomGivenName, randomBpk); } @NotNull private RegisterResult randomRegisterResult(String randomBpk) { return randomRegisterResult(randomGivenName, randomBpk); } @NotNull private RegisterResult randomRegisterResult(String randomGivenName, String randomBpk) { return RegisterResult.builder() .bpk(randomBpk) .pseudonym(Collections.singletonList(randomPseudonym)) .givenName(randomGivenName) .familyName(randomFamilyName) .dateOfBirth(randomBirthDate) .build(); } @NotNull private RegisterResult randomRegisterResult(String randomGivenName, String randomBpk, String placeOfBirth) { return RegisterResult.builder() .bpk(randomBpk) .pseudonym(Collections.singletonList(randomPseudonym)) .givenName(randomGivenName) .familyName(randomFamilyName) .dateOfBirth(randomBirthDate) .placeOfBirth(placeOfBirth) .build(); } @NotNull private AuthenticationResponse buildDummyAuthResponseRandomPerson() throws URISyntaxException { return buildDummyAuthResponse(randomGivenName, randomFamilyName, DE_ST + randomPseudonym, randomBirthDate); } private BigInteger generateRandomProcessId() { return new BigInteger(RandomStringUtils.randomNumeric(10)); } private void checkMatchingSuccessState(IRequest pendingReq, String bpk, String familyName, String givenName, String birhday, String countryCode) { assertNull("Find intermediate matching data but matching should be finished", MatchingTaskUtils.getIntermediateMatchingResult(pendingReq)); assertNotNull("find no eIDAS inbut data", MatchingTaskUtils.getInitialEidasData(pendingReq)); MatchedPersonResult personInfo = MatchingTaskUtils.getFinalMatchingResult(pendingReq); assertNotNull("no final matching result", personInfo); assertEquals("wrong bpk", bpk, personInfo.getBpk()); assertEquals("wrong givenName", givenName, personInfo.getGivenName()); assertEquals("wrong familyName", familyName, personInfo.getFamilyName()); assertEquals("wrong dateOfBirth", birhday, personInfo.getDateOfBirth()); assertEquals("wrong countryCode", countryCode, personInfo.getCountryCode()); } private void checkIntermediateResult(int resultSize) { Boolean transitionGUI = (Boolean) executionContext.get(Constants.TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK); Assert.assertTrue("Wrong transition", transitionGUI); Boolean transitionErnb = (Boolean) executionContext.get(Constants.TRANSITION_TO_CREATE_NEW_ERNP_ENTRY_TASK); Assert.assertNull("Wrong transition", transitionErnb); assertNotNull("find no eIDAS inbut data", MatchingTaskUtils.getInitialEidasData(pendingReq)); assertNull("Find final matching data but no match sould be found", MatchingTaskUtils.getFinalMatchingResult(pendingReq)); RegisterStatusResults result = MatchingTaskUtils.getIntermediateMatchingResult(pendingReq); assertNotNull("Find no intermediate matching data", result); assertEquals("wrong intermediate result size", resultSize, result.getResultCount()); } @NotNull private AuthenticationResponse buildDummyAuthResponse(String givenName, String familyName, String identifier, String dateOfBirth) throws URISyntaxException { return buildDummyAuthResponse(givenName, familyName, identifier, dateOfBirth, null, null, null); } @NotNull private AuthenticationResponse buildDummyAuthResponseDE(String givenName, String familyName, String identifier, String dateOfBirth, String placeOfBirth, String birthName) throws URISyntaxException { return buildDummyAuthResponse(givenName, familyName, identifier, dateOfBirth, null, placeOfBirth, birthName); } @NotNull private AuthenticationResponse buildDummyAuthResponse(String givenName, String familyName, String identifier, String dateOfBirth, String taxNumber, String placeOfBirth, String birthName) throws URISyntaxException { ImmutableAttributeMap.Builder builder = ImmutableAttributeMap.builder() .put(generateStringAttribute(EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER, randomAlphabetic(2), randomAlphabetic(2)), identifier) .put(generateStringAttribute(EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME, randomAlphabetic(3), randomAlphabetic(3)), familyName) .put(generateStringAttribute(EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME, randomAlphabetic(4), randomAlphabetic(4)), givenName) .put(generateDateTimeAttribute(EidasConstants.eIDAS_ATTR_DATEOFBIRTH, randomAlphabetic(5), randomAlphabetic(5)), dateOfBirth); if (taxNumber != null) { builder.put(generateStringAttribute(EidasConstants.eIDAS_ATTR_TAXREFERENCE, randomAlphabetic(6), randomAlphabetic(6)), taxNumber); } if (birthName != null) { builder.put(generateStringAttribute(EidasConstants.eIDAS_ATTR_BIRTHNAME, randomAlphabetic(7), randomAlphabetic(7)), birthName); } if (placeOfBirth != null) { builder.put(generateStringAttribute(EidasConstants.eIDAS_ATTR_PLACEOFBIRTH, randomAlphabetic(8), randomAlphabetic(8)), placeOfBirth); } final ImmutableAttributeMap attributeMap = builder.build(); return new AuthenticationResponse.Builder().id(randomAlphabetic(5)) .issuer(randomAlphabetic(5)).subject(randomAlphabetic(5)).statusCode("200") .inResponseTo(randomAlphabetic(5)).subjectNameIdFormat(randomAlphabetic(5)) .attributes(attributeMap).build(); } private AttributeDefinition generateStringAttribute(String friendlyName, String fragment, String prefix) throws URISyntaxException { return generateAttribute(friendlyName, fragment, prefix, "eu.eidas.auth.commons.attribute.impl" + ".LiteralStringAttributeValueMarshaller"); } @SuppressWarnings("SameParameterValue") private AttributeDefinition generateDateTimeAttribute(String friendlyName, String fragment, String prefix) throws URISyntaxException { return generateAttribute(friendlyName, fragment, prefix, "eu.eidas.auth.commons.attribute.impl" + ".DateTimeAttributeValueMarshaller"); } private AttributeDefinition generateAttribute(String friendlyName, String fragment, String prefix, String marshaller) throws URISyntaxException { return AttributeDefinition.builder() .friendlyName(friendlyName).nameUri(new URI("ad", "sd", fragment)) .personType(PersonType.LEGAL_PERSON).xmlType(new QName("http://saf", "as", prefix)) .attributeValueMarshaller(marshaller).build(); } }