diff options
| author | Christian Kollmann <christian.kollmann@a-sit.at> | 2021-01-12 16:08:36 +0100 | 
|---|---|---|
| committer | Christian Kollmann <christian.kollmann@a-sit.at> | 2021-01-12 16:08:36 +0100 | 
| commit | 7961d920b67a4ce6cfb6bb9d9941242041bd47c6 (patch) | |
| tree | b7762944dc066ba19c59fbd452c58ef2cc57532c | |
| parent | 1247debcf4807dfdd9d9080d48f546c933a472b1 (diff) | |
| parent | eae2f7b06fc41741863d1ea6ec9c8b6da8a6b8fb (diff) | |
| download | National_eIDAS_Gateway-7961d920b67a4ce6cfb6bb9d9941242041bd47c6.tar.gz National_eIDAS_Gateway-7961d920b67a4ce6cfb6bb9d9941242041bd47c6.tar.bz2 National_eIDAS_Gateway-7961d920b67a4ce6cfb6bb9d9941242041bd47c6.zip | |
Personen Matching Part 1: Register Search
See merge request egiz/eidas_at_proxy!5
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 | 
