aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas <>2022-02-03 20:28:50 +0100
committerThomas <>2022-02-08 09:35:52 +0100
commit84bb45ecb417f6a92b7d8884048bd7901c6b1b42 (patch)
treefe4d921881009d747400f2ba88c6f8b8ed578424
parent837ce1a4fbece82fd84ab77fdd1eb0284ad2106a (diff)
downloadNational_eIDAS_Gateway-84bb45ecb417f6a92b7d8884048bd7901c6b1b42.tar.gz
National_eIDAS_Gateway-84bb45ecb417f6a92b7d8884048bd7901c6b1b42.tar.bz2
National_eIDAS_Gateway-84bb45ecb417f6a92b7d8884048bd7901c6b1b42.zip
fix(ernp): fix some bugs in ERnP client implementation
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/Constants.java2
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java265
-rw-r--r--eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ErnpRestCommunicationException.java24
3 files changed, 180 insertions, 111 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 d48d69bf..6da30299 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
@@ -166,8 +166,6 @@ public class Constants {
public static final String CONIG_PROPS_EIDAS_ERNPCLIENT = CONIG_PROPS_EIDAS_PREFIX + ".ernpclient";
public static final String CONIG_PROPS_EIDAS_ERNPCLIENT_ENDPOINT = CONIG_PROPS_EIDAS_ERNPCLIENT
+ ".endpoint";
- public static final String CONIG_PROPS_EIDAS_ERNPCLIENT_DEBUG_TRACEMESSAGES = CONIG_PROPS_EIDAS_ERNPCLIENT
- + ".debug.logfullmessages";
public static final String CONIG_PROPS_EIDAS_ERNPCLIENT_TIMEOUT_CONNECTION = CONIG_PROPS_EIDAS_ERNPCLIENT
+ ".timeout.connection";
public static final String CONIG_PROPS_EIDAS_ERNPCLIENT_TIMEOUT_RESPONSE = CONIG_PROPS_EIDAS_ERNPCLIENT
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java
index a651385f..605f1d0f 100644
--- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java
@@ -2,6 +2,7 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.clients.ernp;
import java.io.IOException;
import java.text.MessageFormat;
+import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.Arrays;
import java.util.Collections;
@@ -12,9 +13,11 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
+import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
@@ -23,10 +26,10 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.web.client.ResponseErrorHandler;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
-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.RegisterResult;
@@ -42,6 +45,7 @@ import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.ernp.model.SuchenRes
import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.ernp.model.Suchoptionen;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.ernp.model.Suchoptionen.HistorischEnum;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.EidasSAuthenticationException;
+import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.ErnpRestCommunicationException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException;
import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.VersionHolder;
import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;
@@ -60,17 +64,18 @@ import lombok.extern.slf4j.Slf4j;
/**
* Implements an ERnP client that uses REST API for communication.
- *
+ *
* @author tlenz
*
*/
@Slf4j
public class ErnpRestClient implements IErnpClient {
- 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_11 = "module.eidasauth.matching.11";
+ private static final String ERROR_MATCHING_12 = "module.eidasauth.matching.12";
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_ERNP_ERROR =
"Receive an error from ERnP during '{}' operation with msg: {}";
private static final String LOGMSG_ERNP_RESP_PROCESS =
@@ -78,69 +83,73 @@ public class ErnpRestClient implements IErnpClient {
private static final String LOGMSG_ERNP_SOAP_ERROR =
"ERnP anwser for transaction: {0} with code: {1} and message: {2}";
-
- private static final String PROCESS_SEARCH_PERSONAL_IDENTIFIER =
+
+ 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 {0} 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 FRIENDLYNAME_HTTP_CLIENT = "ERnP Client";
-
- private static final String PATTERN_BIRTHDAY_STRING = "{0}-{1}-{2}";
-
-
- @Autowired IConfiguration basicConfig;
- @Autowired EaafKeyStoreFactory keyStoreFactory;
- @Autowired IHttpClientFactory httpClientFactory;
- @Autowired VersionHolder versionHolder;
-
+
+ @Autowired
+ IConfiguration basicConfig;
+ @Autowired
+ EaafKeyStoreFactory keyStoreFactory;
+ @Autowired
+ IHttpClientFactory httpClientFactory;
+ @Autowired
+ VersionHolder versionHolder;
+
private DefaultApi ernpClient;
-
@Override
public ErnpRegisterResult searchWithPersonIdentifier(String personIdentifier, String citizenCountryCode)
throws EidasSAuthenticationException {
-
+
try {
-
+
// build generic request metadata
- GenericRequestParams generic = buildGenericRequestParameters("step1");
+ final GenericRequestParams generic = buildGenericRequestParameters("step1");
- // build search request
- SuchEidas eidasInfos = new SuchEidas();
+ // build search request
+ final SuchEidas eidasInfos = new SuchEidas();
eidasInfos.setArt(Constants.eIDAS_ATTRURN_PERSONALIDENTIFIER);
eidasInfos.setWert(personIdentifier);
eidasInfos.setStaatscode2(citizenCountryCode);
-
- PersonSuchen personSuchen = new PersonSuchen();
+
+ final PersonSuchen personSuchen = new PersonSuchen();
personSuchen.setSuchoptionen(generateSearchParameters());
- personSuchen.setBegruendung(PROCESS_SEARCH_PERSONAL_IDENTIFIER);
- Suchdaten searchInfos = new Suchdaten();
- searchInfos.setEidas(eidasInfos);
+ personSuchen.setBegruendung(PROCESS_SEARCH_PERSONAL_IDENTIFIER);
+ final Suchdaten searchInfos = new Suchdaten();
+ searchInfos.setEidas(eidasInfos);
personSuchen.setSuchdaten(searchInfos);
-
+
// request ERnP
log.trace("Requesting ERnP for '{}' operation", PROCESS_SEARCH_PERSONAL_IDENTIFIER);
- SuchenResponse resp = ernpClient.suchen(generic.getClientBehkz(), generic.clientName,
+ final SuchenResponse resp = ernpClient.suchen(generic.getClientBehkz(), generic.clientName,
generic.getClientRequestTime(), generic.getClientRequestId(), personSuchen);
-
+
// parse ZMR response
return processErnpResponse(resp, citizenCountryCode, true, PROCESS_SEARCH_PERSONAL_IDENTIFIER);
+
+ } catch (RestClientException e) {
+ log.warn(LOGMSG_ERNP_ERROR, PROCESS_SEARCH_PERSONAL_IDENTIFIER, e.getMessage());
+ throw new EidasSAuthenticationException(ERROR_MATCHING_11, new Object[] { e.getMessage() }, e);
- } catch (EidasSAuthenticationException e) {
+ } catch (final EidasSAuthenticationException e) {
throw e;
-
- } catch (Exception e) {
+
+ } catch (final Exception e) {
log.warn(LOGMSG_ERNP_RESP_PROCESS, PROCESS_SEARCH_PERSONAL_IDENTIFIER, e.getMessage());
throw new EidasSAuthenticationException(ERROR_MATCHING_99, new Object[] { e.getMessage() }, e);
}
-
+
}
-
+
@Override
public ErnpRegisterResult searchWithMds(String givenName, String familyName, String dateOfBirth,
String citizenCountryCode) throws EidasSAuthenticationException {
@@ -164,54 +173,66 @@ public class ErnpRestClient implements IErnpClient {
String zipcode, String city, String street) {
return new ErnpRegisterResult(Collections.emptyList());
}
-
+
@PostConstruct
private void initialize() throws EaafException {
- // set-up the Ernp client
- ernpClient = new DefaultApi(new ApiClient(buildRestClient()));
-
// validate additional Ernp communication parameters
valdiateAdditionalConfigParameters();
-
+
+ // set-up the Ernp client
+ final ApiClient baseClient = new ApiClient(buildRestClient());
+ baseClient.setBasePath(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ERNPCLIENT_ENDPOINT));
+ ernpClient = new DefaultApi(baseClient);
+
}
-
+
private void valdiateAdditionalConfigParameters() {
- // TODO Auto-generated method stub
-
+ checkConfigurationValue(Constants.CONIG_PROPS_EIDAS_ERNPCLIENT_ENDPOINT);
+ checkConfigurationValue(Constants.CONIG_PROPS_EIDAS_ERNPCLIENT_REQ_ORGANIZATION_NR);
+
+ }
+
+ private void checkConfigurationValue(String key) {
+ if (StringUtils.isEmpty(basicConfig.getBasicConfiguration(key))) {
+ throw new RuntimeException(MessageFormat.format(LOGMSG_MISSING_CONFIG, key));
+
+ }
}
private Suchoptionen generateSearchParameters() {
- Suchoptionen options = new Suchoptionen();
+ final Suchoptionen options = new Suchoptionen();
options.setZmr(false);
options.setHistorisch(HistorischEnum.AKTUELLUNDHISTORISCH);
options.setSucheMitNamensteilen(false);
- options.setSuchwizard(false);
+ options.setSuchwizard(false);
return options;
-
+
}
@Nonnull
private ErnpRegisterResult processErnpResponse(SuchenResponse resp, @Nonnull String citizenCountryCode,
- boolean forceSinglePersonMatch, @Nonnull String processStepFiendlyname) throws EaafAuthenticationException {
- if (resp.getPerson() == null
+ boolean forceSinglePersonMatch, @Nonnull String processStepFiendlyname)
+ throws EaafAuthenticationException {
+ if (resp.getPerson() == null
|| resp.getPerson().isEmpty()) {
log.debug("ERnP result contains NO 'Person' or 'Person' is empty");
return new ErnpRegisterResult(Collections.emptyList());
-
- } else {
- log.debug("Get #{} person results from '{}' operation",
+
+ } else {
+ log.debug("Get #{} person results from '{}' operation",
resp.getPerson().size(), processStepFiendlyname);
-
+
if (forceSinglePersonMatch) {
return new ErnpRegisterResult(processSearchPersonResponseSingleResult(
resp.getPerson(), citizenCountryCode, processStepFiendlyname));
-
+
} else {
return new ErnpRegisterResult(processSearchPersonResponse(
resp.getPerson(), citizenCountryCode));
-
- }
- }
+
+ }
+ }
}
@Nonnull
@@ -224,22 +245,22 @@ public class ErnpRestClient implements IErnpClient {
.collect(Collectors.toList());
}
-
+
@NonNull
private List<RegisterResult> processSearchPersonResponseSingleResult(
@Nonnull List<Person> persons,
@Nonnull String citizenCountryCode, String processStepFiendlyname) throws EaafAuthenticationException {
if (persons.size() > 1) {
log.error("Find more-than-one ERnP entry with search criteria that has to be unique");
- throw new WorkflowException(processStepFiendlyname,
+ throw new WorkflowException(processStepFiendlyname,
"Find more-than-one ERnP entry with search criteria that has to be unique", true);
-
+
} else {
return Arrays.asList(mapErnpResponseToRegisterResult(persons.get(0), citizenCountryCode));
}
}
-
+
@Nonnull
private RegisterResult mapErnpResponseToRegisterResult(@Nonnull Person person,
@Nonnull String citizenCountryCode) {
@@ -254,15 +275,28 @@ public class ErnpRestClient implements IErnpClient {
.placeOfBirth(selectSingleEidasDocument(person, citizenCountryCode,
Constants.eIDAS_ATTRURN_PLACEOFBIRTH))
.birthName(selectSingleEidasDocument(person, citizenCountryCode,
- Constants.eIDAS_ATTRURN_BIRTHNAME))
+ Constants.eIDAS_ATTRURN_BIRTHNAME))
.build();
}
-
+
+ /**
+ * Build AT specific Date String 'yyyy-MM-dd' from ERnP birthday representation.
+ *
+ * <p>
+ * <b>Info:</b> {@link LocalDate} can not be used, because '1940-00-00' is also
+ * a valid birthday on ERnP site.
+ * </p>
+ *
+ * @param geburtsdatum ERnP birthday representation
+ * @return birthday in 'yyyy-MM-dd' format
+ */
private String buildTextualBirthday(PartialDate geburtsdatum) {
- return MessageFormat.format(PATTERN_BIRTHDAY_STRING,
- geburtsdatum.getJahr(), geburtsdatum.getMonat(), geburtsdatum.getTag());
-
+ return MessageFormat.format("{0}-{1}-{2}",
+ String.valueOf(geburtsdatum.getJahr()),
+ String.format("%02d", geburtsdatum.getMonat()),
+ String.format("%02d", geburtsdatum.getTag()));
+
}
/**
@@ -276,12 +310,18 @@ public class ErnpRestClient implements IErnpClient {
*/
@NonNull
private List<String> selectAllEidasDocument(Person person, String citizenCountryCode,
- String eidasAttrurnPersonalidentifier) {
- return person.getEidas().stream()
- .filter(el -> eidasAttrurnPersonalidentifier.equals(el.getArt())
- && el.getStaatscode2().equals(citizenCountryCode))
- .map(el -> el.getWert())
- .collect(Collectors.toList());
+ String eidasAttrurnPersonalidentifier) {
+ if (person.getEidas() != null) {
+ return person.getEidas().stream()
+ .filter(el -> eidasAttrurnPersonalidentifier.equals(el.getArt())
+ && el.getStaatscode2().equals(citizenCountryCode))
+ .map(el -> el.getWert())
+ .collect(Collectors.toList());
+
+ } else {
+ return Collections.emptyList();
+
+ }
}
@@ -297,15 +337,20 @@ public class ErnpRestClient implements IErnpClient {
@Nullable
private String selectSingleEidasDocument(Person person, String citizenCountryCode,
String eidasAttrurnPersonalidentifier) {
- return person.getEidas().stream()
- .filter(el -> eidasAttrurnPersonalidentifier.equals(el.getArt())
- && el.getStaatscode2().equals(citizenCountryCode))
- .findFirst()
- .map(el -> el.getWert())
- .orElse(null);
+ if (person.getEidas() != null) {
+ return person.getEidas().stream()
+ .filter(el -> eidasAttrurnPersonalidentifier.equals(el.getArt())
+ && el.getStaatscode2().equals(citizenCountryCode))
+ .findFirst()
+ .map(el -> el.getWert())
+ .orElse(null);
+
+ } else {
+ return null;
+ }
}
-
+
private RestTemplate buildRestClient() throws EaafException {
log.debug("Building REST-Client for ERnP communication ... ");
final HttpClient httpClient = httpClientFactory.getHttpClient(buildHttpClientConfiguration());
@@ -316,42 +361,44 @@ public class ErnpRestClient implements IErnpClient {
return springClient;
}
-
+
private HttpMessageConverter<?> buildCustomJacksonObjectMapper() {
- MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.setSerializationInclusion(Include.NON_NULL);
- converter.setObjectMapper(objectMapper);
+ final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
+ converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
+ converter.getObjectMapper()
+ .setSerializationInclusion(Include.NON_NULL);
return converter;
-
+
}
-
+
@Nonnull
private ResponseErrorHandler buildErrorHandler() {
return new ResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
- return response.getStatusCode().is4xxClientError()
- || response.getStatusCode().is5xxServerError();
-
+ return response.getStatusCode().is4xxClientError()
+ || response.getStatusCode().is5xxServerError();
+
}
@Override
- public void handleError(ClientHttpResponse response) throws IOException {
- //TODO: implement errorHandling based on response infos
-
+ public void handleError(ClientHttpResponse response) throws IOException {
+ // TODO: implement errorHandling based on response infos
+
if (response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR) {
log.warn("Receive http-server-error: {} from ERnP", response.getRawStatusCode());
-
+
} else if (response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR) {
log.warn("Receive http-client-error: {} from ERnP", response.getRawStatusCode());
-
+
}
+
+ throw new ErnpRestCommunicationException(response.getRawStatusCode());
}
};
}
-
+
@Nonnull
private HttpClientConfiguration buildHttpClientConfiguration() throws EaafException {
final HttpClientConfiguration config = new HttpClientConfiguration(FRIENDLYNAME_HTTP_CLIENT);
@@ -369,39 +416,39 @@ public class ErnpRestClient implements IErnpClient {
basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_COMMON_CLIENT_SSL_KEYS_ALIAS));
config.setSslKeyPassword(
basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_COMMON_CLIENT_SSL_KEY_PASSWORD));
-
+
// Set connection parameters
- //TODO: update EAAF-components to allow custom HTTP Connection-Timeouts
-
+ // TODO: update EAAF-components to allow custom HTTP Connection-Timeouts
+
return config;
}
-
@AllArgsConstructor
@Getter
public static class ErnpRegisterResult {
private final List<RegisterResult> personResult;
-
+
}
private GenericRequestParams buildGenericRequestParameters(String operationIdentifier) {
return GenericRequestParams.builder()
- .clientBehkz(basicConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_ORGANIZATION_NR))
+ .clientBehkz(basicConfig.getBasicConfiguration(
+ Constants.CONIG_PROPS_EIDAS_ZMRCLIENT_REQ_ORGANIZATION_NR))
.clientName(MessageFormat.format(Constants.CLIENT_INFO, versionHolder.getVersion()))
.clientRequestTime(OffsetDateTime.now())
.clientRequestId(TransactionIdUtils.getTransactionId() + "_" + operationIdentifier)
- .build();
-
+ .build();
+
}
-
+
@Builder
@Getter
- private static class GenericRequestParams {
+ private static class GenericRequestParams {
String clientBehkz;
- String clientName;
- OffsetDateTime clientRequestTime;
+ String clientName;
+ OffsetDateTime clientRequestTime;
String clientRequestId;
-
+
}
}
diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ErnpRestCommunicationException.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ErnpRestCommunicationException.java
new file mode 100644
index 00000000..72a5ece6
--- /dev/null
+++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ErnpRestCommunicationException.java
@@ -0,0 +1,24 @@
+package at.asitplus.eidas.specific.modules.auth.eidas.v2.exception;
+
+import java.io.IOException;
+
+import lombok.Getter;
+
+/**
+ * ERnP exception in case of a REST communication error.
+ *
+ * @author tlenz
+ *
+ */
+public class ErnpRestCommunicationException extends IOException {
+ private static final long serialVersionUID = -3178689122077228976L;
+
+ @Getter
+ int httpStatusCode;
+
+ public ErnpRestCommunicationException(int rawStatusCode) {
+ super("ERnP service answers with an error and HTTP status-code: " + rawStatusCode);
+ this.httpStatusCode = rawStatusCode;
+ }
+
+}