aboutsummaryrefslogtreecommitdiff
path: root/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus
diff options
context:
space:
mode:
Diffstat (limited to 'eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus')
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java129
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/EidasAuthenticationSpringResourceProvider.java6
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/AbstractSoapClient.java199
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/szr/SzrClient.java548
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/szr/SzrService.java (renamed from eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrService.java)4
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/IZmrClient.java98
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/ZmrSoapClient.java775
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/config/EidasConnectorMessageSource.java21
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/ErnbEidData.java115
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MatchedPersonResult.java41
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/RegisterResult.java51
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SelectedLoginMethod.java5
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleEidasData.java99
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleMobileSignatureData.java53
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/DummyErnpClient.java74
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/ernp/IErnpClient.java46
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/InvalidUserInputException.java34
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ManualFixNecessaryException.java44
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/WorkflowException.java94
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ZmrCommunicationException.java38
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java170
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/CountrySpecificDetailSearchProcessor.java56
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/DeSpecificDetailSearchProcessor.java82
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/INationalEidProcessor.java16
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/ItSpecificDetailSearchProcessor.java53
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthConstants.java106
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthEventConstants.java7
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthMetadataConfiguration.java463
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthRequestBuilderConfiguration.java300
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/controller/IdAustriaClientAuthMetadataController.java122
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/controller/IdAustriaClientAuthSignalController.java95
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthCredentialProvider.java132
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthHealthCheck.java56
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthMetadataProvider.java169
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/CcSpecificEidProcessingService.java4
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/ICcSpecificEidProcessingService.java10
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/RegisterSearchService.java339
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java522
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java186
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java479
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateNewErnpEntryTask.java93
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAustrianResidenceGuiTask.java76
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java285
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateMobilePhoneSignatureRequestTask.java145
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateOtherLoginMethodGuiTask.java82
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/InitialSearchTask.java202
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAustrianResidenceGuiResponseTask.java214
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseAlternativeTask.java131
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseTask.java135
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveMobilePhoneSignatureResponseTask.java369
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveOtherLoginMethodGuiResponseTask.java122
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/EidasResponseUtils.java280
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/MatchingTaskUtils.java169
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/VersionHolder.java40
54 files changed, 6904 insertions, 1280 deletions
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 1732a61a..5dfc9e68 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
@@ -27,11 +27,37 @@ import at.gv.egiz.eaaf.core.api.data.EaafConstants;
public class Constants {
+ //TODO: should we make it configurable?
+ public static final String MATCHING_INTERNAL_BPK_TARGET = EaafConstants.URN_PREFIX_CDID + "ZP";
+
+ public static final String ERRORCODE_00 = "module.eidasauth.00";
+
public static final String DATA_REQUESTERID = "req_requesterId";
public static final String DATA_PROVIDERNAME = "req_providerName";
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_FULL_EIDAS_RESPONSE_ALTERNATIVE = "resp_fulleIDASResponseAlternative";
+
+ /**
+ * Stored when one match from register was found.
+ */
+ public static final String DATA_RESULT_MATCHING_BPK = "matching_result_bpk";
+
+ /**
+ * Stored before Step 2 from Matching Concept, input from user eIDAS authn.
+ */
+ public static final String DATA_SIMPLE_EIDAS = "matching_simple_eidas_data";
+
+ /**
+ * Stored intermediate mathing results where matching is still on-going.
+ */
+ public static final String DATA_INTERMEDIATE_RESULT = "matching_intermediate_result";
+
+ /**
+ * Stored after Step 8 from Matching Concept, results from search in registers with MDS.
+ */
+ public static final String DATA_PERSON_MATCH_RESULT = "matching_result";
// templates for post-binding forwarding
public static final String TEMPLATE_POST_FORWARD_NAME = "eidas_node_forward.html";
@@ -79,6 +105,46 @@ public class Constants {
public static final String FORWARD_METHOD_POST = "POST";
public static final String FORWARD_METHOD_GET = "GET";
+ // ZMR Client configuration properties
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT = CONIG_PROPS_EIDAS_PREFIX + ".zmrclient";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_ENDPOINT = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".endpoint";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_DEBUG_TRACEMESSAGES = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".debug.logfullmessages";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_TIMEOUT_CONNECTION = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".timeout.connection";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_TIMEOUT_RESPONSE = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".timeout.response";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYSTORE_PATH = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.keyStore.path";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYSTORE_PASSWORD = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.keyStore.password";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYSTORE_TYPE = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.keyStore.type";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYSTORE_NAME = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.keyStore.name";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYS_ALIAS = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.key.alias";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEY_PASSWORD = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.key.password";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_TRUSTSTORE_PATH = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.trustStore.path";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_TRUSTSTORE_PASSWORD = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.trustStore.password";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_TRUSTSTORE_TYPE = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.trustStore.type";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_TRUSTSTORE_NAME = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".ssl.trustStore.name";
+
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_ORGANIZATION_NR = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".req.organisation.behoerdennr";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_UPDATE_REASON_CODE = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".req.update.reason.code";
+ public static final String CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_UPDATE_REASON_TEXT = CONIG_PROPS_EIDAS_ZMRCLIENT
+ + ".req.update.reason.text";
+
+
+ // SZR Client configuration properties
public static final String CONIG_PROPS_EIDAS_SZRCLIENT = CONIG_PROPS_EIDAS_PREFIX + ".szrclient";
public static final String CONIG_PROPS_EIDAS_SZRCLIENT_USETESTSERVICE = CONIG_PROPS_EIDAS_SZRCLIENT
+ ".useTestService";
@@ -100,10 +166,22 @@ public class Constants {
+ ".ssl.keyStore.path";
public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_PASSWORD = CONIG_PROPS_EIDAS_SZRCLIENT
+ ".ssl.keyStore.password";
+ public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_TYPE = CONIG_PROPS_EIDAS_SZRCLIENT
+ + ".ssl.keyStore.type";
+ public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_NAME = CONIG_PROPS_EIDAS_SZRCLIENT
+ + ".ssl.keyStore.name";
+ public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYS_ALIAS = CONIG_PROPS_EIDAS_SZRCLIENT
+ + ".ssl.key.alias";
+ public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEY_PASSWORD = CONIG_PROPS_EIDAS_SZRCLIENT
+ + ".ssl.key.password";
public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_PATH = CONIG_PROPS_EIDAS_SZRCLIENT
+ ".ssl.trustStore.path";
public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_PASSWORD = CONIG_PROPS_EIDAS_SZRCLIENT
+ ".ssl.trustStore.password";
+ public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_TYPE = CONIG_PROPS_EIDAS_SZRCLIENT
+ + ".ssl.trustStore.type";
+ public static final String CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_NAME = CONIG_PROPS_EIDAS_SZRCLIENT
+ + ".ssl.trustStore.name";
public static final String CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_EDOCUMENTTYPE = CONIG_PROPS_EIDAS_SZRCLIENT
+ ".params.documenttype";
@@ -149,13 +227,27 @@ 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";
+
+ //eIDAS attribute URN
+ public static final String eIDAS_ATTRURN_PREFIX = "http://eidas.europa.eu/attributes/";
+ public static final String eIDAS_ATTRURN_PREFIX_NATURAL = eIDAS_ATTRURN_PREFIX + "naturalperson/";
+
+ public static final String eIDAS_ATTRURN_PERSONALIDENTIFIER =
+ eIDAS_ATTRURN_PREFIX_NATURAL + eIDAS_ATTR_PERSONALIDENTIFIER;
+ public static final String eIDAS_ATTRURN_PLACEOFBIRTH =
+ eIDAS_ATTRURN_PREFIX_NATURAL + eIDAS_ATTR_PLACEOFBIRTH;
+ public static final String eIDAS_ATTRURN_BIRTHNAME =
+ eIDAS_ATTRURN_PREFIX_NATURAL + eIDAS_ATTR_BIRTHNAME;
+
+
public static final String eIDAS_REQ_PARAM_SECTOR_PUBLIC = "public";
public static final String eIDAS_REQ_PARAM_SECTOR_PRIVATE = "private";
-
+
public static final String POLICY_DEFAULT_ALLOWED_TARGETS =
EaafConstants.URN_PREFIX_CDID.replaceAll("\\.", "\\\\.").replaceAll("\\+", "\\\\+") + ".*";
@@ -184,4 +276,39 @@ 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";
+
+ /**
+ * {@link at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.CreateNewErnpEntryTask}.
+ */
+ public static final String TRANSITION_TO_CREATE_NEW_ERNP_ENTRY_TASK = "TASK_CreateNewErnpEntryTask";
+
+ /**
+ * {@link at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.GenerateOtherLoginMethodGuiTask}.
+ */
+ public static final String TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK =
+ "TASK_GenerateOtherLoginMethodGuiTask";
+
+ /**
+ * {@link at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.GenerateAustrianResidenceGuiTask}.
+ */
+ public static final String TRANSITION_TO_GENERATE_GUI_QUERY_AUSTRIAN_RESIDENCE_TASK =
+ "TASK_GenerateAustrianResidenceGuiTask";
+
+ /**
+ * {@link at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.GenerateMobilePhoneSignatureRequestTask}.
+ */
+ public static final String TRANSITION_TO_GENERATE_MOBILE_PHONE_SIGNATURE_REQUEST_TASK =
+ "TASK_GenerateMobilePhoneSignatureRequestTask";
+
+ /**
+ * {@link at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.GenerateAuthnRequestTask}.
+ */
+ public static final String TRANSITION_TO_GENERATE_EIDAS_LOGIN = "TASK_GenerateAlternativeEidasAuthn";
+
+ /**
+ * Stores login selection from user.
+ */
+ public static final String REQ_SELECTED_LOGIN_METHOD_PARAMETER = "loginSelection";
}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/EidasAuthenticationSpringResourceProvider.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/EidasAuthenticationSpringResourceProvider.java
index 535e4f97..e5b10185 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/EidasAuthenticationSpringResourceProvider.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/EidasAuthenticationSpringResourceProvider.java
@@ -45,8 +45,10 @@ public class EidasAuthenticationSpringResourceProvider implements SpringResource
public Resource[] getResourcesToLoad() {
final ClassPathResource eidasAuthConfig = new ClassPathResource("/eidas_v2_auth.beans.xml",
EidasAuthenticationSpringResourceProvider.class);
-
- return new Resource[] { eidasAuthConfig };
+ final ClassPathResource eidasRefImplConfig = new ClassPathResource("/eidas_v2_auth_ref_impl_config.beans.xml",
+ EidasAuthenticationSpringResourceProvider.class);
+
+ return new Resource[] { eidasRefImplConfig, eidasAuthConfig };
}
}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/AbstractSoapClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/AbstractSoapClient.java
new file mode 100644
index 00000000..a039881c
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/AbstractSoapClient.java
@@ -0,0 +1,199 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.clients;
+
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.SSLContext;
+import javax.xml.ws.BindingProvider;
+import javax.xml.ws.handler.Handler;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.frontend.ClientProxy;
+import org.apache.cxf.jaxws.DispatchImpl;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.lang.Nullable;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.LoggingHandler;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory;
+import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration;
+import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.http.HttpUtils;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class AbstractSoapClient {
+
+ @Autowired
+ protected IConfiguration basicConfig;
+ @Autowired
+ EaafKeyStoreFactory keyStoreFactory;
+
+ @Builder
+ @Getter
+ public static class HttpClientConfig {
+
+ private final String clientName;
+
+ private final String clientUrl;
+ private final String clientType;
+
+ private final String connectionTimeout;
+ private final String responseTimeout;
+
+ private final KeyStoreConfiguration keyStoreConfig;
+ private final String keyAlias;
+ private final String keyPassword;
+
+ private final KeyStoreConfiguration trustStoreConfig;
+
+ @Builder.Default
+ private final boolean trustAll = false;
+
+ }
+
+ /**
+ * Build a validated KeyStore Configuration-Object from configuration keys.
+ *
+ * @param keyStoreTypeKey Configuration key for type
+ * @param keyStorePathKey Configuration key for path
+ * @param keyStorePasswordKey Configuration key for password
+ * @param keyStoreNameKey Configuration key for name
+ * @param friendlyName Friendlyname for logging and errorhandling
+ * @return Valid KeyStore configuration or <code>null</code> if no type was
+ * defined
+ * @throws EaafConfigurationException In case of validation error
+ */
+ @Nullable
+ protected KeyStoreConfiguration buildKeyStoreConfiguration(String keyStoreTypeKey, String keyStorePathKey,
+ String keyStorePasswordKey, String keyStoreNameKey, String friendlyName)
+ throws EaafConfigurationException {
+ if (StringUtils.isNotEmpty(basicConfig.getBasicConfiguration(keyStoreTypeKey))) {
+ log.debug("Starting configuration of: {} ... ", friendlyName);
+ final KeyStoreConfiguration config = new KeyStoreConfiguration();
+ config.setFriendlyName(friendlyName);
+ config.setKeyStoreType(basicConfig.getBasicConfiguration(keyStoreTypeKey, KeyStoreType.PKCS12.name()));
+ config.setKeyStoreName(basicConfig.getBasicConfiguration(keyStoreNameKey));
+ config.setSoftKeyStoreFilePath(basicConfig.getBasicConfiguration(keyStorePathKey));
+ config.setSoftKeyStorePassword(basicConfig.getBasicConfiguration(keyStorePasswordKey));
+
+ // validate keystore configuration
+ config.validate();
+
+ return config;
+
+ } else {
+ log.info("Skipping configuration of: {}", friendlyName);
+ return null;
+
+ }
+
+ }
+
+ protected void injectHttpClient(Object raw, HttpClientConfig config) {
+ // extract client from implementation
+ Client client;
+ if (raw instanceof DispatchImpl<?>) {
+ client = ((DispatchImpl<?>) raw).getClient();
+ } else if (raw instanceof Client) {
+ client = ClientProxy.getClient(raw);
+ } else {
+ throw new RuntimeException("SOAP Client for SZR connection is of UNSUPPORTED type: " + raw.getClass()
+ .getName());
+ }
+
+ // set basic connection policies
+ final HTTPConduit http = (HTTPConduit) client.getConduit();
+
+ // set timeout policy
+ final HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
+ httpClientPolicy.setConnectionTimeout(Integer.parseInt(config.getConnectionTimeout()) * 1000L);
+ httpClientPolicy.setReceiveTimeout(Integer.parseInt(config.getResponseTimeout()) * 1000L);
+ http.setClient(httpClientPolicy);
+
+ // inject SSL context in case of https
+ if (config.getClientUrl().toLowerCase().startsWith("https")) {
+ try {
+ log.debug("Adding SSLContext to client: " + config.getClientType() + " ... ");
+
+ final TLSClientParameters tlsParams = new TLSClientParameters();
+ if (config.getKeyStoreConfig() != null) {
+ final SSLContext sslContext = HttpUtils.buildSslContextWithSslClientAuthentication(
+ keyStoreFactory.buildNewKeyStore(config.getKeyStoreConfig()),
+ config.getKeyAlias(),
+ config.getKeyPassword(),
+ loadTrustStore(config.getTrustStoreConfig(), config.getClientName()),
+ config.isTrustAll(),
+ config.getClientName());
+ tlsParams.setSSLSocketFactory(sslContext.getSocketFactory());
+
+ } else {
+ log.debug(
+ "No KeyStore for SSL Client Auth. found. Initializing SSLContext for: {} without authentication ... ",
+ config.getClientName());
+ tlsParams.setSSLSocketFactory(SSLContextBuilder.create().build().getSocketFactory());
+
+ }
+
+ http.setTlsClientParameters(tlsParams);
+ log.info("SSLContext initialized for client: " + config.getClientType());
+
+ } catch (EaafException | KeyManagementException | NoSuchAlgorithmException e) {
+ log.error("SSLContext initialization FAILED.", e);
+ throw new RuntimeException("SSLContext initialization FAILED.", e);
+
+ }
+ }
+ }
+
+ private Pair<KeyStore, Provider> loadTrustStore(KeyStoreConfiguration trustStoreConfig, String friendlyName)
+ throws EaafException {
+ if (trustStoreConfig != null) {
+ log.info("Build custom SSL truststore for: {}", friendlyName);
+ return keyStoreFactory.buildNewKeyStore(trustStoreConfig);
+
+ } else {
+ log.info("Use default SSL truststore for: {}", friendlyName);
+ return null;
+
+ }
+
+ }
+
+ protected void injectBindingProvider(BindingProvider bindingProvider, String clientType, String szrUrl,
+ boolean enableTraceLogging) {
+ final Map<String, Object> requestContext = bindingProvider.getRequestContext();
+ requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, szrUrl);
+
+ log.trace("Adding JAX-WS request/response trace handler to client: " + clientType);
+ List<Handler> handlerList = bindingProvider.getBinding().getHandlerChain();
+ if (handlerList == null) {
+ handlerList = new ArrayList<>();
+ bindingProvider.getBinding().setHandlerChain(handlerList);
+
+ }
+
+ // add logging handler to trace messages if required
+ if (enableTraceLogging) {
+ final LoggingHandler loggingHandler = new LoggingHandler();
+ handlerList.add(loggingHandler);
+
+ }
+ bindingProvider.getBinding().setHandlerChain(handlerList);
+ }
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/szr/SzrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/szr/SzrClient.java
new file mode 100644
index 00000000..397cbe46
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/szr/SzrClient.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright 2018 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.clients.szr;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.ws.BindingProvider;
+import javax.xml.ws.Dispatch;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.xpath.XPathAPI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.AbstractSoapClient;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.AbstractSoapClient.HttpClientConfig.HttpClientConfigBuilder;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.SzrCommunicationException;
+import at.gv.e_government.reference.namespace.persondata._20020228.AlternativeNameType;
+import at.gv.e_government.reference.namespace.persondata._20020228.IdentificationType;
+import at.gv.e_government.reference.namespace.persondata._20020228.PersonNameType;
+import at.gv.e_government.reference.namespace.persondata._20020228.PhysicalPersonType;
+import at.gv.egiz.eaaf.core.api.data.EaafConstants;
+import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions;
+import at.gv.egiz.eaaf.core.api.data.XmlNamespaceConstants;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.impl.utils.DomUtils;
+import szrservices.GetBPK;
+import szrservices.GetBPKResponse;
+import szrservices.GetIdentityLinkEidas;
+import szrservices.GetIdentityLinkEidasResponse;
+import szrservices.IdentityLinkType;
+import szrservices.JwsHeaderParam;
+import szrservices.ObjectFactory;
+import szrservices.PersonInfoType;
+import szrservices.SZR;
+import szrservices.SZRException_Exception;
+import szrservices.SignContent;
+import szrservices.SignContentEntry;
+import szrservices.SignContentResponseType;
+import szrservices.TravelDocumentType;
+
+
+@Service("SZRClientForeIDAS")
+public class SzrClient extends AbstractSoapClient {
+ private static final Logger log = LoggerFactory.getLogger(SzrClient.class);
+
+ private static final String CLIENT_DEFAULT = "DefaultClient";
+ private static final String CLIENT_RAW = "RawClient";
+
+ private static final String ATTR_NAME_VSZ = "urn:eidgvat:attributes.vsz.value";
+ private static final String ATTR_NAME_PUBKEYS = "urn:eidgvat:attributes.user.pubkeys";
+ private static final String ATTR_NAME_STATUS = "urn:eidgvat:attributes.eid.status";
+ private static final String KEY_BC_BIND = "bcBindReq";
+ private static final String JOSE_HEADER_USERCERTPINNING_TYPE = "urn:at.gv.eid:bindtype";
+ private static final String JOSE_HEADER_USERCERTPINNING_EIDASBIND = "urn:at.gv.eid:eidasBind";
+ public static final String ATTR_NAME_MDS = "urn:eidgvat:mds";
+
+ // client for anything, without identitylink
+ private SZR szr = null;
+
+ // RAW client is needed for identitylink
+ private Dispatch<Source> dispatch = null;
+
+ final ObjectMapper mapper = new ObjectMapper();
+
+ /**
+ * Get IdentityLink of a person.
+ *
+ *
+ * @param eidData minimum dataset of person
+ * @return IdentityLink
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public IdentityLinkType getIdentityLinkInRawMode(SimpleEidasData eidData)
+ throws SzrCommunicationException {
+ try {
+ final GetIdentityLinkEidas getIdl = new GetIdentityLinkEidas();
+ getIdl.setPersonInfo(generateSzrRequest(eidData));
+
+ return getIdentityLinkGeneric(getIdl);
+
+ } catch (final Exception e) {
+ log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e);
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
+
+ }
+ }
+
+ /**
+ * Get IdentityLink of a person.
+ *
+ *
+ * @param matchedPersonData eID information of an already matched person.
+ * @return IdentityLink
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public IdentityLinkType getIdentityLinkInRawMode(MatchedPersonResult matchedPersonData)
+ throws SzrCommunicationException {
+ try {
+ final GetIdentityLinkEidas getIdl = new GetIdentityLinkEidas();
+ getIdl.setPersonInfo(generateSzrRequest(matchedPersonData));
+
+ return getIdentityLinkGeneric(getIdl);
+
+ } catch (final Exception e) {
+ log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e);
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
+
+ }
+ }
+
+ /**
+ * Get bPK of person.
+ *
+ *
+ * @param eidData Minimum dataset of person
+ * @param target requested bPK target
+ * @param vkz Verfahrenskennzeichen
+ * @return bPK for this person
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public List<String> getBpk(SimpleEidasData eidData, String target, String vkz)
+ throws SzrCommunicationException {
+ try {
+ final GetBPK parameters = new GetBPK();
+ parameters.setPersonInfo(generateSzrRequest(eidData));
+ parameters.getBereichsKennung().add(target);
+ parameters.setVKZ(vkz);
+ final GetBPKResponse result = this.szr.getBPK(parameters);
+
+ return result.getGetBPKReturn();
+
+ } catch (final SZRException_Exception e) {
+ log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e);
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
+
+ }
+
+ }
+
+ /**
+ * Creates a new ERnP entry.
+ * TODO Is this correct? Ask BMI.
+ *
+ * @param eidasData Minimum dataset of person
+ * @return encrypted baseId
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public String createNewErnpEntry(final SimpleEidasData eidasData) throws SzrCommunicationException {
+ final String resp;
+ try {
+ resp = this.szr.getStammzahlEncrypted(generateSzrRequest(eidasData), true);
+ } catch (SZRException_Exception e) {
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
+ }
+ if (StringUtils.isEmpty(resp)) {
+ throw new SzrCommunicationException("ernb.01", new Object[]{"Stammzahl response empty"}); // TODO error handling
+ }
+ return resp;
+ }
+
+ /**
+ * Request a encrypted baseId from SZR.
+ *
+ * <b>Note</b>: Previously, this method did create a new ERnP entry, if it did not exist. This is
+ * <b>not</b> the case any more. See {@link #createNewErnpEntry(SimpleEidasData)} for that functionality.
+ *
+ * @param eidData Minimum dataset of person
+ * @return encrypted baseId
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public String getEncryptedStammzahl(final SimpleEidasData eidData)
+ throws SzrCommunicationException {
+ final String resp;
+ try {
+ resp = this.szr.getStammzahlEncrypted(generateSzrRequest(eidData), false);
+ } catch (SZRException_Exception e) {
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
+ }
+
+ if (StringUtils.isEmpty(resp)) {
+ throw new SzrCommunicationException("ernb.01", new Object[]{"Stammzahl response empty"}); // TODO error handling
+ }
+
+ return resp;
+
+ }
+
+ /**
+ * Request a encrypted baseId from SZR.
+ *
+ * @param matchedPersonData eID information of an already matched person.
+ * @return encrypted baseId
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public String getEncryptedStammzahl(MatchedPersonResult matchedPersonData) throws SzrCommunicationException {
+ final String resp;
+ try {
+ resp = this.szr.getStammzahlEncrypted(generateSzrRequest(matchedPersonData), false);
+
+ } catch (SZRException_Exception e) {
+ throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
+
+ }
+
+ if (StringUtils.isEmpty(resp)) {
+ throw new SzrCommunicationException("ernb.01", new Object[]{"Stammzahl response empty"}); // TODO error handling
+
+ }
+
+ return resp;
+
+ }
+
+ /**
+ * Sign an eidasBind data-structure that combines vsz with user's pubKey and E-ID status.
+ *
+ * @param vsz encrypted baseId
+ * @param bindingPubKey binding PublicKey as PKCS1# (ASN.1) container
+ * @param eidStatus Status of the E-ID
+ * @param eidData eID information that was used for ERnP registration
+ * @return bPK for this person
+ * @throws SzrCommunicationException In case of a SZR error
+ */
+ public String getEidasBind(final String vsz, final String bindingPubKey, final String eidStatus,
+ SimpleEidasData eidData)throws SzrCommunicationException {
+
+ final Map<String, Object> eidsaBindMap = new HashMap<>();
+ eidsaBindMap.put(ATTR_NAME_VSZ, vsz);
+ eidsaBindMap.put(ATTR_NAME_STATUS, eidStatus);
+ eidsaBindMap.put(ATTR_NAME_PUBKEYS, Collections.singletonList(bindingPubKey));
+ eidsaBindMap.put(PvpAttributeDefinitions.EID_ISSUING_NATION_NAME, eidData.getCitizenCountryCode());
+ injectMdsIfAvailableAndActive(eidsaBindMap, eidData);
+
+ try {
+ final String serializedEidasBind = mapper.writeValueAsString(eidsaBindMap);
+ final SignContent req = new SignContent();
+ final SignContentEntry eidasBindInfo = new SignContentEntry();
+ eidasBindInfo.setKey(KEY_BC_BIND);
+ eidasBindInfo.setValue(serializedEidasBind);
+ req.getIn().add(eidasBindInfo);
+ req.setAppendCert(false);
+ final JwsHeaderParam eidasBindJoseHeader = new JwsHeaderParam();
+ eidasBindJoseHeader.setKey(JOSE_HEADER_USERCERTPINNING_TYPE);
+ eidasBindJoseHeader.setValue(JOSE_HEADER_USERCERTPINNING_EIDASBIND);
+ req.getJWSHeaderParam().add(eidasBindJoseHeader);
+
+ log.trace("Requesting SZR to sign bcBind datastructure ... ");
+ final SignContentResponseType resp = szr.signContent(req.isAppendCert(), req.getJWSHeaderParam(), req.getIn());
+ log.trace("Receive SZR response on bcBind siging operation ");
+
+ if (resp == null || resp.getOut() == null
+ || resp.getOut().isEmpty()
+ || StringUtils.isEmpty(resp.getOut().get(0).getValue())) {
+ throw new SzrCommunicationException("ernb.01", new Object[]{"BcBind response empty"});
+ }
+
+ return resp.getOut().get(0).getValue();
+
+ } catch (final JsonProcessingException | SZRException_Exception e) {
+ log.warn("Requesting bcBind by using SZR FAILED.", e);
+ throw new SzrCommunicationException("ernb.02",
+ new Object[]{e.getMessage()}, e);
+ }
+ }
+
+ private PersonInfoType generateSzrRequest(MatchedPersonResult matchedPersonData) {
+ log.trace("Starting connecting SZR Gateway");
+ final PersonInfoType personInfo = new PersonInfoType();
+ final PersonNameType personName = new PersonNameType();
+ final PhysicalPersonType naturalPerson = new PhysicalPersonType();
+ IdentificationType bpk = new IdentificationType();
+
+ naturalPerson.setName(personName);
+ personInfo.setPerson(naturalPerson);
+ naturalPerson.setIdentification(bpk);
+
+ // person information
+ personName.setFamilyName(matchedPersonData.getFamilyName());
+ personName.setGivenName(matchedPersonData.getGivenName());
+ naturalPerson.setDateOfBirth(matchedPersonData.getDateOfBirth());
+ bpk.setValue(matchedPersonData.getBpk());
+ bpk.setType(EaafConstants.URN_PREFIX_CDID + "ZP");
+
+ return personInfo;
+ }
+
+ private PersonInfoType generateSzrRequest(SimpleEidasData eidData) {
+ log.trace("Starting connecting SZR Gateway");
+ final PersonInfoType personInfo = new PersonInfoType();
+ final PersonNameType personName = new PersonNameType();
+ final PhysicalPersonType naturalPerson = new PhysicalPersonType();
+ final TravelDocumentType eDocument = new TravelDocumentType();
+
+ naturalPerson.setName(personName);
+ personInfo.setPerson(naturalPerson);
+ personInfo.setTravelDocument(eDocument);
+
+ // person information
+ personName.setFamilyName(eidData.getFamilyName());
+ personName.setGivenName(eidData.getGivenName());
+ naturalPerson.setDateOfBirth(eidData.getDateOfBirth());
+
+ //TODO: need to be updated to new eIDAS document interface!!!!
+ eDocument.setIssuingCountry(eidData.getCitizenCountryCode());
+ eDocument.setDocumentNumber(eidData.getPseudonym());
+
+ // eID document information
+ String documentType = basicConfig
+ .getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_EDOCUMENTTYPE,
+ Constants.SZR_CONSTANTS_DEFAULT_DOCUMENT_TYPE);
+ eDocument.setDocumentType(documentType);
+
+ // set PlaceOfBirth if available
+ if (eidData.getPlaceOfBirth() != null) {
+ log.trace("Find 'PlaceOfBirth' attribute: " + eidData.getPlaceOfBirth());
+ boolean setPlaceOfBirth = basicConfig
+ .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETPLACEOFBIRTHIFAVAILABLE, true);
+ if (setPlaceOfBirth) {
+ naturalPerson.setPlaceOfBirth(eidData.getPlaceOfBirth());
+ log.trace("Adding 'PlaceOfBirth' to ERnB request ... ");
+ }
+ }
+
+ // set BirthName if available
+ if (eidData.getBirthName() != null) {
+ log.trace("Find 'BirthName' attribute: " + eidData.getBirthName());
+ boolean setBirthName = basicConfig
+ .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETBIRTHNAMEIFAVAILABLE, true);
+ if (setBirthName) {
+ final AlternativeNameType alternativeName = new AlternativeNameType();
+ naturalPerson.setAlternativeName(alternativeName);
+ alternativeName.setFamilyName(eidData.getBirthName());
+ log.trace("Adding 'BirthName' to ERnB request ... ");
+ }
+ }
+
+ return personInfo;
+ }
+
+ private IdentityLinkType getIdentityLinkGeneric(GetIdentityLinkEidas getIdl) throws Exception {
+ final JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
+ final Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ jaxbMarshaller.marshal(getIdl, outputStream);
+ outputStream.flush();
+
+ final Source source = new StreamSource(new ByteArrayInputStream(outputStream.toByteArray()));
+ outputStream.close();
+
+ log.trace("Requesting SZR ... ");
+ final Source response = dispatch.invoke(source);
+ log.trace("Receive RAW response from SZR");
+
+ final byte[] szrResponse = sourceToByteArray(response);
+ final GetIdentityLinkEidasResponse jaxbElement = (GetIdentityLinkEidasResponse) jaxbContext
+ .createUnmarshaller().unmarshal(new ByteArrayInputStream(szrResponse));
+
+ // build response
+ log.trace(new String(szrResponse, StandardCharsets.UTF_8));
+
+ // ok, we have success
+ final Document doc = DomUtils.parseDocument(
+ new ByteArrayInputStream(szrResponse),
+ true,
+ XmlNamespaceConstants.ALL_SCHEMA_LOCATIONS + " " + Constants.SZR_SCHEMA_LOCATIONS,
+ null, null);
+ final String xpathExpression = "//saml:Assertion";
+ final Element nsNode = doc.createElementNS("urn:oasis:names:tc:SAML:1.0:assertion", "saml:NSNode");
+
+ log.trace("Selecting signed doc " + xpathExpression);
+ final Element documentNode = (Element) XPathAPI.selectSingleNode(doc,
+ xpathExpression, nsNode);
+ log.trace("Signed document: " + DomUtils.serializeNode(documentNode));
+
+ final IdentityLinkType idl = new IdentityLinkType();
+ idl.setAssertion(documentNode);
+ idl.setPersonInfo(jaxbElement.getGetIdentityLinkReturn().getPersonInfo());
+
+ return idl;
+
+ }
+
+ @PostConstruct
+ private void initialize() throws EaafConfigurationException {
+ log.info("Starting SZR-Client initialization .... ");
+ final URL url = SzrClient.class.getResource("/wsdl/szr_client/SZR_v4.0.wsdl");
+
+ final boolean useTestSzr = basicConfig.getBasicConfigurationBoolean(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_USETESTSERVICE,
+ true);
+
+ SzrService szrService;
+ QName qname;
+ String szrUrl;
+ if (useTestSzr) {
+ log.debug("Initializing SZR test environment configuration.");
+ qname = SzrService.SZRTestumgebung;
+ szrService = new SzrService(url, new QName("urn:SZRServices", "SZRService"));
+ szr = szrService.getSzrTestumgebung();
+ szrUrl = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_ENDPOINT_TEST);
+
+ } else {
+ log.debug("Initializing SZR productive configuration.");
+ qname = SzrService.SZRProduktionsumgebung;
+ szrService = new SzrService(url, new QName("urn:SZRServices", "SZRService"));
+ szr = szrService.getSzrProduktionsumgebung();
+ szrUrl = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_ENDPOINT_PROD);
+
+ }
+
+ // create raw client;
+ dispatch = szrService.createDispatch(qname, Source.class, javax.xml.ws.Service.Mode.PAYLOAD);
+
+ if (StringUtils.isEmpty(szrUrl)) {
+ log.error("No SZR service-URL found. SZR-Client initalisiation failed.");
+ throw new RuntimeException("No SZR service URL found. SZR-Client initalisiation failed.");
+
+ }
+
+ // check if Clients can be initialized
+ if (szr == null) {
+ log.error("SZR " + CLIENT_DEFAULT + " is 'NULL'. Something goes wrong");
+ throw new RuntimeException("SZR " + CLIENT_DEFAULT + " is 'NULL'. Something goes wrong");
+
+ }
+ if (dispatch == null) {
+ log.error("SZR " + CLIENT_RAW + " is 'NULL'. Something goes wrong");
+ throw new RuntimeException("SZR " + CLIENT_RAW + " is 'NULL'. Something goes wrong");
+
+ }
+
+ // inject handler
+ log.info("Use SZR service-URL: " + szrUrl);
+ injectBindingProvider((BindingProvider) szr, CLIENT_DEFAULT, szrUrl,
+ basicConfig.getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_TRACEMESSAGES, false));
+ injectBindingProvider(dispatch, CLIENT_RAW, szrUrl,
+ basicConfig.getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_TRACEMESSAGES, false));
+
+ // inject http parameters and SSL context
+ log.debug("Inject HTTP client settings ... ");
+ HttpClientConfigBuilder httpClientBuilder = HttpClientConfig.builder()
+ .clientName("SZR Client")
+ .clientUrl(szrUrl)
+ .connectionTimeout(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_TIMEOUT_CONNECTION,
+ Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_CONNECTION))
+ .responseTimeout(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_TIMEOUT_RESPONSE,
+ Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_RESPONSE))
+ .keyStoreConfig(buildKeyStoreConfiguration(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_TYPE,
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_PATH,
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_PASSWORD,
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_NAME,
+ "SZR SSL Client-Authentication KeyStore"))
+ .keyAlias(basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYS_ALIAS))
+ .keyPassword(basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEY_PASSWORD))
+ .trustAll(false)
+ .trustStoreConfig(buildKeyStoreConfiguration(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_TYPE,
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_PATH,
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_PASSWORD,
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_NAME,
+ "SZR SSL Client-Authentication KeyStore"));
+
+ injectHttpClient(szr, httpClientBuilder.clientType(CLIENT_DEFAULT).build());
+ injectHttpClient(dispatch, httpClientBuilder.clientType(CLIENT_RAW).build());
+
+ log.info("SZR-Client initialization successfull");
+ }
+
+ private void injectMdsIfAvailableAndActive(Map<String, Object> eidsaBindMap, SimpleEidasData eidData) {
+ if (basicConfig.getBasicConfigurationBoolean(
+ Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SET_MDS_TO_EIDASBIND, false)) {
+ log.info("Injecting MDS into eidasBind ... ");
+ final Map<String, Object> mds = new HashMap<>();
+ mds.put(PvpAttributeDefinitions.PRINCIPAL_NAME_NAME, eidData.getFamilyName());
+ mds.put(PvpAttributeDefinitions.GIVEN_NAME_NAME, eidData.getGivenName());
+ mds.put(PvpAttributeDefinitions.BIRTHDATE_NAME, eidData.getDateOfBirth());
+ eidsaBindMap.put(ATTR_NAME_MDS, mds);
+
+ }
+ }
+
+ private byte[] sourceToByteArray(Source result) throws TransformerException {
+ final TransformerFactory factory = TransformerFactory.newInstance();
+ factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ final Transformer transformer = factory.newTransformer();
+ transformer.setOutputProperty("omit-xml-declaration", "yes");
+ transformer.setOutputProperty("method", "xml");
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final StreamResult streamResult = new StreamResult();
+ streamResult.setOutputStream(out);
+ transformer.transform(result, streamResult);
+ return out.toByteArray();
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrService.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/szr/SzrService.java
index dde868b1..590f88a4 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrService.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/szr/SzrService.java
@@ -21,7 +21,7 @@
* that you distribute must include a readable copy of the "NOTICE" text file.
*/
-package at.asitplus.eidas.specific.modules.auth.eidas.v2.szr;
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.szr;
import java.net.URL;
@@ -52,7 +52,7 @@ public class SzrService extends Service {
"SZRBusinesspartnerTestumgebung");
static {
- URL url = SzrService.class.getResource("./src/main/resources/szr_client/SZR-1.WSDL");
+ URL url = SzrService.class.getResource("./src/main/resources/wsdl/szr_client/SZR-1.WSDL");
if (url == null) {
url = SzrService.class.getClassLoader().getResource("/szr_client/SZR-1.WSDL");
}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/IZmrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/IZmrClient.java
new file mode 100644
index 00000000..c4e8ece0
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/IZmrClient.java
@@ -0,0 +1,98 @@
+/*
+ * 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.clients.zmr;
+
+import java.math.BigInteger;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.zmr.ZmrSoapClient.ZmrRegisterResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;
+
+public interface IZmrClient {
+
+ /**
+ * Search person based on eIDAS personal identifier.
+ *
+ * @param zmrProzessId ProcessId from ZMR or <code>null</code> if no processId exists
+ * @param personIdentifier Full eIDAS personal identifier with prefix
+ * @param citizenCountryCode CountryCode of the eIDAS proxy-service
+ * @return Search result but never <code>null</code>
+ * @throws EidasSAuthenticationException In case of a communication error
+ */
+ @Nonnull
+ ZmrRegisterResult searchWithPersonIdentifier(@Nullable BigInteger zmrProzessId, @Nonnull String personIdentifier,
+ @Nonnull String citizenCountryCode) throws EidasSAuthenticationException;
+
+ /**
+ * Search person based on eIDSA MDS information.
+ *
+ * @param zmrProzessId ProcessId from ZMR or <code>null</code> if no processId exists
+ * @param givenName eIDAS given name
+ * @param familyName eIDAS principle name
+ * @param dateOfBirth eIDAS date-of-birth
+ * @param citizenCountryCode CountryCode of the eIDAS proxy-service
+ * @return Search result but never <code>null</code>
+ * @throws EidasSAuthenticationException In case of a communication error
+ */
+ @Nonnull
+ ZmrRegisterResult searchWithMds(@Nullable BigInteger zmrProzessId, @Nonnull String givenName,
+ @Nonnull String familyName, @Nonnull String dateOfBirth, @Nonnull String citizenCountryCode)
+ throws EidasSAuthenticationException;
+
+ /**
+ * Search person based on country-specific natural person set.
+ *
+ * @param zmrProzessId ProcessId from ZMR or <code>null</code> if no processId exists
+ * @param personSearchDao Specific set of natural person informations.
+ * @param citizenCountryCode CountryCode of the eIDAS proxy-service
+ * @return Search result but never <code>null</code>
+ * @throws EidasSAuthenticationException In case of a communication error
+ */
+ @Nonnull
+ ZmrRegisterResult searchCountrySpecific(@Nullable BigInteger zmrProzessId,
+ @Nonnull PersonSuchenRequest personSearchDao, @Nonnull String citizenCountryCode)
+ throws EidasSAuthenticationException;
+
+ /**
+ * Update ZMR entry to KITT existing ZMR identity with this eIDAS authentication.
+ *
+ * @param zmrProzessId zmrProzessId ProcessId from ZMR or <code>null</code> if no processId exists
+ * @param registerResult Already matched eIDAS identity that should be KITT
+ * @param eidData eIDAS eID information from current authentication process
+ * @return Update result but never <code>null</code>
+ * @throws EidasSAuthenticationException In case of a communication error
+ */
+ @Nonnull
+ ZmrRegisterResult update(@Nullable BigInteger zmrProzessId, RegisterResult registerResult, SimpleEidasData eidData)
+ throws EidasSAuthenticationException;
+
+ ZmrRegisterResult searchWithResidenceData(@Nullable BigInteger zmrProzessId, String givenName, String familyName,
+ String dateOfBirth, String zipcode, String city, String street);
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/ZmrSoapClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/ZmrSoapClient.java
new file mode 100644
index 00000000..29914e21
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/ZmrSoapClient.java
@@ -0,0 +1,775 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.zmr;
+
+import java.math.BigInteger;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.PostConstruct;
+import javax.xml.ws.BindingProvider;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.AbstractSoapClient;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.ZmrCommunicationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.VersionHolder;
+import at.gv.bmi.namespace.zmr_su.base._20040201.ClientInfoType;
+import at.gv.bmi.namespace.zmr_su.base._20040201.Organisation;
+import at.gv.bmi.namespace.zmr_su.base._20040201.RequestType;
+import at.gv.bmi.namespace.zmr_su.base._20040201.ResponseType;
+import at.gv.bmi.namespace.zmr_su.base._20040201.WorkflowInfoClient;
+import at.gv.bmi.namespace.zmr_su.base._20040201.WorkflowInfoServer;
+import at.gv.bmi.namespace.zmr_su.base._20040201_.Service;
+import at.gv.bmi.namespace.zmr_su.base._20040201_.ServiceFault;
+import at.gv.bmi.namespace.zmr_su.base._20040201_.ServicePort;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.EidasIdentitaetAnlageType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.EidasIdentitaetErgebnisType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.EidasSuchdatenType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.ErgebniskriterienType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.NatuerlichePersonErgebnisType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonAendernInfoType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonAendernRequest;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonErgebnisSatzType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonErgebnisType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonReferenzType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenResponse;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonensucheInfoType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.SuchkriterienType;
+import at.gv.e_government.reference.namespace.persondata.de._20040201.IdentificationType;
+import at.gv.e_government.reference.namespace.persondata.de._20040201.NatuerlichePersonTyp;
+import at.gv.e_government.reference.namespace.persondata.de._20040201.PersonenNameTyp;
+import at.gv.egiz.eaaf.core.api.data.EaafConstants;
+import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ZMR client implementation for eIDAS matching operations.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class ZmrSoapClient extends AbstractSoapClient implements IZmrClient {
+
+ private static final String ERROR_MATCHING_01 = "module.eidasauth.matching.01";
+ private static final String ERROR_MATCHING_02 = "module.eidasauth.matching.02";
+ private static final String ERROR_MATCHING_99 = "module.eidasauth.matching.99";
+
+ private static final String LOGMSG_MISSING_CONFIG = "Missing configuration with key: {0}";
+
+ private static final String LOGMSG_ZMR_ERROR =
+ "Receive an error from ZMR during '{}' operation with msg: {}";
+ private static final String LOGMSG_ZMR_RESP_PROCESS =
+ "Proces ZMR response during '{}' operation failes with msg: {}";
+
+ private static final String LOGMSG_ZMR_SOAP_ERROR =
+ "ZMR anwser for transaction: {} with code: {} and message: {}";
+
+ private static final String PROCESS_GENERAL = "eIDAS_Matching";
+ private static final String PROCESS_SEARCH_PERSONAL_IDENTIFIER =
+ "Searching " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER;
+ private static final String PROCESS_SEARCH_MDS_ONLY = "Searching with MDS only";
+ private static final String PROCESS_SEARCH_COUNTRY_SPECIFIC = "Searching {} specific";
+
+ private static final String PROCESS_KITT_GENERAL = "KITT general-processing";
+ private static final String PROCESS_KITT_IDENITIES_GET = "KITT get-latest-version";
+ private static final String PROCESS_KITT_IDENITIES_UPDATE = "KITT update dataset";
+
+ private static final String CLIENT_INFO = "eIDAS MS-Connector v{0}";
+ private static final String CLIENT_DEFAULT = "ZMR Client";
+
+
+ @Autowired VersionHolder versionHolder;
+
+ private ServicePort zmrClient;
+
+
+ @AllArgsConstructor
+ @Getter
+ public static class ZmrRegisterResult {
+ private final List<RegisterResult> personResult;
+ private final BigInteger processId;
+
+ }
+
+ @Override
+ public ZmrRegisterResult searchWithPersonIdentifier(BigInteger zmrProzessId, String personPseudonym,
+ String citizenCountryCode) throws EidasSAuthenticationException {
+
+ try {
+ // build search request
+ final RequestType req = new RequestType();
+
+ // set eIDAS person information
+ final PersonSuchenRequest searchPersonReq = new PersonSuchenRequest();
+ req.setPersonSuchenRequest(searchPersonReq);
+ final EidasSuchdatenType eidasInfos = new EidasSuchdatenType();
+ searchPersonReq.getEidasSuchdaten().add(eidasInfos);
+ eidasInfos.setEidasArt(Constants.eIDAS_ATTRURN_PERSONALIDENTIFIER);
+ eidasInfos.setEidasWert(personPseudonym);
+
+ // set work-flow client information
+ req.setWorkflowInfoClient(generateWorkFlowInfos(PROCESS_SEARCH_PERSONAL_IDENTIFIER, null));
+ req.setClientInfo(generateClientInfos());
+
+ // set additionl search parameters
+ searchPersonReq.setPersonensucheInfo(generateSearchCriteria(false, true, false));
+
+ // request ZMR
+ log.trace("Requesting ZMR for '{}' operation", PROCESS_SEARCH_PERSONAL_IDENTIFIER);
+ final ResponseType resp = zmrClient.service(req, null);
+
+ // parse ZMR response
+ return processZmrResponse(resp, citizenCountryCode, true, PROCESS_SEARCH_PERSONAL_IDENTIFIER);
+
+ } catch (final ServiceFault e) {
+ final String errorMsg = extractReasonFromError(e);
+ log.warn(LOGMSG_ZMR_ERROR, PROCESS_SEARCH_PERSONAL_IDENTIFIER, errorMsg);
+ throw new ZmrCommunicationException(ERROR_MATCHING_01, new Object[] { errorMsg }, e);
+
+ } catch (EidasSAuthenticationException e) {
+ throw e;
+
+ } catch (final Exception e) {
+ log.warn(LOGMSG_ZMR_RESP_PROCESS, PROCESS_SEARCH_PERSONAL_IDENTIFIER, e.getMessage());
+ throw new EidasSAuthenticationException(ERROR_MATCHING_99, new Object[] { e.getMessage() }, e);
+
+ }
+ }
+
+ @Override
+ public ZmrRegisterResult searchWithMds(BigInteger zmrProzessId, String givenName, String familyName,
+ String dateOfBirth, String citizenCountryCode) throws EidasSAuthenticationException {
+ try {
+ // build search request
+ final RequestType req = new RequestType();
+
+ // set eIDAS person information
+ final PersonSuchenRequest searchPersonReq = new PersonSuchenRequest();
+ req.setPersonSuchenRequest(searchPersonReq);
+
+ final NatuerlichePersonTyp searchNatPerson = new NatuerlichePersonTyp();
+ searchPersonReq.setNatuerlichePerson(searchNatPerson);
+ final PersonenNameTyp searchNatPersonName = new PersonenNameTyp();
+ searchNatPerson.setPersonenName(searchNatPersonName);
+
+ searchNatPersonName.setFamilienname(familyName);
+ searchNatPersonName.setVorname(givenName);
+ searchNatPerson.setGeburtsdatum(dateOfBirth);
+
+ // set work-flow client information
+ req.setWorkflowInfoClient(generateWorkFlowInfos(PROCESS_SEARCH_MDS_ONLY, zmrProzessId));
+ req.setClientInfo(generateClientInfos());
+
+ // set additionl search parameters
+ searchPersonReq.setPersonensucheInfo(generateSearchCriteria(false, true, false));
+
+ // request ZMR
+ log.trace("Requesting ZMR for '{}' operation", PROCESS_SEARCH_MDS_ONLY);
+ final ResponseType resp = zmrClient.service(req, null);
+
+ // parse ZMR response
+ return processZmrResponse(resp, citizenCountryCode, false, PROCESS_SEARCH_MDS_ONLY);
+
+ } catch (final ServiceFault e) {
+ final String errorMsg = extractReasonFromError(e);
+ log.warn(LOGMSG_ZMR_ERROR, PROCESS_SEARCH_MDS_ONLY, errorMsg);
+ throw new ZmrCommunicationException(ERROR_MATCHING_01, new Object[] { errorMsg }, e);
+
+ } catch (EidasSAuthenticationException e) {
+ throw e;
+
+ } catch (final Exception e) {
+ log.warn(LOGMSG_ZMR_RESP_PROCESS, PROCESS_SEARCH_MDS_ONLY, e.getMessage());
+ throw new EidasSAuthenticationException(ERROR_MATCHING_99, new Object[] { e.getMessage() }, e);
+
+ }
+
+ }
+
+ @Override
+ public ZmrRegisterResult searchCountrySpecific(BigInteger zmrProzessId, PersonSuchenRequest personSearchDao,
+ String citizenCountryCode)
+ throws EidasSAuthenticationException {
+ final String friendlyMsg = MessageFormat.format(PROCESS_SEARCH_COUNTRY_SPECIFIC, citizenCountryCode);
+
+ try {
+ // build search request
+ final RequestType req = new RequestType();
+
+ // set eIDAS person information
+ req.setPersonSuchenRequest(personSearchDao);
+
+ // set work-flow client information
+ req.setWorkflowInfoClient(generateWorkFlowInfos(friendlyMsg, zmrProzessId));
+ req.setClientInfo(generateClientInfos());
+
+ // set additionl search parameters
+ personSearchDao.setPersonensucheInfo(generateSearchCriteria(false, true, false));
+
+ // request ZMR
+ log.trace("Requesting ZMR for '{}' operation", friendlyMsg);
+ final ResponseType resp = zmrClient.service(req, null);
+
+ // parse ZMR response
+ return processZmrResponse(resp, citizenCountryCode, true,
+ friendlyMsg);
+
+ } catch (final ServiceFault e) {
+ final String errorMsg = extractReasonFromError(e);
+ log.warn(LOGMSG_ZMR_ERROR, friendlyMsg, errorMsg);
+ throw new ZmrCommunicationException(ERROR_MATCHING_01, new Object[] { errorMsg }, e);
+
+ } catch (EidasSAuthenticationException e) {
+ throw e;
+
+ } catch (final Exception e) {
+ log.warn(LOGMSG_ZMR_RESP_PROCESS, friendlyMsg, e.getMessage());
+ throw new EidasSAuthenticationException(ERROR_MATCHING_99, new Object[] { e.getMessage() }, e);
+
+ }
+ }
+
+ @Override
+ public ZmrRegisterResult update(BigInteger zmrProzessId, RegisterResult registerResult,
+ SimpleEidasData eidData) throws EidasSAuthenticationException {
+ try {
+ //search person with register result, because update needs information from search response
+ PersonErgebnisType zmrPersonToKitt = searchPersonForUpdate(zmrProzessId, registerResult);
+
+ // select elements that have to be updated
+ Collection<? extends EidasIdentitaetAnlageType> eidasDocumentToAdd =
+ selectEidasDocumentsToAdd(zmrPersonToKitt, eidData);
+
+ if (eidasDocumentToAdd.isEmpty()) {
+ log.info("Find no eIDAS document for update during: {}. Looks strange but nothing todo",
+ PROCESS_KITT_GENERAL);
+ return new ZmrRegisterResult(Arrays.asList(registerResult), zmrProzessId);
+
+ } else {
+ log.info("Find #{} eIDAS documents for update during: {}", eidasDocumentToAdd.size(), PROCESS_KITT_GENERAL);
+
+ // update entry based on selected update info's and results from search response
+ return updatePersonInZmr(zmrPersonToKitt, eidasDocumentToAdd, eidData.getCitizenCountryCode());
+
+ }
+
+ } catch (final ServiceFault e) {
+ final String errorMsg = extractReasonFromError(e);
+ log.warn(LOGMSG_ZMR_ERROR, PROCESS_KITT_GENERAL, errorMsg);
+ throw new ZmrCommunicationException(ERROR_MATCHING_01, new Object[] { errorMsg }, e);
+
+ } catch (EidasSAuthenticationException e) {
+ throw e;
+
+ } catch (final Exception e) {
+ log.warn(LOGMSG_ZMR_RESP_PROCESS, PROCESS_KITT_GENERAL, e.getMessage());
+ throw new EidasSAuthenticationException(ERROR_MATCHING_99, new Object[] { e.getMessage() }, e);
+
+ }
+
+ }
+
+ @Override
+ public ZmrRegisterResult searchWithResidenceData(BigInteger zmrProzessId, String givenName, String familyName,
+ String dateOfBirth, String zipcode, String city, String street) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @PostConstruct
+ private void initialize() throws EaafConfigurationException {
+ // set-up the ZMR client
+ initializeTechnicalZmrClient();
+
+ // validate additional ZMR communication parameters
+ valdiateAdditionalConfigParameters();
+
+ }
+
+ private void initializeTechnicalZmrClient() throws EaafConfigurationException {
+ log.info("Starting ZMR-Client initialization .... ");
+ final URL url = ZmrSoapClient.class.getResource("/wsdl/zmr_client/wsdl/Service.wsdl");
+ final Service zmrService = new Service(url);
+ zmrClient = zmrService.getService();
+
+ final String zmrServiceUrl = basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_ENDPOINT);
+ if (StringUtils.isEmpty(zmrServiceUrl)) {
+ log.error("No ZMR service-URL found. ZMR-Client initalisiation failed.");
+ throw new RuntimeException("No ZMR service URL found. ZMR-Client initalisiation failed.");
+
+ }
+
+ // inject handler
+ log.info("Use ZMR service-URL: " + zmrServiceUrl);
+ injectBindingProvider((BindingProvider) zmrClient, CLIENT_DEFAULT, zmrServiceUrl,
+ basicConfig.getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_DEBUG_TRACEMESSAGES,
+ false));
+
+ // inject http parameters and SSL context
+ log.debug("Inject HTTP client settings ... ");
+ injectHttpClient(zmrClient, HttpClientConfig.builder()
+ .clientName(CLIENT_DEFAULT)
+ .clientType(CLIENT_DEFAULT)
+ .clientUrl(zmrServiceUrl)
+ .connectionTimeout(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_TIMEOUT_CONNECTION,
+ Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_CONNECTION))
+ .responseTimeout(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_TIMEOUT_RESPONSE,
+ Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_RESPONSE))
+ .keyStoreConfig(buildKeyStoreConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYSTORE_TYPE,
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYSTORE_PATH,
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYSTORE_PASSWORD,
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYSTORE_NAME,
+ "ZMR SSL Client-Authentication KeyStore"))
+ .keyAlias(basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEYS_ALIAS))
+ .keyPassword(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_KEY_PASSWORD))
+ .trustAll(false)
+ .trustStoreConfig(buildKeyStoreConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_TRUSTSTORE_TYPE,
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_TRUSTSTORE_PATH,
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_TRUSTSTORE_PASSWORD,
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_SSL_TRUSTSTORE_NAME,
+ "ZMR SSL Client-Authentication TrustStore"))
+ .build());
+
+ }
+
+ private void valdiateAdditionalConfigParameters() {
+ checkConfigurationValue(Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_ORGANIZATION_NR);
+ checkConfigurationValue(Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_UPDATE_REASON_CODE);
+
+ }
+
+ private void checkConfigurationValue(String key) {
+ if (StringUtils.isEmpty(basicConfig.getBasicConfiguration(key))) {
+ throw new RuntimeException(MessageFormat.format(LOGMSG_MISSING_CONFIG, key));
+
+ }
+ }
+
+ @Nonnull
+ private WorkflowInfoClient generateWorkFlowInfos(@Nonnull String subStepName,
+ @Nullable BigInteger prozessInstanzId) {
+ final WorkflowInfoClient infos = new WorkflowInfoClient();
+ infos.setProzessName(PROCESS_GENERAL);
+ infos.setVorgangName(subStepName);
+
+ //set processId that we received from ZMR before, if already available
+ if (prozessInstanzId != null) {
+ infos.setProzessInstanzID(prozessInstanzId);
+
+ }
+
+ return infos;
+
+ }
+
+ @Nonnull
+ private PersonensucheInfoType generateSearchCriteria(boolean searchInErnp,
+ boolean searchInZmrHistory, boolean includeHistoryResults) {
+ final PersonensucheInfoType personSearchInfo = new PersonensucheInfoType();
+ final SuchkriterienType searchCriteria = new SuchkriterienType();
+ final ErgebniskriterienType resultCriteria = new ErgebniskriterienType();
+ personSearchInfo.setSuchkriterien(searchCriteria);
+ personSearchInfo.setErgebniskriterien(resultCriteria);
+
+ // TODO: are these flags valid?
+ searchCriteria.setInclusivERnP(searchInErnp);
+ searchCriteria.setInclusivHistorie(searchInZmrHistory);
+
+ // TODO: check 'processSearchPersonResponse' if we change this to 'true'
+ resultCriteria.setInclusivHistorie(includeHistoryResults);
+
+ return personSearchInfo;
+
+ }
+
+ @Nonnull
+ private ClientInfoType generateClientInfos() {
+ final ClientInfoType clientInfo = new ClientInfoType();
+ final Organisation clientOrganisation = new Organisation();
+ clientInfo.setOrganisation(clientOrganisation);
+
+ // set client information
+ clientInfo.setClient(MessageFormat.format(CLIENT_INFO, versionHolder.getVersion()));
+
+ // set Behoerdennummer as organization identifier
+ clientOrganisation.setBehoerdenNr(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_ORGANIZATION_NR));
+
+ return clientInfo;
+ }
+
+ @Nonnull
+ private String extractReasonFromError(ServiceFault e) {
+ if (e.getFaultInfo() != null) {
+ return MessageFormat.format(LOGMSG_ZMR_SOAP_ERROR,
+ e.getFaultInfo().getServerTransaktionNr(),
+ e.getFaultInfo().getErrorCode(),
+ e.getFaultInfo().getErrorMessage());
+
+ } else {
+ log.error("ZMR response without error code", e);
+ return e.getMessage();
+
+ }
+ }
+
+ @Nonnull
+ private ZmrRegisterResult processZmrResponse(@Nonnull ResponseType resp,
+ @Nonnull String citizenCountryCode,
+ boolean forceSinglePersonMatch, @Nonnull String processStepFiendlyname)
+ throws EaafAuthenticationException {
+ final PersonSuchenResponse searchPersonResp = resp.getPersonSuchenResponse();
+ if (searchPersonResp.getPersonensuchergebnis() == null
+ || searchPersonResp.getPersonensuchergebnis().getPersonErgebnisSatz().isEmpty()) {
+ log.debug("ZMR result contains NO 'Personensuchergebnis' or 'PersonErgebnisSatz' is empty");
+ return new ZmrRegisterResult(Collections.emptyList(), extractZmrProcessId(resp.getWorkflowInfoServer()));
+
+ } else {
+ // TODO: what we to with ERnP results?
+ log.debug("Get #{} person results from '{}' operation",
+ searchPersonResp.getPersonensuchergebnis().getGefundeneSaetze(), processStepFiendlyname);
+
+ if (forceSinglePersonMatch) {
+ return new ZmrRegisterResult(processSearchPersonResponseSingleResult(
+ searchPersonResp.getPersonensuchergebnis().getPersonErgebnisSatz(),
+ citizenCountryCode, processStepFiendlyname),
+ extractZmrProcessId(resp.getWorkflowInfoServer()));
+
+ } else {
+ return new ZmrRegisterResult(processSearchPersonResponse(
+ searchPersonResp.getPersonensuchergebnis().getPersonErgebnisSatz(), citizenCountryCode),
+ extractZmrProcessId(resp.getWorkflowInfoServer()));
+
+ }
+ }
+ }
+
+ private BigInteger extractZmrProcessId(WorkflowInfoServer workflowInfoServer) {
+ return workflowInfoServer != null ? workflowInfoServer.getProzessInstanzID() : null;
+
+ }
+
+ @Nonnull
+ private List<RegisterResult> processSearchPersonResponse(
+ @Nonnull List<PersonErgebnisSatzType> personErgebnisSatz,
+ @Nonnull String citizenCountryCode) throws EaafAuthenticationException {
+
+ return personErgebnisSatz.stream()
+ .map(el -> {
+ try {
+ return processPersonResult(el, citizenCountryCode);
+
+ } catch (final EaafAuthenticationException e) {
+ log.warn("Skip ZMR person result by reason: {}", e.getMessage(), e);
+ return null;
+
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ }
+
+ @NonNull
+ private List<RegisterResult> processSearchPersonResponseSingleResult(
+ @Nonnull List<PersonErgebnisSatzType> personErgebnisSatz,
+ @Nonnull String citizenCountryCode, String processStepFiendlyname) throws EaafAuthenticationException {
+ if (personErgebnisSatz.size() > 1) {
+ log.error("Find more-than-one ZMR entry with search criteria that has to be unique");
+ throw new WorkflowException(processStepFiendlyname,
+ "Find more-than-one ZMR entry with search criteria that has to be unique", true);
+
+ } else {
+ return Arrays.asList(processPersonResult(personErgebnisSatz.get(0), citizenCountryCode));
+
+ }
+ }
+
+ @Nonnull
+ private RegisterResult processPersonResult(
+ @Nonnull PersonErgebnisSatzType personEl, @Nonnull String citizenCountryCode)
+ throws EaafAuthenticationException {
+ // TODO: maybe check on 'null' if ERnP data is also allowed
+ log.debug("Find #{} data sets in person information",
+ personEl.getPersonendaten().getPersonErgebnis().size());
+
+ if (personEl.getPersonendaten().getPersonErgebnis().size() > 1) {
+ log.error("Find more than on person with eIDAS personalIdentifier.");
+ throw new EaafAuthenticationException(ERROR_MATCHING_02, null);
+
+ } else {
+ return mapZmrResponseToRegisterResult(
+ personEl.getPersonendaten().getPersonErgebnis().get(0), citizenCountryCode);
+
+ }
+
+ }
+
+ @Nonnull
+ private RegisterResult mapZmrResponseToRegisterResult(@Nonnull PersonErgebnisType person,
+ @Nonnull String citizenCountryCode) {
+ // TODO: kann ich bei historischen daten davon ausgehen dass die Reihenfolge der
+ // Ergebnisse von aktuell --> alt ist?
+
+ // build result
+ return RegisterResult.builder()
+ .pseudonym(selectAllEidasDocument(person, citizenCountryCode,
+ Constants.eIDAS_ATTRURN_PERSONALIDENTIFIER))
+ .familyName(person.getNatuerlichePerson().getPersonenName().getFamilienname())
+ .givenName(person.getNatuerlichePerson().getPersonenName().getVorname())
+ .dateOfBirth(person.getNatuerlichePerson().getGeburtsdatum())
+ .bpk(extractBpkZp(person.getNatuerlichePerson()))
+ .placeOfBirth(selectSingleEidasDocument(person, citizenCountryCode,
+ Constants.eIDAS_ATTRURN_PLACEOFBIRTH))
+ .birthName(selectSingleEidasDocument(person, citizenCountryCode,
+ Constants.eIDAS_ATTRURN_BIRTHNAME))
+ .build();
+
+ }
+
+ private String extractBpkZp(NatuerlichePersonErgebnisType natuerlichePerson) {
+ String bpk = natuerlichePerson.getIdentification().stream()
+ .filter(el -> Constants.MATCHING_INTERNAL_BPK_TARGET.equals(el.getType()))
+ .findFirst()
+ .map(el -> el.getValue())
+ .orElse(null);
+ if (StringUtils.isEmpty(bpk)) {
+ log.warn("ZMR response contains no 'bPK' for target: 'ZP'");
+
+ }
+ return bpk;
+
+ }
+
+ /**
+ * Get all eIDAS document with the specified country code and document type.
+ *
+ * @param person Person information from ZMR
+ * @param citizenCountryCode Country code of the eIDAS attribute
+ * @param eidasAttrurnPersonalidentifier eIDAS attribute identifier
+ * @return {@link List} of eIDAS attribute values or an empty list if's not
+ * found
+ */
+ @NonNull
+ private List<String> selectAllEidasDocument(PersonErgebnisType person, String citizenCountryCode,
+ String eidasAttrurnPersonalidentifier) {
+ return person.getEidasIdentitaet().stream()
+ .filter(el -> eidasAttrurnPersonalidentifier.equals(el.getEidasArt())
+ && el.getStaatscode2().equals(citizenCountryCode))
+ .map(el -> el.getEidasWert())
+ .collect(Collectors.toList());
+
+ }
+
+ /**
+ * Get the first eIDAS document with the specified country code and document
+ * type.
+ *
+ * @param person Person information from ZMR
+ * @param citizenCountryCode Country code of the eIDAS attribute
+ * @param eidasAttrurnPersonalidentifier eIDAS attribute identifier
+ * @return Value of this eIDAS attribute or <code>null</code> if's not found
+ */
+ @Nullable
+ private String selectSingleEidasDocument(PersonErgebnisType person, String citizenCountryCode,
+ String eidasAttrurnPersonalidentifier) {
+ return person.getEidasIdentitaet().stream()
+ .filter(el -> eidasAttrurnPersonalidentifier.equals(el.getEidasArt())
+ && el.getStaatscode2().equals(citizenCountryCode))
+ .findFirst()
+ .map(el -> el.getEidasWert())
+ .orElse(null);
+
+ }
+
+ private PersonErgebnisType searchPersonForUpdate(BigInteger zmrProzessId, RegisterResult registerResult)
+ throws ServiceFault, WorkflowException {
+ // build search request
+ final RequestType req = new RequestType();
+
+ // set eIDAS person information
+ final PersonSuchenRequest searchPersonReq = new PersonSuchenRequest();
+ req.setPersonSuchenRequest(searchPersonReq);
+ NatuerlichePersonTyp natPersonInfos = new NatuerlichePersonTyp();
+ searchPersonReq.setNatuerlichePerson(natPersonInfos);
+ PersonenNameTyp nameInfo = new PersonenNameTyp();
+ natPersonInfos.setPersonenName(nameInfo);
+ IdentificationType bpkInfo = new IdentificationType();
+ natPersonInfos.getIdentification().add(bpkInfo);
+
+ // set MDS
+ nameInfo.setVorname(registerResult.getGivenName());
+ nameInfo.setFamilienname(registerResult.getFamilyName());
+ natPersonInfos.setGeburtsdatum(registerResult.getDateOfBirth());
+
+ //set bPK
+ bpkInfo.setValue(registerResult.getBpk());
+ bpkInfo.setType(EaafConstants.URN_PREFIX_CDID + "ZP");
+
+ // set work-flow client information
+ req.setWorkflowInfoClient(generateWorkFlowInfos(PROCESS_KITT_IDENITIES_GET, zmrProzessId));
+ req.setClientInfo(generateClientInfos());
+
+ // set additionl search parameters
+ searchPersonReq.setPersonensucheInfo(generateSearchCriteria(false, true, false));
+
+ // request ZMR
+ log.trace("Requesting ZMR for '{}' operation", PROCESS_KITT_IDENITIES_GET);
+ ResponseType resp = zmrClient.service(req, null);
+ log.trace("Receive response from ZMR for '{}' operation", PROCESS_KITT_IDENITIES_GET);
+
+ return extractPersonResultForUpdaste(resp);
+
+ }
+
+ private PersonErgebnisType extractPersonResultForUpdaste(ResponseType resp) throws WorkflowException {
+ final PersonSuchenResponse searchPersonResp = resp.getPersonSuchenResponse();
+ if (searchPersonResp.getPersonensuchergebnis() == null
+ || searchPersonResp.getPersonensuchergebnis().getPersonErgebnisSatz().isEmpty()) {
+ log.error("ZMR result contains NO 'Personensuchergebnis' or 'PersonErgebnisSatz' is empty");
+ throw new WorkflowException(PROCESS_KITT_IDENITIES_GET,
+ "Find NO data-set with already matchted eID during ZMR KITT process");
+
+ } else {
+ List<PersonErgebnisSatzType> personErgebnisSatz =
+ searchPersonResp.getPersonensuchergebnis().getPersonErgebnisSatz();
+ if (personErgebnisSatz.size() > 1) {
+ log.error("Find more than on person with aleady matched information.");
+ throw new WorkflowException(PROCESS_KITT_IDENITIES_GET,
+ "Find MORE-THAN-ONE data-sets with already matchted eID during ZMR KITT process");
+
+ } else {
+ return personErgebnisSatz.get(0).getPersonendaten().getPersonErgebnis().get(0);
+
+ }
+ }
+ }
+
+ private Collection<? extends EidasIdentitaetAnlageType> selectEidasDocumentsToAdd(
+ PersonErgebnisType zmrPersonToKitt, SimpleEidasData eidData) {
+
+ //TODO: maybe we should re-factor SimpleEidasData to a generic data-model to facilitate arbitrary eIDAS attributes
+ Set<EidasIdentitaetAnlageType> result = new HashSet<>();
+ addEidasDocumentIfNotAvailable(result, zmrPersonToKitt, eidData.getCitizenCountryCode(),
+ Constants.eIDAS_ATTRURN_PERSONALIDENTIFIER, eidData.getPseudonym(), true);
+ addEidasDocumentIfNotAvailable(result, zmrPersonToKitt, eidData.getCitizenCountryCode(),
+ Constants.eIDAS_ATTRURN_PLACEOFBIRTH, eidData.getPlaceOfBirth(), false);
+ addEidasDocumentIfNotAvailable(result, zmrPersonToKitt, eidData.getCitizenCountryCode(),
+ Constants.eIDAS_ATTRURN_BIRTHNAME, eidData.getBirthName(), false);
+
+ return result;
+
+ }
+
+ private void addEidasDocumentIfNotAvailable(Set<EidasIdentitaetAnlageType> result,
+ PersonErgebnisType zmrPersonToKitt, String citizenCountryCode,
+ String attrName, String attrValue, boolean allowMoreThanOneEntry) {
+
+ if (StringUtils.isEmpty(attrValue)) {
+ log.trace("No eIDAS document: {}. Nothing todo for KITT process ... ", attrName);
+ return;
+
+ }
+
+ // check if eIDAS attribute is already includes an eIDAS-Document
+ boolean alreadyExist = zmrPersonToKitt.getEidasIdentitaet().stream()
+ .filter(el -> el.getEidasWert().equals(attrValue)
+ && el.getEidasArt().equals(attrName)
+ && el.getStaatscode2().equals(citizenCountryCode))
+ .findAny()
+ .isPresent();
+
+ if (!alreadyExist) {
+ // check eIDAS documents already contains a document with this pair of country-code and attribute-name
+ Optional<EidasIdentitaetErgebnisType> oneDocWithNameExists = zmrPersonToKitt.getEidasIdentitaet().stream()
+ .filter(el -> el.getStaatscode2().equals(citizenCountryCode)
+ && el.getEidasArt().equals(attrName))
+ .findAny();
+
+ if (!allowMoreThanOneEntry && oneDocWithNameExists.isPresent()
+ && !oneDocWithNameExists.get().getEidasWert().equals(attrValue)) {
+ log.warn("eIDAS document: {} already exists for country: {} but attribute-value does not match. "
+ + "Skip update process because no multi-value allowed for this ... ",
+ attrName, citizenCountryCode);
+
+ } else {
+ EidasIdentitaetAnlageType eidasDocToAdd = new EidasIdentitaetAnlageType();
+ eidasDocToAdd.setStaatscode2(citizenCountryCode);
+ eidasDocToAdd.setEidasArt(attrName);
+ eidasDocToAdd.setEidasWert(attrValue);
+ log.info("Add eIDAS document: {} for country: {} to ZMR person", attrName, citizenCountryCode);
+ result.add(eidasDocToAdd);
+
+ }
+
+ } else {
+ log.debug("eIDAS document: {} already exists for country: {}. Skip update process for this ... ",
+ attrName, citizenCountryCode);
+
+ }
+ }
+
+ private ZmrRegisterResult updatePersonInZmr(PersonErgebnisType zmrPersonToKitt,
+ Collection<? extends EidasIdentitaetAnlageType> eidasDocumentToAdd, String citizenCountryCode)
+ throws ServiceFault {
+ final RequestType req = new RequestType();
+ PersonAendernRequest updateReq = new PersonAendernRequest();
+ req.setPersonAendernRequest(updateReq);
+
+ // set reference elements for person update
+ PersonReferenzType updateRef = new PersonReferenzType();
+ updateRef.setTechnisch(zmrPersonToKitt.getEntityErgebnisReferenz().getTechnisch());
+ updateRef.setZMRZahl(zmrPersonToKitt.getZMRZahl());
+ updateReq.setPersonReferenz(updateRef);
+
+ // set reason from this update
+ PersonAendernInfoType updateInfo = new PersonAendernInfoType();
+ updateInfo.setGrundCode(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_UPDATE_REASON_CODE));
+ updateInfo.setGrundFreitext(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_UPDATE_REASON_TEXT));
+ updateReq.setPersonAendernInfo(updateInfo);
+
+ // add new eIDAS documents that should be added
+ updateReq.getEidasIdentitaetAnlage().addAll(eidasDocumentToAdd);
+
+ // request ZMR
+ log.trace("Requesting ZMR for '{}' operation", PROCESS_KITT_IDENITIES_UPDATE);
+ ResponseType resp = zmrClient.service(req, null);
+ log.trace("Receive response from ZMR for '{}' operation", PROCESS_KITT_IDENITIES_UPDATE);
+
+ return new ZmrRegisterResult(Arrays.asList(
+ mapZmrResponseToRegisterResult(resp.getPersonAendernResponse().getPersonErgebnis(), citizenCountryCode)),
+ extractZmrProcessId(resp.getWorkflowInfoServer()));
+
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/config/EidasConnectorMessageSource.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/config/EidasConnectorMessageSource.java
new file mode 100644
index 00000000..7a9f472a
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/config/EidasConnectorMessageSource.java
@@ -0,0 +1,21 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.config;
+
+import java.util.Arrays;
+import java.util.List;
+
+import at.gv.egiz.eaaf.core.api.logging.IMessageSourceLocation;
+
+/**
+ * Inject eIDAS Connector specific messages into Spring based message-source.
+ *
+ * @author tlenz
+ *
+ */
+public class EidasConnectorMessageSource implements IMessageSourceLocation {
+
+ @Override
+ public List<String> getMessageSourceLocation() {
+ return Arrays.asList("classpath:/messages/eidas_connector_message");
+ }
+
+}
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
deleted file mode 100644
index 6c7eeb6b..00000000
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/ErnbEidData.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2018 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 java.text.SimpleDateFormat;
-
-import org.joda.time.DateTime;
-
-import at.gv.e_government.reference.namespace.persondata._20020228.PostalAddressType;
-
-public class ErnbEidData {
-
- private String citizenCountryCode = null;
-
- // MDS
- private String pseudonym = null;
- private String givenName = null;
- private String familyName = null;
- private DateTime dateOfBirth = null;
-
- // additional attributes
- private String placeOfBirth = null;
- private String birthName = null;
- private PostalAddressType address = null;
-
- public String getCitizenCountryCode() {
- return citizenCountryCode;
- }
-
- public void setCitizenCountryCode(String citizenCountryCode) {
- this.citizenCountryCode = citizenCountryCode;
- }
-
- public String getPseudonym() {
- return pseudonym;
- }
-
- public void setPseudonym(String pseudonym) {
- this.pseudonym = pseudonym;
- }
-
- public String getGivenName() {
- return givenName;
- }
-
- public void setGivenName(String givenName) {
- this.givenName = givenName;
- }
-
- public String getFamilyName() {
- return familyName;
- }
-
- public void setFamilyName(String familyName) {
- this.familyName = familyName;
- }
-
- public DateTime getDateOfBirth() {
- return dateOfBirth;
- }
-
- public void setDateOfBirth(DateTime dateOfBirth) {
- this.dateOfBirth = dateOfBirth;
- }
-
- public String getPlaceOfBirth() {
- return placeOfBirth;
- }
-
- public void setPlaceOfBirth(String placeOfBirth) {
- this.placeOfBirth = placeOfBirth;
- }
-
- public String getBirthName() {
- return birthName;
- }
-
- public void setBirthName(String birthName) {
- this.birthName = birthName;
- }
-
- public PostalAddressType getAddress() {
- return address;
- }
-
- public void setAddress(PostalAddressType address) {
- this.address = address;
- }
-
- public String getFormatedDateOfBirth() {
- return new SimpleDateFormat("yyyy-MM-dd").format(dateOfBirth.toDate());
- }
-
-}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MatchedPersonResult.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MatchedPersonResult.java
new file mode 100644
index 00000000..1e8fcecf
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MatchedPersonResult.java
@@ -0,0 +1,41 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.dao;
+
+import lombok.Builder;
+import lombok.Getter;
+
+/**
+ * Information about a natural person that is already matched.
+ *
+ * @author tlenz
+ *
+ */
+@Getter
+@Builder
+public class MatchedPersonResult {
+
+ /**
+ * Matched person result from matching result.
+ *
+ * @param matchingResult Result of the matching process
+ * @param citizenCountryCode Country-Code of the eIDAS Proxy-Service
+ */
+ public static MatchedPersonResult generateFormMatchingResult(RegisterResult matchingResult,
+ String citizenCountryCode) {
+ return MatchedPersonResult.builder()
+ .familyName(matchingResult.getFamilyName())
+ .givenName(matchingResult.getGivenName())
+ .dateOfBirth(matchingResult.getDateOfBirth())
+ .bpk(matchingResult.getBpk())
+ .countryCode(citizenCountryCode)
+ .build();
+ }
+
+ private final String countryCode;
+ private final String givenName;
+ private final String familyName;
+ private final String dateOfBirth;
+ private final String bpk;
+
+ private String vsz;
+
+}
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..aa82d806
--- /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,51 @@
+/*
+ * 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 java.util.List;
+
+import at.gv.e_government.reference.namespace.persondata._20020228.PostalAddressType;
+import lombok.Builder;
+import lombok.Getter;
+
+@Builder
+@Getter
+public class RegisterResult {
+
+ // MDS
+ private final List<String> pseudonym;
+ private final String givenName;
+ private final String familyName;
+ private final String dateOfBirth;
+
+ // additional attributes
+ private String placeOfBirth;
+ private String birthName;
+ private String taxNumber;
+ private PostalAddressType address;
+
+ private String bpk;
+
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SelectedLoginMethod.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SelectedLoginMethod.java
new file mode 100644
index 00000000..f8e2ff2e
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SelectedLoginMethod.java
@@ -0,0 +1,5 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.dao;
+
+public enum SelectedLoginMethod {
+ EIDAS_LOGIN, MOBILE_PHONE_SIGNATURE_LOGIN, NO_OTHER_LOGIN
+}
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..35f353f4
--- /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,99 @@
+/*
+ * 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 org.apache.commons.lang3.builder.EqualsBuilder;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException;
+import at.gv.e_government.reference.namespace.persondata._20020228.PostalAddressType;
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class SimpleEidasData {
+
+ /**
+ * Full eIDAS personal identifier with prefix.
+ */
+ private final String personalIdentifier;
+
+ /**
+ * Citizen country-code from eIDAS personal-identifier.
+ */
+ private final String citizenCountryCode;
+
+ // MDS
+ /**
+ * eIDAS personal identifier without prefix.
+ */
+ private final String pseudonym;
+ private final String givenName;
+ private final String familyName;
+ private final String dateOfBirth;
+
+ // additional attributes
+ private final String placeOfBirth;
+ private final String birthName;
+ private final PostalAddressType address;
+ private final 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(RegisterResult result) throws WorkflowException {
+ /*TODO: maybe this is check is not valid, because only the minimum data-set (personalIdentifer, givenName,
+ * familyName, dateOfBirth) has to be always available. Any other attributes are optional.
+ * This check will always evaluate to false if register has more information as current eIDAS process!!!
+ */
+
+ return new EqualsBuilder()
+ .append(result.getGivenName(), givenName)
+ .append(result.getFamilyName(), familyName)
+ .append(result.getDateOfBirth(), dateOfBirth)
+ .append(result.getPlaceOfBirth(), placeOfBirth)
+ .append(result.getBirthName(), birthName)
+ .append(result.getTaxNumber(), taxNumber)
+ .isEquals() && result.getPseudonym().stream()
+ .anyMatch(el -> el.equals(pseudonym));
+ }
+
+ /**
+ * Checks if the MDS (<code>givenName</code>, <code>familyName</code>,
+ * <code>dateOfBirth</code>) matches.
+ */
+ public boolean equalsMds(SimpleEidasData other) {
+ return new EqualsBuilder()
+ .append(other.givenName, givenName)
+ .append(other.familyName, familyName)
+ .append(other.dateOfBirth, dateOfBirth)
+ .isEquals();
+ }
+
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleMobileSignatureData.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleMobileSignatureData.java
new file mode 100644
index 00000000..92e727ea
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleMobileSignatureData.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.commons.lang3.builder.EqualsBuilder;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class SimpleMobileSignatureData {
+
+ private final String bpk;
+ private final String givenName;
+ private final String familyName;
+ private final String dateOfBirth;
+
+ /**
+ * Compares the received authentication data from the mobile phone signature with the eid data received via eIDAS.
+ *
+ * @param simpleEidasData The extracted eIDAS data
+ * @return Returns true, if the eIDAS data matches the mobile phone signature data and false otherwise.
+ */
+ public boolean equalsSimpleEidasData(SimpleEidasData simpleEidasData) {
+ return new EqualsBuilder()
+ .append(simpleEidasData.getGivenName(), givenName)
+ .append(simpleEidasData.getFamilyName(), familyName)
+ .append(simpleEidasData.getDateOfBirth(), dateOfBirth)
+ .isEquals();
+ }
+}
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..77f5e3cd
--- /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,74 @@
+/*
+ * 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 java.util.Collections;
+import java.util.List;
+
+import org.springframework.stereotype.Service;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+
+@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 RegisterResult update(RegisterResult registerResult, SimpleEidasData eidData) {
+ //TODO
+ return null;
+ }
+
+ @Override
+ public List<RegisterResult> searchWithBpkZp(String bpkzp) {
+ //TODO
+ return Collections.emptyList();
+ }
+
+
+}
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..b2a9005b
--- /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,46 @@
+/*
+ * 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 java.util.List;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+
+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);
+
+ RegisterResult update(RegisterResult registerResult, SimpleEidasData eidData);
+
+ List<RegisterResult> searchWithBpkZp(String bpkzp);
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/InvalidUserInputException.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/InvalidUserInputException.java
new file mode 100644
index 00000000..c7df56d0
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/InvalidUserInputException.java
@@ -0,0 +1,34 @@
+/*
+ * 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 InvalidUserInputException extends EidasSAuthenticationException {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidUserInputException(String errorCode) {
+ super(errorCode, null);
+
+ }
+
+}
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..cf69bd2c
--- /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,44 @@
+/*
+ * 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;
+
+ //TODO: should we pass some infos?
+ public ManualFixNecessaryException(String personIdentifier) {
+ super("module.eidasauth.matching.04", new Object[] { personIdentifier });
+ }
+
+ public ManualFixNecessaryException(SimpleEidasData eidData) {
+ super("module.eidasauth.matching.04", new Object[] { eidData.getPseudonym() });
+ }
+
+ public ManualFixNecessaryException(SimpleEidasData eidData, Throwable e) {
+ super("module.eidasauth.matching.04", new Object[] { eidData.getPseudonym() }, e);
+ }
+
+}
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..795b4386
--- /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,94 @@
+/*
+ * 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 lombok.Getter;
+
+@Getter
+public class WorkflowException extends EidasSAuthenticationException {
+ private static final long serialVersionUID = 1L;
+
+ private String processStepName;
+ private String errorReason;
+ private boolean requiresManualFix = false;
+
+ /**
+ * In case of a error during matching work-flow.
+ *
+ * @param processStep Matching step identifier
+ * @param errorReason Reason for this error
+ */
+ public WorkflowException(String processStep, String errorReason) {
+ super("module.eidasauth.matching.03", new Object[]{processStep, errorReason});
+ this.processStepName = processStep;
+ this.errorReason = errorReason;
+
+ }
+
+ /**
+ * In case of a error during matching work-flow.
+ *
+ * @param processStep Matching step identifier
+ * @param errorReason Reason for this error
+ * @param e Catched exception
+ */
+ public WorkflowException(String processStep, String errorReason, Throwable e) {
+ super("module.eidasauth.matching.03", new Object[]{processStep, errorReason}, e);
+ this.processStepName = processStep;
+ this.errorReason = errorReason;
+
+ }
+
+ /**
+ * In case of a error during matching work-flow.
+ *
+ * @param processStep Matching step identifier
+ * @param errorReason Reason for this error
+ * @param needsManualFix Mark this work-flow as manually fixable
+ */
+ public WorkflowException(String processStep, String errorReason, boolean needsManualFix) {
+ super("module.eidasauth.matching.03", new Object[]{processStep, errorReason});
+ this.processStepName = processStep;
+ this.errorReason = errorReason;
+ this.requiresManualFix = needsManualFix;
+
+ }
+
+ /**
+ * In case of a error during matching work-flow.
+ *
+ * @param processStep Matching step identifier
+ * @param errorReason Reason for this error
+ * @param needsManualFix Mark this work-flow as manually fixable
+ * @param e Catched exception
+ */
+ public WorkflowException(String processStep, String errorReason, boolean needsManualFix, Throwable e) {
+ super("module.eidasauth.matching.03", new Object[]{processStep, errorReason}, e);
+ this.processStepName = processStep;
+ this.errorReason = errorReason;
+ this.requiresManualFix = needsManualFix;
+
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ZmrCommunicationException.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ZmrCommunicationException.java
new file mode 100644
index 00000000..a6978458
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ZmrCommunicationException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 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 ZmrCommunicationException extends EidasSAuthenticationException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ZmrCommunicationException(String internalMsgId, Object[] params) {
+ super(internalMsgId, params);
+ }
+
+ public ZmrCommunicationException(String internalMsgId, Object[] params, Throwable e) {
+ super(internalMsgId, params, e);
+ }
+
+}
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 90be9a7a..64c6adeb 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,6 +23,11 @@
package at.asitplus.eidas.specific.modules.auth.eidas.v2.handler;
+
+
+import static at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils.processCountryCode;
+import static at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils.processDateOfBirthToString;
+
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -33,15 +38,13 @@ 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 org.springframework.lang.NonNull;
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.dao.SimpleEidasData;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry;
@@ -51,15 +54,14 @@ import at.gv.egiz.eaaf.core.api.IRequest;
import at.gv.egiz.eaaf.core.api.data.EaafConstants;
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 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 lombok.extern.slf4j.Slf4j;
+@Slf4j
public abstract class AbstractEidProcessor implements INationalEidProcessor {
- private static final Logger log = LoggerFactory.getLogger(AbstractEidProcessor.class);
@Autowired
protected EidasAttributeRegistry attrRegistry;
@@ -72,39 +74,40 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor {
buildLevelOfAssurance(pendingReq.getServiceProviderConfiguration(), authnRequestBuilder);
buildProviderNameAndRequesterIdAttribute(pendingReq, authnRequestBuilder);
buildRequestedAttributes(authnRequestBuilder);
-
}
@Override
- public final ErnbEidData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,
+ public final SimpleEidasData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,
EidasAttributeException {
- final ErnbEidData result = new ErnbEidData();
-
- final Object eIdentifierObj = eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER);
- final Triple<String, String, String> eIdentifier =
- EidasResponseUtils.parseEidasPersonalIdentifier((String) eIdentifierObj);
- result.setCitizenCountryCode(eIdentifier.getFirst());
-
- // MDS attributes
- result.setPseudonym(processPseudonym(eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)));
- result.setFamilyName(processFamilyName(eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTFAMILYNAME)));
- result.setGivenName(processGivenName(eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTGIVENNAME)));
- result.setDateOfBirth(processDateOfBirth(eidasAttrMap.get(Constants.eIDAS_ATTR_DATEOFBIRTH)));
-
- // additional attributes
- result.setPlaceOfBirth(processPlaceOfBirth(eidasAttrMap.get(Constants.eIDAS_ATTR_PLACEOFBIRTH)));
- result.setBirthName(processBirthName(eidasAttrMap.get(Constants.eIDAS_ATTR_BIRTHNAME)));
- result.setAddress(processAddress(eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTADDRESS)));
-
- return result;
-
+ SimpleEidasData.SimpleEidasDataBuilder builder = SimpleEidasData.builder()
+ .personalIdentifier(EidasResponseUtils.processPersonalIdentifier(
+ eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)))
+
+ // MDS attributes
+ .citizenCountryCode(processCountryCode(eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)))
+ .pseudonym(processPseudonym(eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)))
+ .familyName(processFamilyName(eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTFAMILYNAME)))
+ .givenName(processGivenName(eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTGIVENNAME)))
+ .dateOfBirth(processDateOfBirthToString(eidasAttrMap.get(Constants.eIDAS_ATTR_DATEOFBIRTH)))
+
+ // additional attributes
+ .placeOfBirth(processPlaceOfBirth(eidasAttrMap.get(Constants.eIDAS_ATTR_PLACEOFBIRTH)))
+ .birthName(processBirthName(eidasAttrMap.get(Constants.eIDAS_ATTR_BIRTHNAME)))
+ .address(processAddress(eidasAttrMap.get(Constants.eIDAS_ATTR_CURRENTADDRESS)));
+
+ if (eidasAttrMap.containsKey(Constants.eIDAS_ATTR_TAXREFERENCE)) {
+ builder.taxNumber(EidasResponseUtils.processTaxReference(eidasAttrMap.get(Constants.eIDAS_ATTR_TAXREFERENCE)));
+
+ }
+
+ return builder.build();
}
-
+
/**
* Get a Map of country-specific requested attributes.
- *
+ *
* @return
*/
@NonNull
@@ -112,7 +115,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
@@ -120,34 +123,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
@@ -155,27 +136,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
@@ -183,27 +149,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
@@ -211,17 +162,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
@@ -229,17 +175,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
@@ -247,17 +188,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
@@ -265,15 +201,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);
}
/**
@@ -389,7 +317,7 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor {
return builder.build();
}
-
+
private void buildLevelOfAssurance(ISpConfiguration spConfig, Builder authnRequestBuilder) {
// TODO: set matching mode if eIDAS ref. impl. support this method
@@ -412,7 +340,7 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor {
log.debug("Request eIdAS node with LoA: " + loa);
authnRequestBuilder.levelOfAssurance(loa);
-
+
}
}
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..c2a62f5c
--- /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,56 @@
+/*
+ * 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 javax.annotation.Nonnull;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;
+
+public interface CountrySpecificDetailSearchProcessor {
+
+ /**
+ * Get a friendlyName of this post-processor implementation.
+ */
+ String getName();
+
+ /**
+ * Check if this postProcessor is sensitive for a specific country.
+ *
+ * @param countryCode of the eID data that should be processed
+ * @param eidData eID information from eIDAS Proxy-Service
+ * @return true if this implementation can handle the country, otherwise false
+ */
+ boolean canHandle(String countryCode, SimpleEidasData eidData);
+
+ /**
+ * Builds a country-specific search person request for ZMR.
+ *
+ * @param eidData eID information from eIDAS Proxy-Service
+ * @return {@link PersonSuchenRequest} but never <code>null</code>
+ */
+ @Nonnull
+ PersonSuchenRequest generateSearchRequest(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..e05fe86b
--- /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,82 @@
+/*
+ * 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 org.apache.commons.lang3.StringUtils;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.EidasSuchdatenType;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;
+import at.gv.e_government.reference.namespace.persondata.de._20040201.NatuerlichePersonTyp;
+import at.gv.e_government.reference.namespace.persondata.de._20040201.PersonenNameTyp;
+
+public class DeSpecificDetailSearchProcessor implements CountrySpecificDetailSearchProcessor {
+
+ @Override
+ public String getName() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean canHandle(String countryCode, SimpleEidasData eidData) {
+ return countryCode.equalsIgnoreCase(Constants.COUNTRY_CODE_DE)
+ && StringUtils.isNotEmpty(eidData.getBirthName())
+ && StringUtils.isNotEmpty(eidData.getPlaceOfBirth());
+
+ }
+
+ @Override
+ public PersonSuchenRequest generateSearchRequest(SimpleEidasData eidData) {
+ PersonSuchenRequest req = new PersonSuchenRequest();
+
+ //set basic MDS information
+ final NatuerlichePersonTyp searchNatPerson = new NatuerlichePersonTyp();
+ req.setNatuerlichePerson(searchNatPerson);
+ final PersonenNameTyp searchNatPersonName = new PersonenNameTyp();
+ searchNatPerson.setPersonenName(searchNatPersonName);
+ searchNatPersonName.setFamilienname(eidData.getFamilyName());
+ searchNatPersonName.setVorname(eidData.getGivenName());
+ searchNatPerson.setGeburtsdatum(eidData.getDateOfBirth());
+
+ //add addtional eIDAS attributes from DE
+ req.getEidasSuchdaten().add(buildEidasSuchData(
+ Constants.eIDAS_ATTRURN_PLACEOFBIRTH, eidData.getPlaceOfBirth()));
+ req.getEidasSuchdaten().add(buildEidasSuchData(
+ Constants.eIDAS_ATTRURN_BIRTHNAME, eidData.getBirthName()));
+
+ return req;
+
+ }
+
+ private EidasSuchdatenType buildEidasSuchData(String attrName, String attrValue) {
+ EidasSuchdatenType eidasInfos = new EidasSuchdatenType();
+ eidasInfos.setStaatscode2(Constants.COUNTRY_CODE_DE);
+ eidasInfos.setEidasArt(attrName);
+ eidasInfos.setEidasWert(attrValue);
+ return eidasInfos;
+
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/INationalEidProcessor.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/INationalEidProcessor.java
index 577efbcd..79a261fe 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/INationalEidProcessor.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/INationalEidProcessor.java
@@ -25,7 +25,7 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.handler;
import java.util.Map;
-import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.ErnbEidData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException;
import at.gv.egiz.eaaf.core.api.IRequest;
@@ -36,7 +36,7 @@ public interface INationalEidProcessor {
/**
* Get a friendlyName of this post-processor implementation.
- *
+ *
* @return
*/
String getName();
@@ -46,34 +46,34 @@ public interface INationalEidProcessor {
* If more than one Post-Processor implementations can handle the eID data, the
* post-processor with the highest priority are selected. The Default-Processor
* has priority '0'
- *
+ *
* @return Priority of this handler
*/
int getPriority();
/**
* Check if this postProcessor is sensitive for a specific country.
- *
+ *
* @param countryCode of the eID data that should be processed
* @return true if this implementation can handle the country, otherwise false
- *
+ *
*/
boolean canHandle(String countryCode);
/**
* Post-Process eIDAS eID data into national format.
- *
+ *
* @param eidasAttrMap Map of eIDAS attributes in format friendlyName and
* attribute
* @throws EidPostProcessingException In case of a post-processing error
* @throws EidasAttributeException In case of an invalid eIDAS attribute
*/
- ErnbEidData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,
+ SimpleEidasData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,
EidasAttributeException;
/**
* Pre-Process eIDAS Request to national requirements.
- *
+ *
* @param pendingReq current pending request
* @param authnRequestBuilder eIDAS {@link ILightRequest} builder
*/
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..b49c355d
--- /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,53 @@
+/*
+ * 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 org.apache.commons.lang3.StringUtils;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;
+
+public class ItSpecificDetailSearchProcessor implements CountrySpecificDetailSearchProcessor {
+
+ @Override
+ public String getName() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean canHandle(String countryCode, SimpleEidasData eidData) {
+ return countryCode.equalsIgnoreCase(Constants.COUNTRY_CODE_IT)
+ && StringUtils.isNotEmpty(eidData.getTaxNumber());
+
+ }
+
+ @Override
+ public PersonSuchenRequest generateSearchRequest(SimpleEidasData eidData) {
+
+ //TODO: add IT specific search request if TaxNumber attribute is defined by IT
+ return new PersonSuchenRequest();
+
+ }
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthConstants.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthConstants.java
new file mode 100644
index 00000000..46d0e77d
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthConstants.java
@@ -0,0 +1,106 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import at.gv.egiz.eaaf.core.api.data.EaafConstants;
+import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions;
+import at.gv.egiz.eaaf.core.impl.data.Triple;
+
+
+public class IdAustriaClientAuthConstants {
+
+ private IdAustriaClientAuthConstants() {
+
+ }
+
+ public static final String ERRORCODE_02 = "module.eidasauth.idaustria.02";
+ public static final String ERRORCODE_05 = "module.eidasauth.idaustria.05";
+
+ public static final String SAML2_STATUSCODE_USERSTOP = "1005";
+
+ public static final String MODULE_NAME_FOR_LOGGING = "ID Austria Client";
+
+ public static final int METADATA_VALIDUNTIL_IN_HOURS = 24;
+
+ // public static final String HTTP_PARAM_CENTRAL_EIDAS_AUTH_SELECTION =
+ // AuthHandlerConstants.HTTP_PARAM_EIDAS_PROCESS;
+
+ public static final String ENDPOINT_POST = "/sp/idaustria/post";
+ public static final String ENDPOINT_REDIRECT = "/sp/idaustria/redirect";
+ public static final String ENDPOINT_METADATA = "/sp/idaustria/metadata";
+
+ public static final String CONFIG_PROPS_PREFIX = "modules.idaustriaclient.";
+ public static final String CONFIG_PROPS_KEYSTORE_TYPE = CONFIG_PROPS_PREFIX + "keystore.type";
+ public static final String CONFIG_PROPS_KEYSTORE_NAME = CONFIG_PROPS_PREFIX + "keystore.name";
+ public static final String CONFIG_PROPS_KEYSTORE_PATH = CONFIG_PROPS_PREFIX + "keystore.path";
+ public static final String CONFIG_PROPS_KEYSTORE_PASSWORD = CONFIG_PROPS_PREFIX + "keystore.password";
+ public static final String CONFIG_PROPS_SIGN_METADATA_KEY_PASSWORD = CONFIG_PROPS_PREFIX
+ + "metadata.sign.password";
+ public static final String CONFIG_PROPS_SIGN_METADATA_ALIAS = CONFIG_PROPS_PREFIX
+ + "metadata.sign.alias";
+ public static final String CONFIG_PROPS_SIGN_SIGNING_KEY_PASSWORD = CONFIG_PROPS_PREFIX
+ + "request.sign.password";
+ public static final String CONFIG_PROPS_SIGN_SIGNING_ALIAS = CONFIG_PROPS_PREFIX
+ + "request.sign.alias";
+ public static final String CONFIG_PROPS_ENCRYPTION_KEY_PASSWORD = CONFIG_PROPS_PREFIX
+ + "response.encryption.password";
+ public static final String CONFIG_PROPS_ENCRYPTION_ALIAS = CONFIG_PROPS_PREFIX
+ + "response.encryption.alias";
+
+ public static final String CONFIG_PROPS_TRUSTSTORE_TYPE = CONFIG_PROPS_PREFIX + "truststore.type";
+ public static final String CONFIG_PROPS_TRUSTSTORE_NAME = CONFIG_PROPS_PREFIX + "truststore.name";
+ public static final String CONFIG_PROPS_TRUSTSTORE_PATH = CONFIG_PROPS_PREFIX + "truststore.path";
+ public static final String CONFIG_PROPS_TRUSTSTORE_PASSWORD = CONFIG_PROPS_PREFIX + "truststore.password";
+
+ public static final String CONFIG_PROPS_REQUIRED_PVP_ATTRIBUTES_LIST = CONFIG_PROPS_PREFIX
+ + "required.additional.attributes";
+ public static final String CONFIG_PROPS_REQUIRED_LOA = CONFIG_PROPS_PREFIX
+ + "required.loa";
+ public static final String CONFIG_PROPS_ID_AUSTRIA_ENTITYID = CONFIG_PROPS_PREFIX + "idaustria.idp.entityId";
+ public static final String CONFIG_PROPS_ID_AUSTRIA_METADATAURL = CONFIG_PROPS_PREFIX + "idaustria.idp.metadataUrl";
+
+ public static final String CONFIG_DEFAULT_LOA_EIDAS_LEVEL = EaafConstants.EIDAS_LOA_HIGH;
+
+ public static final List<Triple<String, String, Boolean>> DEFAULT_REQUIRED_PVP_ATTRIBUTES =
+ Collections.unmodifiableList(new ArrayList<Triple<String, String, Boolean>>() {
+ private static final long serialVersionUID = 1L;
+
+ {
+ // add PVP Version attribute
+ add(Triple.newInstance(PvpAttributeDefinitions.PVP_VERSION_NAME,
+ PvpAttributeDefinitions.PVP_VERSION_FRIENDLY_NAME, true));
+
+ // entity metadata information
+ add(Triple.newInstance(PvpAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME,
+ PvpAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_FRIENDLY_NAME, true));
+ add(Triple.newInstance(PvpAttributeDefinitions.EID_ISSUING_NATION_NAME,
+ PvpAttributeDefinitions.EID_ISSUING_NATION_FRIENDLY_NAME, true));
+
+ add(Triple.newInstance(PvpAttributeDefinitions.PRINCIPAL_NAME_NAME,
+ PvpAttributeDefinitions.PRINCIPAL_NAME_FRIENDLY_NAME, true));
+ add(Triple.newInstance(PvpAttributeDefinitions.GIVEN_NAME_NAME,
+ PvpAttributeDefinitions.GIVEN_NAME_FRIENDLY_NAME, true));
+ add(Triple.newInstance(PvpAttributeDefinitions.BIRTHDATE_NAME,
+ PvpAttributeDefinitions.BIRTHDATE_FRIENDLY_NAME, true));
+ add(Triple.newInstance(PvpAttributeDefinitions.BPK_NAME,
+ PvpAttributeDefinitions.BPK_FRIENDLY_NAME, true));
+
+ }
+ });
+
+ public static final List<String> DEFAULT_REQUIRED_PVP_ATTRIBUTE_NAMES =
+
+ Collections.unmodifiableList(new ArrayList<String>() {
+ private static final long serialVersionUID = 1L;
+
+ {
+ for (final Triple<String, String, Boolean> el : DEFAULT_REQUIRED_PVP_ATTRIBUTES) {
+ add(el.getFirst());
+ }
+ }
+ });
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthEventConstants.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthEventConstants.java
new file mode 100644
index 00000000..03e570fc
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthEventConstants.java
@@ -0,0 +1,7 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient;
+
+public class IdAustriaClientAuthEventConstants {
+
+ public static final int AUTHPROCESS_ID_AUSTRIA_RESPONSE_RECEIVED = 6202;
+ public static final int AUTHPROCESS_ID_AUSTRIA_RESPONSE_RECEIVED_ERROR = 6203;
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthMetadataConfiguration.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthMetadataConfiguration.java
new file mode 100644
index 00000000..4527ced4
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthMetadataConfiguration.java
@@ -0,0 +1,463 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.NameIDType;
+import org.opensaml.saml.saml2.metadata.ContactPerson;
+import org.opensaml.saml.saml2.metadata.Organization;
+import org.opensaml.saml.saml2.metadata.RequestedAttribute;
+import org.opensaml.security.credential.Credential;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider.IdAustriaClientAuthCredentialProvider;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.data.Triple;
+import at.gv.egiz.eaaf.modules.pvp2.api.IPvp2BasicConfiguration;
+import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential;
+import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpMetadataBuilderConfiguration;
+import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException;
+import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PvpAttributeBuilder;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Configuration object to generate PVP S-Profile metadata for SAML2 client.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class IdAustriaClientAuthMetadataConfiguration implements IPvpMetadataBuilderConfiguration {
+
+ private Collection<RequestedAttribute> additionalAttributes = null;
+
+ private final String authUrl;
+ private final IdAustriaClientAuthCredentialProvider credentialProvider;
+ private final IPvp2BasicConfiguration pvpConfiguration;
+
+ /**
+ * Configuration object to create PVP2 S-Profile metadata information.
+ *
+ * @param authUrl Public URL prefix of the application
+ * @param credentialProvider Credentials used by PVP2 S-Profile end-point
+ * @param pvpConfiguration Basic PVP2 S-Profile end-point configuration
+ */
+ public IdAustriaClientAuthMetadataConfiguration(String authUrl,
+ IdAustriaClientAuthCredentialProvider credentialProvider,
+ IPvp2BasicConfiguration pvpConfiguration) {
+ this.authUrl = authUrl;
+ this.credentialProvider = credentialProvider;
+ this.pvpConfiguration = pvpConfiguration;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getMetadataValidUntil()
+ */
+ @Override
+ public int getMetadataValidUntil() {
+ return IdAustriaClientAuthConstants.METADATA_VALIDUNTIL_IN_HOURS;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * buildEntitiesDescriptorAsRootElement()
+ */
+ @Override
+ public boolean buildEntitiesDescriptorAsRootElement() {
+ return false;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * buildIDPSSODescriptor()
+ */
+ @Override
+ public boolean buildIdpSsoDescriptor() {
+ return false;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * buildSPSSODescriptor()
+ */
+ @Override
+ public boolean buildSpSsoDescriptor() {
+ return true;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getEntityIDPostfix()
+ */
+ @Override
+ public String getEntityID() {
+ return authUrl + IdAustriaClientAuthConstants.ENDPOINT_METADATA;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getEntityFriendlyName()
+ */
+ @Override
+ public String getEntityFriendlyName() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getContactPersonInformation()
+ */
+ @Override
+ public List<ContactPerson> getContactPersonInformation() {
+ try {
+ return pvpConfiguration.getIdpContacts();
+
+ } catch (final EaafException e) {
+ log.warn("Can not load Metadata entry: Contect Person", e);
+ return null;
+
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getOrgansiationInformation()
+ */
+ @Override
+ public Organization getOrgansiationInformation() {
+ try {
+ return pvpConfiguration.getIdpOrganisation();
+
+ } catch (final EaafException e) {
+ log.warn("Can not load Metadata entry: Organisation", e);
+ return null;
+
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getMetadataSigningCredentials()
+ */
+ @Override
+ public EaafX509Credential getMetadataSigningCredentials() throws CredentialsNotAvailableException {
+ return credentialProvider.getMetaDataSigningCredential();
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getRequestorResponseSigningCredentials()
+ */
+ @Override
+ public Credential getRequestorResponseSigningCredentials() throws CredentialsNotAvailableException {
+ return credentialProvider.getMessageSigningCredential();
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getEncryptionCredentials()
+ */
+ @Override
+ public Credential getEncryptionCredentials() throws CredentialsNotAvailableException {
+ return credentialProvider.getMessageEncryptionCredential();
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getIDPWebSSOPostBindingURL()
+ */
+ @Override
+ public String getIdpWebSsoPostBindingUrl() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getIDPWebSSORedirectBindingURL()
+ */
+ @Override
+ public String getIdpWebSsoRedirectBindingUrl() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getIDPSLOPostBindingURL()
+ */
+ @Override
+ public String getIdpSloPostBindingUrl() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getIDPSLORedirectBindingURL()
+ */
+ @Override
+ public String getIdpSloRedirectBindingUrl() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getSPAssertionConsumerServicePostBindingURL()
+ */
+ @Override
+ public String getSpAssertionConsumerServicePostBindingUrl() {
+ return authUrl + IdAustriaClientAuthConstants.ENDPOINT_POST;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getSPAssertionConsumerServiceRedirectBindingURL()
+ */
+ @Override
+ public String getSpAssertionConsumerServiceRedirectBindingUrl() {
+ return authUrl + IdAustriaClientAuthConstants.ENDPOINT_REDIRECT;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getSPSLOPostBindingURL()
+ */
+ @Override
+ public String getSpSloPostBindingUrl() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getSPSLORedirectBindingURL()
+ */
+ @Override
+ public String getSpSloRedirectBindingUrl() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getSPSLOSOAPBindingURL()
+ */
+ @Override
+ public String getSpSloSoapBindingUrl() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getIDPPossibleAttributes()
+ */
+ @Override
+ public List<Attribute> getIdpPossibleAttributes() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getIDPPossibleNameITTypes()
+ */
+ @Override
+ public List<String> getIdpPossibleNameIdTypes() {
+ return null;
+ }
+
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getSPRequiredAttributes()
+ */
+ @Override
+ public Collection<RequestedAttribute> getSpRequiredAttributes() {
+ final Map<String, RequestedAttribute> requestedAttributes = new HashMap<>();
+
+
+ log.trace("Build required attributes for ID Austria operaton ... ");
+ injectDefinedAttributes(requestedAttributes,
+ IdAustriaClientAuthConstants.DEFAULT_REQUIRED_PVP_ATTRIBUTES);
+
+
+
+ if (additionalAttributes != null) {
+ log.trace("Add additional PVP attributes into metadata ... ");
+ for (final RequestedAttribute el : additionalAttributes) {
+ if (requestedAttributes.containsKey(el.getName())) {
+ log.debug("Attribute " + el.getName()
+ + " is already added by default configuration. Overwrite it by user configuration");
+ }
+ requestedAttributes.put(el.getName(), el);
+ }
+ }
+
+ return requestedAttributes.values();
+
+ }
+
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.builder.AbstractPVPMetadataBuilder#
+ * getSPAllowedNameITTypes()
+ */
+ @Override
+ public List<String> getSpAllowedNameIdTypes() {
+ return Arrays.asList(NameIDType.PERSISTENT);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPMetadataBuilderConfiguration#getSPNameForLogging()
+ */
+ @Override
+ public String getSpNameForLogging() {
+ return IdAustriaClientAuthConstants.MODULE_NAME_FOR_LOGGING;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPMetadataBuilderConfiguration#wantAssertionSigned()
+ */
+ @Override
+ public boolean wantAssertionSigned() {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPMetadataBuilderConfiguration#wantAuthnRequestSigned()
+ */
+ @Override
+ public boolean wantAuthnRequestSigned() {
+ return true;
+ }
+
+ /**
+ * Add additonal PVP attributes that are required by this deployment.
+ *
+ * @param additionalAttr List of PVP attribute name and isRequired flag
+ */
+ public void setAdditionalRequiredAttributes(List<Pair<String, Boolean>> additionalAttr) {
+ if (additionalAttr != null && !additionalAttr.isEmpty()) {
+ additionalAttributes = new ArrayList<>();
+ for (final Pair<String, Boolean> el : additionalAttr) {
+ final Attribute attributBuilder = PvpAttributeBuilder.buildEmptyAttribute(el.getFirst());
+ if (attributBuilder != null) {
+ additionalAttributes.add(
+ PvpAttributeBuilder.buildReqAttribute(
+ attributBuilder.getName(),
+ attributBuilder.getFriendlyName(),
+ el.getSecond()));
+
+ } else {
+ log.info("NO PVP attribute with name: " + el.getFirst());
+ }
+
+ }
+ }
+ }
+
+ private void injectDefinedAttributes(Map<String, RequestedAttribute> requestedAttributes,
+ List<Triple<String, String, Boolean>> attributes) {
+ for (final Triple<String, String, Boolean> el : attributes) {
+ requestedAttributes.put(el.getFirst(), PvpAttributeBuilder.buildReqAttribute(el.getFirst(), el
+ .getSecond(), el.getThird()));
+
+ }
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthRequestBuilderConfiguration.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthRequestBuilderConfiguration.java
new file mode 100644
index 00000000..65b6a198
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/IdAustriaClientAuthRequestBuilderConfiguration.java
@@ -0,0 +1,300 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient;
+
+import java.util.List;
+
+import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential;
+import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EaafRequestedAttribute;
+import at.gv.egiz.eaaf.modules.pvp2.sp.api.IPvpAuthnRequestBuilderConfiguruation;
+
+import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
+import org.opensaml.saml.saml2.core.NameIDType;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.w3c.dom.Element;
+
+public class IdAustriaClientAuthRequestBuilderConfiguration implements IPvpAuthnRequestBuilderConfiguruation {
+
+ private boolean isPassive;
+ private String spEntityId;
+ private String qaaLevel;
+ private EntityDescriptor idpEntity;
+ private EaafX509Credential signCred;
+ private String scopeRequesterId;
+ private String providerName;
+ private List<EaafRequestedAttribute> requestedAttributes;
+ private String reqId;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#isPassivRequest()
+ */
+ @Override
+ public Boolean isPassivRequest() {
+ return this.isPassive;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getAssertionConsumerServiceId()
+ */
+ @Override
+ public Integer getAssertionConsumerServiceId() {
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getEntityID()
+ */
+ @Override
+ public String getSpEntityID() {
+ return this.spEntityId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getNameIDPolicy()
+ */
+ @Override
+ public String getNameIdPolicyFormat() {
+ return NameIDType.PERSISTENT;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getNameIDPolicy()
+ */
+ @Override
+ public boolean getNameIdPolicyAllowCreation() {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getAuthnContextClassRef()
+ */
+ @Override
+ public String getAuthnContextClassRef() {
+ return this.qaaLevel;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getAuthnContextComparison()
+ */
+ @Override
+ public AuthnContextComparisonTypeEnumeration getAuthnContextComparison() {
+ return AuthnContextComparisonTypeEnumeration.MINIMUM;
+ }
+
+ /**
+ * Set isPassive flag in SAML2 request.
+ *
+ * @param isPassive the isPassive to set.
+ */
+ public void setPassive(boolean isPassive) {
+ this.isPassive = isPassive;
+ }
+
+ /**
+ * Set the requester EntityId.
+ *
+ * @param spEntityId EntityId of SP
+ */
+ public void setSpEntityID(String spEntityId) {
+ this.spEntityId = spEntityId;
+ }
+
+ /**
+ * Set required LoA.
+ *
+ * @param loa the LoA to set.
+ */
+ public void setRequestedLoA(String loa) {
+ qaaLevel = loa;
+ }
+
+ /**
+ * Set EntityId of IDP.
+ *
+ * @param idpEntity the idpEntity to set.
+ */
+ public void setIdpEntity(EntityDescriptor idpEntity) {
+ this.idpEntity = idpEntity;
+ }
+
+ /**
+ * Set message signing credentials.
+ *
+ * @param signCred the signCred to set.
+ */
+ public void setSignCred(EaafX509Credential signCred) {
+ this.signCred = signCred;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getAuthnRequestSigningCredential()
+ */
+ @Override
+ public EaafX509Credential getAuthnRequestSigningCredential() {
+ return this.signCred;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getIDPEntityDescriptor()
+ */
+ @Override
+ public EntityDescriptor getIdpEntityDescriptor() {
+ return this.idpEntity;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getSubjectNameID()
+ */
+ @Override
+ public String getSubjectNameID() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getSPNameForLogging()
+ */
+ @Override
+ public String getSpNameForLogging() {
+ return IdAustriaClientAuthConstants.MODULE_NAME_FOR_LOGGING;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getSubjectNameIDFormat()
+ */
+ @Override
+ public String getSubjectNameIdFormat() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getRequestID()
+ */
+ @Override
+ public String getRequestID() {
+ return this.reqId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getSubjectNameIDQualifier()
+ */
+ @Override
+ public String getSubjectNameIdQualifier() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getSubjectConformationMethode()
+ */
+ @Override
+ public String getSubjectConformationMethode() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see at.gv.egovernment.moa.id.protocols.pvp2x.config.
+ * IPVPAuthnRequestBuilderConfiguruation#getSubjectConformationDate()
+ */
+ @Override
+ public Element getSubjectConformationDate() {
+ return null;
+ }
+
+ @Override
+ public List<EaafRequestedAttribute> getRequestedAttributes() {
+ return this.requestedAttributes;
+
+ }
+
+ @Override
+ public String getProviderName() {
+ return this.providerName;
+ }
+
+ @Override
+ public String getScopeRequesterId() {
+ return this.scopeRequesterId;
+ }
+
+ /**
+ * Set the entityId of the SP that requests the proxy for eIDAS authentication.
+ *
+ * @param scopeRequesterId RequestId in SAML2 Proxy extension
+ */
+ public void setScopeRequesterId(String scopeRequesterId) {
+ this.scopeRequesterId = scopeRequesterId;
+ }
+
+ /**
+ * Set a friendlyName for the SP that requests the proxy for eIDAS
+ * authentication.
+ *
+ * @param providerName SAML2 provider-name attribute-value
+ */
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+ /**
+ * Set a Set of PVP attributes that a requested by using requested attributes.
+ *
+ * @param requestedAttributes Requested SAML2 attributes
+ */
+ public void setRequestedAttributes(List<EaafRequestedAttribute> requestedAttributes) {
+ this.requestedAttributes = requestedAttributes;
+ }
+
+ /**
+ * Set a RequestId for this Authn. Request.
+ *
+ * @param reqId SAML2 message requestId
+ */
+ public void setRequestId(String reqId) {
+ this.reqId = reqId;
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/controller/IdAustriaClientAuthMetadataController.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/controller/IdAustriaClientAuthMetadataController.java
new file mode 100644
index 00000000..1e4b27f7
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/controller/IdAustriaClientAuthMetadataController.java
@@ -0,0 +1,122 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.controller;
+
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.google.common.net.MediaType;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthConstants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthMetadataConfiguration;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider.IdAustriaClientAuthCredentialProvider;
+import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.impl.http.HttpUtils;
+import at.gv.egiz.eaaf.core.impl.idp.controller.AbstractController;
+import at.gv.egiz.eaaf.modules.pvp2.api.IPvp2BasicConfiguration;
+import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PvpMetadataBuilder;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Controller that generates SAML2 metadata for eIDAS authentication client.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+@Controller
+public class IdAustriaClientAuthMetadataController extends AbstractController {
+
+ private static final String ERROR_CODE_INTERNAL_00 = "eaaf.core.00";
+
+ @Autowired
+ PvpMetadataBuilder metadatabuilder;
+ @Autowired
+ IdAustriaClientAuthCredentialProvider credentialProvider;
+ @Autowired
+ IPvp2BasicConfiguration pvpConfiguration;
+
+ /**
+ * Default construction with logging.
+ *
+ */
+ public IdAustriaClientAuthMetadataController() {
+ super();
+ log.debug("Registering servlet " + getClass().getName()
+ + " with mappings '" + IdAustriaClientAuthConstants.ENDPOINT_METADATA
+ + "'.");
+
+ }
+
+ /**
+ * End-point that produce PVP2 metadata for eIDAS authentication client.
+ *
+ * @param req http Request
+ * @param resp http Response
+ * @throws IOException In case of an I/O error
+ * @throws EaafException In case of a metadata generation error
+ */
+ @RequestMapping(value = IdAustriaClientAuthConstants.ENDPOINT_METADATA,
+ method = { RequestMethod.GET })
+ public void getSpMetadata(HttpServletRequest req, HttpServletResponse resp) throws IOException,
+ EaafException {
+ // check PublicURL prefix
+ try {
+ final String authUrl = getAuthUrlFromHttpContext(req);
+
+ // initialize metadata builder configuration
+ final IdAustriaClientAuthMetadataConfiguration metadataConfig =
+ new IdAustriaClientAuthMetadataConfiguration(authUrl, credentialProvider, pvpConfiguration);
+ // metadataConfig.setAdditionalRequiredAttributes(getAdditionalRequiredAttributes());
+
+ // build metadata
+ final String xmlMetadata = metadatabuilder.buildPvpMetadata(metadataConfig);
+
+ // write response
+ final byte[] content = xmlMetadata.getBytes("UTF-8");
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentLength(content.length);
+ resp.setContentType(MediaType.XML_UTF_8.toString());
+ resp.getOutputStream().write(content);
+
+ } catch (final Exception e) {
+ log.warn("Build federated-authentication PVP metadata FAILED.", e);
+ protAuthService.handleErrorNoRedirect(e, req, resp, false);
+
+ }
+
+ }
+
+ private String getAuthUrlFromHttpContext(HttpServletRequest req) throws EaafException {
+ // check if End-Point is valid
+ final String authUrlString = HttpUtils.extractAuthUrlFromRequest(req);
+ URL authReqUrl;
+ try {
+ authReqUrl = new URL(authUrlString);
+
+ } catch (final MalformedURLException e) {
+ log.warn("Requested URL: {} is not a valid URL.", authUrlString);
+ throw new EaafAuthenticationException(ERROR_CODE_INTERNAL_00, new Object[] { authUrlString }, e);
+
+ }
+
+ final String idpAuthUrl = authConfig.validateIdpUrl(authReqUrl);
+ if (idpAuthUrl == null) {
+ log.warn("Requested URL: {} is NOT found in configuration.", authReqUrl);
+ throw new EaafAuthenticationException(ERROR_CODE_INTERNAL_00, new Object[] { authUrlString });
+
+ }
+
+ return idpAuthUrl;
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/controller/IdAustriaClientAuthSignalController.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/controller/IdAustriaClientAuthSignalController.java
new file mode 100644
index 00000000..eca21683
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/controller/IdAustriaClientAuthSignalController.java
@@ -0,0 +1,95 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.controller;
+
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringEscapeUtils;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthConstants;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.impl.idp.controller.AbstractProcessEngineSignalController;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * IdAustria client controller that receives the response from ID Austria system.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+@Controller
+public class IdAustriaClientAuthSignalController extends AbstractProcessEngineSignalController {
+
+ public static final String HTTP_PARAM_RELAYSTATE = "RelayState";
+
+ /**
+ * Default constructor with logging.
+ *
+ */
+ public IdAustriaClientAuthSignalController() {
+ super();
+ log.debug("Registering servlet " + getClass().getName()
+ + " with mappings '" + IdAustriaClientAuthConstants.ENDPOINT_POST
+ + "' and '" + IdAustriaClientAuthConstants.ENDPOINT_REDIRECT + "'.");
+
+ }
+
+ /**
+ * HTTP end-point for incoming SAML2 Response from ID Austria system.
+ *
+ * @param req HTTP request
+ * @param resp HTTP response
+ * @throws IOException In case of a HTTP communication error
+ * @throws EaafException In case of a state-validation problem
+ */
+ @RequestMapping(value = { IdAustriaClientAuthConstants.ENDPOINT_POST,
+ IdAustriaClientAuthConstants.ENDPOINT_REDIRECT },
+ method = { RequestMethod.POST, RequestMethod.GET })
+ public void performAuthentication(HttpServletRequest req, HttpServletResponse resp)
+ throws IOException, EaafException {
+ signalProcessManagement(req, resp);
+
+ }
+
+ /**
+ * Read the PendingRequestId from SAML2 RelayState parameter.
+ */
+ @Override
+ public String getPendingRequestId(HttpServletRequest request) {
+ String relayState = StringEscapeUtils.escapeHtml4(request.getParameter(HTTP_PARAM_RELAYSTATE));
+ if (StringUtils.isNotEmpty(relayState)) {
+ try {
+ String pendingReqId = transactionStorage.get(relayState, String.class);
+ if (StringUtils.isNotEmpty(pendingReqId)) {
+
+ return pendingReqId;
+
+ } else {
+ log.info("SAML2 RelayState from request is unknown. Can NOT restore session ... ");
+
+ }
+
+ } catch (EaafException e) {
+ log.error("Can NOT map SAML2 RelayState to pendingRequestId", e);
+
+ } finally {
+ transactionStorage.remove(relayState);
+
+ }
+
+ } else {
+ log.info("No SAML2 relaystate. Can NOT restore session ... ");
+
+ }
+
+ return null;
+
+ }
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthCredentialProvider.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthCredentialProvider.java
new file mode 100644
index 00000000..5b6085c1
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthCredentialProvider.java
@@ -0,0 +1,132 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthConstants;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration;
+import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType;
+import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider;
+
+/**
+ * Credential provider for eIDAS PVP S-Profile client.
+ *
+ * @author tlenz
+ *
+ */
+public class IdAustriaClientAuthCredentialProvider extends AbstractCredentialProvider {
+
+ @Autowired
+ IConfiguration authConfig;
+
+ private static final String FRIENDLYNAME = "ID Austria authentication";
+
+ @Override
+ public KeyStoreConfiguration getBasicKeyStoreConfig() throws EaafConfigurationException {
+ final KeyStoreConfiguration keyStoreConfig = new KeyStoreConfiguration();
+ keyStoreConfig.setFriendlyName(FRIENDLYNAME);
+ keyStoreConfig.setKeyStoreType(
+ authConfig.getBasicConfiguration(IdAustriaClientAuthConstants.CONFIG_PROPS_KEYSTORE_TYPE,
+ KeyStoreType.PKCS12.getKeyStoreType()));
+ keyStoreConfig.setKeyStoreName(
+ authConfig.getBasicConfiguration(IdAustriaClientAuthConstants.CONFIG_PROPS_KEYSTORE_NAME));
+ keyStoreConfig.setSoftKeyStoreFilePath(getKeyStoreFilePath());
+ keyStoreConfig.setSoftKeyStorePassword(
+ authConfig.getBasicConfiguration(IdAustriaClientAuthConstants.CONFIG_PROPS_KEYSTORE_PASSWORD));
+
+ return keyStoreConfig;
+
+ }
+
+ private String getKeyStoreFilePath() throws EaafConfigurationException {
+ final String path = authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_KEYSTORE_PATH);
+ if (path == null) {
+ throw new EaafConfigurationException(Constants.ERRORCODE_00,
+ new Object[] { IdAustriaClientAuthConstants.CONFIG_PROPS_KEYSTORE_PATH });
+
+ }
+ return path;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.signer.AbstractCredentialProvider#
+ * getMetadataKeyAlias()
+ */
+ @Override
+ public String getMetadataKeyAlias() {
+ return authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_SIGN_METADATA_ALIAS);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.signer.AbstractCredentialProvider#
+ * getMetadataKeyPassword()
+ */
+ @Override
+ public String getMetadataKeyPassword() {
+ return authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_SIGN_METADATA_KEY_PASSWORD);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.signer.AbstractCredentialProvider#
+ * getSignatureKeyAlias()
+ */
+ @Override
+ public String getSignatureKeyAlias() {
+ return authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_SIGN_SIGNING_ALIAS);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.signer.AbstractCredentialProvider#
+ * getSignatureKeyPassword()
+ */
+ @Override
+ public String getSignatureKeyPassword() {
+ return authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_SIGN_SIGNING_KEY_PASSWORD);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.signer.AbstractCredentialProvider#
+ * getEncryptionKeyAlias()
+ */
+ @Override
+ public String getEncryptionKeyAlias() {
+ return authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_ENCRYPTION_ALIAS);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.protocols.pvp2x.signer.AbstractCredentialProvider#
+ * getEncryptionKeyPassword()
+ */
+ @Override
+ public String getEncryptionKeyPassword() {
+ return authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_ENCRYPTION_KEY_PASSWORD);
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthHealthCheck.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthHealthCheck.java
new file mode 100644
index 00000000..b54b501d
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthHealthCheck.java
@@ -0,0 +1,56 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider;
+
+import org.apache.commons.lang3.StringUtils;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.health.Health;
+import org.springframework.boot.actuate.health.HealthIndicator;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthConstants;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import lombok.extern.slf4j.Slf4j;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * Spring Actuator HealthCheck for ID Austria client that evaluates the current status of
+ * ID Austria SAML2 metadata that are loaded into MS-Connector.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class IdAustriaClientAuthHealthCheck implements HealthIndicator {
+
+ @Autowired IConfiguration authConfig;
+ @Autowired IdAustriaClientAuthMetadataProvider metadataService;
+
+ @Override
+ public Health health() {
+ String msNodeEntityID = authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_ID_AUSTRIA_ENTITYID);
+
+ if (StringUtils.isEmpty(msNodeEntityID)) {
+ log.trace("No ID Austria EntityId in configuration. Skipping tests ... ");
+ return Health.unknown().build();
+
+ }
+
+ try {
+ EntityDescriptor connectorMetadata =
+ metadataService.getEntityDescriptor(msNodeEntityID);
+ if (connectorMetadata != null) {
+ return Health.up().build();
+
+ } else {
+ return Health.outOfService().withDetail("Reason", "No SAML2 metadata").build();
+
+ }
+
+ } catch (ResolverException e) {
+ return Health.down(e).build();
+
+ }
+
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthMetadataProvider.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthMetadataProvider.java
new file mode 100644
index 00000000..7c87548f
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/idaustriaclient/provider/IdAustriaClientAuthMetadataProvider.java
@@ -0,0 +1,169 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider;
+
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.cert.CertificateException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.StringUtils;
+import org.opensaml.saml.metadata.resolver.MetadataResolver;
+import org.opensaml.saml.metadata.resolver.filter.MetadataFilter;
+import org.opensaml.saml.metadata.resolver.filter.MetadataFilterChain;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthConstants;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory;
+import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration;
+import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.http.IHttpClientFactory;
+import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException;
+import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.AbstractChainingMetadataProvider;
+import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.PvpMetadataResolverFactory;
+import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.SchemaValidationFilter;
+import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.SimpleMetadataSignatureVerificationFilter;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * SAML2 metadata-provider implementation for ID Austria client.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class IdAustriaClientAuthMetadataProvider extends AbstractChainingMetadataProvider {
+
+ private static final String FRIENDLYNAME_METADATA_TRUSTSTORE = "'ID Austria client metadata truststore'";
+ private static final String PROVIDER_ID_PATTERN = "eIDAS resolver: {0}";
+ public static final String PROVIDER_ID = "'ID Austria client metadata-provider'";
+
+ @Autowired
+ private IConfiguration basicConfig;
+
+ @Autowired
+ private PvpMetadataResolverFactory metadataProviderFactory;
+ @Autowired
+ private IHttpClientFactory httpClientFactory;
+
+ @Autowired
+ private EaafKeyStoreFactory keyStoreFactory;
+
+ private Pair<KeyStore, Provider> metadataSigningTrustStore;
+
+ @Override
+ protected String getMetadataUrl(String entityId) throws EaafConfigurationException {
+ log.trace("ID Austria. uses SAML2 well-known location approach. EntityId is Metadata-URL");
+ return entityId;
+
+ }
+
+ @Override
+ protected MetadataResolver createNewMetadataProvider(String entityId) throws EaafConfigurationException,
+ IOException, CertificateException {
+ final List<MetadataFilter> filterList = new ArrayList<>();
+ filterList.add(new SchemaValidationFilter(true));
+ filterList.add(new SimpleMetadataSignatureVerificationFilter(
+ metadataSigningTrustStore.getFirst(), entityId));
+
+ final MetadataFilterChain filter = new MetadataFilterChain();
+ filter.setFilters(filterList);
+
+ try {
+ return metadataProviderFactory.createMetadataProvider(getMetadataUrl(entityId),
+ filter,
+ MessageFormat.format(PROVIDER_ID_PATTERN, entityId),
+ httpClientFactory.getHttpClient());
+
+ } catch (final Pvp2MetadataException e) {
+ log.info("Can NOT build metadata provider for entityId: {}", entityId);
+ throw new EaafConfigurationException(IdAustriaClientAuthConstants.ERRORCODE_05,
+ new Object[] { entityId, e.getMessage() }, e);
+
+ }
+ }
+
+ @Override
+ protected List<String> getAllMetadataUrlsFromConfiguration() throws EaafConfigurationException {
+ return Collections.emptyList();
+
+ }
+
+ @Override
+ protected String getMetadataProviderId() {
+ return PROVIDER_ID;
+
+ }
+
+ @Override
+ public void runGarbageCollector() {
+ log.trace("Garbage collection is NOT supported by: {}", getId());
+ }
+
+ @Override
+ public void doDestroy() {
+ super.fullyDestroy();
+
+ }
+
+ @PostConstruct
+ private void initialize() throws EaafException {
+ // initialize truststore to validate metadata signing certificates
+ initializeTrustStore();
+
+ // load metadata with metadataURL, as backup
+ initializeFileSystemMetadata();
+
+ }
+
+ private void initializeFileSystemMetadata() {
+ try {
+ final String metadataUrl = basicConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_ID_AUSTRIA_METADATAURL);
+ if (StringUtils.isNotEmpty(metadataUrl)) {
+ log.info("Use not recommended metadata-provider initialization!"
+ + " SAML2 'Well-Known-Location' is the preferred methode.");
+ log.info("Initialize 'ms-specific eIDAS node' metadata-provider with URL: {}", metadataUrl);
+
+ addMetadataResolverIntoChain(createNewMetadataProvider(metadataUrl));
+ }
+
+ } catch (final EaafConfigurationException | CertificateException | IOException e) {
+ log.warn("Can NOT inject static eIDAS Node metadata-soure.", e);
+ log.warn("eIDAS Node communication can be FAIL.");
+
+ }
+ }
+
+ private void initializeTrustStore() throws EaafException {
+ // set configuration
+ final KeyStoreConfiguration trustStoreConfig = new KeyStoreConfiguration();
+ trustStoreConfig.setFriendlyName(FRIENDLYNAME_METADATA_TRUSTSTORE);
+ trustStoreConfig.setKeyStoreType(basicConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_TRUSTSTORE_TYPE,
+ KeyStoreType.JKS.getKeyStoreType()));
+ trustStoreConfig.setKeyStoreName(basicConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_TRUSTSTORE_NAME));
+ trustStoreConfig.setSoftKeyStoreFilePath(basicConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_TRUSTSTORE_PATH));
+ trustStoreConfig.setSoftKeyStorePassword(basicConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_TRUSTSTORE_PASSWORD));
+
+ // validate configuration
+ trustStoreConfig.validate();
+
+ // open new TrustStore
+ metadataSigningTrustStore = keyStoreFactory.buildNewKeyStore(trustStoreConfig);
+
+ }
+
+}
+
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/CcSpecificEidProcessingService.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/CcSpecificEidProcessingService.java
index 230d6052..b5493edb 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/CcSpecificEidProcessingService.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/CcSpecificEidProcessingService.java
@@ -32,6 +32,7 @@ import java.util.Map.Entry;
import javax.annotation.PostConstruct;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +41,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
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;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.INationalEidProcessor;
@@ -104,7 +104,7 @@ public class CcSpecificEidProcessingService implements ICcSpecificEidProcessingS
}
@Override
- public ErnbEidData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,
+ public SimpleEidasData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,
EidasAttributeException {
// extract citizen country from eIDAS unique identifier
final Object eIdentifierObj = eidasAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER);
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/ICcSpecificEidProcessingService.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/ICcSpecificEidProcessingService.java
index ebbc15e4..fb9ba318 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/ICcSpecificEidProcessingService.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/ICcSpecificEidProcessingService.java
@@ -25,7 +25,7 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.service;
import java.util.Map;
-import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.ErnbEidData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException;
import at.gv.egiz.eaaf.core.api.IRequest;
@@ -36,20 +36,20 @@ public interface ICcSpecificEidProcessingService {
/**
* Post-process eIDAS eID attributes into national format.
- *
+ *
* @param eidasAttrMap Map of eIDAS attributes in format friendlyName and
* attribute
- *
+ *
* @return eID attributes for SZR request
* @throws EidPostProcessingException In case of a post-processing error
* @throws EidasAttributeException In case of an invalid eIDAS attribute value
*/
- ErnbEidData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,
+ SimpleEidasData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,
EidasAttributeException;
/**
* Pre Process eIDAS request into national requirements.
- *
+ *
* @param selectedCC Citizen Country from selection
* @param pendingReq current pending request
* @param authnRequestBuilder eIDAS {@link ILightRequest} builder
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/RegisterSearchService.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/RegisterSearchService.java
new file mode 100644
index 00000000..232b1d11
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/RegisterSearchService.java
@@ -0,0 +1,339 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.service;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.zmr.IZmrClient;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.zmr.ZmrSoapClient.ZmrRegisterResult;
+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.EidasSAuthenticationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.ZmrCommunicationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.CountrySpecificDetailSearchProcessor;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.ListUtils;
+import org.jetbrains.annotations.Nullable;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Nonnull;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@Slf4j
+@Service("registerSearchService")
+public class RegisterSearchService {
+
+ private final IZmrClient zmrClient;
+ private final IErnpClient ernpClient;
+
+ private final List<CountrySpecificDetailSearchProcessor> handlers;
+
+ /**
+ * Service that combines ZMR and ERnP register search operations.
+ *
+ * @param handlers Available country-specific search processors
+ * @param zmrClient ZMR client
+ * @param ernpClient ERnP client
+ */
+ public RegisterSearchService(List<CountrySpecificDetailSearchProcessor> handlers, IZmrClient zmrClient,
+ IErnpClient ernpClient) {
+ this.zmrClient = zmrClient;
+ this.ernpClient = ernpClient;
+ this.handlers = handlers;
+ log.info("Init with #{} search services for country-specific details", handlers.size());
+
+ }
+
+ /**
+ * Search with Person Identifier (eIDAS Pseudonym) in ZMR and ERnP.
+ *
+ * @param eidasData Received eIDAS data
+ * @throws WorkflowException In case of a register interaction error
+ */
+ @Nonnull
+ public RegisterStatusResults searchWithPersonIdentifier(SimpleEidasData eidasData)
+ throws WorkflowException {
+ try {
+ final ZmrRegisterResult resultsZmr = zmrClient.searchWithPersonIdentifier(
+ null, eidasData.getPseudonym(), eidasData.getCitizenCountryCode());
+ final List<RegisterResult> resultsErnp = ernpClient.searchWithPersonIdentifier(
+ eidasData.getPersonalIdentifier());
+
+ return RegisterStatusResults.fromZmrAndErnp(resultsZmr, resultsErnp);
+
+ } catch (final EidasSAuthenticationException e) {
+ throw new WorkflowException("searchWithPersonalIdentifier", e.getMessage(),
+ !(e instanceof ZmrCommunicationException), e);
+
+ }
+ }
+
+ /**
+ * Search with MDS (Given Name, Family Name, Date of Birth) in ZMR and ERnP.
+ *
+ * @param operationStatus Current register-operation status that contains processing informations
+ * @param eidasData Received eIDAS data
+ * @throws WorkflowException In case of a register interaction error
+ */
+ @Nonnull
+ public RegisterStatusResults searchWithMds(RegisterOperationStatus operationStatus, SimpleEidasData eidasData)
+ throws WorkflowException {
+ try {
+ final ZmrRegisterResult resultsZmr =
+ zmrClient.searchWithMds(operationStatus.getZmrProcessId(), eidasData.getGivenName(),
+ eidasData.getFamilyName(), eidasData.getDateOfBirth(), eidasData.getCitizenCountryCode());
+
+ final List<RegisterResult> resultsErnp =
+ ernpClient.searchWithMds(eidasData.getGivenName(), eidasData.getFamilyName(), eidasData
+ .getDateOfBirth());
+
+ return RegisterStatusResults.fromZmrAndErnp(resultsZmr, resultsErnp);
+
+ } catch (final EidasSAuthenticationException e) {
+ throw new WorkflowException("searchWithMDSOnly", e.getMessage(),
+ !(e instanceof ZmrCommunicationException), e);
+
+ }
+ }
+
+ /**
+ * Search with country-specific parameters based on information from available
+ * {@link CountrySpecificDetailSearchProcessor} implementations.
+ *
+ * @param operationStatus Current register-operation status that contains processing informations
+ * @param eidasData Receive eIDAS eID information
+ * @return Results from ZMR or ERnP search
+ * @throws WorkflowException In case of a register interaction error
+ */
+ @Nonnull
+ public RegisterStatusResults searchWithCountrySpecifics(RegisterOperationStatus operationStatus,
+ SimpleEidasData eidasData) throws WorkflowException {
+ try {
+ @Nullable final CountrySpecificDetailSearchProcessor ccSpecificProcessor = findSpecificProcessor(eidasData);
+ if (ccSpecificProcessor != null) {
+ log.debug("Selecting country-specific search processor: {}", ccSpecificProcessor.getName());
+ final ZmrRegisterResult resultsZmr =
+ zmrClient.searchCountrySpecific(operationStatus.getZmrProcessId(),
+ ccSpecificProcessor.generateSearchRequest(eidasData),
+ eidasData.getCitizenCountryCode());
+ return RegisterStatusResults.fromZmr(resultsZmr);
+
+ } else {
+ // TODO: add search procesfor for ERnP searching
+ return RegisterStatusResults.fromErnp(operationStatus, Collections.emptyList());
+
+ }
+
+ } catch (final EidasSAuthenticationException e) {
+ throw new WorkflowException("searchWithCountrySpecifics", e.getMessage(),
+ !(e instanceof ZmrCommunicationException), e);
+
+ }
+ }
+
+ /**
+ * Search with residence infos.
+ *
+ * @param operationStatus Current register-operation status that contains processing informations
+ * @param zipcode Provided Zipcode
+ * @param city Provided City
+ * @param street Provided street
+ * @return Results from ZMR or ERnP search
+ */
+ public RegisterStatusResults searchWithResidence(RegisterOperationStatus operationStatus, SimpleEidasData eidasData,
+ String zipcode, String city, String street) {
+ final ZmrRegisterResult resultsZmr = zmrClient.searchWithResidenceData(
+ operationStatus.getZmrProcessId(), eidasData.getGivenName(), eidasData.getFamilyName(),
+ eidasData.getDateOfBirth(), zipcode, city, street);
+ return RegisterStatusResults.fromZmr(resultsZmr);
+
+ }
+
+ /**
+ * Automatic process to fix the register entries.
+ * Called when the initial eIDAS authn leads to a match in a register.
+ *
+ * @param registerResult Result of last register search
+ * @param initialEidasData Received eidas data from initial authn
+ * @return
+ */
+ public RegisterStatusResults step7aKittProcess(RegisterStatusResults registerResult,
+ SimpleEidasData initialEidasData) throws WorkflowException {
+ log.trace("Starting step7aKittProcess");
+ // TODO verify with which data this method gets called
+ if (registerResult.getResultCount() != 1) {
+ throw new WorkflowException("step7aKittProcess", "getResultCount() != 1");
+ }
+ try {
+ if (registerResult.getResultsZmr().size() == 1) {
+ RegisterResult entryZmr = registerResult.getResultsZmr().get(0);
+ ZmrRegisterResult updateZmr = zmrClient
+ .update(registerResult.getOperationStatus().getZmrProcessId(), entryZmr, initialEidasData);
+ return RegisterStatusResults.fromZmr(updateZmr);
+ } else {
+ RegisterResult entryErnp = registerResult.getResultsErnp().get(0);
+ RegisterResult updateErnp = ernpClient.update(entryErnp, initialEidasData);
+ return RegisterStatusResults.fromErnp(registerResult.operationStatus, Collections.singletonList(updateErnp));
+ }
+ } catch (final EidasSAuthenticationException e) {
+ throw new WorkflowException("kittMatchedIdentitiess", e.getMessage(),
+ !(e instanceof ZmrCommunicationException), e);
+ }
+ }
+
+ /**
+ * Automatic process to fix the register entries.
+ * Called when the alternative eIDAS authn leads to a match in a register.
+ *
+ * @param registerResult Result of last register search
+ * @param initialEidasData Received eidas data from initial authentication
+ * @param altEidasData Received eidas data from alternative authentication
+ * @return
+ */
+ public RegisterStatusResults step7bKittProcess(RegisterStatusResults registerResult,
+ SimpleEidasData initialEidasData, SimpleEidasData altEidasData)
+ throws WorkflowException {
+ log.trace("Starting step7bKittProcess");
+ // TODO verify with which data this method gets called
+ if (registerResult.getResultCount() != 1) {
+ throw new WorkflowException("step7bKittProcess", "getResultCount() != 1");
+ }
+ try {
+ if (registerResult.getResultsZmr().size() == 1) {
+ RegisterResult entryZmr = registerResult.getResultsZmr().get(0);
+ ZmrRegisterResult updateAlt = zmrClient
+ .update(registerResult.getOperationStatus().getZmrProcessId(), entryZmr, altEidasData);
+ ZmrRegisterResult updateInitial = zmrClient
+ .update(registerResult.getOperationStatus().getZmrProcessId(), entryZmr, initialEidasData);
+ return new RegisterStatusResults(registerResult.getOperationStatus(),
+ ListUtils.union(updateAlt.getPersonResult(), updateInitial.getPersonResult()),
+ Collections.emptyList());
+ } else {
+ RegisterResult entryErnp = registerResult.getResultsErnp().get(0);
+ RegisterResult updateAlt = ernpClient.update(entryErnp, altEidasData);
+ RegisterResult updateInitial = ernpClient.update(entryErnp, initialEidasData);
+ return new RegisterStatusResults(registerResult.getOperationStatus(), Collections.emptyList(),
+ Arrays.asList(updateAlt, updateInitial));
+ }
+ } catch (final EidasSAuthenticationException e) {
+ throw new WorkflowException("kittMatchedIdentitiess", e.getMessage(),
+ !(e instanceof ZmrCommunicationException), e);
+ }
+ }
+
+ @Nullable
+ private CountrySpecificDetailSearchProcessor findSpecificProcessor(SimpleEidasData eidasData) {
+ final String citizenCountry = eidasData.getCitizenCountryCode();
+ for (final CountrySpecificDetailSearchProcessor processor : handlers) {
+ if (processor.canHandle(citizenCountry, eidasData)) {
+ log.debug("Found suitable search handler for {} by using: {}", citizenCountry, processor.getName());
+ return processor;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Register releated information that are needed for any request.
+ *
+ * @author tlenz
+ */
+ @AllArgsConstructor
+ @Getter
+ public static class RegisterOperationStatus {
+
+ /**
+ * ZMR internal processId that is required for any further request in the same process.
+ */
+ private BigInteger zmrProcessId;
+
+
+ }
+
+
+ /**
+ * Response container for {@link RegisterSearchService} that holds a set of {@link RegisterResult}.
+ *
+ * @author tlenz
+ */
+ @Getter
+ @RequiredArgsConstructor
+ public static class RegisterStatusResults {
+ /**
+ * Operation status for this result.
+ */
+ private final RegisterOperationStatus operationStatus;
+
+ /**
+ * Current ZMR search result.
+ */
+ private final List<RegisterResult> resultsZmr;
+
+ /**
+ * Current ERnP search result.
+ */
+ private final List<RegisterResult> resultsErnp;
+
+ /**
+ * Get sum of ZMR and ERnP results.
+ *
+ * @return number of results
+ */
+ 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("readRegisterResults", "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("readRegisterResults", "getResultCount() != 1");
+ }
+ if (resultsZmr.size() == 1) {
+ return resultsZmr.get(0);
+
+ } else {
+ return resultsErnp.get(0);
+
+ }
+ }
+
+ static RegisterStatusResults fromZmr(ZmrRegisterResult result) {
+ return new RegisterStatusResults(new RegisterOperationStatus(result.getProcessId()),
+ result.getPersonResult(), Collections.emptyList());
+ }
+
+ static RegisterStatusResults fromZmrAndErnp(ZmrRegisterResult result, List<RegisterResult> resultsErnp) {
+ return new RegisterStatusResults(new RegisterOperationStatus(result.getProcessId()),
+ result.getPersonResult(), resultsErnp);
+ }
+
+ static RegisterStatusResults fromErnp(RegisterOperationStatus status, List<RegisterResult> resultsErnp) {
+ return new RegisterStatusResults(status, Collections.emptyList(), resultsErnp);
+ }
+
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java
deleted file mode 100644
index 1f5837d6..00000000
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/szr/SzrClient.java
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright 2018 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.szr;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.UnrecoverableKeyException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.PostConstruct;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.xml.XMLConstants;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.Marshaller;
-import javax.xml.namespace.QName;
-import javax.xml.transform.Source;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.ws.BindingProvider;
-import javax.xml.ws.Dispatch;
-import javax.xml.ws.handler.Handler;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.cxf.configuration.jsse.TLSClientParameters;
-import org.apache.cxf.endpoint.Client;
-import org.apache.cxf.frontend.ClientProxy;
-import org.apache.cxf.jaxws.DispatchImpl;
-import org.apache.cxf.transport.http.HTTPConduit;
-import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
-import org.apache.xpath.XPathAPI;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-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.SzrCommunicationException;
-import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.LoggingHandler;
-import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions;
-import at.gv.egiz.eaaf.core.api.data.XmlNamespaceConstants;
-import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
-import at.gv.egiz.eaaf.core.impl.utils.DomUtils;
-import at.gv.egiz.eaaf.core.impl.utils.FileUtils;
-import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils;
-import szrservices.GetBPK;
-import szrservices.GetBPKResponse;
-import szrservices.GetIdentityLinkEidas;
-import szrservices.GetIdentityLinkEidasResponse;
-import szrservices.IdentityLinkType;
-import szrservices.JwsHeaderParam;
-import szrservices.ObjectFactory;
-import szrservices.PersonInfoType;
-import szrservices.SZR;
-import szrservices.SZRException_Exception;
-import szrservices.SignContent;
-import szrservices.SignContentEntry;
-import szrservices.SignContentResponseType;
-
-
-@Service("SZRClientForeIDAS")
-public class SzrClient {
- private static final Logger log = LoggerFactory.getLogger(SzrClient.class);
-
- private static final String CLIENT_DEFAULT = "DefaultClient";
- private static final String CLIENT_RAW = "RawClient";
-
- private static final String ATTR_NAME_VSZ = "urn:eidgvat:attributes.vsz.value";
- private static final String ATTR_NAME_PUBKEYS = "urn:eidgvat:attributes.user.pubkeys";
- private static final String ATTR_NAME_STATUS = "urn:eidgvat:attributes.eid.status";
- private static final String KEY_BC_BIND = "bcBindReq";
- private static final String JOSE_HEADER_USERCERTPINNING_TYPE = "urn:at.gv.eid:bindtype";
- private static final String JOSE_HEADER_USERCERTPINNING_EIDASBIND = "urn:at.gv.eid:eidasBind";
- public static final String ATTR_NAME_MDS = "urn:eidgvat:mds";
-
- @Autowired
- private IConfiguration basicConfig;
-
- // client for anything, without identitylink
- private SZR szr = null;
-
- // RAW client is needed for identitylink
- private Dispatch<Source> dispatch = null;
-
- private SzrService szrService = null;
- private String szrUrl = null;
- private QName qname = null;
-
- final ObjectMapper mapper = new ObjectMapper();
-
- /**
- * Get IdentityLink of a person.
- *
- * @param personInfo Person identification information
- * @return IdentityLink
- * @throws SzrCommunicationException In case of a SZR error
- */
- public IdentityLinkType getIdentityLinkInRawMode(PersonInfoType personInfo)
- throws SzrCommunicationException {
- try {
- final GetIdentityLinkEidas getIdl = new GetIdentityLinkEidas();
- getIdl.setPersonInfo(personInfo);
-
- final JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
- final Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
-
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- jaxbMarshaller.marshal(getIdl, outputStream);
- outputStream.flush();
-
- final Source source = new StreamSource(new ByteArrayInputStream(outputStream.toByteArray()));
- outputStream.close();
-
- log.trace("Requesting SZR ... ");
- final Source response = dispatch.invoke(source);
- log.trace("Receive RAW response from SZR");
-
- final byte[] szrResponse = sourceToByteArray(response);
- final GetIdentityLinkEidasResponse jaxbElement = (GetIdentityLinkEidasResponse) jaxbContext
- .createUnmarshaller().unmarshal(new ByteArrayInputStream(szrResponse));
-
- // build response
- log.trace(new String(szrResponse, "UTF-8"));
-
- // ok, we have success
- final Document doc = DomUtils.parseDocument(
- new ByteArrayInputStream(szrResponse),
- true,
- XmlNamespaceConstants.ALL_SCHEMA_LOCATIONS + " " + Constants.SZR_SCHEMA_LOCATIONS,
- null, null);
- final String xpathExpression = "//saml:Assertion";
- final Element nsNode = doc.createElementNS("urn:oasis:names:tc:SAML:1.0:assertion", "saml:NSNode");
-
- log.trace("Selecting signed doc " + xpathExpression);
- final Element documentNode = (Element) XPathAPI.selectSingleNode(doc,
- xpathExpression, nsNode);
- log.trace("Signed document: " + DomUtils.serializeNode(documentNode));
-
- final IdentityLinkType idl = new IdentityLinkType();
- idl.setAssertion(documentNode);
- idl.setPersonInfo(jaxbElement.getGetIdentityLinkReturn().getPersonInfo());
-
- return idl;
-
- } catch (final Exception e) {
- log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e);
- throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
-
- }
-
- }
-
- /**
- * Get bPK of person.
- *
- * @param personInfo Person identification information
- * @param target requested bPK target
- * @param vkz Verfahrenskennzeichen
- * @return bPK for this person
- * @throws SzrCommunicationException In case of a SZR error
- */
- public List<String> getBpk(PersonInfoType personInfo, String target, String vkz)
- throws SzrCommunicationException {
- try {
- final GetBPK parameters = new GetBPK();
- parameters.setPersonInfo(personInfo);
- parameters.getBereichsKennung().add(target);
- parameters.setVKZ(vkz);
- final GetBPKResponse result = this.szr.getBPK(parameters);
-
- return result.getGetBPKReturn();
-
- } catch (final SZRException_Exception e) {
- log.warn("SZR communication FAILED. Reason: " + e.getMessage(), e);
- throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
-
- }
-
- }
-
- /**
- * Request a encryped baseId from SRZ.
- *
- * @param personInfo Minimum dataset of person
- * @return encrypted baseId
- * @throws SzrCommunicationException In case of a SZR error
- */
- public String getEncryptedStammzahl(final PersonInfoType personInfo)
- throws SzrCommunicationException {
-
- final String resp;
- try {
- resp = this.szr.getStammzahlEncrypted(personInfo, true);
- } catch (SZRException_Exception e) {
- throw new SzrCommunicationException("ernb.02", new Object[]{e.getMessage()}, e);
- }
-
- if (StringUtils.isEmpty(resp)) {
- throw new SzrCommunicationException("ernb.01", new Object[]{"Stammzahl response empty"}); // TODO error handling
- }
-
- return resp;
-
- }
-
- /**
- * Sign an eidasBind data-structure that combines vsz with user's pubKey and E-ID status.
- *
- * @param vsz encryped baseId
- * @param bindingPubKey binding PublikKey as PKCS1# (ASN.1) container
- * @param eidStatus Status of the E-ID
- * @param eidData eID information that was used for ERnP registration
- * @return bPK for this person
- * @throws SzrCommunicationException In case of a SZR error
- */
- public String getEidsaBind(final String vsz, final String bindingPubKey, final String eidStatus,
- ErnbEidData eidData)throws SzrCommunicationException {
-
- final Map<String, Object> eidsaBindMap = new HashMap<>();
- eidsaBindMap.put(ATTR_NAME_VSZ, vsz);
- eidsaBindMap.put(ATTR_NAME_STATUS, eidStatus);
- eidsaBindMap.put(ATTR_NAME_PUBKEYS, Arrays.asList(bindingPubKey));
- eidsaBindMap.put(PvpAttributeDefinitions.EID_ISSUING_NATION_NAME, eidData.getCitizenCountryCode());
- injectMdsIfAvailableAndActive(eidsaBindMap, eidData);
-
- try {
- final String serializedEidasBind = mapper.writeValueAsString(eidsaBindMap);
- final SignContent req = new SignContent();
- final SignContentEntry eidasBindInfo = new SignContentEntry();
- eidasBindInfo.setKey(KEY_BC_BIND);
- eidasBindInfo.setValue(serializedEidasBind);
- req.getIn().add(eidasBindInfo);
- req.setAppendCert(false);
- final JwsHeaderParam eidasBindJoseHeader = new JwsHeaderParam();
- eidasBindJoseHeader.setKey(JOSE_HEADER_USERCERTPINNING_TYPE);
- eidasBindJoseHeader.setValue(JOSE_HEADER_USERCERTPINNING_EIDASBIND);
- req.getJWSHeaderParam().add(eidasBindJoseHeader);
-
- log.trace("Requesting SZR to sign bcBind datastructure ... ");
- final SignContentResponseType resp = szr.signContent(req.isAppendCert(), req.getJWSHeaderParam(), req.getIn());
- log.trace("Receive SZR response on bcBind siging operation ");
-
- if (resp == null || resp.getOut() == null
- || resp.getOut().isEmpty()
- || StringUtils.isEmpty(resp.getOut().get(0).getValue())) {
- throw new SzrCommunicationException("ernb.01", new Object[]{"BcBind response empty"});
- }
-
- return resp.getOut().get(0).getValue();
-
- } catch (final JsonProcessingException | SZRException_Exception e) {
- log.warn("Requesting bcBind by using SZR FAILED. Reason: {}", e.getMessage(), null, e);
- throw new SzrCommunicationException("ernb.02",
- new Object[]{e.getMessage()}, e);
- }
- }
-
- @PostConstruct
- private void initialize() {
- log.info("Starting SZR-Client initialization .... ");
- final URL url = SzrClient.class.getResource("/szr_client/SZR_v4.0.wsdl");
-
- final boolean useTestSzr = basicConfig.getBasicConfigurationBoolean(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_USETESTSERVICE,
- true);
-
- if (useTestSzr) {
- log.debug("Initializing SZR test environment configuration.");
- qname = SzrService.SZRTestumgebung;
- szrService = new SzrService(url, new QName("urn:SZRServices", "SZRService"));
- szr = szrService.getSzrTestumgebung();
- szrUrl = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_ENDPOINT_TEST);
-
- } else {
- log.debug("Initializing SZR productive configuration.");
- qname = SzrService.SZRProduktionsumgebung;
- szrService = new SzrService(url, new QName("urn:SZRServices", "SZRService"));
- szr = szrService.getSzrProduktionsumgebung();
- szrUrl = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_ENDPOINT_PROD);
-
- }
-
- // create raw client;
- dispatch = szrService.createDispatch(qname, Source.class, javax.xml.ws.Service.Mode.PAYLOAD);
-
- if (StringUtils.isEmpty(szrUrl)) {
- log.error("No SZR service-URL found. SZR-Client initalisiation failed.");
- throw new RuntimeException("No SZR service URL found. SZR-Client initalisiation failed.");
-
- }
-
- // check if Clients can be initialized
- if (szr == null) {
- log.error("SZR " + CLIENT_DEFAULT + " is 'NULL'. Something goes wrong");
- throw new RuntimeException("SZR " + CLIENT_DEFAULT + " is 'NULL'. Something goes wrong");
-
- }
- if (dispatch == null) {
- log.error("SZR " + CLIENT_RAW + " is 'NULL'. Something goes wrong");
- throw new RuntimeException("SZR " + CLIENT_RAW + " is 'NULL'. Something goes wrong");
-
- }
-
- // inject handler
- log.info("Use SZR service-URL: " + szrUrl);
- injectBindingProvider((BindingProvider) szr, CLIENT_DEFAULT);
- injectBindingProvider(dispatch, CLIENT_RAW);
-
- // inject http parameters and SSL context
- log.debug("Inject HTTP client settings ... ");
- injectHttpClient(szr, CLIENT_DEFAULT);
- injectHttpClient(dispatch, CLIENT_RAW);
-
- log.info("SZR-Client initialization successfull");
- }
-
- private void injectHttpClient(Object raw, String clientType) {
- // extract client from implementation
- Client client = null;
- if (raw instanceof DispatchImpl<?>) {
- client = ((DispatchImpl<?>) raw).getClient();
- } else if (raw instanceof Client) {
- client = ClientProxy.getClient(raw);
- } else {
- throw new RuntimeException("SOAP Client for SZR connection is of UNSUPPORTED type: " + raw.getClass()
- .getName());
- }
-
- // set basic connection policies
- final HTTPConduit http = (HTTPConduit) client.getConduit();
-
- // set timeout policy
- final HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
- httpClientPolicy.setConnectionTimeout(
- Integer.parseInt(basicConfig.getBasicConfiguration(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_TIMEOUT_CONNECTION,
- Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_CONNECTION)) * 1000);
- httpClientPolicy.setReceiveTimeout(
- Integer.parseInt(basicConfig.getBasicConfiguration(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_TIMEOUT_RESPONSE,
- Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_RESPONSE)) * 1000);
- http.setClient(httpClientPolicy);
-
- // inject SSL context in case of https
- if (szrUrl.toLowerCase().startsWith("https")) {
- log.debug("Adding SSLContext to client: " + clientType + " ... ");
- final TLSClientParameters tlsParams = new TLSClientParameters();
- tlsParams.setSSLSocketFactory(createSslContext(clientType).getSocketFactory());
- http.setTlsClientParameters(tlsParams);
- log.info("SSLContext initialized for client: " + clientType);
-
- }
-
- }
-
- private void injectBindingProvider(BindingProvider bindingProvider, String clientType) {
- final Map<String, Object> requestContext = bindingProvider.getRequestContext();
- requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, szrUrl);
-
- log.trace("Adding JAX-WS request/response trace handler to client: " + clientType);
- List<Handler> handlerList = bindingProvider.getBinding().getHandlerChain();
- if (handlerList == null) {
- handlerList = new ArrayList<>();
- bindingProvider.getBinding().setHandlerChain(handlerList);
-
- }
-
- // add logging handler to trace messages if required
- if (basicConfig.getBasicConfigurationBoolean(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_TRACEMESSAGES,
- false)) {
- final LoggingHandler loggingHandler = new LoggingHandler();
- handlerList.add(loggingHandler);
-
- }
- bindingProvider.getBinding().setHandlerChain(handlerList);
- }
-
- private SSLContext createSslContext(String clientType) {
- try {
- final SSLContext context = SSLContext.getInstance("TLS");
-
- // initialize key-mangager for SSL client-authentication
- KeyManager[] keyManager = null;
- final String keyStorePath = basicConfig.getBasicConfiguration(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_PATH);
- final String keyStorePassword = basicConfig.getBasicConfiguration(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_KEYSTORE_PASSWORD);
- if (StringUtils.isNotEmpty(keyStorePath)) {
- log.trace("Find keyStore path: " + keyStorePath + " Injecting SSL client certificate ... ");
- try {
- final KeyStore keyStore = KeyStoreUtils.loadKeyStore(
- FileUtils.makeAbsoluteUrl(keyStorePath, basicConfig.getConfigurationRootDirectory()),
- keyStorePassword);
-
- final KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
- kmf.init(keyStore, keyStorePassword.toCharArray());
- keyManager = kmf.getKeyManagers();
- log.debug("SSL client certificate injected to client: " + clientType);
-
- } catch (KeyStoreException | IOException | UnrecoverableKeyException e) {
- log.error("Can NOT load SSL client certificate from path: " + keyStorePath);
- throw new RuntimeException("Can NOT load SSL client certificate from path: " + keyStorePath, e);
-
- }
- } else {
- log.debug(
- "No KeyStore for SSL Client Auth. found. Initializing SSLContext without authentication ... ");
-
- }
-
- // initialize SSL TrustStore
- TrustManager[] trustManager = null;
- final String trustStorePath = basicConfig.getBasicConfiguration(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_PATH);
- final String trustStorePassword = basicConfig.getBasicConfiguration(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SSL_TRUSTSTORE_PASSWORD);
- if (StringUtils.isNotEmpty(trustStorePath)) {
- log.trace("Find trustStore path: " + trustStorePath + " Injecting SSL TrustStore ... ");
- try {
- final KeyStore trustStore = KeyStoreUtils.loadKeyStore(
- FileUtils.makeAbsoluteUrl(trustStorePath, basicConfig.getConfigurationRootDirectory()),
- trustStorePassword);
-
- final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
- tmf.init(trustStore);
- trustManager = tmf.getTrustManagers();
- log.debug("SSL TrustStore injected to client: " + clientType);
-
- } catch (KeyStoreException | IOException e) {
- log.error("Can NOT open SSL TrustStore from path: " + trustStorePath);
- throw new RuntimeException("Can NOT open SSL TrustStore from path: " + trustStorePath, e);
-
- }
-
- } else {
- log.debug("No custom SSL TrustStore found. Initializing SSLContext with JVM default truststore ... ");
-
- }
-
- context.init(keyManager, trustManager, new SecureRandom());
- return context;
-
- } catch (NoSuchAlgorithmException | KeyManagementException e) {
- log.error("SSLContext initialization FAILED.", e);
- throw new RuntimeException("SSLContext initialization FAILED.", e);
-
- }
-
- }
-
- private void injectMdsIfAvailableAndActive(Map<String, Object> eidsaBindMap, ErnbEidData eidData) {
- if (basicConfig.getBasicConfigurationBoolean(
- Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SET_MDS_TO_EIDASBIND, false)) {
- log.info("Injecting MDS into eidasBind ... ");
- final Map<String, Object> mds = new HashMap<>();
- mds.put(PvpAttributeDefinitions.PRINCIPAL_NAME_NAME, eidData.getFamilyName());
- mds.put(PvpAttributeDefinitions.GIVEN_NAME_NAME, eidData.getGivenName());
- mds.put(PvpAttributeDefinitions.BIRTHDATE_NAME, eidData.getFormatedDateOfBirth());
- eidsaBindMap.put(ATTR_NAME_MDS, mds);
-
- }
- }
-
- private byte[] sourceToByteArray(Source result) throws TransformerException {
- final TransformerFactory factory = TransformerFactory.newInstance();
- factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
- final Transformer transformer = factory.newTransformer();
- transformer.setOutputProperty("omit-xml-declaration", "yes");
- transformer.setOutputProperty("method", "xml");
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- final StreamResult streamResult = new StreamResult();
- streamResult.setOutputStream(out);
- transformer.transform(result, streamResult);
- return out.toByteArray();
- }
-
-}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java
new file mode 100644
index 00000000..4705c56b
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/AlternativeSearchTask.java
@@ -0,0 +1,186 @@
+/*
+ * 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.MatchedPersonResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterOperationStatus;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterStatusResults;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import eu.eidas.auth.commons.light.ILightResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+import java.util.Objects;
+
+import static at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants.TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK;
+
+/**
+ * Searches registers (ERnP and ZMR) after alternative eIDAS authn, before adding person to SZR.
+ * Input:
+ * <ul>
+ * <li>{@link Constants#DATA_FULL_EIDAS_RESPONSE_ALTERNATIVE} data from the alternative eIDAS authn</li>
+ * <li>{@link Constants#DATA_SIMPLE_EIDAS} data from the initial eIDAS authn</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#DATA_PERSON_MATCH_RESULT} results after second search in registers with MDS</li>
+ * <li>{@link Constants#DATA_RESULT_MATCHING_BPK} if one register result found</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link GenerateOtherLoginMethodGuiTask} if no results in registers were found for this user</li>
+ * <li>{@link CreateIdentityLinkTask} if search in register returned one match, user is uniquely identified</li>
+ * </ul>
+ *
+ * @author amarsalek
+ * @author ckollmann
+ * @author tlenz
+ */
+@Slf4j
+@Component("AlternativeSearchTask")
+@SuppressWarnings("PMD.TooManyStaticImports")
+public class AlternativeSearchTask extends AbstractAuthServletTask {
+
+ private final RegisterSearchService registerSearchService;
+ private final ICcSpecificEidProcessingService eidPostProcessor;
+
+ /**
+ * Constructor.
+ *
+ * @param registerSearchService Service for register search access
+ * @param eidPostProcessor Country-Specific post processing of attributes
+ */
+ public AlternativeSearchTask(RegisterSearchService registerSearchService,
+ ICcSpecificEidProcessingService eidPostProcessor) {
+ this.registerSearchService = registerSearchService;
+ this.eidPostProcessor = eidPostProcessor;
+ }
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ final SimpleEidasData altEidasData = convertEidasAttrToSimpleData();
+ final SimpleEidasData initialEidasData = MatchingTaskUtils.getInitialEidasData(pendingReq);
+ verifyAlternativeEidasData(altEidasData, initialEidasData);
+ step11RegisterSearchWithPersonIdentifier(executionContext, altEidasData, initialEidasData);
+ } catch (WorkflowException e) {
+ throw new TaskExecutionException(pendingReq, "Initial search failed", e);
+ } catch (final Exception e) {
+ log.error("Initial search failed", e);
+ throw new TaskExecutionException(pendingReq, "Initial search failed with a generic error", e);
+ }
+ }
+
+ private void verifyAlternativeEidasData(SimpleEidasData altEidasData, SimpleEidasData initialEidasData)
+ throws WorkflowException {
+ if (initialEidasData == null) {
+ throw new WorkflowException("step11", "No initial eIDAS authn data");
+ }
+ if (!Objects.equals(altEidasData.getCitizenCountryCode(), initialEidasData.getCitizenCountryCode())) {
+ throw new WorkflowException("step11", "Country Code of alternative eIDAS authn not matching", true);
+ }
+ if (!altEidasData.equalsMds(initialEidasData)) {
+ throw new WorkflowException("step11", "MDS of alternative eIDAS authn does not match initial authn", true);
+ }
+ }
+
+ private void step11RegisterSearchWithPersonIdentifier(
+ ExecutionContext executionContext, SimpleEidasData initialEidasData, SimpleEidasData altEidasData)
+ throws WorkflowException, EaafStorageException {
+ try {
+ log.trace("Starting step11RegisterSearchWithPersonIdentifier");
+ RegisterStatusResults searchResult = registerSearchService.searchWithPersonIdentifier(altEidasData);
+ int resultCount = searchResult.getResultCount();
+ if (resultCount == 0) {
+ step12CountrySpecificSearch(executionContext, searchResult.getOperationStatus(), initialEidasData,
+ altEidasData);
+ } else if (resultCount == 1) {
+ foundMatchFinalizeTask(searchResult, altEidasData);
+ } else {
+ throw new WorkflowException("step11RegisterSearchWithPersonIdentifier",
+ "More than one entry with unique personal-identifier", true);
+ }
+ } catch (WorkflowException e) {
+ //TODO: what we do in case of a workflow error and manual matching are necessary??
+ log.warn("Workflow error during matching step: {}. Reason: {}", e.getProcessStepName(), e.getErrorReason());
+ throw e;
+ }
+ }
+
+ private void step12CountrySpecificSearch(ExecutionContext executionContext,
+ RegisterOperationStatus registerOperationStatus,
+ SimpleEidasData initialEidasData,
+ SimpleEidasData altEidasData)
+ throws EaafStorageException, WorkflowException {
+ log.trace("Starting 'step12CountrySpecificSearch' ... ");
+ RegisterStatusResults searchResult = registerSearchService.searchWithCountrySpecifics(
+ registerOperationStatus, altEidasData);
+ if (searchResult.getResultCount() == 0) {
+ log.trace("'step12CountrySpecificSearch' ends with no result. Forward to GUI based matching step ... ");
+ log.debug("Forward to GUI based matching steps ... ");
+ executionContext.put(TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK, true);
+ } else if (searchResult.getResultCount() == 1) {
+ log.trace("'step12CountrySpecificSearch' finds a person. Forward to 'step7aKittProcess' step ... ");
+ registerSearchService.step7bKittProcess(searchResult, initialEidasData, altEidasData);
+ foundMatchFinalizeTask(searchResult, altEidasData);
+ } else {
+ throw new WorkflowException("step12CountrySpecificSearch",
+ "More than one entry with unique country-specific information", true);
+ }
+ }
+
+ private void foundMatchFinalizeTask(RegisterStatusResults searchResult, SimpleEidasData eidasData)
+ throws WorkflowException, EaafStorageException {
+ MatchedPersonResult result = MatchedPersonResult.generateFormMatchingResult(
+ searchResult.getResult(), eidasData.getCitizenCountryCode());
+ MatchingTaskUtils.storeFinalMatchingResult(pendingReq, result);
+ }
+
+ @NotNull
+ private SimpleEidasData convertEidasAttrToSimpleData()
+ throws EidasAttributeException, EidPostProcessingException {
+ final ILightResponse eidasResponse = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq)
+ .getGenericDataFromSession(Constants.DATA_FULL_EIDAS_RESPONSE_ALTERNATIVE, ILightResponse.class);
+ Map<String, Object> simpleMap = MatchingTaskUtils.convertEidasAttrToSimpleMap(
+ eidasResponse.getAttributes().getAttributeMap(), log);
+ return eidPostProcessor.postProcess(simpleMap);
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java
index f4849b07..35717ae0 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateIdentityLinkTask.java
@@ -25,44 +25,39 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks;
import java.io.IOException;
import java.io.InputStream;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
-import org.apache.commons.lang3.StringUtils;
-import org.joda.time.DateTime;
+import org.jetbrains.annotations.Nullable;
+import org.jose4j.lang.JoseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
+import com.fasterxml.jackson.core.JsonProcessingException;
import at.asitplus.eidas.specific.connector.MsConnectorEventCodes;
import at.asitplus.eidas.specific.connector.MsEidasNodeConstants;
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.clients.szr.SzrClient;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.SzrCommunicationException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.AuthBlockSigningService;
-import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService;
-import at.asitplus.eidas.specific.modules.auth.eidas.v2.szr.SzrClient;
-import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils;
-import at.gv.e_government.reference.namespace.persondata._20020228.AlternativeNameType;
-import at.gv.e_government.reference.namespace.persondata._20020228.PersonNameType;
-import at.gv.e_government.reference.namespace.persondata._20020228.PhysicalPersonType;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils;
import at.gv.egiz.eaaf.core.api.data.EaafConstants;
import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions;
import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink;
import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
import at.gv.egiz.eaaf.core.impl.builder.BpkBuilder;
import at.gv.egiz.eaaf.core.impl.data.Pair;
@@ -71,37 +66,45 @@ import at.gv.egiz.eaaf.core.impl.idp.auth.data.SimpleIdentityLinkAssertionParser
import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
import at.gv.egiz.eaaf.core.impl.utils.DomUtils;
import at.gv.egiz.eaaf.core.impl.utils.XPathUtils;
-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.Data;
import lombok.extern.slf4j.Slf4j;
import szrservices.IdentityLinkType;
-import szrservices.PersonInfoType;
-import szrservices.TravelDocumentType;
/**
- * Task that creates the IdentityLink for an eIDAS authenticated person.
- *
+ * Task that creates the IdentityLink for an eIDAS authenticated person.
+ * Input:
+ * <ul>
+ * <li>{@link Constants#DATA_SIMPLE_EIDAS} initial login data from user</li>
+ * <li>{@link Constants#DATA_RESULT_MATCHING_BPK} the BPK of the matched entry in a register</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#EIDAS_BIND} the binding block</li>
+ * <li>{@link Constants#SZR_AUTHBLOCK} the auth block</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link at.gv.egiz.eaaf.core.impl.idp.controller.tasks.FinalizeAuthenticationTask}</li>
+ * </ul>
+ * TODO Take Constants#DATA_SIMPLE_EIDAS and Constants#DATA_RESULT_MATCHING_BPK
+ * TODO Only do VSZ Erstellung and eidasBind -- this is always the end of the whole process
+ * TODO Move Eintragung to separate Task, as it does not happen every time
* @author tlenz
*/
@Slf4j
@Component("CreateIdentityLinkTask")
public class CreateIdentityLinkTask extends AbstractAuthServletTask {
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private IConfiguration basicConfig;
@Autowired
private SzrClient szrClient;
@Autowired
- private ICcSpecificEidProcessingService eidPostProcessor;
-
- @Autowired
private AuthBlockSigningService authBlockSigner;
private static final String EID_STATUS = "urn:eidgvat:eid.status.eidas";
-
+
/*
* (non-Javadoc)
*
@@ -113,348 +116,269 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {
@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 Map<String, Object> simpleAttrMap = convertEidasAttrToSimpleMap(
- eidasResponse.getAttributes().getAttributeMap());
-
- // post-process eIDAS attributes
- final ErnbEidData eidData = eidPostProcessor.postProcess(simpleAttrMap);
-
- // write MDS into technical log and revision log
+ try {
+
+ /*TODO: needs more re-factoring if we finalize CreateNewErnpEntryTask and we know how add entries into ERnP
+ * Maybe, we can fully replace eidData by matchedPersonData,
+ * because matchedPersonData holds the result after a successful matching process.
+ *
+ * Currently, we only add a work-around to operate without new ERnP implementation.
+ */
+ final SimpleEidasData eidData = MatchingTaskUtils.getInitialEidasData(pendingReq);
+ MatchedPersonResult matchedPersonData = MatchingTaskUtils.getFinalMatchingResult(pendingReq);
+
writeMdsLogInformation(eidData);
- //build IdentityLink or VSZ and eidasBind
if (basicConfig.getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USEDUMMY, false)) {
- SzrResultHolder idlResult = createDummyIdentityLinkForTestDeployment(eidData);
- //inject personal-data into session
- authProcessData.setIdentityLink(idlResult.getIdentityLink());
+ buildDummyIdentityLink(eidData);
- // set bPK and bPKType into auth session
- authProcessData.setGenericDataToSession(PvpAttributeDefinitions.BPK_NAME, extendBpkByPrefix(
- idlResult.getBpK(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()));
- authProcessData.setGenericDataToSession(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME,
- pendingReq.getServiceProviderConfiguration()
- .getAreaSpecificTargetIdentifier());
-
} else {
- //build SZR request from eIDAS data
- final PersonInfoType personInfo = generateSzrRequest(eidData);
-
//request SZR based on IDL or E-ID mode
if (pendingReq.getServiceProviderConfiguration()
.isConfigurationValue(MsEidasNodeConstants.PROP_CONFIG_SP_NEW_EID_MODE, false)) {
-
- // get encrypted baseId
- String vsz = szrClient.getEncryptedStammzahl(personInfo);
-
- //write revision-Log entry and extended infos personal-identifier mapping
- revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_VSZ_RECEIVED);
- writeExtendedRevisionLogEntry(simpleAttrMap, eidData);
-
-
- // get eIDAS bind
- String signedEidasBind = szrClient.getEidsaBind(vsz,
- authBlockSigner.getBase64EncodedPublicKey(),
- EID_STATUS, eidData);
- revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_EIDASBIND_RECEIVED);
- authProcessData.setGenericDataToSession(Constants.EIDAS_BIND, signedEidasBind);
-
- //get signed AuthBlock
- String jwsSignature = authBlockSigner.buildSignedAuthBlock(pendingReq);
- revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.TECH_AUCHBLOCK_CREATED);
- authProcessData.setGenericDataToSession(Constants.SZR_AUTHBLOCK, jwsSignature);
+ executeEidMode(eidData, matchedPersonData);
- //inject personal-data into session
- authProcessData.setEidProcess(true);
-
} else {
- //request SZR
- SzrResultHolder idlResult = requestSzrForIdentityLink(personInfo);
+ executeIdlMode(eidData, matchedPersonData);
- //write revision-Log entry for personal-identifier mapping
- writeExtendedRevisionLogEntry(simpleAttrMap, eidData);
-
- //check result-data and write revision-log based on current state
- checkStateAndWriteRevisionLog(idlResult);
-
- //inject personal-data into session
- authProcessData.setIdentityLink(idlResult.getIdentityLink());
- authProcessData.setEidProcess(false);
-
- // set bPK and bPKType into auth session
- authProcessData.setGenericDataToSession(PvpAttributeDefinitions.BPK_NAME, extendBpkByPrefix(
- idlResult.getBpK(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()));
- authProcessData.setGenericDataToSession(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME,
- pendingReq.getServiceProviderConfiguration()
- .getAreaSpecificTargetIdentifier());
-
- }
+ }
}
- //add generic info's into session
- authProcessData.setForeigner(true);
- authProcessData.setGenericDataToSession(PvpAttributeDefinitions.EID_ISSUING_NATION_NAME, EidasResponseUtils
- .parseEidasPersonalIdentifier((String) simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER))
- .getFirst());
- authProcessData.setQaaLevel(eidasResponse.getLevelOfAssurance());
-
- // store pending-request
+ storeGenericInfoToSession(eidData);
requestStoreage.storePendingRequest(pendingReq);
-
} catch (final EidasAttributeException e) {
throw new TaskExecutionException(pendingReq, "Minimum required eIDAS attributeset not found.", e);
-
+
} catch (final EaafException e) {
throw new TaskExecutionException(pendingReq, "IdentityLink generation for foreign person FAILED.", e);
-
+
} catch (final Exception e) {
log.error("IdentityLink generation for foreign person FAILED.", e);
throw new TaskExecutionException(pendingReq, "IdentityLink generation for foreign person FAILED.", e);
+
+ }
+ }
+
+ private void storeGenericInfoToSession(SimpleEidasData eidData) throws EaafStorageException {
+ AuthProcessDataWrapper authProcessData = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq);
+ authProcessData.setForeigner(true);
+ authProcessData.setGenericDataToSession(PvpAttributeDefinitions.EID_ISSUING_NATION_NAME,
+ eidData.getCitizenCountryCode());
+ }
+
+ private void executeIdlMode(SimpleEidasData eidData, MatchedPersonResult matchedPersonData) throws EaafException {
+ //request SZR
+ SzrResultHolder idlResult = requestSzrForIdentityLink(eidData, matchedPersonData);
+
+ //write revision-Log entry for personal-identifier mapping
+ writeExtendedRevisionLogEntry(eidData, eidData.getPersonalIdentifier());
+ //check result-data and write revision-log based on current state
+ checkStateAndWriteRevisionLog(idlResult);
+
+ //inject personal-data into session
+ AuthProcessDataWrapper authProcessDataWrapper = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq);
+ authProcessDataWrapper.setIdentityLink(idlResult.getIdentityLink());
+ authProcessDataWrapper.setEidProcess(false);
+
+ // set bPK and bPKType into auth session
+ authProcessDataWrapper.setGenericDataToSession(PvpAttributeDefinitions.BPK_NAME, extendBpkByPrefix(
+ idlResult.getBpK(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()));
+ authProcessDataWrapper.setGenericDataToSession(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME,
+ pendingReq.getServiceProviderConfiguration()
+ .getAreaSpecificTargetIdentifier());
+ }
+ private void executeEidMode(SimpleEidasData eidData, MatchedPersonResult matchedPersonData)
+ throws JsonProcessingException, EaafException, JoseException {
+ // get encrypted baseId
+ String vsz;
+ if (matchedPersonData != null) {
+ log.debug("Requesting encrypted baseId by already matched person information ... ");
+ vsz = szrClient.getEncryptedStammzahl(matchedPersonData);
+
+ } else {
+ log.debug("Requesting encrypted baseId by using eIDAS information directly ... ");
+ vsz = szrClient.createNewErnpEntry(eidData);
+
}
+
+ //write revision-Log entry and extended infos personal-identifier mapping
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_VSZ_RECEIVED);
+ writeExtendedRevisionLogEntry(eidData, eidData.getPersonalIdentifier());
+
+ // get eIDAS bind
+ String signedEidasBind = szrClient
+ .getEidasBind(vsz, authBlockSigner.getBase64EncodedPublicKey(), EID_STATUS, eidData);
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_EIDASBIND_RECEIVED);
+ AuthProcessDataWrapper authProcessDataWrapper = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq);
+ authProcessDataWrapper.setGenericDataToSession(Constants.EIDAS_BIND, signedEidasBind);
+
+ //get signed AuthBlock
+ String jwsSignature = authBlockSigner.buildSignedAuthBlock(pendingReq);
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.TECH_AUCHBLOCK_CREATED);
+ authProcessDataWrapper.setGenericDataToSession(Constants.SZR_AUTHBLOCK, jwsSignature);
+
+ //inject personal-data into session
+ authProcessDataWrapper.setEidProcess(true);
+
+ }
+
+ private void buildDummyIdentityLink(SimpleEidasData eidData)
+ throws ParserConfigurationException, SAXException, IOException, EaafException {
+ AuthProcessDataWrapper authProcessDataWrapper = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq);
+ SzrResultHolder idlResult = createDummyIdentityLinkForTestDeployment(eidData);
+ //inject personal-data into session
+ authProcessDataWrapper.setIdentityLink(idlResult.getIdentityLink());
+
+ // set bPK and bPKType into auth session
+ authProcessDataWrapper.setGenericDataToSession(PvpAttributeDefinitions.BPK_NAME, extendBpkByPrefix(
+ idlResult.getBpK(), pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()));
+ authProcessDataWrapper.setGenericDataToSession(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME,
+ pendingReq.getServiceProviderConfiguration()
+ .getAreaSpecificTargetIdentifier());
}
- private void writeExtendedRevisionLogEntry(Map<String, Object> simpleAttrMap, ErnbEidData eidData) {
- // write ERnB input-data into revision-log
+ private void writeExtendedRevisionLogEntry(SimpleEidasData eidData, String personalIdentifier) {
+ // write ERnP input-data into revision-log
if (basicConfig.getBasicConfigurationBoolean(
Constants.CONIG_PROPS_EIDAS_SZRCLIENT_WORKAROUND_REVISIONLOGDATASTORE_ACTIVE, false)) {
- revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_ERNB_EIDAS_RAW_ID,
- (String) simpleAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER));
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_ERNB_EIDAS_RAW_ID, personalIdentifier);
revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_ERNB_EIDAS_ERNB_ID, eidData.getPseudonym());
}
}
-
- private PersonInfoType generateSzrRequest(ErnbEidData eidData) {
- log.debug("Starting connecting SZR Gateway");
- final PersonInfoType personInfo = new PersonInfoType();
- final PersonNameType personName = new PersonNameType();
- final PhysicalPersonType naturalPerson = new PhysicalPersonType();
- final TravelDocumentType eDocument = new TravelDocumentType();
-
- naturalPerson.setName(personName);
- personInfo.setPerson(naturalPerson);
- personInfo.setTravelDocument(eDocument);
-
- // person information
- personName.setFamilyName(eidData.getFamilyName());
- personName.setGivenName(eidData.getGivenName());
- naturalPerson.setDateOfBirth(eidData.getFormatedDateOfBirth());
- eDocument.setIssuingCountry(eidData.getCitizenCountryCode());
- eDocument.setDocumentNumber(eidData.getPseudonym());
-
- // eID document information
- eDocument.setDocumentType(basicConfig
- .getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_EDOCUMENTTYPE,
- Constants.SZR_CONSTANTS_DEFAULT_DOCUMENT_TYPE));
-
- // set PlaceOfBirth if available
- if (eidData.getPlaceOfBirth() != null) {
- log.trace("Find 'PlaceOfBirth' attribute: " + eidData.getPlaceOfBirth());
- if (basicConfig
- .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETPLACEOFBIRTHIFAVAILABLE,
- true)) {
- naturalPerson.setPlaceOfBirth(eidData.getPlaceOfBirth());
- log.trace("Adding 'PlaceOfBirth' to ERnB request ... ");
-
- }
- }
- // set BirthName if available
- if (eidData.getBirthName() != null) {
- log.trace("Find 'BirthName' attribute: " + eidData.getBirthName());
- if (basicConfig
- .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_SETBIRTHNAMEIFAVAILABLE,
- true)) {
- final AlternativeNameType alternativeName = new AlternativeNameType();
- naturalPerson.setAlternativeName(alternativeName);
- alternativeName.setFamilyName(eidData.getBirthName());
- log.trace("Adding 'BirthName' to ERnB request ... ");
+ private SzrResultHolder requestSzrForIdentityLink(SimpleEidasData eidData,
+ MatchedPersonResult matchedPersonData) throws EaafException {
+ //request IdentityLink from SZR
+ IdentityLinkType result;
- }
+ if (matchedPersonData != null) {
+ log.debug("Requesting encrypted baseId by already matched person information ... ");
+ result = szrClient.getIdentityLinkInRawMode(matchedPersonData);
+
+ } else {
+ log.debug("Requesting encrypted baseId by using eIDAS information directly ... ");
+ result = szrClient.getIdentityLinkInRawMode(eidData);
+
}
- return personInfo;
-
- }
-
- private SzrResultHolder requestSzrForIdentityLink(PersonInfoType personInfo)
- throws SzrCommunicationException, EaafException {
- //request IdentityLink from SZR
- final IdentityLinkType result = szrClient.getIdentityLinkInRawMode(personInfo);
final Element idlFromSzr = (Element) result.getAssertion();
- IIdentityLink identityLink = new SimpleIdentityLinkAssertionParser(idlFromSzr).parseIdentityLink();
+ final IIdentityLink identityLink = new SimpleIdentityLinkAssertionParser(idlFromSzr).parseIdentityLink();
// get bPK from SZR
String bpk = null;
- if (basicConfig
- .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USESRZFORBPKGENERATION, true)) {
- List<String> bpkList = szrClient
- .getBpk(personInfo, pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier(),
- basicConfig
- .getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ, "no VKZ defined"));
+ String targetId = pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier();
+ boolean debugUseSzrForBpk = basicConfig
+ .getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_DEBUG_USESRZFORBPKGENERATION, true);
+ if (debugUseSzrForBpk) {
+ String vkz = basicConfig
+ .getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ, "no VKZ defined");
+ List<String> bpkList = szrClient.getBpk(eidData, targetId, vkz);
if (!bpkList.isEmpty()) {
bpk = bpkList.get(0);
-
}
-
-
} else {
log.debug("Calculating bPK from baseId ... ");
- new BpkBuilder();
- final Pair<String, String> bpkCalc = BpkBuilder
- .generateAreaSpecificPersonIdentifier(identityLink.getIdentificationValue(),
- identityLink.getIdentificationType(),
- pendingReq.getServiceProviderConfiguration()
- .getAreaSpecificTargetIdentifier());
+ String idValue = identityLink.getIdentificationValue();
+ String idType = identityLink.getIdentificationType();
+ final Pair<String, String> bpkCalc = BpkBuilder.generateAreaSpecificPersonIdentifier(idValue, idType, targetId);
bpk = bpkCalc.getFirst();
-
}
-
+
return new SzrResultHolder(identityLink, bpk);
-
}
-
+
private void checkStateAndWriteRevisionLog(SzrResultHolder idlResult) throws SzrCommunicationException {
// write some infos into revision log
if (idlResult.getIdentityLink() == null) {
log.error("ERnB did not return an identity link.");
throw new SzrCommunicationException("ernb.00", null);
-
}
- revisionsLogger.logEvent(pendingReq,
- MsConnectorEventCodes.SZR_IDL_RECEIVED,
- idlResult.getIdentityLink().getSamlAssertion()
- .getAttribute(SimpleIdentityLinkAssertionParser.ASSERTIONID));
+
+ String assertionId = idlResult.getIdentityLink().getSamlAssertion()
+ .getAttribute(SimpleIdentityLinkAssertionParser.ASSERTIONID);
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_IDL_RECEIVED, assertionId);
if (idlResult.getBpK() == null) {
log.error("ERnB did not return a bPK for target: " + pendingReq.getServiceProviderConfiguration()
- .getAreaSpecificTargetIdentifier());
+ .getAreaSpecificTargetIdentifier());
throw new SzrCommunicationException("ernb.01", null);
-
}
+
revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.SZR_BPK_RECEIVED);
log.debug("ERnB communication was successfull");
-
}
-
- private String extendBpkByPrefix(String bpk, String type) {
- String bpkType = null;
-
- if (type.startsWith(EaafConstants.URN_PREFIX_WBPK)) {
- bpkType = type.substring(EaafConstants.URN_PREFIX_WBPK.length());
- } else if (type.startsWith(EaafConstants.URN_PREFIX_CDID)) {
- bpkType = type.substring(EaafConstants.URN_PREFIX_CDID.length());
- } else if (type.startsWith(EaafConstants.URN_PREFIX_EIDAS)) {
- bpkType = type.substring(EaafConstants.URN_PREFIX_EIDAS.length());
- }
+ private String extendBpkByPrefix(String bpk, String type) {
+ String bpkType = getBpkType(type);
if (bpkType != null) {
log.trace("Authenticate user with bPK/wbPK " + bpk + " and Type=" + bpkType);
return bpkType + ":" + bpk;
-
} else {
log.warn("Service Provider Target with: " + type + " is NOT supported. Set bPK as it is ...");
return bpk;
-
}
-
}
- 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));
- 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");
- }
-
- }
+ @Nullable
+ private String getBpkType(String type) {
+ if (type.startsWith(EaafConstants.URN_PREFIX_WBPK)) {
+ return type.substring(EaafConstants.URN_PREFIX_WBPK.length());
+ } else if (type.startsWith(EaafConstants.URN_PREFIX_CDID)) {
+ return type.substring(EaafConstants.URN_PREFIX_CDID.length());
+ } else if (type.startsWith(EaafConstants.URN_PREFIX_EIDAS)) {
+ return type.substring(EaafConstants.URN_PREFIX_EIDAS.length());
+ } else {
+ return null;
}
-
- log.debug("Receive #" + result.size() + " attributes with names: " + result.keySet().toString());
-
- return result;
}
- private void writeMdsLogInformation(ErnbEidData eidData) {
- // log MDS and country code into technical log
- if (basicConfig
- .getBasicConfigurationBoolean(MsEidasNodeConstants.PROP_CONFIG_TECHNICALLOG_WRITE_MDS_INTO_TECH_LOG, false)) {
+
+ /**
+ * write MDS into technical log and revision log.
+ */
+ private void writeMdsLogInformation(SimpleEidasData eidData) {
+ boolean writeMdsInTechLog = basicConfig
+ .getBasicConfigurationBoolean(MsEidasNodeConstants.PROP_CONFIG_TECHNICALLOG_WRITE_MDS_INTO_TECH_LOG, false);
+ if (writeMdsInTechLog) {
log.info("eIDAS Auth. for user: " + eidData.getGivenName() + " " + eidData.getFamilyName() + " " + eidData
- .getFormatedDateOfBirth() + " " + "from " + eidData.getCitizenCountryCode());
+ .getDateOfBirth() + " " + "from " + eidData.getCitizenCountryCode());
}
- // log MDS and country code into revision log
- if (basicConfig
+ boolean writeMdsInRevLog = basicConfig
.getBasicConfigurationBoolean(MsEidasNodeConstants.PROP_CONFIG_REVISIONLOG_WRITE_MDS_INTO_REVISION_LOG,
- false)) {
+ false);
+ if (writeMdsInRevLog) {
revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.RESPONSE_FROM_EIDAS_MDSDATA,
- "{" + eidData.getGivenName() + "," + eidData.getFamilyName() + "," + eidData
- .getFormatedDateOfBirth() + "," + eidData.getCitizenCountryCode() + "}");
+ "{" + eidData.getGivenName() + "," + eidData.getFamilyName() + "," + eidData
+ .getDateOfBirth() + "," + eidData.getCitizenCountryCode() + "}");
}
-
}
-
+
@Data
- private static class SzrResultHolder {
+ private static class SzrResultHolder {
final IIdentityLink identityLink;
final String bpK;
-
+
}
-
+
/**
* Build a dummy IdentityLink and a dummy bPK based on eIDAS information.
- *
+ *
* <br><br>
* <b>FOR LOCAL TESTING ONLY!!!</b>
- *
+ *
* @param eidData Information from eIDAS response
* @return IdentityLink and bPK
* @throws ParserConfigurationException In case of an IDL processing error
- * @throws SAXException In case of an IDL processing error
- * @throws IOException In case of an IDL processing error
- * @throws EaafException In case of a bPK generation error
+ * @throws SAXException In case of an IDL processing error
+ * @throws IOException In case of an IDL processing error
+ * @throws EaafException In case of a bPK generation error
*/
- private SzrResultHolder createDummyIdentityLinkForTestDeployment(ErnbEidData eidData)
+ private SzrResultHolder createDummyIdentityLinkForTestDeployment(SimpleEidasData eidData)
throws ParserConfigurationException, SAXException, IOException, EaafException {
log.warn("SZR-Dummy IS ACTIVE! IdentityLink is NOT VALID!!!!");
// create fake IdL
@@ -487,17 +411,16 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {
final Node prDateOfBirth = XPathUtils
.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_DATE_OF_BIRTH_XPATH);
- prDateOfBirth.getFirstChild().setNodeValue(eidData.getFormatedDateOfBirth());
+ prDateOfBirth.getFirstChild().setNodeValue(eidData.getDateOfBirth());
identityLink = new SimpleIdentityLinkAssertionParser(idlassertion).parseIdentityLink();
- final Pair<String, String> bpkCalc = BpkBuilder
- .generateAreaSpecificPersonIdentifier(identityLink.getIdentificationValue(),
- identityLink.getIdentificationType(),
- pendingReq.getServiceProviderConfiguration()
- .getAreaSpecificTargetIdentifier());
+ String idValue = identityLink.getIdentificationValue();
+ String idType = identityLink.getIdentificationType();
+ String targetId = pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier();
+ final Pair<String, String> bpkCalc = BpkBuilder.generateAreaSpecificPersonIdentifier(idValue, idType, targetId);
return new SzrResultHolder(identityLink, bpkCalc.getFirst());
-
+
}
}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateNewErnpEntryTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateNewErnpEntryTask.java
new file mode 100644
index 00000000..6fc6d499
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/CreateNewErnpEntryTask.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2021 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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.stereotype.Component;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+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.idp.auth.modules.AbstractAuthServletTask;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Task that searches ERnP and ZMR before adding person to SZR.
+ * This corresponds to Step 9 in the eIDAS Matching Concept.
+ * Input:
+ * <ul>
+ * <li>{@link Constants#DATA_SIMPLE_EIDAS}</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>TODO MDS, BPK of new entry</li>
+ * </ul>
+ *
+ * @author amarsalek
+ * @author ckollmann
+ */
+@Slf4j
+@Component("CreateNewErnbEntryTask")
+public class CreateNewErnpEntryTask extends AbstractAuthServletTask {
+
+ //private final SzrClient szrClient;
+
+ ///**
+ // * Constructor.
+ // * @param szrClient SZR client for creating a new ERnP entry
+ // */
+ //public CreateNewErnpEntryTask(SzrClient szrClient) {
+ // this.szrClient = szrClient;
+ //}
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ //SimpleEidasData simpleEidasData = MatchingTaskUtils.getInitialEidasData(pendingReq);
+
+ // insert person into ERnP
+ //TODO: should we insert it directly into ERnP?
+ //TODO: has to updated to new eIDAS document model in ERnP
+ //String vsz = szrClient.createNewErnpEntry(simpleEidasData);
+
+ // finish matching process, because new user-entry uniquly matches
+ //log.info("User successfully registerred into ERnP and matching tasks are finished ");
+ //MatchingTaskUtils.storeFinalMatchingResult(pendingReq,
+ // MatchedPersonResult.builder()
+ // .vsz(vsz)
+ // .build());
+
+ log.warn("Skipping new insert ERnP task, because it's currently unknown who we should it");
+
+
+ } catch (final Exception e) {
+ log.error("Initial search FAILED.", e);
+ throw new TaskExecutionException(pendingReq, "Initial search FAILED.", e);
+ }
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAustrianResidenceGuiTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAustrianResidenceGuiTask.java
new file mode 100644
index 00000000..d8266398
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAustrianResidenceGuiTask.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021 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.connector.MsEidasNodeConstants;
+import at.asitplus.eidas.specific.connector.gui.StaticGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+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.idp.auth.modules.AbstractAuthServletTask;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Show GUI where user can provide an Austrian residence address, to provide further input to match the identity.
+ *
+ * @author ckollmann
+ */
+@Slf4j
+@Component("GenerateAustrianResidenceGuiTask")
+public class GenerateAustrianResidenceGuiTask extends AbstractAuthServletTask {
+
+ @Autowired
+ private ISpringMvcGuiFormBuilder guiBuilder;
+ @Autowired
+ private IConfiguration basicConfig;
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ final IGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration(
+ basicConfig,
+ pendingReq,
+ basicConfig.getBasicConfiguration(//TODO
+ MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_TEMPLATES_RESIDENCY,
+ MsEidasNodeConstants.TEMPLATE_HTML_RESIDENCY),
+ MsEidasNodeConstants.ENDPOINT_RESIDENCY_INPUT,
+ resourceLoader);
+
+ guiBuilder.build(request, response, config, "Query Austrian residency");
+
+ } catch (final Exception e) {
+ log.error("Initial search FAILED.", e);
+ throw new TaskExecutionException(pendingReq, "Gui creation FAILED.", e);
+ }
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java
index 9900fa98..74525e65 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateAuthnRequestTask.java
@@ -19,10 +19,11 @@
* 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 java.io.IOException;
import java.util.UUID;
import javax.servlet.ServletException;
@@ -30,6 +31,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@@ -39,6 +41,7 @@ import at.asitplus.eidas.specific.connector.MsConnectorEventCodes;
import at.asitplus.eidas.specific.connector.MsEidasNodeConstants;
import at.asitplus.eidas.specific.connector.gui.StaticGuiBuilderConfiguration;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService;
import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder;
@@ -46,6 +49,8 @@ import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage;
import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.GuiBuildException;
import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
import eu.eidas.auth.commons.EidasParameterKeys;
@@ -58,146 +63,163 @@ import eu.eidas.specificcommunication.exception.SpecificCommunicationException;
import eu.eidas.specificcommunication.protocol.SpecificCommunicationService;
import lombok.extern.slf4j.Slf4j;
+
/**
- * Authentication-process task that generates the Authn. Request to eIDAS Node.
- *
- * @author tlenz
+ * Generates the authn request to the eIDAS Node. This is the first task in the process.
+ * Input:
+ * <ul>
+ * <li>none</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>none</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.ReceiveAuthnResponseTask}
+ * to read the response from the eIDAS Node</li>
+ * </ul>
*
+ * @author tlenz
+ * @author ckollmann
*/
@Slf4j
-@Component("ConnecteIDASNodeTask")
+@Component("GenerateAuthnRequestTask")
public class GenerateAuthnRequestTask extends AbstractAuthServletTask {
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
IConfiguration basicConfig;
+
@Autowired
ApplicationContext context;
+
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
ITransactionStorage transactionStore;
+
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
ISpringMvcGuiFormBuilder guiBuilder;
+
@Autowired
ICcSpecificEidProcessingService ccSpecificProcessing;
@Override
- public void execute(ExecutionContext executionContext,
- HttpServletRequest request, HttpServletResponse response)
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
throws TaskExecutionException {
-
try {
- // get target, environment and validate citizen countryCode
- final String citizenCountryCode = (String) executionContext.get(
- MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY);
- final String environment = (String) executionContext.get(
- MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT);
-
- if (StringUtils.isEmpty(citizenCountryCode)) {
- // illegal state; task should not have been executed without a selected country
- throw new EidasSAuthenticationException("eidas.03", new Object[] { "" });
-
- }
-
- // TODO: maybe add countryCode validation before request ref. impl. eIDAS node
- log.info("Request eIDAS auth. for citizen of country: " + citizenCountryCode);
- revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.COUNTRY_SELECTED, citizenCountryCode);
-
- // build eIDAS AuthnRequest
- final LightRequest.Builder authnRequestBuilder = LightRequest.builder();
- authnRequestBuilder.id(UUID.randomUUID().toString());
-
- // set nameIDFormat
- authnRequestBuilder.nameIdFormat(
- authConfig.getBasicConfiguration(Constants.CONFIG_PROP_EIDAS_NODE_NAMEIDFORMAT));
-
- // set citizen country code for foreign uses
- authnRequestBuilder.citizenCountryCode(citizenCountryCode);
-
- //set Issuer
- final String issur = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_ENTITYID);
- if (StringUtils.isEmpty(issur)) {
- log.error("Found NO 'eIDAS node issuer' in configuration. Authentication NOT possible!");
- throw new EaafConfigurationException("config.27",
- new Object[] { "Application config containts NO " + Constants.CONIG_PROPS_EIDAS_NODE_ENTITYID });
-
- }
- authnRequestBuilder.issuer(issur);
-
+ final String citizenCountryCode = extractCitizenCountryCode(executionContext);
+ final String environment = (String) executionContext.get(MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT);
+ final String issuer = loadIssuerFromConfig();
+ final LightRequest lightAuthnReq = buildEidasAuthnRequest(citizenCountryCode, issuer);
- // Add country-specific informations into eIDAS request
- ccSpecificProcessing.preProcess(citizenCountryCode, pendingReq, authnRequestBuilder);
-
- // build request
- final LightRequest lightAuthnReq = authnRequestBuilder.build();
-
- // put request into Hazelcast cache
final BinaryLightToken token = putRequestInCommunicationCache(lightAuthnReq);
final String tokenBase64 = BinaryLightTokenHelper.encodeBinaryLightTokenBase64(token);
-
- // Workaround, because eIDAS node ref. impl. does not return relayState
- if (basicConfig.getBasicConfigurationBoolean(
- Constants.CONIG_PROPS_EIDAS_NODE_WORKAROUND_USEREQUESTIDASTRANSACTIONIDENTIFIER,
- false)) {
- log.trace("Put lightRequestId into transactionstore as session-handling backup");
- transactionStore.put(lightAuthnReq.getId(), pendingReq.getPendingRequestId(), -1);
-
- }
-
- // select forward URL regarding the selected environment
- String forwardUrl = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL);
- if (StringUtils.isNotEmpty(environment)) {
- forwardUrl = selectedForwardUrlForEnvironment(environment);
- }
-
- if (StringUtils.isEmpty(forwardUrl)) {
- log.warn("NO ForwardURL defined in configuration. Can NOT forward to eIDAS node! Process stops");
- throw new EaafConfigurationException("config.08", new Object[] {
- environment == null ? Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL
- : Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL + "." + environment
- });
-
- }
- log.debug("ForwardURL: " + forwardUrl + " selected to forward eIDAS request");
-
- if (basicConfig.getBasicConfiguration(
- Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_METHOD,
- Constants.FORWARD_METHOD_GET).equals(Constants.FORWARD_METHOD_GET)) {
-
- log.debug("Use http-redirect for eIDAS node forwarding ... ");
- // send redirect
- final UriComponentsBuilder redirectUrl = UriComponentsBuilder.fromHttpUrl(forwardUrl);
- redirectUrl.queryParam(EidasParameterKeys.TOKEN.toString(), tokenBase64);
- response.sendRedirect(redirectUrl.build().encode().toString());
-
+ workaroundRelayState(lightAuthnReq);
+ final String forwardUrl = selectForwardUrl(environment);
+
+ String configValue = basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_METHOD, Constants.FORWARD_METHOD_GET);
+ boolean useHttpRedirect = configValue.equals(Constants.FORWARD_METHOD_GET);
+ if (useHttpRedirect) {
+ sendRedirect(response, tokenBase64, forwardUrl);
} else {
- log.debug("Use http-post for eIDAS node forwarding ... ");
- final StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration(
- basicConfig,
- pendingReq,
- Constants.TEMPLATE_POST_FORWARD_NAME,
- null,
- resourceLoader);
-
- config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_ENDPOINT, forwardUrl);
- config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_NAME,
- EidasParameterKeys.TOKEN.toString());
- config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_VALUE,
- tokenBase64);
-
- guiBuilder.build(request, response, config, "Forward to eIDASNode form");
-
+ sendPost(request, response, tokenBase64, forwardUrl);
}
revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.EIDAS_NODE_CONNECTED, lightAuthnReq.getId());
-
} catch (final EidasSAuthenticationException e) {
throw new TaskExecutionException(pendingReq, "eIDAS AuthnRequest generation FAILED.", e);
-
} catch (final Exception e) {
log.warn("eIDAS AuthnRequest generation FAILED.", e);
throw new TaskExecutionException(pendingReq, e.getMessage(), e);
+ }
+ }
+
+ @NotNull
+ private String extractCitizenCountryCode(ExecutionContext executionContext) throws EidasSAuthenticationException {
+ final String result = (String) executionContext.get(MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY);
+ // illegal state; task should not have been executed without a selected country
+ if (StringUtils.isEmpty(result)) {
+ throw new EidasSAuthenticationException("eidas.03", new Object[]{""});
+ }
+ // TODO: maybe add countryCode validation before request ref. impl. eIDAS node
+ log.info("Request eIDAS auth. for citizen of country: {}", result);
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.COUNTRY_SELECTED, result);
+ return result;
+ }
+
+ @NotNull
+ private String loadIssuerFromConfig() throws EaafConfigurationException {
+ final String result = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_ENTITYID);
+ if (StringUtils.isEmpty(result)) {
+ log.error("Found NO 'eIDAS node issuer' in configuration. Authentication NOT possible!");
+ throw new EaafConfigurationException("config.27",
+ new Object[]{"Application config containts NO " + Constants.CONIG_PROPS_EIDAS_NODE_ENTITYID});
+ }
+ return result;
+ }
+ @NotNull
+ private LightRequest buildEidasAuthnRequest(String citizenCountryCode, String issuer)
+ throws EidPostProcessingException {
+ final LightRequest.Builder builder = LightRequest.builder();
+ builder.id(UUID.randomUUID().toString());
+
+ // set nameIDFormat
+ builder.nameIdFormat(
+ authConfig.getBasicConfiguration(Constants.CONFIG_PROP_EIDAS_NODE_NAMEIDFORMAT));
+
+ builder.citizenCountryCode(citizenCountryCode);
+ builder.issuer(issuer);
+ // Add country-specific information into eIDAS request
+ ccSpecificProcessing.preProcess(citizenCountryCode, pendingReq, builder);
+ return builder.build();
+ }
+
+ private BinaryLightToken putRequestInCommunicationCache(ILightRequest lightRequest)
+ throws ServletException {
+ final BinaryLightToken binaryLightToken;
+ try {
+ String beanName = SpecificCommunicationDefinitionBeanNames.SPECIFIC_CONNECTOR_COMMUNICATION_SERVICE.toString();
+ final SpecificCommunicationService service = (SpecificCommunicationService) context.getBean(beanName);
+ binaryLightToken = service.putRequest(lightRequest);
+ } catch (final SpecificCommunicationException e) {
+ log.error("Unable to process specific request");
+ throw new ServletException(e);
}
+ return binaryLightToken;
+ }
+
+ /**
+ * Workaround, because eIDAS node ref. impl. does not return relayState
+ */
+ private void workaroundRelayState(LightRequest lightAuthnReq) throws EaafException {
+ if (basicConfig.getBasicConfigurationBoolean(
+ Constants.CONIG_PROPS_EIDAS_NODE_WORKAROUND_USEREQUESTIDASTRANSACTIONIDENTIFIER,
+ false)) {
+ log.trace("Put lightRequestId into transactionstore as session-handling backup");
+ transactionStore.put(lightAuthnReq.getId(), pendingReq.getPendingRequestId(), -1);
+ }
+ }
+
+ @NotNull
+ private String selectForwardUrl(String environment) throws EaafConfigurationException {
+ String result = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL);
+ if (StringUtils.isNotEmpty(environment)) {
+ result = selectedForwardUrlForEnvironment(environment);
+ }
+ if (StringUtils.isEmpty(result)) {
+ log.warn("NO ForwardURL defined in configuration. Can NOT forward to eIDAS node! Process stops");
+ throw new EaafConfigurationException("config.08", new Object[]{
+ environment == null ? Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL
+ : Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL + "." + environment
+ });
+ }
+ log.debug("ForwardURL: {} selected to forward eIDAS request", result);
+ return result;
}
/**
@@ -205,51 +227,48 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask {
* <br>
* <b>Info: </b> This method is needed, because eIDAS Ref. Impl only supports
* one countrycode on each instance. In consequence, more than one eIDAS Ref.
- * Impl nodes are required to support producation, testing, or QS stages for one
+ * Impl nodes are required to support production, testing, or QS stages for one
* country by using one ms-specific eIDAS connector
- *
+ *
* @param environment Environment selector from CountrySlection page
- * @return
+ * @return the URL from the configuration
*/
private String selectedForwardUrlForEnvironment(String environment) {
- log.trace("Starting endpoint selection process for environment: " + environment + " ... ");
+ log.trace("Starting endpoint selection process for environment: {} ... ", environment);
if (environment.equalsIgnoreCase(MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_PRODUCTION)) {
return basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL);
} else if (environment.equalsIgnoreCase(MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_QS)) {
return basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL
+ "." + MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_QS);
- } else if (environment.equalsIgnoreCase(
- MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_TESTING)) {
+ } else if (environment.equalsIgnoreCase(MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_TESTING)) {
return basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL
+ "." + MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_TESTING);
- } else if (environment.equalsIgnoreCase(
- MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_DEVELOPMENT)) {
+ } else if (environment.equalsIgnoreCase(MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_DEVELOPMENT)) {
return basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_URL
+ "." + MsEidasNodeConstants.REQ_PARAM_SELECTED_ENVIRONMENT_VALUE_DEVELOPMENT);
}
-
- log.info("Environment selector: " + environment + " is not supported");
+ log.info("Environment selector: {} is not supported", environment);
return null;
-
}
- private BinaryLightToken putRequestInCommunicationCache(ILightRequest lightRequest)
- throws ServletException {
- final BinaryLightToken binaryLightToken;
- try {
- final SpecificCommunicationService springManagedSpecificConnectorCommunicationService =
- (SpecificCommunicationService) context.getBean(
- SpecificCommunicationDefinitionBeanNames.SPECIFIC_CONNECTOR_COMMUNICATION_SERVICE.toString());
-
- binaryLightToken = springManagedSpecificConnectorCommunicationService.putRequest(lightRequest);
-
- } catch (final SpecificCommunicationException e) {
- log.error("Unable to process specific request");
- throw new ServletException(e);
-
- }
+ private void sendRedirect(HttpServletResponse response, String tokenBase64, String forwardUrl) throws IOException {
+ log.debug("Use http-redirect for eIDAS node forwarding ... ");
+ final UriComponentsBuilder redirectUrl = UriComponentsBuilder.fromHttpUrl(forwardUrl);
+ redirectUrl.queryParam(EidasParameterKeys.TOKEN.toString(), tokenBase64);
+ response.sendRedirect(redirectUrl.build().encode().toString());
+
+ }
- return binaryLightToken;
+ private void sendPost(HttpServletRequest request, HttpServletResponse response, String tokenBase64, String forwardUrl)
+ throws GuiBuildException {
+ log.debug("Use http-post for eIDAS node forwarding ... ");
+ final StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration(
+ basicConfig, pendingReq, Constants.TEMPLATE_POST_FORWARD_NAME, null, resourceLoader);
+ config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_ENDPOINT, forwardUrl);
+ String token = EidasParameterKeys.TOKEN.toString();
+ config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_NAME, token);
+ config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_VALUE, tokenBase64);
+ guiBuilder.build(request, response, config, "Forward to eIDASNode form");
}
}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateMobilePhoneSignatureRequestTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateMobilePhoneSignatureRequestTask.java
new file mode 100644
index 00000000..e6484e63
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateMobilePhoneSignatureRequestTask.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2021 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.idaustriaclient.IdAustriaClientAuthConstants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthRequestBuilderConfiguration;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider.IdAustriaClientAuthCredentialProvider;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider.IdAustriaClientAuthMetadataProvider;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import at.gv.egiz.eaaf.core.impl.utils.Random;
+import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException;
+import at.gv.egiz.eaaf.modules.pvp2.sp.impl.PvpAuthnRequestBuilder;
+import lombok.extern.slf4j.Slf4j;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import net.shibboleth.utilities.java.support.security.SecureRandomIdentifierGenerationStrategy;
+import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.text.MessageFormat;
+
+/**
+ * Generate a SAML2 AuthnRequest to authenticate the user at ID Austria system.
+ * This corresponds to Step 15A in the eIDAS Matching Concept.
+ *
+ * @author tlenz
+ */
+@Slf4j
+@Component("GenerateMobilePhoneSignatureRequestTask")
+public class GenerateMobilePhoneSignatureRequestTask extends AbstractAuthServletTask {
+
+ private static final String ERROR_MSG_1 =
+ "Requested 'ms-specific eIDAS node' {0} has no valid metadata or metadata is not found";
+
+ @Autowired
+ PvpAuthnRequestBuilder authnReqBuilder;
+ @Autowired
+ IdAustriaClientAuthCredentialProvider credential;
+ @Autowired
+ IdAustriaClientAuthMetadataProvider metadataService;
+ @Autowired
+ private IConfiguration basicConfig;
+ @Autowired
+ protected ITransactionStorage transactionStorage;
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ log.trace("Starting GenerateMobilePhoneSignatureRequestTask");
+ final String entityId = loadEntityId();
+ final EntityDescriptor entityDesc = loadEntityDescriptor(entityId);
+ final IdAustriaClientAuthRequestBuilderConfiguration authnReqConfig = buildAuthnRequestConfig(entityDesc);
+ final String relayState = buildRelayState();
+ authnReqBuilder.buildAuthnRequest(pendingReq, authnReqConfig, relayState, response); // also transmits!
+ } catch (final Exception e) {
+ throw new TaskExecutionException(pendingReq, "Generation of SAML2 AuthnRequest to ID Austria System FAILED", e);
+ }
+ }
+
+ @NotNull
+ private String loadEntityId() throws EaafConfigurationException {
+ final String msNodeEntityID = basicConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_ID_AUSTRIA_ENTITYID);
+ if (StringUtils.isEmpty(msNodeEntityID)) {
+ log.warn("ID Austria authentication not possible -> NO EntityID for ID Austria System FOUND!");
+ throw new EaafConfigurationException(Constants.ERRORCODE_00,
+ new Object[]{IdAustriaClientAuthConstants.CONFIG_PROPS_ID_AUSTRIA_ENTITYID});
+ }
+ return msNodeEntityID;
+ }
+
+ /**
+ * Build relayState for session synchronization, because SAML2 only allows RelayState with 80 characters
+ * but encrypted PendingRequestId is much longer.
+ */
+ @NotNull
+ private String buildRelayState() throws EaafException {
+ String relayState = Random.nextProcessReferenceValue();
+ transactionStorage.put(relayState, pendingReq.getPendingRequestId(), -1);
+ return relayState;
+ }
+
+ @NotNull
+ private EntityDescriptor loadEntityDescriptor(String msNodeEntityID)
+ throws ResolverException, EaafConfigurationException {
+ final EntityDescriptor entityDesc = metadataService.getEntityDescriptor(msNodeEntityID);
+ if (entityDesc == null) {
+ throw new EaafConfigurationException(IdAustriaClientAuthConstants.ERRORCODE_02,
+ new Object[]{MessageFormat.format(ERROR_MSG_1, msNodeEntityID)});
+
+ }
+ return entityDesc;
+ }
+
+ @NotNull
+ private IdAustriaClientAuthRequestBuilderConfiguration buildAuthnRequestConfig(EntityDescriptor entityDesc)
+ throws CredentialsNotAvailableException {
+ final IdAustriaClientAuthRequestBuilderConfiguration authnReqConfig =
+ new IdAustriaClientAuthRequestBuilderConfiguration();
+ final SecureRandomIdentifierGenerationStrategy gen = new SecureRandomIdentifierGenerationStrategy();
+ authnReqConfig.setRequestId(gen.generateIdentifier());
+ authnReqConfig.setIdpEntity(entityDesc);
+ authnReqConfig.setPassive(false);
+ authnReqConfig.setSignCred(credential.getMessageSigningCredential());
+ authnReqConfig.setSpEntityID(
+ pendingReq.getAuthUrlWithOutSlash() + IdAustriaClientAuthConstants.ENDPOINT_METADATA);
+ authnReqConfig.setRequestedLoA(authConfig.getBasicConfiguration(
+ IdAustriaClientAuthConstants.CONFIG_PROPS_REQUIRED_LOA,
+ IdAustriaClientAuthConstants.CONFIG_DEFAULT_LOA_EIDAS_LEVEL));
+ return authnReqConfig;
+ }
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateOtherLoginMethodGuiTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateOtherLoginMethodGuiTask.java
new file mode 100644
index 00000000..dbdda78e
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/GenerateOtherLoginMethodGuiTask.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import at.asitplus.eidas.specific.connector.MsEidasNodeConstants;
+import at.asitplus.eidas.specific.connector.gui.StaticGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfiguration;
+import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+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.idp.auth.modules.AbstractAuthServletTask;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Task that provides GUI for user to select an alternative login method.
+ * This page is shown when the matching of the eIDAS data to ZMR/ERnP data is ambiguous.
+ * This corresponds to Steps 10, 14, 16 in the eIDAS Matching Concept.
+ * The response is handled in {@link ReceiveOtherLoginMethodGuiResponseTask}
+ *
+ * @author amarsalek
+ * @author ckollmann
+ */
+@Slf4j
+@Component("GenerateOtherLoginMethodGuiTask")
+public class GenerateOtherLoginMethodGuiTask extends AbstractAuthServletTask {
+
+ @Autowired
+ private ISpringMvcGuiFormBuilder guiBuilder;
+
+ @Autowired
+ private IConfiguration basicConfig;
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ final IGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration(
+ basicConfig,
+ pendingReq,
+ basicConfig.getBasicConfiguration(
+ MsEidasNodeConstants.PROP_CONFIG_WEBCONTENT_TEMPLATES_OTHER_LOGIN_METHOD_SELECTION,
+ MsEidasNodeConstants.TEMPLATE_HTML_OTHERLOGINMETHODS),
+ MsEidasNodeConstants.ENDPOINT_OTHER_LOGIN_METHOD_SELECTION,
+ resourceLoader);
+
+ guiBuilder.build(request, response, config, "Other login methods selection form");
+
+ } catch (final Exception e) {
+ log.error("Initial search FAILED.", e);
+ throw new TaskExecutionException(pendingReq, "Gui creation FAILED.", e);
+ }
+ }
+
+}
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..2341b733
--- /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,202 @@
+/*
+ * 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.MatchedPersonResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidPostProcessingException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasAttributeException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterOperationStatus;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterStatusResults;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import eu.eidas.auth.commons.light.ILightResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+
+import static at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants.TRANSITION_TO_CREATE_NEW_ERNP_ENTRY_TASK;
+import static at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants.TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK;
+
+/**
+ * Searches registers (ERnP and ZMR) after initial user auth, before adding person to SZR.
+ * Input:
+ * <ul>
+ * <li>{@link Constants#DATA_FULL_EIDAS_RESPONSE}</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#DATA_SIMPLE_EIDAS} converted from Full eIDAS Response</li>
+ * <li>{@link Constants#DATA_INTERMEDIATE_RESULT} results from first search in registers with
+ * PersonIdentifier</li>
+ * <li>{@link Constants#DATA_PERSON_MATCH_RESULT} results after second search in registers with MDS</li>
+ * <li>{@link Constants#DATA_RESULT_MATCHING_BPK} if one register result found</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link CreateNewErnpEntryTask} if no results in registers where found for this user</li>
+ * <li>{@link GenerateOtherLoginMethodGuiTask} if search with MDS returns more than one match, user may provide
+ * alternative login methods to get an unique match</li>
+ * <li>{@link CreateIdentityLinkTask} if search in register returned one match, user is uniquely identified</li>
+ * </ul>
+ *
+ * @author amarsalek
+ * @author ckollmann
+ * @author tlenz
+ */
+@Slf4j
+@Component("InitialSearchTask")
+@SuppressWarnings("PMD.TooManyStaticImports")
+public class InitialSearchTask extends AbstractAuthServletTask {
+
+ private final RegisterSearchService registerSearchService;
+ private final ICcSpecificEidProcessingService eidPostProcessor;
+
+ /**
+ * Constructor.
+ *
+ * @param registerSearchService Service for register search access
+ * @param eidPostProcessor Country-Specific post processing of attributes
+ */
+ public InitialSearchTask(RegisterSearchService registerSearchService,
+ ICcSpecificEidProcessingService eidPostProcessor) {
+ this.registerSearchService = registerSearchService;
+ this.eidPostProcessor = eidPostProcessor;
+ }
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ final SimpleEidasData eidasData = convertEidasAttrToSimpleData();
+ MatchingTaskUtils.storeInitialEidasData(pendingReq, eidasData);
+ step2RegisterSearchWithPersonIdentifier(executionContext, eidasData);
+ } catch (WorkflowException e) {
+ throw new TaskExecutionException(pendingReq, "Initial search failed", e);
+ } catch (final Exception e) {
+ log.error("Initial search failed", e);
+ throw new TaskExecutionException(pendingReq, "Initial search failed with a generic error", e);
+ }
+ }
+
+ private void step2RegisterSearchWithPersonIdentifier(
+ ExecutionContext executionContext, SimpleEidasData eidasData) throws WorkflowException, EaafStorageException {
+ try {
+ log.trace("Starting step2RegisterSearchWithPersonIdentifier");
+ RegisterStatusResults searchResult = registerSearchService.searchWithPersonIdentifier(eidasData);
+ int resultCount = searchResult.getResultCount();
+ if (resultCount == 0) {
+ step6CountrySpecificSearch(executionContext, searchResult.getOperationStatus(), eidasData);
+ } else if (resultCount == 1) {
+ foundMatchFinalizeTask(searchResult, eidasData);
+ } else {
+ throw new WorkflowException("step2RegisterSearchWithPersonIdentifier",
+ "More than one entry with unique personal-identifier", true);
+ }
+ } catch (WorkflowException e) {
+ //TODO: what we do in case of a workflow error and manual matching are necessary??
+ log.warn("Workflow error during matching step: {}. Reason: {}", e.getProcessStepName(), e.getErrorReason());
+ throw e;
+ }
+ }
+
+ private void step6CountrySpecificSearch(
+ ExecutionContext executionContext, RegisterOperationStatus registerOperationStatus, SimpleEidasData eidasData)
+ throws EaafStorageException, WorkflowException {
+ log.trace("Starting 'step6CountrySpecificSearch' ... ");
+ RegisterStatusResults searchResult = registerSearchService.searchWithCountrySpecifics(
+ registerOperationStatus, eidasData);
+ if (searchResult.getResultCount() == 0) {
+ log.trace("'step6CountrySpecificSearch' ends with no result. Forward to next matching step ... ");
+ step8RegisterSearchWithMds(executionContext, searchResult.getOperationStatus(), eidasData);
+ } else if (searchResult.getResultCount() == 1) {
+ log.trace("'step6CountrySpecificSearch' finds a person. Forward to 'step7aKittProcess' step ... ");
+ registerSearchService.step7aKittProcess(searchResult, eidasData);
+ foundMatchFinalizeTask(searchResult, eidasData);
+ } else {
+ throw new WorkflowException("step6CountrySpecificSearch",
+ "More than one entry with unique country-specific information", true);
+ }
+ }
+
+ private void step8RegisterSearchWithMds(ExecutionContext executionContext,
+ RegisterOperationStatus registerOperationStatus, SimpleEidasData eidasData)
+ throws EaafStorageException, WorkflowException {
+ log.trace("Starting step8RegisterSearchWithMds");
+ RegisterStatusResults registerData = registerSearchService.searchWithMds(registerOperationStatus, eidasData);
+ if (registerData.getResultCount() == 0) {
+ log.debug("Matching step: 'step8RegisterSearchWithMds' has no result. Forward to create new ERnP entry ... ");
+ executionContext.put(TRANSITION_TO_CREATE_NEW_ERNP_ENTRY_TASK, true);
+ } else {
+ log.debug("Matching step: 'step8RegisterSearchWithMds' has #{} results. "
+ + "Forward to GUI based matching steps ... ", registerData.getResultCount());
+ MatchingTaskUtils.storeIntermediateMatchingResult(pendingReq, registerData);
+ executionContext.put(TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK, true);
+ }
+ }
+
+ private void foundMatchFinalizeTask(RegisterStatusResults searchResult, SimpleEidasData eidasData)
+ throws WorkflowException, EaafStorageException {
+ RegisterResult updatedResult = step3CheckRegisterUpdateNecessary(searchResult.getResult(), eidasData);
+ MatchedPersonResult result = MatchedPersonResult.generateFormMatchingResult(
+ updatedResult, eidasData.getCitizenCountryCode());
+ MatchingTaskUtils.storeFinalMatchingResult(pendingReq, result);
+ }
+
+ private RegisterResult step3CheckRegisterUpdateNecessary(RegisterResult searchResult,
+ SimpleEidasData eidasData) throws WorkflowException {
+ log.trace("Starting step3CheckRegisterUpdateNecessary");
+ if (!eidasData.equalsRegisterData(searchResult)) {
+ log.info("Skipping update-register-information step, because it's not supported yet");
+ //TODO: return updated search result if updates are allowed
+ return searchResult;
+ } else {
+ log.debug("Register information match to eIDAS information. No update required");
+ return searchResult;
+ }
+ }
+
+ @NotNull
+ private SimpleEidasData convertEidasAttrToSimpleData()
+ throws EidasAttributeException, EidPostProcessingException {
+ final ILightResponse eidasResponse = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq)
+ .getGenericDataFromSession(Constants.DATA_FULL_EIDAS_RESPONSE, ILightResponse.class);
+ Map<String, Object> simpleMap = MatchingTaskUtils.convertEidasAttrToSimpleMap(
+ eidasResponse.getAttributes().getAttributeMap(), log);
+ return eidPostProcessor.postProcess(simpleMap);
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAustrianResidenceGuiResponseTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAustrianResidenceGuiResponseTask.java
new file mode 100644
index 00000000..83fdf771
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAustrianResidenceGuiResponseTask.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2021 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 java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.stereotype.Component;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.InvalidUserInputException;
+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.service.RegisterSearchService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterStatusResults;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+
+/**
+ * Task receives the response of {@link GenerateAustrianResidenceGuiTask} and handles it.
+ * This corresponds to Steps 17B, 18, 19 in the eIDAS Matching Concept.
+ * Input:
+ * <ul>
+ * <li>{@link Constants#DATA_SIMPLE_EIDAS} initial login data from user</li>
+ * <li>{@link Constants#DATA_INTERMEDIATE_RESULT} results from search in registers with personIdentifier</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#DATA_RESULT_MATCHING_BPK} if one register result found</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link CreateNewErnpEntryTask} if no results from search with residency data in registers</li>
+ * <li>{@link CreateIdentityLinkTask} if one exact match between initial register search (with MDS) and results
+ * from search with residency data in registers exists</li>
+ * </ul>
+ *
+ * @author amarsalek
+ * @author ckollmann
+ * @author tlenz
+ */
+@Slf4j
+@Component("ReceiveAustrianResidenceGuiResponseTask")
+public class ReceiveAustrianResidenceGuiResponseTask extends AbstractAuthServletTask {
+
+ public static final String PARAM_FORMER_RESIDENCE_AVAILABLE = "formerResidenceAvailable";
+ public static final String PARAM_STREET = "street";
+ public static final String PARAM_CITY = "city";
+ public static final String PARAM_ZIPCODE = "zipcode";
+ private final RegisterSearchService registerSearchService;
+
+ public ReceiveAustrianResidenceGuiResponseTask(RegisterSearchService registerSearchService) {
+ this.registerSearchService = registerSearchService;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class UserInput {
+ private boolean formerResidenceAvailable;
+ private String zipcode;
+ private String city;
+ private String street;
+ }
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ log.trace("Starting ReceiveAustrianResidenceGuiResponseTask");
+
+ UserInput input = parseHtmlInput(request);
+ if (!input.isFormerResidenceAvailable()) {
+ moveToNextTask(executionContext);
+ return;
+
+ }
+
+ //TODO: Here, we need an error handling an can not stop full process if form input was invalid
+ //TODO: check minimum form elements
+ /*TODO: maybe we can switch to custom controller and use WebMVC form-binding feature.
+ * Binding element can be add as attribute to this request
+ */
+ if (input.getStreet().isEmpty() || input.getCity().isEmpty() || input.getZipcode().isEmpty()) {
+ // HTML form should ensure that mandatory fields are set => this should never happen
+ throw new TaskExecutionException(pendingReq, "Invalid user input",
+ new InvalidUserInputException("module.eidasauth.matching.06"));
+
+ }
+
+
+
+ try {
+ SimpleEidasData eidasData = MatchingTaskUtils.getInitialEidasData(pendingReq);
+ RegisterStatusResults initialSearchResult = MatchingTaskUtils.getIntermediateMatchingResult(pendingReq);
+
+ RegisterStatusResults residencyResult =
+ registerSearchService.searchWithResidence(initialSearchResult.getOperationStatus(),
+ eidasData, input.zipcode, input.city, input.street);
+ if (residencyResult.getResultCount() == 0) {
+ //TODO: her we should add a GUI step of result is zero to inform user an forward process by click
+ moveToNextTask(executionContext);
+
+ } else if (residencyResult.getResultCount() == 1) {
+ compareSearchResultWithInitialData(executionContext, residencyResult, eidasData);
+
+ } else {
+ /*TODO: align with form generation task and to better error handling in case of more-than-one result.
+ * Maybe the user has to provide more information.
+ */
+ throw new TaskExecutionException(pendingReq,
+ "Manual Fix necessary", new ManualFixNecessaryException(eidasData));
+
+ }
+
+ } catch (EaafStorageException e) {
+ log.error("Search with residency data failed", e);
+ throw new TaskExecutionException(pendingReq, "Search with residency data failed", e);
+
+ }
+ }
+
+ private void compareSearchResultWithInitialData(ExecutionContext executionContext,
+ RegisterStatusResults residencyResult, SimpleEidasData eidasData)
+ throws TaskExecutionException, EaafStorageException {
+ try {
+ /*TODO: check 'equalsRegisterData' because this method maybe this method evaluate to an invalid result.
+ * See TODO in methods body
+ */
+ if (eidasData.equalsRegisterData(residencyResult.getResult())) {
+ // update register information
+ registerSearchService.step7aKittProcess(residencyResult, eidasData);
+
+ // store search result to re-used in CreateIdentityLink step, because there we need bPK and MDS
+ MatchingTaskUtils.storeFinalMatchingResult(pendingReq,
+ MatchedPersonResult.generateFormMatchingResult(
+ residencyResult.getResult(), eidasData.getCitizenCountryCode()));
+
+ } else {
+ moveToNextTask(executionContext);
+
+ }
+
+ } catch (WorkflowException e) {
+ throw new TaskExecutionException(pendingReq, "Search failed", new ManualFixNecessaryException(eidasData));
+
+ }
+ }
+
+ private void moveToNextTask(ExecutionContext executionContext) {
+ // Later on, this should transition to Step 20
+ executionContext.put(Constants.TRANSITION_TO_CREATE_NEW_ERNP_ENTRY_TASK, true);
+
+ }
+
+ private @NotNull UserInput parseHtmlInput(HttpServletRequest request) {
+ Enumeration<String> reqParamNames = request.getParameterNames();
+ UserInput result = new UserInput();
+ while (reqParamNames.hasMoreElements()) {
+ final String paramName = reqParamNames.nextElement();
+ String escaped = StringEscapeUtils.escapeHtml(request.getParameter(paramName));
+ if (PARAM_FORMER_RESIDENCE_AVAILABLE.equalsIgnoreCase(paramName)) {
+ result.setFormerResidenceAvailable(Boolean.parseBoolean(escaped));
+
+ } else if (PARAM_STREET.equalsIgnoreCase(paramName)) {
+ result.setStreet(escaped);
+
+ } else if (PARAM_CITY.equalsIgnoreCase(paramName)) {
+ result.setCity(escaped);
+
+ } else if (PARAM_ZIPCODE.equalsIgnoreCase(paramName)) {
+ result.setZipcode(escaped);
+
+ }
+ }
+ return result;
+
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseAlternativeTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseAlternativeTask.java
new file mode 100644
index 00000000..aa04f55e
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveAuthnResponseAlternativeTask.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 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.connector.MsEidasNodeConstants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasValidationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.validator.EidasResponseValidator;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+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.auth.modules.AbstractAuthServletTask;
+import eu.eidas.auth.commons.light.ILightResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Receives the authn response from the eIDAS Node, containing the (alternative) eIDAS authentication.
+ * Input:
+ * <ul>
+ * <li>none</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#DATA_FULL_EIDAS_RESPONSE_ALTERNATIVE} the full response details</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link InitialSearchTask} to perform search in registers</li>
+ * </ul>
+ *
+ * @author tlenz
+ * @author ckollmann
+ */
+@Slf4j
+@Component("ReceiveAuthnResponseTask")
+public class ReceiveAuthnResponseAlternativeTask extends AbstractAuthServletTask {
+
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
+ @Autowired
+ private IConfiguration basicConfig;
+
+ @Autowired
+ private EidasAttributeRegistry attrRegistry;
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request,
+ HttpServletResponse response) throws TaskExecutionException {
+ try {
+ final ILightResponse eidasResponse = extractEidasResponse(request);
+ checkStatusCode(eidasResponse);
+ validateMsSpecificResponse(executionContext, eidasResponse);
+ storeInSession(eidasResponse);
+ } catch (final Exception e) {
+ log.warn("eIDAS Response processing FAILED.", e);
+ throw new TaskExecutionException(pendingReq, e.getMessage(),
+ new EidasSAuthenticationException("eidas.05", new Object[]{e.getMessage()}, e));
+ }
+ }
+
+ @NotNull
+ private ILightResponse extractEidasResponse(HttpServletRequest request) throws EidasSAuthenticationException {
+ final ILightResponse eidasResponse = (ILightResponse) request.getAttribute(Constants.DATA_FULL_EIDAS_RESPONSE);
+ if (eidasResponse == null) {
+ log.warn("NO eIDAS response-message found.");
+ throw new EidasSAuthenticationException("eidas.01", null);
+ }
+ log.debug("Receive eIDAS response with RespId: {} for ReqId: {}",
+ eidasResponse.getId(), eidasResponse.getInResponseToId());
+ log.trace("Full eIDAS-Resp: {}", eidasResponse);
+ return eidasResponse;
+ }
+
+ private void checkStatusCode(ILightResponse eidasResponse) throws EidasSAuthenticationException {
+ if (!eidasResponse.getStatus().getStatusCode().equals(Constants.SUCCESS_URI)) {
+ log.info("Receive eIDAS Response with StatusCode: {} Subcode: {} Msg: {}",
+ eidasResponse.getStatus().getStatusCode(),
+ eidasResponse.getStatus().getSubStatusCode(),
+ eidasResponse.getStatus().getStatusMessage());
+ throw new EidasSAuthenticationException("eidas.02", new Object[]{eidasResponse.getStatus()
+ .getStatusCode(), eidasResponse.getStatus().getStatusMessage()});
+ }
+ }
+
+ private void validateMsSpecificResponse(ExecutionContext executionContext, ILightResponse eidasResponse)
+ throws EidasValidationException {
+ final String spCountry = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, "AT");
+ final String citizenCountryCode = (String) executionContext.get(MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY);
+ EidasResponseValidator.validateResponse(pendingReq, eidasResponse, spCountry, citizenCountryCode, attrRegistry);
+ }
+
+ private void storeInSession(ILightResponse eidasResponse) throws EaafException {
+ log.debug("Store eIDAS response information into pending-request.");
+ final AuthProcessDataWrapper authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class);
+ authProcessData.setQaaLevel(eidasResponse.getLevelOfAssurance());
+ authProcessData.setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE_ALTERNATIVE, eidasResponse);
+ requestStoreage.storePendingRequest(pendingReq);
+ }
+
+}
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 6cab9214..86cd2164 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
@@ -19,13 +19,14 @@
* 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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -33,6 +34,7 @@ import at.asitplus.eidas.specific.connector.MsConnectorEventCodes;
import at.asitplus.eidas.specific.connector.MsEidasNodeConstants;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasValidationException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.validator.EidasResponseValidator;
import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
@@ -44,87 +46,102 @@ import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
import eu.eidas.auth.commons.light.ILightResponse;
import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Receives the authn response from the eIDAS Node, containing the (initial) eIDAS authentication.
+ * Input:
+ * <ul>
+ * <li>none</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#DATA_FULL_EIDAS_RESPONSE} the full response details</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link InitialSearchTask} to perform search in registers</li>
+ * </ul>
+ *
+ * @author tlenz
+ * @author ckollmann
+ */
@Slf4j
-@Component("ReceiveResponseFromeIDASNodeTask")
+@Component("ReceiveAuthnResponseTask")
public class ReceiveAuthnResponseTask extends AbstractAuthServletTask {
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private IConfiguration basicConfig;
+
@Autowired
private EidasAttributeRegistry attrRegistry;
@Override
public void execute(ExecutionContext executionContext, HttpServletRequest request,
- HttpServletResponse response) throws TaskExecutionException {
+ HttpServletResponse response) throws TaskExecutionException {
try {
- final ILightResponse eidasResponse = (ILightResponse) request.getAttribute(
- Constants.DATA_FULL_EIDAS_RESPONSE);
- if (eidasResponse == null) {
- log.warn("NO eIDAS response-message found.");
- throw new EidasSAuthenticationException("eidas.01", null);
-
- }
-
- log.debug("Receive eIDAS response with RespId:" + eidasResponse.getId() + " for ReqId:" + eidasResponse
- .getInResponseToId());
- log.trace("Full eIDAS-Resp: " + eidasResponse.toString());
- revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.RESPONSE_FROM_EIDAS_NODE, eidasResponse
- .getId());
-
- // check response StatusCode
- if (!eidasResponse.getStatus().getStatusCode().equals(Constants.SUCCESS_URI)) {
- log.info("Receice eIDAS Response with StatusCode:" + eidasResponse.getStatus().getStatusCode()
- + " Subcode:" + eidasResponse.getStatus().getSubStatusCode() + " Msg:" + eidasResponse.getStatus()
- .getStatusMessage());
- throw new EidasSAuthenticationException("eidas.02", new Object[] { eidasResponse.getStatus()
- .getStatusCode(), eidasResponse.getStatus().getStatusMessage() });
-
- }
-
- // extract all Attributes from response
-
- // **********************************************************
- // ******* MS-specificresponse validation **********
- // **********************************************************
- final String spCountry = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE,
- "AT");
- final String citizenCountryCode = (String) executionContext.get(
- MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY);
- EidasResponseValidator.validateResponse(pendingReq, eidasResponse, spCountry, citizenCountryCode,
- attrRegistry);
-
- // **********************************************************
- // ******* Store resonse infos into session object **********
- // **********************************************************
-
- // update MOA-Session data with received information
- log.debug("Store eIDAS response information into pending-request.");
- final EidAuthProcessDataWrapper authProcessData = pendingReq.getSessionData(EidAuthProcessDataWrapper.class);
- authProcessData.setQaaLevel(eidasResponse.getLevelOfAssurance());
- authProcessData.setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, eidasResponse);
-
-
- //inject set flag to inject
- authProcessData.setTestIdentity(
- basicConfig.getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_IS_TEST_IDENTITY, false));
-
- // store MOA-session to database
- requestStoreage.storePendingRequest(pendingReq);
+ final ILightResponse eidasResponse = extractEidasResponse(request);
+ checkStatusCode(eidasResponse);
+ validateMsSpecificResponse(executionContext, eidasResponse);
+ storeInSession(eidasResponse);
revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.RESPONSE_FROM_EIDAS_NODE_VALID);
-
} catch (final EaafException e) {
revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.RESPONSE_FROM_EIDAS_NODE_NOT_VALID);
throw new TaskExecutionException(pendingReq, "eIDAS Response processing FAILED.", e);
-
} catch (final Exception e) {
log.warn("eIDAS Response processing FAILED.", e);
revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.RESPONSE_FROM_EIDAS_NODE_NOT_VALID);
throw new TaskExecutionException(pendingReq, e.getMessage(),
- new EidasSAuthenticationException("eidas.05", new Object[] { e.getMessage() }, e));
+ new EidasSAuthenticationException("eidas.05", new Object[]{e.getMessage()}, e));
+ }
+ }
+ @NotNull
+ private ILightResponse extractEidasResponse(HttpServletRequest request) throws EidasSAuthenticationException {
+ final ILightResponse eidasResponse = (ILightResponse) request.getAttribute(Constants.DATA_FULL_EIDAS_RESPONSE);
+ if (eidasResponse == null) {
+ log.warn("NO eIDAS response-message found.");
+ throw new EidasSAuthenticationException("eidas.01", null);
}
+ log.debug("Receive eIDAS response with RespId: {} for ReqId: {}",
+ eidasResponse.getId(), eidasResponse.getInResponseToId());
+ log.trace("Full eIDAS-Resp: {}", eidasResponse);
+ revisionsLogger.logEvent(pendingReq, MsConnectorEventCodes.RESPONSE_FROM_EIDAS_NODE, eidasResponse.getId());
+ return eidasResponse;
+ }
+
+ private void checkStatusCode(ILightResponse eidasResponse) throws EidasSAuthenticationException {
+ if (!eidasResponse.getStatus().getStatusCode().equals(Constants.SUCCESS_URI)) {
+ log.info("Receive eIDAS Response with StatusCode: {} Subcode: {} Msg: {}",
+ eidasResponse.getStatus().getStatusCode(),
+ eidasResponse.getStatus().getSubStatusCode(),
+ eidasResponse.getStatus().getStatusMessage());
+ throw new EidasSAuthenticationException("eidas.02", new Object[]{eidasResponse.getStatus()
+ .getStatusCode(), eidasResponse.getStatus().getStatusMessage()});
+ }
+ }
+
+ private void validateMsSpecificResponse(ExecutionContext executionContext, ILightResponse eidasResponse)
+ throws EidasValidationException {
+ final String spCountry = basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, "AT");
+ final String citizenCountryCode = (String) executionContext.get(MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY);
+ EidasResponseValidator.validateResponse(pendingReq, eidasResponse, spCountry, citizenCountryCode, attrRegistry);
+ }
+ private void storeInSession(ILightResponse eidasResponse) throws EaafException {
+ log.debug("Store eIDAS response information into pending-request.");
+ final EidAuthProcessDataWrapper authProcessData = pendingReq.getSessionData(EidAuthProcessDataWrapper.class);
+ authProcessData.setQaaLevel(eidasResponse.getLevelOfAssurance());
+
+ //inject set flag to inject
+ authProcessData.setTestIdentity(
+ basicConfig.getBasicConfigurationBoolean(Constants.CONIG_PROPS_EIDAS_IS_TEST_IDENTITY, false));
+
+
+ authProcessData.setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, eidasResponse);
+ requestStoreage.storePendingRequest(pendingReq);
+
}
}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveMobilePhoneSignatureResponseTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveMobilePhoneSignatureResponseTask.java
new file mode 100644
index 00000000..d43a175f
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveMobilePhoneSignatureResponseTask.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2021 A-SIT Plus GmbH
+ * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ,
+ * A-SIT Plus GmbH, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "License");
+ * You may not use this work except in compliance with the License.
+ * You may obtain a copy of the License at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ */
+
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks;
+
+import static at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthConstants.MODULE_NAME_FOR_LOGGING;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.transform.TransformerException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.messaging.decoder.MessageDecodingException;
+import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.core.StatusCode;
+import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
+import org.springframework.stereotype.Component;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleMobileSignatureData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.InvalidUserInputException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthConstants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthEventConstants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider.IdAustriaClientAuthCredentialProvider;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.provider.IdAustriaClientAuthMetadataProvider;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterStatusResults;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils;
+import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.exceptions.EaafBuilderException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import at.gv.egiz.eaaf.modules.pvp2.api.binding.IDecoder;
+import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException;
+import at.gv.egiz.eaaf.modules.pvp2.exception.SamlAssertionValidationExeption;
+import at.gv.egiz.eaaf.modules.pvp2.exception.SamlSigningException;
+import at.gv.egiz.eaaf.modules.pvp2.impl.binding.PostBinding;
+import at.gv.egiz.eaaf.modules.pvp2.impl.binding.RedirectBinding;
+import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage;
+import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileResponse;
+import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils;
+import at.gv.egiz.eaaf.modules.pvp2.impl.validation.EaafUriCompare;
+import at.gv.egiz.eaaf.modules.pvp2.impl.validation.TrustEngineFactory;
+import at.gv.egiz.eaaf.modules.pvp2.impl.verification.SamlVerificationEngine;
+import at.gv.egiz.eaaf.modules.pvp2.sp.exception.AssertionValidationExeption;
+import at.gv.egiz.eaaf.modules.pvp2.sp.exception.AuthnResponseValidationException;
+import at.gv.egiz.eaaf.modules.pvp2.sp.impl.utils.AssertionAttributeExtractor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Task that receives the SAML2 response from ID Austria system.
+ * This corresponds to Step 15 in the eIDAS Matching Concept.
+ * Input:
+ * <ul>
+ * <li>{@link Constants#DATA_SIMPLE_EIDAS} initial login data from user</li>
+ * <li>{@link Constants#DATA_INTERMEDIATE_RESULT} results from search in registers with personIdentifier</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#DATA_RESULT_MATCHING_BPK} if one register result found</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link GenerateAustrianResidenceGuiTask} if no results in registers were found</li>
+ * <li>{@link CreateIdentityLinkTask} if one exact match between initial register search (with MDS) data and
+ * register search with MPS data exists</li>
+ * </ul>
+ *
+ * @author tlenz
+ * @author ckollmann
+ */
+@Slf4j
+@Component("ReceiveMobilePhoneSignatureResponseTask")
+public class ReceiveMobilePhoneSignatureResponseTask extends AbstractAuthServletTask {
+
+ private final SamlVerificationEngine samlVerificationEngine;
+ private final RegisterSearchService registerSearchService;
+ private final IdAustriaClientAuthCredentialProvider credentialProvider;
+ private final IdAustriaClientAuthMetadataProvider metadataProvider;
+
+ private static final String ERROR_PVP_03 = "sp.pvp2.03";
+ private static final String ERROR_PVP_05 = "sp.pvp2.05";
+ private static final String ERROR_PVP_06 = "sp.pvp2.06";
+ private static final String ERROR_PVP_08 = "sp.pvp2.08";
+ private static final String ERROR_PVP_10 = "sp.pvp2.10";
+ private static final String ERROR_PVP_11 = "sp.pvp2.11";
+ private static final String ERROR_PVP_12 = "sp.pvp2.12";
+
+ private static final String ERROR_MSG_00 = "Receive INVALID PVP Response from ID Austria system";
+ private static final String ERROR_MSG_01 = "Processing PVP response from 'ID Austria system' FAILED.";
+ private static final String ERROR_MSG_02 = "PVP response decryption FAILED. No credential found.";
+ private static final String ERROR_MSG_03 = "PVP response validation FAILED.";
+
+ /**
+ * Creates the new task, with autowired dependencies from Spring.
+ */
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
+ public ReceiveMobilePhoneSignatureResponseTask(SamlVerificationEngine samlVerificationEngine,
+ RegisterSearchService registerSearchService,
+ IdAustriaClientAuthCredentialProvider credentialProvider,
+ IdAustriaClientAuthMetadataProvider metadataProvider) {
+ this.samlVerificationEngine = samlVerificationEngine;
+ this.registerSearchService = registerSearchService;
+ this.credentialProvider = credentialProvider;
+ this.metadataProvider = metadataProvider;
+ }
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ log.trace("Starting ReceiveMobilePhoneSignatureResponseTask");
+ IDecoder decoder = loadDecoder(request);
+ EaafUriCompare comparator = loadComparator(request);
+ InboundMessage inboundMessage = decodeAndVerifyMessage(request, response, decoder, comparator);
+ Pair<PvpSProfileResponse, Boolean> processedMsg = validateAssertion((PvpSProfileResponse) inboundMessage);
+ if (processedMsg.getSecond()) {
+ // forward to next matching step in case of ID Autria authentication was stopped by user
+ executionContext.put(Constants.TRANSITION_TO_GENERATE_GUI_QUERY_AUSTRIAN_RESIDENCE_TASK, true);
+ return;
+
+ }
+
+ // validate SAML2 response
+ validateEntityId(inboundMessage);
+ log.info("Receive a valid assertion from IDP " + inboundMessage.getEntityID());
+
+ // load already existing information from session
+ SimpleEidasData eidasData = MatchingTaskUtils.getInitialEidasData(pendingReq);
+ RegisterStatusResults initialSearchResult = MatchingTaskUtils.getIntermediateMatchingResult(pendingReq);
+
+ // extract user information from ID Austria authentication
+ AssertionAttributeExtractor extractor = new AssertionAttributeExtractor(processedMsg.getFirst().getResponse());
+ SimpleMobileSignatureData simpleMobileSignatureData = getAuthDataFromInterfederation(extractor);
+
+ // check if MDS from ID Austria authentication matchs to eIDAS authentication
+ if (!simpleMobileSignatureData.equalsSimpleEidasData(eidasData)) {
+ // user has cheated!?
+ throw new InvalidUserInputException("module.eidasauth.matching.05");
+
+ }
+
+ // search entry in initial search result from steps before and build new RegisterSearchResult
+ RegisterStatusResults registerResult = new RegisterStatusResults(initialSearchResult.getOperationStatus(),
+ extractEntriesByBpk(initialSearchResult.getResultsZmr().stream(), simpleMobileSignatureData.getBpk()),
+ extractEntriesByBpk(initialSearchResult.getResultsErnp().stream(), simpleMobileSignatureData.getBpk()));
+
+ if (registerResult.getResultCount() != 1) {
+ throw new WorkflowException("matchWithIDAustriaAuthentication",
+ "Suspect state detected. MDS matches to eIDAS authentication "
+ + "but register search-result with MDS contains #" + registerResult.getResultCount()
+ + " entry with bPK from ID Austria authentication", false);
+
+ } else {
+ // perform kit operation
+ registerSearchService.step7aKittProcess(registerResult, eidasData);
+
+ // store search result to re-used in CreateIdentityLink step, because there we need bPK and MDS
+ MatchingTaskUtils.storeFinalMatchingResult(pendingReq,
+ MatchedPersonResult.generateFormMatchingResult(registerResult.getResult(),
+ eidasData.getCitizenCountryCode()));
+
+ }
+
+ } catch (final AuthnResponseValidationException e) {
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_03, e);
+
+ } catch (MessageDecodingException | SecurityException | SamlSigningException e) {
+ //final String samlRequest = request.getParameter("SAMLRequest");
+ //log.debug("Receive INVALID PVP Response from 'ms-specific eIDAS node': {}",
+ // samlRequest, null, e);
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_00,
+ new AuthnResponseValidationException(ERROR_PVP_11, new Object[]{MODULE_NAME_FOR_LOGGING}, e));
+
+ } catch (IOException | MarshallingException | TransformerException e) {
+ log.debug("Processing PVP response from 'ms-specific eIDAS node' FAILED.", e);
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_01,
+ new AuthnResponseValidationException(ERROR_PVP_12, new Object[]{MODULE_NAME_FOR_LOGGING, e.getMessage()}, e));
+
+ } catch (final CredentialsNotAvailableException e) {
+ log.debug("PVP response decryption FAILED. No credential found.", e);
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_02,
+ new AuthnResponseValidationException(ERROR_PVP_10, new Object[]{MODULE_NAME_FOR_LOGGING}, e));
+
+ } catch (final Exception e) {
+ // todo catch ManualFixNecessaryException in any other way?
+ log.debug("PVP response validation FAILED. Msg:" + e.getMessage(), e);
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_03,
+ new AuthnResponseValidationException(ERROR_PVP_12, new Object[]{MODULE_NAME_FOR_LOGGING, e.getMessage()}, e));
+
+ }
+ }
+
+ private List<RegisterResult> extractEntriesByBpk(Stream<RegisterResult> stream, String bpk) {
+ return stream.filter(el -> bpk.equals(el.getBpk())).collect(Collectors.toList());
+
+ }
+
+ @NotNull
+ private InboundMessage decodeAndVerifyMessage(HttpServletRequest request, HttpServletResponse response,
+ IDecoder decoder, EaafUriCompare comparator) throws Exception {
+ InboundMessage inboundMessage = (InboundMessage) decoder.decode(request, response, metadataProvider,
+ IDPSSODescriptor.DEFAULT_ELEMENT_NAME, comparator);
+ if (!inboundMessage.isVerified()) {
+ samlVerificationEngine.verify(inboundMessage, TrustEngineFactory.getSignatureKnownKeysTrustEngine(
+ metadataProvider));
+ inboundMessage.setVerified(true);
+ }
+ return inboundMessage;
+ }
+
+ private void validateEntityId(InboundMessage inboundMessage) throws AuthnResponseValidationException {
+ final String msNodeEntityID = authConfig
+ .getBasicConfiguration(IdAustriaClientAuthConstants.CONFIG_PROPS_ID_AUSTRIA_ENTITYID);
+ final String respEntityId = inboundMessage.getEntityID();
+ if (!msNodeEntityID.equals(respEntityId)) {
+ log.warn("Response Issuer is not from valid 'ID Austria IDP'. Stopping ID Austria authentication ...");
+ throw new AuthnResponseValidationException(ERROR_PVP_08,
+ new Object[]{MODULE_NAME_FOR_LOGGING,
+ inboundMessage.getEntityID()});
+ }
+ }
+
+ @NotNull
+ private EaafUriCompare loadComparator(HttpServletRequest request) throws AuthnResponseValidationException {
+ if (request.getMethod().equalsIgnoreCase("POST")) {
+ log.trace("Receive PVP Response from 'ID Austria system', by using POST-Binding.");
+ return new EaafUriCompare(pendingReq.getAuthUrl() + IdAustriaClientAuthConstants.ENDPOINT_POST);
+ } else if (request.getMethod().equalsIgnoreCase("GET")) {
+ log.trace("Receive PVP Response from 'ID Austria system', by using Redirect-Binding.");
+ return new EaafUriCompare(pendingReq.getAuthUrl() + IdAustriaClientAuthConstants.ENDPOINT_REDIRECT);
+ } else {
+ log.warn("Receive PVP Response from 'ID Austria system', but Binding {} is not supported.", request.getMethod());
+ throw new AuthnResponseValidationException(ERROR_PVP_03, new Object[]{MODULE_NAME_FOR_LOGGING});
+ }
+ }
+
+ @NotNull
+ private IDecoder loadDecoder(HttpServletRequest request) throws AuthnResponseValidationException {
+ if (request.getMethod().equalsIgnoreCase("POST")) {
+ log.trace("Receive PVP Response from 'ID Austria system', by using POST-Binding.");
+ return new PostBinding();
+ } else if (request.getMethod().equalsIgnoreCase("GET")) {
+ log.trace("Receive PVP Response from 'ID Austria system', by using Redirect-Binding.");
+ return new RedirectBinding();
+ } else {
+ log.warn("Receive PVP Response from 'ID Austria system', but Binding {} is not supported.", request.getMethod());
+ throw new AuthnResponseValidationException(ERROR_PVP_03, new Object[]{MODULE_NAME_FOR_LOGGING});
+ }
+ }
+
+ private Pair<PvpSProfileResponse, Boolean> validateAssertion(PvpSProfileResponse msg)
+ throws IOException, MarshallingException, TransformerException,
+ CredentialsNotAvailableException, AuthnResponseValidationException, SamlAssertionValidationExeption {
+ log.debug("Start PVP21 assertion processing... ");
+ final Response response = (Response) msg.getResponse();
+ if (response.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS)) {
+ samlVerificationEngine.validateAssertion(response,
+ credentialProvider.getMessageEncryptionCredential(),
+ pendingReq.getAuthUrl() + IdAustriaClientAuthConstants.ENDPOINT_METADATA,
+ MODULE_NAME_FOR_LOGGING);
+ msg.setSamlMessage(Saml2Utils.asDomDocument(response).getDocumentElement());
+ revisionsLogger.logEvent(pendingReq,
+ IdAustriaClientAuthEventConstants.AUTHPROCESS_ID_AUSTRIA_RESPONSE_RECEIVED,
+ response.getID());
+ return Pair.newInstance(msg, false);
+ } else {
+ log.info("Receive StatusCode {} from 'ms-specific eIDAS node'.", response.getStatus().getStatusCode().getValue());
+ StatusCode subStatusCode = getSubStatusCode(response);
+ if (subStatusCode != null
+ && IdAustriaClientAuthConstants.SAML2_STATUSCODE_USERSTOP.equals(subStatusCode.getValue())) {
+ log.info("Find 'User-Stop operation' in SAML2 response. Stopping authentication process ... ");
+ return Pair.newInstance(msg, true);
+ }
+
+ revisionsLogger.logEvent(pendingReq,
+ IdAustriaClientAuthEventConstants.AUTHPROCESS_ID_AUSTRIA_RESPONSE_RECEIVED_ERROR);
+ throw new AuthnResponseValidationException(ERROR_PVP_05,
+ new Object[]{MODULE_NAME_FOR_LOGGING,
+ response.getIssuer().getValue(),
+ response.getStatus().getStatusCode().getValue(),
+ response.getStatus().getStatusMessage().getMessage()});
+ }
+ }
+
+ /**
+ * Get SAML2 Sub-StatusCode if not <code>null</code>.
+ *
+ * @param samlResp SAML2 response
+ * @return Sub-StatusCode or <code>null</code> if it's not set
+ */
+ private StatusCode getSubStatusCode(Response samlResp) {
+ if (samlResp.getStatus().getStatusCode().getStatusCode() != null
+ && StringUtils.isNotEmpty(samlResp.getStatus().getStatusCode().getStatusCode().getValue())) {
+ return samlResp.getStatus().getStatusCode().getStatusCode();
+ }
+ return null;
+ }
+
+ private SimpleMobileSignatureData getAuthDataFromInterfederation(AssertionAttributeExtractor extractor)
+ throws EaafBuilderException {
+ List<String> requiredAttributes = IdAustriaClientAuthConstants.DEFAULT_REQUIRED_PVP_ATTRIBUTE_NAMES;
+ SimpleMobileSignatureData.SimpleMobileSignatureDataBuilder builder = SimpleMobileSignatureData.builder();
+ if (!extractor.containsAllRequiredAttributes(requiredAttributes)) {
+ log.warn("PVP Response from 'ID Austria node' contains not all requested attributes.");
+ AssertionValidationExeption e = new AssertionValidationExeption(ERROR_PVP_06,
+ new Object[]{MODULE_NAME_FOR_LOGGING});
+ throw new EaafBuilderException(ERROR_PVP_06, null, e.getMessage(), e);
+ }
+ final Set<String> includedAttrNames = extractor.getAllIncludeAttributeNames();
+ for (final String attrName : includedAttrNames) {
+ if (PvpAttributeDefinitions.BPK_NAME.equals(attrName)) {
+ builder.bpk(extractor.getSingleAttributeValue(attrName));
+ }
+ if (PvpAttributeDefinitions.GIVEN_NAME_NAME.equals(attrName)) {
+ builder.givenName(extractor.getSingleAttributeValue(attrName));
+ }
+ if (PvpAttributeDefinitions.PRINCIPAL_NAME_NAME.equals(attrName)) {
+ builder.familyName(extractor.getSingleAttributeValue(attrName));
+ }
+ if (PvpAttributeDefinitions.BIRTHDATE_NAME.equals(attrName)) {
+ builder.dateOfBirth(extractor.getSingleAttributeValue(attrName));
+ }
+ if (PvpAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME.equals(attrName)) {
+ MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq).setQaaLevel(
+ extractor.getSingleAttributeValue(attrName));
+ }
+ }
+ MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq).setIssueInstant(extractor.getAssertionIssuingDate());
+ return builder.build();
+
+ }
+
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveOtherLoginMethodGuiResponseTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveOtherLoginMethodGuiResponseTask.java
new file mode 100644
index 00000000..8431d968
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveOtherLoginMethodGuiResponseTask.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2021 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 java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.springframework.stereotype.Component;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SelectedLoginMethod;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.InvalidUserInputException;
+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.idp.controller.tasks.AbstractLocaleAuthServletTask;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Handles user's selection from {@link GenerateOtherLoginMethodGuiTask}.
+ * This corresponds to Steps 10, 14, 16 in the eIDAS Matching Concept.
+ * Input:
+ * <ul>
+ * <li>{@link Constants#DATA_SIMPLE_EIDAS} initial login data from user</li>
+ * <li>{@link Constants#DATA_INTERMEDIATE_RESULT} results from search in registers with personIdentifier</li>
+ * </ul>
+ * Output:
+ * <ul>
+ * <li>{@link Constants#DATA_RESULT_MATCHING_BPK} if one register result found</li>
+ * </ul>
+ * Transitions:
+ * <ul>
+ * <li>{@link GenerateMobilePhoneSignatureRequestTask} if selected by user</li>
+ * <li>{@link GenerateAustrianResidenceGuiTask} if selected by user</li>
+ * </ul>
+ *
+ * @author amarsalek
+ * @author ckollmann
+ */
+@Slf4j
+@Component("ReceiveOtherLoginMethodGuiResponseTask")
+public class ReceiveOtherLoginMethodGuiResponseTask extends AbstractLocaleAuthServletTask {
+
+ @Override
+ public void executeWithLocale(ExecutionContext executionContext, HttpServletRequest request,
+ HttpServletResponse response) throws TaskExecutionException {
+ try {
+ SelectedLoginMethod selection = SelectedLoginMethod.valueOf(extractUserSelection(request));
+ executionContext.put(Constants.REQ_SELECTED_LOGIN_METHOD_PARAMETER, selection);
+ transitionToNextTask(executionContext, selection);
+
+ } catch (final IllegalArgumentException e) {
+ log.error("Parsing selected login method FAILED.", e);
+ throw new TaskExecutionException(pendingReq, "Parsing selected login method FAILED.",
+ new InvalidUserInputException("module.eidasauth.matching.98"));
+
+ } catch (final Exception e) {
+ log.error("Parsing selected login method FAILED.", e);
+ throw new TaskExecutionException(pendingReq, "Parsing selected login method FAILED.", e);
+
+ }
+ }
+
+ private String extractUserSelection(HttpServletRequest request) throws InvalidUserInputException {
+ Enumeration<String> paramNames = request.getParameterNames();
+ while (paramNames.hasMoreElements()) {
+ String paramName = paramNames.nextElement();
+ if (Constants.REQ_SELECTED_LOGIN_METHOD_PARAMETER.equalsIgnoreCase(paramName)) {
+ return StringEscapeUtils.escapeHtml(request.getParameter(paramName));
+
+ }
+ }
+
+ throw new InvalidUserInputException("module.eidasauth.matching.98");
+
+ }
+
+ private void transitionToNextTask(ExecutionContext executionContext, SelectedLoginMethod selection)
+ throws InvalidUserInputException {
+ switch (selection) {
+ case EIDAS_LOGIN:
+ executionContext.put(Constants.TRANSITION_TO_GENERATE_EIDAS_LOGIN, true);
+ return;
+
+ case MOBILE_PHONE_SIGNATURE_LOGIN:
+ executionContext.put(Constants.TRANSITION_TO_GENERATE_MOBILE_PHONE_SIGNATURE_REQUEST_TASK, true);
+ return;
+
+ case NO_OTHER_LOGIN:
+ executionContext.put(Constants.TRANSITION_TO_GENERATE_GUI_QUERY_AUSTRIAN_RESIDENCE_TASK, true);
+ return;
+
+ default:
+ throw new InvalidUserInputException("module.eidasauth.matching.98");
+
+ }
+ }
+
+}
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 c8c5a069..6b541135 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
@@ -19,10 +19,12 @@
* 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.utils;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
@@ -39,6 +41,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
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 eu.eidas.auth.commons.attribute.AttributeDefinition;
import eu.eidas.auth.commons.attribute.AttributeValue;
@@ -49,13 +53,12 @@ import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress;
public class EidasResponseUtils {
private static final Logger log = LoggerFactory.getLogger(EidasResponseUtils.class);
-
public static final String PERSONALIDENIFIER_VALIDATION_PATTERN = "^[A-Z,a-z]{2}/[A-Z,a-z]{2}/.*";
/**
* 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
@@ -71,9 +74,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>
@@ -81,37 +84,40 @@ 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;
}
return Triple.newInstance(uniqueID.substring(0, 2), uniqueID.substring(3, 5), uniqueID.substring(6));
-
}
/**
- * 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.
+ * @param attributeValues Attributes from eIDAS response
+ * @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,
- ImmutableSet<? extends AttributeValue<?>> attributeValues) {
+ @Nullable ImmutableSet<? extends AttributeValue<?>> attributeValues) {
final List<String> stringListAttribute = new ArrayList<>();
- if (attributeValues != null) {
- final AttributeValueMarshaller<?> attributeValueMarshaller = attributeDefinition
- .getAttributeValueMarshaller();
- for (final AttributeValue<?> attributeValue : attributeValues.asList()) {
+ if (attributeValues == null) {
+ log.info("Can not extract infos from 'null' attribute value");
+
+ } else {
+ final AttributeValueMarshaller<?> attributeValueMarshaller =
+ attributeDefinition.getAttributeValueMarshaller();
+ for (final AttributeValue<?> attributeValue : attributeValues) {
String valueString = null;
try {
valueString = attributeValueMarshaller.marshal((AttributeValue) attributeValue);
log.trace("Find attr: {} with value: {} nonLatinFlag: {} needTransliteration: {}",
attributeDefinition.getFriendlyName(), attributeValue.toString(),
- attributeValue.isNonLatinScriptAlternateVersion(),
+ attributeValue.isNonLatinScriptAlternateVersion(),
AttributeValueTransliterator.needsTransliteration(valueString));
// if (attributeValue.isNonLatinScriptAlternateVersion()) {
@@ -122,36 +128,47 @@ public class EidasResponseUtils {
log.trace("Find 'needsTransliteration' flag. Setting this value at last list element ... ");
stringListAttribute.add(valueString);
- }
+ log.trace("Find attr: {} with value: {} nonLatinFlag: {} needTransliteration: {}",
+ attributeDefinition.getFriendlyName(), attributeValue.toString(),
+ attributeValue.isNonLatinScriptAlternateVersion(),
+ AttributeValueTransliterator.needsTransliteration(valueString));
+ // if (attributeValue.isNonLatinScriptAlternateVersion()) {
+ if (!AttributeValueTransliterator.needsTransliteration(valueString)) {
+ stringListAttribute.add(0, valueString);
+
+ } else {
+ log.trace("Find 'needsTransliteration' flag. Setting this value at last list element ... ");
+ stringListAttribute.add(valueString);
+
+ }
+ }
} catch (final AttributeValueMarshallingException e) {
throw new IllegalStateException(e);
}
- }
- log.trace("Extract values: {} for attr: {}",
+ }
+ log.trace("Extract values: {} for attr: {}",
StringUtils.join(stringListAttribute, ","), attributeDefinition.getFriendlyName());
-
- } else {
- log.info("Can not extract infos from 'null' attribute value");
-
+
}
return stringListAttribute;
}
+
/**
- * Convert eIDAS DateTime attribute to Java Object.
- *
+ * Convert eIDAS DateTime attribute to Java Object.
+ *
* @param attributeDefinition eIDAS attribute definition.
- * @param attributeValues eIDAS attribute value
+ * @param attributeValues eIDAS attribute value
* @return
*/
@Nullable
public static DateTime translateDateAttribute(AttributeDefinition<?> attributeDefinition,
- ImmutableList<? extends AttributeValue<?>> attributeValues) {
+ ImmutableList<? extends AttributeValue<?>> attributeValues) {
if (attributeValues.size() != 0) {
final AttributeValue<?> firstAttributeValue = attributeValues.get(0);
return (DateTime) firstAttributeValue.getValue();
@@ -163,17 +180,216 @@ public class EidasResponseUtils {
/**
* Concert eIDAS Address attribute to Java object.
- *
+ *
* @param attributeDefinition eIDAS attribute definition
- * @param attributeValues eIDAS attribute value
+ * @param attributeValues eIDAS attribute value
* @return
*/
@Nullable
public static PostalAddress translateAddressAttribute(AttributeDefinition<?> attributeDefinition,
- ImmutableList<? extends AttributeValue<?>> attributeValues) {
+ 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 String) {
+ try {
+ new SimpleDateFormat("yyyy-MM-dd").parse((String) dateOfBirthObj);
+ return (String) dateOfBirthObj;
+ } catch (ParseException e) {
+ throw new EidasAttributeException(Constants.eIDAS_ATTR_DATEOFBIRTH);
+ }
+ }
+ 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 personal identifier attribute.
+ *
+ * @param personalIdentifierObj eIDAS personal identifier attribute-information
+ * @return formated user's full personal identifier
+ * @throws EidasAttributeException if NO attribute is available
+ */
+ public static String processPersonalIdentifier(Object personalIdentifierObj) throws EidasAttributeException {
+ if (!(personalIdentifierObj instanceof String)) {
+ throw new EidasAttributeException(Constants.eIDAS_ATTR_PERSONALIDENTIFIER);
+ }
+ return (String) personalIdentifierObj;
+ }
+
+
+ /**
+ * 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 pseudonym to citizen country code.
+ *
+ * @param personalIdObj eIDAS PersonalIdentifierAttribute
+ * @return Citizen Country Code
+ * @throws EidasAttributeException if NO attribute is available
+ */
+ public static String processCountryCode(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.getFirst() == null) {
+ throw new EidasAttributeException("Error processing eIdentifier");
+ }
+ return eIdentifier.getFirst();
+ }
+
+ /**
+ * 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_TAXREFERENCE);
+ }
+ return (String) taxReferenceObj;
+ }
+} \ No newline at end of file
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/MatchingTaskUtils.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/MatchingTaskUtils.java
new file mode 100644
index 00000000..c7631f53
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/MatchingTaskUtils.java
@@ -0,0 +1,169 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.utils;
+
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MatchedPersonResult;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.ICcSpecificEidProcessingService;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterStatusResults;
+import at.gv.egiz.eaaf.core.api.IRequest;
+import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper;
+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.protocol.eidas.impl.PostalAddress;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.springframework.lang.NonNull;
+
+import javax.annotation.Nullable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MatchingTaskUtils {
+
+ /**
+ * Get eIDAS log-in information from session.
+ *
+ * @param pendingReq Current pendingRequest
+ * @return eIDAS infos or <code>null</code> if not exist
+ */
+ @Nullable
+ public static SimpleEidasData getInitialEidasData(IRequest pendingReq) {
+ return getAuthProcessDataWrapper(pendingReq).getGenericDataFromSession(
+ Constants.DATA_SIMPLE_EIDAS, SimpleEidasData.class);
+
+ }
+
+ /**
+ * Set eIDAS log-in information to session.
+ *
+ * @param pendingReq Current pendingRequest
+ * @param eidasData infos from eIDAS Proxy-Service
+ * @throws EaafStorageException In case of data can not be add into session
+ */
+ @Nullable
+ public static void storeInitialEidasData(IRequest pendingReq, SimpleEidasData eidasData)
+ throws EaafStorageException {
+ getAuthProcessDataWrapper(pendingReq).setGenericDataToSession(Constants.DATA_SIMPLE_EIDAS, eidasData);
+
+ }
+
+ /**
+ * Get intermediate matching result from session.
+ *
+ * @param pendingReq Current pendingRequest
+ * @return Intermediate matching result or <code>null</code> if not exist
+ */
+ @Nullable
+ public static RegisterStatusResults getIntermediateMatchingResult(IRequest pendingReq) {
+ return getAuthProcessDataWrapper(pendingReq).getGenericDataFromSession(Constants.DATA_INTERMEDIATE_RESULT,
+ RegisterStatusResults.class);
+
+ }
+
+ /**
+ * Store intermediate matching result into session.
+ *
+ * @param pendingReq Current pendingRequest
+ * @param registerData Intermediate matching result information
+ * @throws EaafStorageException In case of data can not be add into session
+ */
+ @Nullable
+ public static void storeIntermediateMatchingResult(IRequest pendingReq, RegisterStatusResults registerData)
+ throws EaafStorageException {
+ getAuthProcessDataWrapper(pendingReq).setGenericDataToSession(
+ Constants.DATA_INTERMEDIATE_RESULT, registerData);
+
+ }
+
+ /**
+ * Get intermediate matching result from session.
+ *
+ * @param pendingReq Current pendingRequest
+ * @return Intermediate matching result or <code>null</code> if not exist
+ */
+ @Nullable
+ public static MatchedPersonResult getFinalMatchingResult(IRequest pendingReq) {
+ return getAuthProcessDataWrapper(pendingReq).getGenericDataFromSession(Constants.DATA_PERSON_MATCH_RESULT,
+ MatchedPersonResult.class);
+
+ }
+
+ /**
+ * Store intermediate matching result into session.
+ *
+ * @param pendingReq Current pendingRequest
+ * @param personInfos Person information after a successful match
+ * @throws EaafStorageException In case of data can not be add into session
+ */
+ @Nullable
+ public static void storeFinalMatchingResult(IRequest pendingReq, MatchedPersonResult personInfos)
+ throws EaafStorageException {
+ getAuthProcessDataWrapper(pendingReq).setGenericDataToSession(
+ Constants.DATA_PERSON_MATCH_RESULT, personInfos);
+
+ }
+
+ /**
+ * Get holder for authentication information for the current process.
+ *
+ * @param pendingReq Current pendingRequest
+ * @return {@link AuthProcessDataWrapper}
+ */
+ @NonNull
+ public static AuthProcessDataWrapper getAuthProcessDataWrapper(IRequest pendingReq) {
+ return pendingReq.getSessionData(AuthProcessDataWrapper.class);
+
+ }
+
+ /**
+ * Convert attributes from eIDAS Authn Response into a simple map, to be used from
+ * {@link ICcSpecificEidProcessingService#postProcess(Map)}.
+ */
+ public static Map<String, Object> convertEidasAttrToSimpleMap(
+ ImmutableMap<AttributeDefinition<?>, ImmutableSet<? extends AttributeValue<?>>> attributeMap, Logger log) {
+ 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 '{}' with value: {}", el.getFriendlyName(), attribute);
+ } 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 '{}' with value: {}", el.getFriendlyName(), addressAttribute);
+ } else {
+ log.info("Ignore empty 'PostalAddress' attribute");
+ }
+ } else {
+ final List<String> natPersonIdObj = EidasResponseUtils.translateStringListAttribute(el, attributeMap.get(el));
+ final String stringAttr = natPersonIdObj.get(0);
+ if (StringUtils.isNotEmpty(stringAttr)) {
+ result.put(el.getFriendlyName(), stringAttr);
+ log.trace("Find attr '{}' with value: {}", el.getFriendlyName(), stringAttr);
+ } else {
+ log.info("Ignore empty 'String' attribute");
+ }
+ }
+ }
+ log.debug("Receive #{} attributes with names: {}", result.size(), result.keySet());
+ return result;
+ }
+
+ private MatchingTaskUtils() {
+ //hide constructor in case of class contains only static methods
+
+ }
+
+}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/VersionHolder.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/VersionHolder.java
new file mode 100644
index 00000000..dbe88d33
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/utils/VersionHolder.java
@@ -0,0 +1,40 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.utils;
+
+import java.util.Optional;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * SpringBoot based implementation of an application-version holder.
+ *
+ * @author tlenz
+ *
+ */
+public class VersionHolder {
+
+ private final String version;
+
+ /**
+ * Build up a holder that contains the current version of this application.
+ *
+ * @param context SprintBoot context
+ */
+ public VersionHolder(ApplicationContext context) {
+ version = context.getBeansWithAnnotation(SpringBootApplication.class).entrySet().stream()
+ .findFirst()
+ .flatMap(es -> Optional.ofNullable(es.getValue().getClass().getPackage().getImplementationVersion()))
+ .orElse("unknown");
+
+ }
+
+ /**
+ * Get version of this application.
+ *
+ * @return version
+ */
+ public String getVersion() {
+ return version;
+
+ }
+}