diff options
26 files changed, 1740 insertions, 138 deletions
@@ -12,3 +12,4 @@ target .metadata *.iml notCommit +.idea diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 81a4a4dd..5b324d5f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,7 +12,6 @@ variables: include: - template: Dependency-Scanning.gitlab-ci.yml - - template: Security/SAST.gitlab-ci.yml - template: Secret-Detection.gitlab-ci.yml stages: diff --git a/connector/src/main/resources/properties/status_messages_en.properties b/connector/src/main/resources/properties/status_messages_en.properties index 80228a47..c430fc90 100644 --- a/connector/src/main/resources/properties/status_messages_en.properties +++ b/connector/src/main/resources/properties/status_messages_en.properties @@ -6,6 +6,9 @@ eidas.04=Request contains no sessionToken. Authentication process stops eidas.05=Received eIDAS response-message is not valid. Reason: {0} eidas.06=LoA from eIDAS response-message {0} does not match to requested LoA eidas.07=eIDAS Response attribute-validation FAILED. Attribute:{0} Reason: {1} +eidas.08=An unexpected error occurred. +eidas.09=An error occurred while loading your data from official registers. Please contact the support. + config.01=No configuration-file parameter found. Maybe Java SystemD parameter is missing config.03=Can not load configuration from path {0} (See logs for more details) diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java index 83a2afa6..54f7f8fa 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java @@ -32,6 +32,7 @@ public class Constants { public static final String DATA_REQUESTED_LOA_LIST = "req_requestedLoA"; public static final String DATA_REQUESTED_LOA_COMPERISON = "req_requestedLoAComperision"; public static final String DATA_FULL_EIDAS_RESPONSE = "resp_fulleIDASResponse"; + public static final String DATA_RESULT_MATCHING_BPK = "matching-result-bpk"; // templates for post-binding forwarding public static final String TEMPLATE_POST_FORWARD_NAME = "eidas_node_forward.html"; @@ -137,6 +138,7 @@ public class Constants { public static final String eIDAS_ATTR_PLACEOFBIRTH = "PlaceOfBirth"; public static final String eIDAS_ATTR_BIRTHNAME = "BirthName"; public static final String eIDAS_ATTR_CURRENTADDRESS = "CurrentAddress"; + public static final String eIDAS_ATTR_TAXREFERENCE = "TaxReference"; public static final String eIDAS_ATTR_LEGALPERSONIDENTIFIER = "LegalPersonIdentifier"; public static final String eIDAS_ATTR_LEGALNAME = "LegalName"; @@ -169,4 +171,6 @@ public class Constants { "AJZyj/+sdCMDRq9RkvbFcgSTVn/OfS8EUE81ddwP8MNuJ1kd1SWBUJPaQX2JLJHrL54mkOhrkhH2M/zcuOTu8nW9TOEg" + "XGjrRB/0HpiYKpV+VDJViyyc/GacNLxN4Anw4pima6gHYaJIw9hQkL/nuO2hyh8PGJd7rxeFXJmbLy+X"; + public static final String COUNTRY_CODE_DE = "DE"; + public static final String COUNTRY_CODE_IT = "IT"; } diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/ErnbEidData.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/ErnbEidData.java index 6c7eeb6b..b780d3e8 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/ErnbEidData.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/ErnbEidData.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 A-SIT Plus GmbH + * 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. * diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MergedRegisterSearchResult.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MergedRegisterSearchResult.java new file mode 100644 index 00000000..e147b8aa --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MergedRegisterSearchResult.java @@ -0,0 +1,75 @@ +/* + * 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.dao; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException; +import lombok.Data; + +import java.util.List; + +@Data +public class MergedRegisterSearchResult { + + final List<RegisterResult> resultsZmr; + final List<RegisterResult> resultsErnp; + + public MergedRegisterSearchResult(List<RegisterResult> resultsZmr, List<RegisterResult> resultsErnp) { + this.resultsZmr = resultsZmr; + this.resultsErnp = resultsErnp; + } + + public int getResultCount() { + return resultsZmr.size() + resultsErnp.size(); + } + + /** + * Verifies that there is only one match and returns the bpk. + * + * @return bpk bpk of the match + * @throws WorkflowException if multiple results have been found + */ + public String getBpk() throws WorkflowException { + if (getResultCount() != 1) { + throw new WorkflowException("getResultCount() != 1"); + } + return getResult().getBpk(); + } + + /** + * Returns the results, if there is exactly one, throws exception otherwise. + * + * @return The result + * @throws WorkflowException Results does not contain exactly one result + */ + public RegisterResult getResult() throws WorkflowException { + if (getResultCount() != 1) { + throw new WorkflowException("getResultCount() != 1"); + } + if (resultsZmr.size() == 1) { + return resultsZmr.get(0); + } else { + return resultsErnp.get(0); + } + } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/RegisterResult.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/RegisterResult.java new file mode 100644 index 00000000..1cc36fe9 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/RegisterResult.java @@ -0,0 +1,88 @@ +/* + * 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.dao; + +import at.gv.e_government.reference.namespace.persondata._20020228.PostalAddressType; +import lombok.Data; + +@Data +public class RegisterResult { + + // MDS + private String pseudonym; + private String givenName; + private String familyName; + private String dateOfBirth; + + // additional attributes + private String placeOfBirth; + private String birthName; + private String taxNumber; + private PostalAddressType address; + + private String bpk; + + /** + * Register search result. + * + * @param bpk The bpk + * @param pseudonym The pseudonym + * @param givenName The givenName + * @param familyName The familyName + * @param dateOfBirth The dateOfBirth + */ + public RegisterResult(String bpk, String pseudonym, String givenName, String familyName, String dateOfBirth) { + this.bpk = bpk; + this.pseudonym = pseudonym; + this.givenName = givenName; + this.familyName = familyName; + this.dateOfBirth = dateOfBirth; + } + + /** + * Register search result. + * + * @param bpk The bpk + * @param pseudonym The pseudonym + * @param givenName The givenName + * @param familyName The familyName + * @param dateOfBirth The dateOfBirth + * @param placeOfBirth The placeOfBirth + * @param birthName The birthName + * @param taxNumber The taxNumber + * @param address The address + */ + public RegisterResult(String bpk, String pseudonym, String givenName, String familyName, String dateOfBirth, + String placeOfBirth, String birthName, String taxNumber, PostalAddressType address) { + this.bpk = bpk; + this.pseudonym = pseudonym; + this.givenName = givenName; + this.familyName = familyName; + this.dateOfBirth = dateOfBirth; + this.placeOfBirth = placeOfBirth; + this.birthName = birthName; + this.taxNumber = taxNumber; + this.address = address; + } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleEidasData.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleEidasData.java new file mode 100644 index 00000000..57597122 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleEidasData.java @@ -0,0 +1,81 @@ +/* + * 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.dao; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException; +import at.gv.e_government.reference.namespace.persondata._20020228.PostalAddressType; +import lombok.Data; + +@Data +public class SimpleEidasData { + + private String citizenCountryCode; + + // MDS + private String pseudonym; + private String givenName; + private String familyName; + private String dateOfBirth; + + // additional attributes + private String placeOfBirth; + private String birthName; + private PostalAddressType address; + private String taxNumber; + + /** + * Compares the register result with the eidas data. + * @param result The register data to use for comparison + * @return true or false depending of the data matches + * @throws WorkflowException if multiple results have been found + */ + public boolean equalsRegisterData(MergedRegisterSearchResult result) throws WorkflowException { + return equalsRegisterData(result.getResult()); + } + + private boolean equalsRegisterData(RegisterResult result) { + if (!result.getPseudonym().equals(pseudonym)) { + return false; + } + if (!result.getGivenName().equals(givenName)) { + return false; + } + if (!result.getFamilyName().equals(familyName)) { + return false; + } + if (!result.getDateOfBirth().equals(dateOfBirth)) { + return false; + } + if (!result.getPlaceOfBirth().equals(placeOfBirth)) { + return false; + } + if (!result.getBirthName().equals(birthName)) { + return false; + } + if (!result.getTaxNumber().equals(taxNumber)) { + return false; + } + return true; + } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/DummyErnpClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/DummyErnpClient.java new file mode 100644 index 00000000..e514c808 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/DummyErnpClient.java @@ -0,0 +1,66 @@ +/* + * 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.ernp; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +@Service("ErnbClientForeIDAS") +public class DummyErnpClient implements IErnpClient { + + @Override + public List<RegisterResult> searchWithPersonIdentifier(String personIdentifier) { + return Collections.emptyList(); + } + + @Override + public List<RegisterResult> searchWithMds(String givenName, String familyName, String dateOfBirth) { + //TODO will I only receive matches where all three values match perfectly? + return Collections.emptyList(); + } + + @Override + public List<RegisterResult> searchDeSpecific(String givenName, String familyName, String dateOfBirth, + String birthPlace, String birthName) { + //TODO + return Collections.emptyList(); + } + + @Override + public List<RegisterResult> searchItSpecific(String taxNumber) { + //TODO + return Collections.emptyList(); + } + + @Override + public void update(RegisterResult registerResult, SimpleEidasData eidData) { + //TODO + } + + +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/IErnpClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/IErnpClient.java new file mode 100644 index 00000000..b9641c5c --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/IErnpClient.java @@ -0,0 +1,43 @@ +/* + * 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.ernp; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; + +import java.util.List; + +public interface IErnpClient { + + List<RegisterResult> searchWithPersonIdentifier(String personIdentifier); + + List<RegisterResult> searchWithMds(String givenName, String familyName, String dateOfBirth); + + List<RegisterResult> searchDeSpecific(String givenName, String familyName, String dateOfBirth, + String birthPlace, String birthName); + + List<RegisterResult> searchItSpecific(String taxNumber); + + void update(RegisterResult registerResult, SimpleEidasData eidData); +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ManualFixNecessaryException.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ManualFixNecessaryException.java new file mode 100644 index 00000000..2fecaa6b --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ManualFixNecessaryException.java @@ -0,0 +1,38 @@ +/* + * 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.exception; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; + +public class ManualFixNecessaryException extends EidasSAuthenticationException { + private static final long serialVersionUID = 1L; + + public ManualFixNecessaryException(String personIdentifier) { + super("eidas.09", new Object[] { personIdentifier }); + } + + public ManualFixNecessaryException(SimpleEidasData eidData) { + super("eidas.09", new Object[] { eidData.getPseudonym() });//TODO what info to pass??? + } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/WorkflowException.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/WorkflowException.java new file mode 100644 index 00000000..b6f3309b --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/WorkflowException.java @@ -0,0 +1,33 @@ +/* + * 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.exception; + +public class WorkflowException extends EidasSAuthenticationException { + private static final long serialVersionUID = 1L; + + public WorkflowException(String data) { + super("eidas.08", new Object[]{data}); + } + +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java index fe839c37..e3c1e00f 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java @@ -23,18 +23,6 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.handler; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import com.google.common.collect.ImmutableSortedSet; - import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.ErnbEidData; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException; @@ -46,12 +34,21 @@ import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.idp.IConfigurationWithSP; import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration; import at.gv.egiz.eaaf.core.impl.data.Triple; +import com.google.common.collect.ImmutableSortedSet; import edu.umd.cs.findbugs.annotations.NonNull; import eu.eidas.auth.commons.attribute.AttributeDefinition; import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; import eu.eidas.auth.commons.light.impl.LightRequest.Builder; import eu.eidas.auth.commons.protocol.eidas.SpType; -import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public abstract class AbstractEidProcessor implements INationalEidProcessor { private static final Logger log = LoggerFactory.getLogger(AbstractEidProcessor.class); @@ -66,7 +63,6 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { buildProviderNameAttribute(pendingReq, authnRequestBuilder); buildRequestedAttributes(authnRequestBuilder); - } @Override @@ -91,13 +87,12 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { result.setAddress(processAddress(eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTADDRESS))); return result; - } - + /** * Get a Map of country-specific requested attributes. - * + * * @return */ @NonNull @@ -105,7 +100,7 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { /** * Post-Process the eIDAS CurrentAddress attribute. - * + * * @param currentAddressObj eIDAS current address information * @return current address or null if no attribute is available * @throws EidPostProcessingException if post-processing fails @@ -113,34 +108,12 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { */ protected PostalAddressType processAddress(Object currentAddressObj) throws EidPostProcessingException, EidasAttributeException { - - if (currentAddressObj != null) { - if (currentAddressObj instanceof PostalAddress) { - final PostalAddressType result = new PostalAddressType(); - result.setPostalCode(((PostalAddress) currentAddressObj).getPostCode()); - result.setMunicipality(((PostalAddress) currentAddressObj).getPostName()); - - // TODO: add more mappings - - return result; - - } else { - log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_CURRENTADDRESS + " is of WRONG type"); - throw new EidasAttributeException(Constants.eIDAS_ATTR_CURRENTADDRESS); - - } - - } else { - log.debug("NO '" + Constants.eIDAS_ATTR_CURRENTADDRESS + "' attribute. Post-Processing skipped ... "); - } - - return null; - + return EidasResponseUtils.processAddress(currentAddressObj); } /** * Post-Process the eIDAS BirthName attribute. - * + * * @param birthNameObj eIDAS birthname information * @return birthName or null if no attribute is available * @throws EidPostProcessingException if post-processing fails @@ -148,27 +121,12 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { */ protected String processBirthName(Object birthNameObj) throws EidPostProcessingException, EidasAttributeException { - if (birthNameObj != null) { - if (birthNameObj instanceof String) { - return (String) birthNameObj; - - } else { - log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_BIRTHNAME + " is of WRONG type"); - throw new EidasAttributeException(Constants.eIDAS_ATTR_BIRTHNAME); - - } - - } else { - log.debug("NO '" + Constants.eIDAS_ATTR_BIRTHNAME + "' attribute. Post-Processing skipped ... "); - } - - return null; - + return EidasResponseUtils.processBirthName(birthNameObj); } /** * Post-Process the eIDAS PlaceOfBirth attribute. - * + * * @param placeOfBirthObj eIDAS Place-of-Birth information * @return place of Birth or null if no attribute is available * @throws EidPostProcessingException if post-processing fails @@ -176,27 +134,12 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { */ protected String processPlaceOfBirth(Object placeOfBirthObj) throws EidPostProcessingException, EidasAttributeException { - if (placeOfBirthObj != null) { - if (placeOfBirthObj instanceof String) { - return (String) placeOfBirthObj; - - } else { - log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_PLACEOFBIRTH + " is of WRONG type"); - throw new EidasAttributeException(Constants.eIDAS_ATTR_PLACEOFBIRTH); - - } - - } else { - log.debug("NO '" + Constants.eIDAS_ATTR_PLACEOFBIRTH + "' attribute. Post-Processing skipped ... "); - } - - return null; - + return EidasResponseUtils.processPlaceOfBirth(placeOfBirthObj); } /** * Post-Process the eIDAS DateOfBirth attribute. - * + * * @param dateOfBirthObj eIDAS date-of-birth attribute information * @return formated user's date-of-birth * @throws EidasAttributeException if NO attribute is available @@ -204,17 +147,12 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { */ protected DateTime processDateOfBirth(Object dateOfBirthObj) throws EidPostProcessingException, EidasAttributeException { - if (dateOfBirthObj == null || !(dateOfBirthObj instanceof DateTime)) { - throw new EidasAttributeException(Constants.eIDAS_ATTR_DATEOFBIRTH); - } - - return (DateTime) dateOfBirthObj; - + return EidasResponseUtils.processDateOfBirth(dateOfBirthObj); } /** * Post-Process the eIDAS GivenName attribute. - * + * * @param givenNameObj eIDAS givenName attribute information * @return formated user's givenname * @throws EidasAttributeException if NO attribute is available @@ -222,17 +160,12 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { */ protected String processGivenName(Object givenNameObj) throws EidPostProcessingException, EidasAttributeException { - if (givenNameObj == null || !(givenNameObj instanceof String)) { - throw new EidasAttributeException(Constants.eIDAS_ATTR_CURRENTGIVENNAME); - } - - return (String) givenNameObj; - + return EidasResponseUtils.processGivenName(givenNameObj); } /** * Post-Process the eIDAS FamilyName attribute. - * + * * @param familyNameObj eIDAS familyName attribute information * @return formated user's familyname * @throws EidasAttributeException if NO attribute is available @@ -240,17 +173,12 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { */ protected String processFamilyName(Object familyNameObj) throws EidPostProcessingException, EidasAttributeException { - if (familyNameObj == null || !(familyNameObj instanceof String)) { - throw new EidasAttributeException(Constants.eIDAS_ATTR_CURRENTFAMILYNAME); - } - - return (String) familyNameObj; - + return EidasResponseUtils.processFamilyName(familyNameObj); } /** * Post-Process the eIDAS pseudonym to ERnB unique identifier. - * + * * @param personalIdObj eIDAS PersonalIdentifierAttribute * @return Unique personal identifier without country-code information * @throws EidasAttributeException if NO attribute is available @@ -258,15 +186,7 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { */ protected String processPseudonym(Object personalIdObj) throws EidPostProcessingException, EidasAttributeException { - if (personalIdObj == null || !(personalIdObj instanceof String)) { - throw new EidasAttributeException(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); - } - - final Triple<String, String, String> eIdentifier = - EidasResponseUtils.parseEidasPersonalIdentifier((String) personalIdObj); - - return eIdentifier.getThird(); - + return EidasResponseUtils.processPseudonym(personalIdObj); } private void buildRequestedAttributes(Builder authnRequestBuilder) { @@ -332,8 +252,8 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor { final String providerName = pendingReq.getRawData(Constants.DATA_PROVIDERNAME, String.class); if (StringUtils.isNotEmpty(providerName) && basicConfig.getBasicConfigurationBoolean( - Constants.CONIG_PROPS_EIDAS_NODE_WORKAROUND_ADD_ALWAYS_PROVIDERNAME, - false)) { + Constants.CONIG_PROPS_EIDAS_NODE_WORKAROUND_ADD_ALWAYS_PROVIDERNAME, + false)) { authnRequestBuilder.providerName(providerName); } diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/CountrySpecificDetailSearchProcessor.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/CountrySpecificDetailSearchProcessor.java new file mode 100644 index 00000000..c5b3b231 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/CountrySpecificDetailSearchProcessor.java @@ -0,0 +1,61 @@ +/* + * 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.handler; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MergedRegisterSearchResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.ernp.IErnpClient; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr.IZmrClient; + +public abstract class CountrySpecificDetailSearchProcessor { + + protected IErnpClient ernbClient; + protected IZmrClient zmrClient; + + public CountrySpecificDetailSearchProcessor(IErnpClient ernbClient, IZmrClient zmrClient) { + this.ernbClient = ernbClient; + this.zmrClient = zmrClient; + } + + /** + * Get a friendlyName of this post-processor implementation. + * + * @return + */ + public String getName() { + return this.getClass().getName(); + } + + /** + * Check if this postProcessor is sensitive for a specific country. + * + * @param countryCode of the eID data that should be processed + * @param eidData eID data + * @return true if this implementation can handle the country, otherwise false + */ + public abstract boolean canHandle(String countryCode, SimpleEidasData eidData); + + public abstract MergedRegisterSearchResult search(SimpleEidasData eidData); + +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/DeSpecificDetailSearchProcessor.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/DeSpecificDetailSearchProcessor.java new file mode 100644 index 00000000..544d5b0c --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/DeSpecificDetailSearchProcessor.java @@ -0,0 +1,65 @@ +/* + * 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.handler; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MergedRegisterSearchResult; +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.ernp.IErnpClient; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr.IZmrClient; + +import java.util.List; + +public class DeSpecificDetailSearchProcessor extends CountrySpecificDetailSearchProcessor { + + public DeSpecificDetailSearchProcessor(IErnpClient ernbClient, IZmrClient zmrClient) { + super(ernbClient, zmrClient); + } + + @Override + public boolean canHandle(String countryCode, SimpleEidasData eidData) { + if (!countryCode.equalsIgnoreCase(Constants.COUNTRY_CODE_DE)) { + return false; + } + if (eidData.getBirthName() == null || eidData.getBirthName().isEmpty()) { + return false; + } + if (eidData.getPlaceOfBirth() == null || eidData.getPlaceOfBirth().isEmpty()) { + return false; + } + return true; + } + + @Override + public MergedRegisterSearchResult search(SimpleEidasData eidData) { + List<RegisterResult> resultsZmr = + zmrClient.searchDeSpecific(eidData.getGivenName(), eidData.getFamilyName(), eidData.getDateOfBirth(), + eidData.getPlaceOfBirth(), eidData.getBirthName()); + List<RegisterResult> resultsErnb = + ernbClient.searchDeSpecific(eidData.getGivenName(), eidData.getFamilyName(), eidData.getDateOfBirth(), + eidData.getPlaceOfBirth(), eidData.getBirthName()); + return new MergedRegisterSearchResult(resultsZmr, resultsErnb); + } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/ItSpecificDetailSearchProcessor.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/ItSpecificDetailSearchProcessor.java new file mode 100644 index 00000000..370a111c --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/ItSpecificDetailSearchProcessor.java @@ -0,0 +1,58 @@ +/* + * 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.handler; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MergedRegisterSearchResult; +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.ernp.IErnpClient; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr.IZmrClient; + +import java.util.List; + +public class ItSpecificDetailSearchProcessor extends CountrySpecificDetailSearchProcessor { + + public ItSpecificDetailSearchProcessor(IErnpClient ernbClient, IZmrClient zmrClient) { + super(ernbClient, zmrClient); + } + + @Override + public boolean canHandle(String countryCode, SimpleEidasData eidData) { + if (!countryCode.equalsIgnoreCase(Constants.COUNTRY_CODE_IT)) { + return false; + } + if (eidData.getTaxNumber() == null || eidData.getTaxNumber().isEmpty()) { + return false; + } + return true; + } + + @Override + public MergedRegisterSearchResult search(SimpleEidasData eidData) { + List<RegisterResult> resultsZmr = zmrClient.searchItSpecific(eidData.getTaxNumber()); + List<RegisterResult> resultsErnb = ernbClient.searchItSpecific(eidData.getTaxNumber()); + return new MergedRegisterSearchResult(resultsZmr, resultsErnb); + } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/InitialSearchTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/InitialSearchTask.java new file mode 100644 index 00000000..fd9e93e0 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/InitialSearchTask.java @@ -0,0 +1,303 @@ +/* + * 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 at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MergedRegisterSearchResult; +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.ernp.IErnpClient; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.ManualFixNecessaryException; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.CountrySpecificDetailSearchProcessor; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr.IZmrClient; +import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; +import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; +import at.gv.egiz.eaaf.core.impl.data.Triple; +import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; +import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.AttributeValue; +import eu.eidas.auth.commons.light.ILightResponse; +import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Task that searches ErnP and ZMR before adding person to SZR. + * + * @author amarsalek + */ +@Slf4j +@Component("InitialSearchTask") +public class InitialSearchTask extends AbstractAuthServletTask { + + private final List<CountrySpecificDetailSearchProcessor> handlers; + private final IErnpClient ernpClient; + private final IZmrClient zmrClient; + + /** + * Constructor. + * @param handlers List of countrySpecificSearchProcessors + * @param ernpClient Ernp client + * @param zmrClient ZMR client + */ + public InitialSearchTask(List<CountrySpecificDetailSearchProcessor> handlers, IErnpClient ernpClient, + IZmrClient zmrClient) { + this.ernpClient = ernpClient; + this.zmrClient = zmrClient; + this.handlers = handlers; + log.info("# " + handlers.size() + " country specific detail search services are registered"); + } + + @Override + public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response) + throws TaskExecutionException { + try { + final AuthProcessDataWrapper authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class); + final ILightResponse eidasResponse = authProcessData + .getGenericDataFromSession(Constants.DATA_FULL_EIDAS_RESPONSE, ILightResponse.class); + + final SimpleEidasData eidData = convertSimpleMapToSimpleData(convertEidasAttrToSimpleMap( + eidasResponse.getAttributes().getAttributeMap())); + + String bpK = step2RegisterSearchWithPersonidentifier(eidData); + authProcessData.setGenericDataToSession(Constants.DATA_RESULT_MATCHING_BPK, bpK); + } catch (final Exception e) { + log.error("Initial search FAILED.", e); + throw new TaskExecutionException(pendingReq, "Initial search FAILED.", e); + } + } + + private String step2RegisterSearchWithPersonidentifier(SimpleEidasData eidData) throws TaskExecutionException { + log.trace("Starting step2RegisterSearchWithPersonidentifier"); + String personIdentifier = eidData.getPseudonym(); + MergedRegisterSearchResult result = searchInZmrAndErnp(personIdentifier); + if (result.getResultCount() == 0) { + return step5CheckAndPerformCountrySpecificSearchIfPossible(result, eidData); + } else if (result.getResultCount() == 1) { + return step3CheckRegisterUpdateNecessary(result, eidData); + } + throw new TaskExecutionException(pendingReq, "Initial search - Kitt Process necessary.", + new ManualFixNecessaryException(personIdentifier)); + } + + private String step3CheckRegisterUpdateNecessary(MergedRegisterSearchResult result, SimpleEidasData eidData) + throws TaskExecutionException { + log.trace("Starting step3CheckRegisterUpdateNecessary"); + try { + if (eidData.equalsRegisterData(result)) { + //No update necessary, just return bpk + return result.getBpk(); + } else { + return step4UpdateRegisterData(result, eidData); + } + } catch (WorkflowException e) { + throw new TaskExecutionException(pendingReq, "Initial search - Kitt Process necessary.", e); + } + } + + private String step4UpdateRegisterData(MergedRegisterSearchResult result, + SimpleEidasData eidData) throws WorkflowException { + log.trace("Starting step4UpdateRegisterData"); + log.debug("Update " + result + " with " + eidData); + //TODO wann rechtlich möglich? + + return result.getBpk(); + } + + private String step5CheckAndPerformCountrySpecificSearchIfPossible( + MergedRegisterSearchResult result, SimpleEidasData eidData) throws TaskExecutionException { + log.trace("Starting step5CheckAndPerformCountrySpecificSearchIfPossible"); + String citizenCountry = eidData.getCitizenCountryCode(); + CountrySpecificDetailSearchProcessor foundHandler = null; + for (final CountrySpecificDetailSearchProcessor el : handlers) { + if (el.canHandle(citizenCountry, eidData)) { + log.debug("Found suitable country specific search handler for " + citizenCountry + + " by using: " + el.getName()); + foundHandler = el; + break; + } + } + if (foundHandler == null) { + return step8RegisterSearchWithMds(result, eidData); + } else { + return step6CountrySpecificSearch(foundHandler, result, eidData); + } + } + + private String step6CountrySpecificSearch(CountrySpecificDetailSearchProcessor countrySpecificDetailSearchProcessor, + MergedRegisterSearchResult initialSearchResult, SimpleEidasData eidData) + throws TaskExecutionException { + log.trace("Starting step6CountrySpecificSearch"); + MergedRegisterSearchResult countrySpecificDetailSearchResult = + countrySpecificDetailSearchProcessor.search(eidData); + + switch (countrySpecificDetailSearchResult.getResultCount()) { + case 0: + return step8RegisterSearchWithMds(initialSearchResult, eidData); + case 1: + return step7aKittProcess(initialSearchResult, countrySpecificDetailSearchResult, eidData); + default: + throw new TaskExecutionException(pendingReq, "Detail search - Kitt Process necessary.", + new ManualFixNecessaryException(eidData)); + } + } + + private String step7aKittProcess(MergedRegisterSearchResult initialSearchResult, + MergedRegisterSearchResult countrySpecificDetailSearchResult, + SimpleEidasData eidData) throws TaskExecutionException { + log.trace("Starting step7aKittProcess"); + try { + if (initialSearchResult.getResultCount() != 0) { + throw new WorkflowException("initialSearchResult.getResultCount() != 0"); + } + if (countrySpecificDetailSearchResult.getResultCount() != 1) { + throw new WorkflowException("countrySpecificDetailSearchResult.getResultCount() != 1"); + } + if (countrySpecificDetailSearchResult.getResultsZmr().size() == 1) { + zmrClient.update(countrySpecificDetailSearchResult.getResultsZmr().get(0), eidData); + } + if (countrySpecificDetailSearchResult.getResultsErnp().size() == 1) { + ernpClient.update(countrySpecificDetailSearchResult.getResultsErnp().get(0), eidData); + } + String bpK = countrySpecificDetailSearchResult.getBpk(); + return bpK; + } catch (WorkflowException e) { + throw new TaskExecutionException(pendingReq, "Step7a failed.", e); + } + } + + private String step8RegisterSearchWithMds(MergedRegisterSearchResult initialSearchResult, + SimpleEidasData eidData) { + log.trace("Starting step8RegisterSearchWithMds"); + List<RegisterResult> resultsZmr = + zmrClient.searchWithMds(eidData.getGivenName(), eidData.getFamilyName(), eidData.getDateOfBirth()); + List<RegisterResult> resultsErnp = + ernpClient.searchWithMds(eidData.getGivenName(), eidData.getFamilyName(), eidData.getDateOfBirth()); + MergedRegisterSearchResult mdsSearchResult = new MergedRegisterSearchResult(resultsZmr, resultsErnp); + log.debug("Automerge " + initialSearchResult + " with " + eidData + " " + mdsSearchResult); + //TODO implement next phase and return correct value + return "TODO-Temporary-Endnode-105"; + } + + private MergedRegisterSearchResult searchInZmrAndErnp(String personIdentifier) { + List<RegisterResult> resultsZmr = zmrClient.searchWithPersonIdentifier(personIdentifier); + List<RegisterResult> resultsErnp = ernpClient.searchWithPersonIdentifier(personIdentifier); + return new MergedRegisterSearchResult(resultsZmr, resultsErnp); + } + + private SimpleEidasData convertSimpleMapToSimpleData(Map<String, Object> eidasAttrMap) + throws EidasAttributeException { + SimpleEidasData simpleEidasData = new SimpleEidasData(); + + final Object eIdentifierObj = eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); + final Triple<String, String, String> eIdentifier = + EidasResponseUtils.parseEidasPersonalIdentifier((String) eIdentifierObj); + if (eIdentifier == null) { + throw new EidasAttributeException("Error processing eIdentifier"); + } + simpleEidasData.setCitizenCountryCode(eIdentifier.getFirst()); + + // MDS attributes + simpleEidasData.setPseudonym(EidasResponseUtils.processPseudonym( + eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER))); + simpleEidasData.setFamilyName(EidasResponseUtils.processFamilyName( + eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTFAMILYNAME))); + simpleEidasData.setGivenName(EidasResponseUtils.processGivenName( + eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTGIVENNAME))); + simpleEidasData.setDateOfBirth(EidasResponseUtils.processDateOfBirthToString( + eidasAttrMap.get(Constants.eIDAS_ATTR_DATEOFBIRTH))); + + // additional attributes + simpleEidasData.setPlaceOfBirth(EidasResponseUtils.processPlaceOfBirth( + eidasAttrMap.get(Constants.eIDAS_ATTR_PLACEOFBIRTH))); + simpleEidasData.setBirthName(EidasResponseUtils.processBirthName( + eidasAttrMap.get(Constants.eIDAS_ATTR_BIRTHNAME))); + simpleEidasData.setAddress(EidasResponseUtils.processAddress( + eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTADDRESS))); + + if (eidasAttrMap.containsKey(Constants.eIDAS_ATTR_TAXREFERENCE)) { + simpleEidasData.setTaxNumber(EidasResponseUtils.processTaxReference( + eidasAttrMap.get(Constants.eIDAS_ATTR_TAXREFERENCE))); + } + + //TODO other additional attributes + return simpleEidasData; + } + + private Map<String, Object> convertEidasAttrToSimpleMap( + ImmutableMap<AttributeDefinition<?>, ImmutableSet<? extends AttributeValue<?>>> attributeMap) { + final Map<String, Object> result = new HashMap<>(); + + for (final AttributeDefinition<?> el : attributeMap.keySet()) { + + final Class<?> parameterizedType = el.getParameterizedType(); + if (DateTime.class.equals(parameterizedType)) { + final DateTime attribute = EidasResponseUtils.translateDateAttribute(el, attributeMap.get(el).asList()); + if (attribute != null) { + result.put(el.getFriendlyName(), attribute); + log.trace("Find attr '" + el.getFriendlyName() + "' with value: " + attribute.toString()); + + } else { + log.info("Ignore empty 'DateTime' attribute"); + } + } else if (PostalAddress.class.equals(parameterizedType)) { + final PostalAddress addressAttribute = EidasResponseUtils + .translateAddressAttribute(el, attributeMap.get(el).asList()); + if (addressAttribute != null) { + result.put(el.getFriendlyName(), addressAttribute); + log.trace("Find attr '" + el.getFriendlyName() + "' with value: " + addressAttribute.toString()); + + } else { + log.info("Ignore empty 'PostalAddress' attribute"); + } + } else { + final List<String> natPersonIdObj = EidasResponseUtils + .translateStringListAttribute(el, attributeMap.get(el).asList()); + final String stringAttr = natPersonIdObj.get(0); + if (StringUtils.isNotEmpty(stringAttr)) { + result.put(el.getFriendlyName(), stringAttr); + log.trace("Find attr '" + el.getFriendlyName() + "' with value: " + stringAttr); + } else { + log.info("Ignore empty 'String' attribute"); + } + } + } + log.debug("Receive #" + result.size() + " attributes with names: " + result.keySet().toString()); + return result; + } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseTask.java index 684546f7..0f733e8d 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseTask.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseTask.java @@ -94,7 +94,7 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { attrRegistry); // ********************************************************** - // ******* Store resonse infos into session object ********** + // ******* Store response infos into session object ********** // ********************************************************** // update MOA-Session data with received information diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/EidasResponseUtils.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/EidasResponseUtils.java index ebd2ae78..aafcd8b9 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/EidasResponseUtils.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/EidasResponseUtils.java @@ -23,28 +23,28 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.utils; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nullable; - -import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ImmutableList; - import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException; +import at.gv.e_government.reference.namespace.persondata._20020228.PostalAddressType; import at.gv.egiz.eaaf.core.impl.data.Triple; +import com.google.common.collect.ImmutableList; import eu.eidas.auth.commons.attribute.AttributeDefinition; import eu.eidas.auth.commons.attribute.AttributeValue; import eu.eidas.auth.commons.attribute.AttributeValueMarshaller; import eu.eidas.auth.commons.attribute.AttributeValueMarshallingException; import eu.eidas.auth.commons.attribute.AttributeValueTransliterator; import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class EidasResponseUtils { private static final Logger log = LoggerFactory.getLogger(EidasResponseUtils.class); @@ -54,7 +54,7 @@ public class EidasResponseUtils { /** * Validate a eIDAS PersonalIdentifier attribute value This validation is done * according to eIDAS SAML Attribute Profile - Section 2.2.3 Unique Identifier - * + * * @param uniqueID eIDAS attribute value of a unique identifier * @return true if the uniqueID matches to eIDAS to Unique Identifier * specification, otherwise false @@ -70,9 +70,9 @@ public class EidasResponseUtils { * Parse an eIDAS PersonalIdentifier attribute value into it components. This * processing is done according to eIDAS SAML Attribute Profile - Section 2.2.3 * Unique Identifier - * + * * @param uniqueID eIDAS attribute value of a unique identifier - * @return {@link Trible} that contains: <br> + * @return {@link Triple} that contains: <br> * First : citizen country <br> * Second: destination country <br> * Third : unique identifier <br> @@ -80,7 +80,7 @@ public class EidasResponseUtils { */ public static Triple<String, String, String> parseEidasPersonalIdentifier(String uniqueID) { if (!validateEidasPersonalIdentifier(uniqueID)) { - log.error("eIDAS attribute value for {} looks wrong formated. Value: {}", + log.error("eIDAS attribute value for {} looks wrong formated. Value: {}", Constants.eIDAS_ATTR_PERSONALIDENTIFIER, uniqueID); return null; @@ -90,11 +90,11 @@ public class EidasResponseUtils { } /** - * Get eIDAS attribute-values from eIDAS Node attributes. - * + * Get eIDAS attribute-values from eIDAS Node attributes. + * * @param attributeDefinition eIDAS attribute definition * @param attributeValues Attributes from eIDAS response - * @return Set of attribute values. If more then one value than the first value contains the 'Latin' value. + * @return Set of attribute values. If more then one value than the first value contains the 'Latin' value. */ // TODO: check possible problem with nonLatinCharacters public static List<String> translateStringListAttribute(AttributeDefinition<?> attributeDefinition, @@ -109,7 +109,7 @@ public class EidasResponseUtils { log.trace("Find attr: {} with value: {} nonLatinFlag: {} needTransliteration: {}", attributeDefinition.getFriendlyName(), attributeValue.toString(), - attributeValue.isNonLatinScriptAlternateVersion(), + attributeValue.isNonLatinScriptAlternateVersion(), AttributeValueTransliterator.needsTransliteration(valueString)); // if (attributeValue.isNonLatinScriptAlternateVersion()) { @@ -128,7 +128,7 @@ public class EidasResponseUtils { } } - log.trace("Extract values: {} for attr: {}", + log.trace("Extract values: {} for attr: {}", StringUtils.join(stringListAttribute, ","), attributeDefinition.getFriendlyName()); return stringListAttribute; @@ -136,8 +136,8 @@ public class EidasResponseUtils { } /** - * Convert eIDAS DateTime attribute to Java Object. - * + * Convert eIDAS DateTime attribute to Java Object. + * * @param attributeDefinition eIDAS attribute definition. * @param attributeValues eIDAS attribute value * @return @@ -156,7 +156,7 @@ public class EidasResponseUtils { /** * Concert eIDAS Address attribute to Java object. - * + * * @param attributeDefinition eIDAS attribute definition * @param attributeValues eIDAS attribute value * @return @@ -166,7 +166,164 @@ public class EidasResponseUtils { ImmutableList<? extends AttributeValue<?>> attributeValues) { final AttributeValue<?> firstAttributeValue = attributeValues.get(0); return (PostalAddress) firstAttributeValue.getValue(); + } + + /** + * Post-Process the eIDAS CurrentAddress attribute. + * + * @param currentAddressObj eIDAS current address information + * @return current address or null if no attribute is available + * @throws EidasAttributeException if eIDAS attribute is of a wrong type + */ + public static PostalAddressType processAddress(Object currentAddressObj) throws EidasAttributeException { + if (currentAddressObj != null) { + if (currentAddressObj instanceof PostalAddress) { + final PostalAddressType result = new PostalAddressType(); + result.setPostalCode(((PostalAddress) currentAddressObj).getPostCode()); + result.setMunicipality(((PostalAddress) currentAddressObj).getPostName()); + // TODO: add more mappings + return result; + } else { + log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_CURRENTADDRESS + " is of WRONG type"); + throw new EidasAttributeException(Constants.eIDAS_ATTR_CURRENTADDRESS); + } + } else { + log.debug("NO '" + Constants.eIDAS_ATTR_CURRENTADDRESS + "' attribute. Post-Processing skipped ... "); + } + return null; + } + + /** + * Post-Process the eIDAS BirthName attribute. + * + * @param birthNameObj eIDAS birthname information + * @return birthName or null if no attribute is available + * @throws EidasAttributeException if eIDAS attribute is of a wrong type + */ + public static String processBirthName(Object birthNameObj) throws EidasAttributeException { + if (birthNameObj != null) { + if (birthNameObj instanceof String) { + return (String) birthNameObj; + } else { + log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_BIRTHNAME + " is of WRONG type"); + throw new EidasAttributeException(Constants.eIDAS_ATTR_BIRTHNAME); + } + } else { + log.debug("NO '" + Constants.eIDAS_ATTR_BIRTHNAME + "' attribute. Post-Processing skipped ... "); + } + return null; + } + + /** + * Post-Process the eIDAS PlaceOfBirth attribute. + * + * @param placeOfBirthObj eIDAS Place-of-Birth information + * @return place of Birth or null if no attribute is available + * @throws EidasAttributeException if eIDAS attribute is of a wrong type + */ + public static String processPlaceOfBirth(Object placeOfBirthObj) throws EidasAttributeException { + if (placeOfBirthObj != null) { + if (placeOfBirthObj instanceof String) { + return (String) placeOfBirthObj; + + } else { + log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_PLACEOFBIRTH + " is of WRONG type"); + throw new EidasAttributeException(Constants.eIDAS_ATTR_PLACEOFBIRTH); + + } + + } else { + log.debug("NO '" + Constants.eIDAS_ATTR_PLACEOFBIRTH + "' attribute. Post-Processing skipped ... "); + } + return null; + } + + /** + * Post-Process the eIDAS DateOfBirth attribute. + * + * @param dateOfBirthObj eIDAS date-of-birth attribute information + * @return formated user's date-of-birth + * @throws EidasAttributeException if NO attribute is available + */ + public static DateTime processDateOfBirth(Object dateOfBirthObj) throws EidasAttributeException { + if (!(dateOfBirthObj instanceof DateTime)) { + throw new EidasAttributeException(Constants.eIDAS_ATTR_DATEOFBIRTH); + } + return (DateTime) dateOfBirthObj; + } + /** + * Post-Process the eIDAS DateOfBirth attribute to a string. + * + * @param dateOfBirthObj eIDAS date-of-birth attribute information + * @return formated user's date-of-birth as string + * @throws EidasAttributeException if NO attribute is available + */ + public static String processDateOfBirthToString(Object dateOfBirthObj) throws EidasAttributeException { + if (!(dateOfBirthObj instanceof DateTime)) { + throw new EidasAttributeException(Constants.eIDAS_ATTR_DATEOFBIRTH); + } + return new SimpleDateFormat("yyyy-MM-dd").format(((DateTime) dateOfBirthObj).toDate()); } + /** + * Post-Process the eIDAS GivenName attribute. + * + * @param givenNameObj eIDAS givenName attribute information + * @return formated user's givenname + * @throws EidasAttributeException if NO attribute is available + */ + public static String processGivenName(Object givenNameObj) throws EidasAttributeException { + if (!(givenNameObj instanceof String)) { + throw new EidasAttributeException(Constants.eIDAS_ATTR_CURRENTGIVENNAME); + } + return (String) givenNameObj; + } + + /** + * Post-Process the eIDAS FamilyName attribute. + * + * @param familyNameObj eIDAS familyName attribute information + * @return formated user's familyname + * @throws EidasAttributeException if NO attribute is available + */ + public static String processFamilyName(Object familyNameObj) throws EidasAttributeException { + if (!(familyNameObj instanceof String)) { + throw new EidasAttributeException(Constants.eIDAS_ATTR_CURRENTFAMILYNAME); + } + return (String) familyNameObj; + } + + /** + * Post-Process the eIDAS pseudonym to ERnB unique identifier. + * + * @param personalIdObj eIDAS PersonalIdentifierAttribute + * @return Unique personal identifier without country-code information + * @throws EidasAttributeException if NO attribute is available + */ + public static String processPseudonym(Object personalIdObj) throws EidasAttributeException { + if (!(personalIdObj instanceof String)) { + throw new EidasAttributeException(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); + } + final Triple<String, String, String> eIdentifier = + EidasResponseUtils.parseEidasPersonalIdentifier((String) personalIdObj); + if (eIdentifier == null || eIdentifier.getThird() == null) { + throw new EidasAttributeException("Error processing eIdentifier"); + } + return eIdentifier.getThird(); + } + + /** + * Post-Process the eIDAS TaxReference attribute. + * + * @param taxReferenceObj eIDAS TaxReference attribute information + * @return formated user's TaxReference + * @throws EidasAttributeException if NO attribute is available + */ + public static String processTaxReference(Object taxReferenceObj) throws EidasAttributeException { + if (!(taxReferenceObj instanceof String)) { + throw new EidasAttributeException(Constants.eIDAS_ATTR_CURRENTGIVENNAME); + } + return (String) taxReferenceObj; + } } diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/DummyZmrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/DummyZmrClient.java new file mode 100644 index 00000000..b12c1bcb --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/DummyZmrClient.java @@ -0,0 +1,65 @@ +/* + * 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.zmr; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +@Service("ZmrClientForeIDAS") +public class DummyZmrClient implements IZmrClient { + + @Override + public List<RegisterResult> searchWithPersonIdentifier(String personIdentifier) { + return Collections.emptyList(); + } + + @Override + public List<RegisterResult> searchWithMds(String givenName, String familyName, String dateOfBirth) { + //TODO will I only receive matches where all three values match perfectly? + return Collections.emptyList(); + } + + @Override + public List<RegisterResult> searchDeSpecific(String givenName, String familyName, String dateOfBirth, + String birthPlace, String birthName) { + //TODO + return Collections.emptyList(); + } + + @Override + public List<RegisterResult> searchItSpecific(String taxNumber) { + //TODO + return Collections.emptyList(); + } + + @Override + public void update(RegisterResult registerResult, SimpleEidasData eidData) { + //TODO + } + +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/IZmrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/IZmrClient.java new file mode 100644 index 00000000..5175cd7b --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/IZmrClient.java @@ -0,0 +1,43 @@ +/* + * 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.zmr; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; + +import java.util.List; + +public interface IZmrClient { + + List<RegisterResult> searchWithPersonIdentifier(String personIdentifier); + + List<RegisterResult> searchWithMds(String givenName, String familyName, String dateOfBirth); + + List<RegisterResult> searchDeSpecific(String givenName, String familyName, String dateOfBirth, + String birthPlace, String birthName); + + List<RegisterResult> searchItSpecific(String taxNumber); + + void update(RegisterResult registerResult, SimpleEidasData eidData); +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/resources/eIDAS.Authentication.process.xml b/eidas_modules/authmodule-eIDAS-v2/src/main/resources/eIDAS.Authentication.process.xml index 55bb1ace..e199d379 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/resources/eIDAS.Authentication.process.xml +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/resources/eIDAS.Authentication.process.xml @@ -10,6 +10,8 @@ class="FinalizeAuthenticationTask" /> <pd:Task id="generateIdentityLink" class="CreateIdentityLinkTask" /> + <pd:Task id="initialRegisterSearch" + class="InitialSearchTask" /> <pd:StartEvent id="start" /> @@ -18,6 +20,8 @@ <pd:Transition from="createAuthnRequest" to="receiveAuthnResponse" /> <pd:Transition from="receiveAuthnResponse" + to="initialRegisterSearch" /> + <pd:Transition from="initialRegisterSearch" to="generateIdentityLink" /> <pd:Transition from="generateIdentityLink" to="finalizeAuthentication" /> diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/resources/eidas_v2_auth.beans.xml b/eidas_modules/authmodule-eIDAS-v2/src/main/resources/eidas_v2_auth.beans.xml index 6cc704ab..52404bab 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/resources/eidas_v2_auth.beans.xml +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/resources/eidas_v2_auth.beans.xml @@ -17,6 +17,12 @@ <bean id="SZRClientForeIDAS" class="at.asitplus.eidas.specific.modules.auth.eidas.v2.szr.SzrClient" /> + <bean id="ErnbClientForeIDAS" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.ernp.DummyErnpClient" /> + + <bean id="ZmrClientForeIDAS" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr.DummyZmrClient" /> + <bean id="specificConnectorAttributesFile" class="java.lang.String"> <constructor-arg value="eidas-attributes.xml" /> @@ -74,6 +80,14 @@ <property name="priority" value="0" /> </bean> + <bean id="DE-Specific-Search" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.DeSpecificDetailSearchProcessor"> + </bean> + + <bean id="IT-Specific-Search" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.ItSpecificDetailSearchProcessor"> + </bean> + <!-- Authentication Process Tasks --> <bean id="ConnecteIDASNodeTask" class="at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.GenerateAuthnRequestTask" @@ -87,4 +101,8 @@ class="at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.CreateIdentityLinkTask" scope="prototype" /> + <bean id="InitialSearchTask" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.InitialSearchTask" + scope="prototype" /> + </beans>
\ No newline at end of file diff --git a/eidas_modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/EidasRequestPreProcessingSecondTest.java b/eidas_modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/EidasRequestPreProcessingSecondTest.java index da7e3d85..e7bacded 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/EidasRequestPreProcessingSecondTest.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/EidasRequestPreProcessingSecondTest.java @@ -107,7 +107,7 @@ public class EidasRequestPreProcessingSecondTest { final LightRequest lightReq = authnRequestBuilder.build(); - Assert.assertEquals("ProviderName is not Static", "myNode", lightReq.getProviderName()); + Assert.assertEquals("ProviderName is not Static", "myNode", lightReq.getProviderName());//Fixme "myNode" Assert.assertEquals("no PublicSP", "public", lightReq.getSpType()); Assert.assertEquals("Requested attribute size not match", 8, lightReq.getRequestedAttributes().size()); diff --git a/eidas_modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/InitialSearchTaskFirstTest.java b/eidas_modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/InitialSearchTaskFirstTest.java new file mode 100644 index 00000000..9f58ba71 --- /dev/null +++ b/eidas_modules/authmodule-eIDAS-v2/src/test/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/test/tasks/InitialSearchTaskFirstTest.java @@ -0,0 +1,460 @@ +/* + * 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 at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.ernp.IErnpClient; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.ManualFixNecessaryException; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.DeSpecificDetailSearchProcessor; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.ItSpecificDetailSearchProcessor; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.InitialSearchTask; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr.IZmrClient; +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.protocol.impl.AuthenticationResponse; +import lombok.val; +import org.apache.commons.lang3.RandomStringUtils; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +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 javax.xml.namespace.QName; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Random; + +import static org.junit.Assert.assertThrows; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("/SpringTest-context_tasks_test.xml") +@DirtiesContext(classMode = ClassMode.BEFORE_CLASS) +public class InitialSearchTaskFirstTest { + + private static final String DE_ST = "de/st/"; + private static final String IT_ST = "it/st/"; + + private InitialSearchTask task; + @Mock + private IZmrClient zmrClient; + @Mock + private IErnpClient ernpClient; + + final ExecutionContext executionContext = new ExecutionContextImpl(); + private TestRequestImpl pendingReq; + private final String randomBpk = RandomStringUtils.randomNumeric(6); + private final String randomIdentifier = RandomStringUtils.randomNumeric(10); + private final String randomPseudonym = DE_ST + randomIdentifier; + private final String randomFamilyName = RandomStringUtils.randomAlphabetic(10); + private final String randomGivenName = RandomStringUtils.randomAlphabetic(10); + private final String randomPlaceOfBirth = RandomStringUtils.randomAlphabetic(10); + private final String randomBirthName = RandomStringUtils.randomAlphabetic(10); + private final String randomDate = "2011-01-" + (10 + new Random().nextInt(18)); + + /** + * jUnit class initializer. + * + * @throws IOException In case of an error + */ + @BeforeClass + public static void classInitializer() throws IOException { + final String current = new java.io.File(".").toURI().toString(); + System.setProperty("eidas.ms.configuration", current + + "src/test/resources/config/junit_config_1.properties"); + } + + /** + * jUnit test set-up. + */ + @Before + public void setUp() throws URISyntaxException, EaafStorageException { + MockitoAnnotations.initMocks(this); + task = new InitialSearchTask(new ArrayList<>(), ernpClient, zmrClient); + + 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); + } + + /** + * One match, but register update needed + */ + @Test + @DirtiesContext + public void testNode100_UserIdentifiedUpdateNecessary_a() throws Exception { + String newFirstName = RandomStringUtils.randomAlphabetic(10); + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.singletonList( + new RegisterResult(randomBpk, randomPseudonym, newFirstName, randomFamilyName, randomDate))); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + + task.execute(pendingReq, executionContext); + String bPk = (String) + pendingReq.getSessionData(AuthProcessDataWrapper.class) + .getGenericDataFromSession(Constants.DATA_RESULT_MATCHING_BPK); + + Assert.assertEquals("Wrong bpk", bPk, randomBpk); + } + + /** + * One match, but register update needed + */ + @Test + @DirtiesContext + public void testNode100_UserIdentifiedUpdateNecessary_b() throws TaskExecutionException { + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + String newRandomGivenName = RandomStringUtils.randomAlphabetic(10); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.singletonList( + new RegisterResult(randomBpk, randomPseudonym, newRandomGivenName, randomFamilyName, randomDate))); + + task.execute(pendingReq, executionContext); + String bPk = (String) + pendingReq.getSessionData(AuthProcessDataWrapper.class) + .getGenericDataFromSession(Constants.DATA_RESULT_MATCHING_BPK); + + Assert.assertEquals("Wrong bpk", bPk, randomBpk); + } + + /** + * Two matches found in ZMR + */ + @Test + @DirtiesContext + public void testNode101_ManualFixNecessary_a() { + ArrayList<RegisterResult> zmrResult = new ArrayList<>(); + zmrResult.add(new RegisterResult(randomBpk, randomPseudonym, randomGivenName, randomFamilyName, randomDate)); + String newRandomGivenName = randomGivenName + RandomStringUtils.randomAlphabetic(2); + zmrResult.add(new RegisterResult(randomBpk, randomPseudonym, newRandomGivenName, randomFamilyName, randomDate)); + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(zmrResult); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + + TaskExecutionException exception = assertThrows(TaskExecutionException.class, + () -> task.execute(pendingReq, executionContext)); + + Throwable origE = exception.getOriginalException(); + Assert.assertTrue("Wrong exception", (origE.getCause() instanceof ManualFixNecessaryException)); + } + + + /** + * Two matches found in ErnP + */ + @Test + @DirtiesContext + public void testNode101_ManualFixNecessary_b() { + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + ArrayList<RegisterResult> ernpResult = new ArrayList<>(); + ernpResult.add(new RegisterResult(randomBpk, randomPseudonym, randomGivenName, randomFamilyName, randomDate)); + String newRandomGivenName = randomGivenName + RandomStringUtils.randomAlphabetic(2); + ernpResult.add( + new RegisterResult(randomBpk, randomPseudonym, newRandomGivenName, randomFamilyName, randomDate)); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(ernpResult); + + TaskExecutionException exception = assertThrows(TaskExecutionException.class, + () -> task.execute(pendingReq, executionContext)); + + Throwable origE = exception.getOriginalException(); + Assert.assertTrue("Wrong exception", (origE.getCause() instanceof ManualFixNecessaryException)); + } + + /** + * One match, no register update needed + */ + @Test + @DirtiesContext + public void testNode102_UserIdentified_a() throws Exception { + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.singletonList( + new RegisterResult(randomBpk, randomPseudonym, randomGivenName, randomFamilyName, randomDate))); + + task.execute(pendingReq, executionContext); + String bPk = (String) + pendingReq.getSessionData(AuthProcessDataWrapper.class) + .getGenericDataFromSession(Constants.DATA_RESULT_MATCHING_BPK); + Assert.assertEquals("Wrong bpk", bPk, randomBpk); + } + + /** + * One match, no register update needed + */ + @Test + @DirtiesContext + public void testNode102_UserIdentified_b() throws Exception { + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.singletonList( + new RegisterResult(randomBpk, randomPseudonym, randomGivenName, randomFamilyName, randomDate))); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + + task.execute(pendingReq, executionContext); + + String bPk = (String) + pendingReq.getSessionData(AuthProcessDataWrapper.class) + .getGenericDataFromSession(Constants.DATA_RESULT_MATCHING_BPK); + Assert.assertEquals("Wrong bpk", bPk, randomBpk); + } + + /** + * One match found in ZMR and ErnP with detail search + */ + @Test + @DirtiesContext + public void testNode103_UserIdentified_IT() throws Exception { + String taxNumber = RandomStringUtils.randomNumeric(14); + final AuthenticationResponse response = buildDummyAuthResponseRandomPersonIT_Tax(taxNumber); + TestRequestImpl pendingReq1 = new TestRequestImpl(); + pendingReq1.getSessionData(AuthProcessDataWrapper.class) + .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + String newRandomPseudonym = IT_ST + randomIdentifier + RandomStringUtils.randomNumeric(2); + Mockito.when(zmrClient.searchItSpecific(taxNumber)).thenReturn(Collections.singletonList( + new RegisterResult(randomBpk, newRandomPseudonym, randomGivenName, randomFamilyName, + randomDate, null, null, taxNumber, null))); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + task = new InitialSearchTask( + Collections.singletonList(new ItSpecificDetailSearchProcessor(ernpClient, zmrClient)), + ernpClient, zmrClient); + + task.execute(pendingReq1, executionContext); + + String bPk = (String) + pendingReq1.getSessionData(AuthProcessDataWrapper.class) + .getGenericDataFromSession(Constants.DATA_RESULT_MATCHING_BPK); + Assert.assertEquals("Wrong bpk", bPk, randomBpk); + } + + /** + * Multiple matches found in ZMR and ErnP with detail search + */ + @Test + @DirtiesContext + public void testNode103_UserIdentified_DE() throws Exception { + final AuthenticationResponse response = buildDummyAuthResponseDE(randomGivenName, randomFamilyName, + randomPseudonym, + randomDate, randomPlaceOfBirth, randomBirthName); + TestRequestImpl pendingReq1 = new TestRequestImpl(); + pendingReq1.getSessionData(AuthProcessDataWrapper.class) + .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + Mockito.when(zmrClient.searchDeSpecific(randomGivenName, randomFamilyName, randomDate, randomPlaceOfBirth, + randomBirthName)) + .thenReturn(Collections.singletonList(new RegisterResult(randomBpk, randomPseudonym, randomGivenName, + randomFamilyName, randomDate, randomPlaceOfBirth, randomBirthName, null, null))); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + task = new InitialSearchTask( + Collections.singletonList(new DeSpecificDetailSearchProcessor(ernpClient, zmrClient)), + ernpClient, zmrClient); + + task.execute(pendingReq1, executionContext); + + String resultBpk = (String) + pendingReq1.getSessionData(AuthProcessDataWrapper.class) + .getGenericDataFromSession(Constants.DATA_RESULT_MATCHING_BPK); + Assert.assertEquals("Wrong bpk", resultBpk, randomBpk); + } + + /** + * Multiple matches found in ZMR and ErnP with detail search + */ + @Test + @DirtiesContext + public void testNode104_ManualFixNecessary_DE() throws Exception { + String newRandomPseudonym = randomPseudonym + RandomStringUtils.randomNumeric(2); + String newRandomBpk = randomBpk + RandomStringUtils.randomNumeric(6); + final AuthenticationResponse response = buildDummyAuthResponseDE(randomGivenName, randomFamilyName, + randomPseudonym, + randomDate, randomPlaceOfBirth, randomBirthName); + TestRequestImpl pendingReq1 = new TestRequestImpl(); + pendingReq1.getSessionData(AuthProcessDataWrapper.class) + .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + ArrayList<RegisterResult> zmrResultSpecific = new ArrayList<>(); + zmrResultSpecific.add( + new RegisterResult(randomBpk, randomPseudonym, randomGivenName, randomFamilyName, randomDate, + randomPlaceOfBirth, randomBirthName, null, null)); + zmrResultSpecific.add(new RegisterResult(newRandomBpk, newRandomPseudonym, randomGivenName, randomFamilyName, randomDate, + randomPlaceOfBirth, randomBirthName, null, null)); + Mockito.when(zmrClient.searchDeSpecific(randomGivenName, randomFamilyName, randomDate, randomPlaceOfBirth, + randomBirthName)).thenReturn(zmrResultSpecific); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + task = new InitialSearchTask( + Collections.singletonList(new DeSpecificDetailSearchProcessor(ernpClient, zmrClient)), + ernpClient, zmrClient); + + TaskExecutionException exception = assertThrows(TaskExecutionException.class, + () -> task.execute(pendingReq1, executionContext)); + + Throwable origE = exception.getOriginalException(); + Assert.assertTrue("Wrong exception", (origE.getCause() instanceof ManualFixNecessaryException)); + } + + /** + * Multiple matches found in ZMR and ErnP with detail search + */ + @Test + @DirtiesContext + public void testNode104_ManualFixNecessary_IT() throws Exception { + String randomTaxNumber = RandomStringUtils.randomNumeric(14); + final AuthenticationResponse response = buildDummyAuthResponseRandomPersonIT_Tax(randomTaxNumber); + TestRequestImpl pendingReq1 = new TestRequestImpl(); + pendingReq1.getSessionData(AuthProcessDataWrapper.class) + .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + ArrayList<RegisterResult> zmrResultSpecific = new ArrayList<>(); + String randomPseudonym = IT_ST + randomIdentifier + "4"; + zmrResultSpecific.add(new RegisterResult(randomBpk, randomPseudonym, randomGivenName, + randomFamilyName, randomDate, null, null, randomTaxNumber, null)); + String newRandomPseudonym = IT_ST + randomIdentifier + "5"; + String newRandomBpk = RandomStringUtils.randomNumeric(6); + zmrResultSpecific.add(new RegisterResult(newRandomBpk, newRandomPseudonym, randomGivenName, + randomFamilyName, randomDate, null, null, randomTaxNumber, null)); + Mockito.when(zmrClient.searchItSpecific(randomTaxNumber)).thenReturn(zmrResultSpecific); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + task = new InitialSearchTask( + Collections.singletonList(new ItSpecificDetailSearchProcessor(ernpClient, zmrClient)), + ernpClient, zmrClient); + + TaskExecutionException exception = assertThrows(TaskExecutionException.class, + () -> task.execute(pendingReq1, executionContext)); + + Throwable origE = exception.getOriginalException(); + Assert.assertTrue("Wrong exception", (origE.getCause() instanceof ManualFixNecessaryException)); + } + + /** + * NO match found in ZMR and ErnP with Initial search + */ + @Test + @DirtiesContext + public void testNode105_TemporaryEnd() throws TaskExecutionException { + Mockito.when(zmrClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + Mockito.when(ernpClient.searchWithPersonIdentifier(randomIdentifier)).thenReturn(Collections.emptyList()); + + task.execute(pendingReq, executionContext); + + String bPk = (String) + pendingReq.getSessionData(AuthProcessDataWrapper.class) + .getGenericDataFromSession(Constants.DATA_RESULT_MATCHING_BPK); + Assert.assertEquals("Wrong bpk", "TODO-Temporary-Endnode-105", bPk); + } + + + @NotNull + private AuthenticationResponse buildDummyAuthResponseRandomPerson() throws URISyntaxException { + return buildDummyAuthResponse(randomGivenName, randomFamilyName, DE_ST + randomIdentifier, randomDate); + } + + private AuthenticationResponse buildDummyAuthResponseRandomPersonIT_Tax(String taxNumber) + throws URISyntaxException { + return buildDummyAuthResponse(randomGivenName, randomFamilyName, IT_ST + randomIdentifier, randomDate, + taxNumber, null, null); + } + + @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(Constants.eIDAS_ATTR_PERSONALIDENTIFIER, "ff", "af"), identifier) + .put(generateStringAttribute(Constants.eIDAS_ATTR_CURRENTFAMILYNAME, "fff", "aff"), familyName) + .put(generateStringAttribute(Constants.eIDAS_ATTR_CURRENTGIVENNAME, "ffff", "afff"), givenName) + .put(generateDateTimeAttribute(Constants.eIDAS_ATTR_DATEOFBIRTH, "fffff", "affff"), dateOfBirth); + if (taxNumber != null) { + builder.put(generateStringAttribute(Constants.eIDAS_ATTR_TAXREFERENCE, "ffffff", "afffff"), taxNumber); + } + if (birthName != null) { + builder.put(generateStringAttribute(Constants.eIDAS_ATTR_BIRTHNAME, "fffffff", "affffff"), birthName); + } + if (placeOfBirth != null) { + builder.put(generateStringAttribute(Constants.eIDAS_ATTR_PLACEOFBIRTH, "ffffffff", "afffffff"), + placeOfBirth); + } + final ImmutableAttributeMap attributeMap = builder.build(); + + val b = new AuthenticationResponse.Builder(); + return b.id("aasdf").issuer("asd").subject("asf").statusCode("200").inResponseTo("asdf").subjectNameIdFormat( + "afaf").attributes(attributeMap).build(); + } + + private AttributeDefinition<Object> generateStringAttribute(String friendlyName, String fragment, String prefix) + throws URISyntaxException { + return generateAttribute(friendlyName, fragment, prefix, "eu.eidas.auth.commons.attribute.impl" + + ".LiteralStringAttributeValueMarshaller"); + } + + private AttributeDefinition<Object> generateDateTimeAttribute(String friendlyName, String fragment, String prefix) + throws URISyntaxException { + return generateAttribute(friendlyName, fragment, prefix, "eu.eidas.auth.commons.attribute.impl" + + ".DateTimeAttributeValueMarshaller"); + } + + private AttributeDefinition<Object> 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(); + } + +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/test/resources/SpringTest-context_tasks_test.xml b/eidas_modules/authmodule-eIDAS-v2/src/test/resources/SpringTest-context_tasks_test.xml index 7d5a4c53..1f3a984b 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/test/resources/SpringTest-context_tasks_test.xml +++ b/eidas_modules/authmodule-eIDAS-v2/src/test/resources/SpringTest-context_tasks_test.xml @@ -61,4 +61,21 @@ class="at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.CreateIdentityLinkTask" scope="prototype" /> + <bean id="InitialSearchTask" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.InitialSearchTask" + scope="prototype" /> + + <bean id="ErnbClientForeIDAS" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.ernp.DummyErnpClient" /> + + <bean id="ZmrClientForeIDAS" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr.DummyZmrClient" /> + + <bean id="DE-Specific-Search" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.DeSpecificDetailSearchProcessor"> + </bean> + + <bean id="IT-Specific-Search" + class="at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.ItSpecificDetailSearchProcessor"> + </bean> </beans>
\ No newline at end of file |