From 9da6640b1aa6ffd60866a6f34ea92f70ada1a3e6 Mon Sep 17 00:00:00 2001
From: Thomas <>
Date: Fri, 7 Oct 2022 16:57:46 +0200
Subject: feat(matching): check ZMR and ERnP response if enities are closed
ZMR and ERnP always return the latest version of an entity.
However, that latest version can also have status closed and in that case
the entity is not valid any more.
---
.../auth/eidas/v2/clients/ernp/ErnpRestClient.java | 80 ++++++++++++++++++----
.../auth/eidas/v2/clients/zmr/ZmrSoapClient.java | 80 +++++++++++++++++++---
.../eidas/v2/handler/AbstractEidProcessor.java | 2 +-
3 files changed, 135 insertions(+), 27 deletions(-)
(limited to 'modules/authmodule-eIDAS-v2/src/main/java/at/asitplus')
diff --git a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java
index a847a519..feb2853a 100644
--- a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java
+++ b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/ernp/ErnpRestClient.java
@@ -3,6 +3,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.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Arrays;
import java.util.Collection;
@@ -417,30 +418,79 @@ public class ErnpRestClient implements IErnpClient {
"Find more-than-one ERnP entry with search criteria that has to be unique", true);
} else {
- return Arrays.asList(mapErnpResponseToRegisterResult(persons.get(0), citizenCountryCode));
+ RegisterResult activeResult = mapErnpResponseToRegisterResult(persons.get(0), citizenCountryCode);
+ if (activeResult == null) {
+ log.error("ERnP entry, which was selected by matching, looks already closed. "
+ + "Automated operations on closed entries not supported my matching");
+ throw new WorkflowException(processStepFiendlyname,
+ "ERnP entry, which was selected by matching, is not active any more.", true);
+
+ }
+
+ return Arrays.asList(activeResult);
}
}
- @Nonnull
+
+
+ /**
+ * Process a single Person data-set from ERnP.
+ *
+ * @param personEl Person data-set from ERnP
+ * @param citizenCountryCode Country-Code of the citizen
+ * @return Simplified register result, or null
if the person data-set is not active anymore
+ * @throws EaafAuthenticationException In case of a validation error
+ */
+ @Nullable
private RegisterResult mapErnpResponseToRegisterResult(@Nonnull Person person,
@Nonnull String citizenCountryCode) {
- // build result
- return RegisterResult.builder()
- .pseudonym(selectAllEidasDocument(person, citizenCountryCode,
- EidasConstants.eIDAS_ATTRURN_PERSONALIDENTIFIER))
- .familyName(person.getPersonendaten().getFamilienname())
- .givenName(person.getPersonendaten().getVorname())
- .dateOfBirth(getTextualBirthday(person.getPersonendaten().getGeburtsdatum()))
- .bpk(person.getPersonendaten().getBpkZp())
- .placeOfBirth(selectSingleEidasDocument(person, citizenCountryCode,
- EidasConstants.eIDAS_ATTRURN_PLACEOFBIRTH))
- .birthName(selectSingleEidasDocument(person, citizenCountryCode,
- EidasConstants.eIDAS_ATTRURN_BIRTHNAME))
- .build();
+
+ if (checkIfPersonIsActive(person)) {
+ // build result
+ return RegisterResult.builder()
+ .pseudonym(selectAllEidasDocument(person, citizenCountryCode,
+ EidasConstants.eIDAS_ATTRURN_PERSONALIDENTIFIER))
+ .familyName(person.getPersonendaten().getFamilienname())
+ .givenName(person.getPersonendaten().getVorname())
+ .dateOfBirth(getTextualBirthday(person.getPersonendaten().getGeburtsdatum()))
+ .bpk(person.getPersonendaten().getBpkZp())
+ .placeOfBirth(selectSingleEidasDocument(person, citizenCountryCode,
+ EidasConstants.eIDAS_ATTRURN_PLACEOFBIRTH))
+ .birthName(selectSingleEidasDocument(person, citizenCountryCode,
+ EidasConstants.eIDAS_ATTRURN_BIRTHNAME))
+ .build();
+
+ } else {
+ log.debug("Entity is not valid anymore. Skip it ... ");
+ return null;
+
+ }
}
+ private boolean checkIfPersonIsActive(Person person) {
+ if (person.getGueltigBis() != null) {
+ LocalDateTime validTo = person.getGueltigBis().toLocalDateTime();
+ LocalDateTime now = LocalDateTime.now();
+ if (validTo.isBefore(now)) {
+ log.warn("Enity was valid to: {}, but now its: {}. Ignore that entry", validTo, now);
+ return false;
+
+ } else {
+ log.debug("Entity has a 'validTo' element, but it is in the future.");
+
+ }
+
+ } else {
+ log.trace("Entity has no 'validTo' element. Therefore it should be valid");
+
+ }
+
+ return true;
+ }
+
+
private Suchdaten mapCountrySpecificSearchData(PersonSuchenRequest personSearchDao) {
final Suchdaten searchInfos = new Suchdaten();
searchInfos.setFamilienname(personSearchDao.getNatuerlichePerson().getPersonenName().getFamilienname());
diff --git a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/ZmrSoapClient.java b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/ZmrSoapClient.java
index 904afc37..444bd4e7 100644
--- a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/ZmrSoapClient.java
+++ b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/zmr/ZmrSoapClient.java
@@ -3,6 +3,7 @@ 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.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -33,6 +34,7 @@ import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.ZmrCommunicati
import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.VersionHolder;
import at.asitplus.eidas.specific.modules.core.eidas.EidasConstants;
import at.gv.bmi.namespace.zmr_su.base._20040201.ClientInfoType;
+import at.gv.bmi.namespace.zmr_su.base._20040201.EntityErgebnisReferenzType;
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;
@@ -78,6 +80,7 @@ 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_04 = "module.eidasauth.matching.04";
private static final String ERROR_MATCHING_99 = "module.eidasauth.matching.99";
private static final String LOGMSG_MISSING_CONFIG = "Missing configuration with key: {0}";
@@ -556,29 +559,84 @@ public class ZmrSoapClient extends AbstractSoapClient implements IZmrClient {
"Find more-than-one ZMR entry with search criteria that has to be unique", true);
} else {
- return Arrays.asList(processPersonResult(personErgebnisSatz.get(0), citizenCountryCode));
+ RegisterResult activeResult = processPersonResult(personErgebnisSatz.get(0), citizenCountryCode);
+ if (activeResult == null) {
+ log.error("ZMR entry, which was selected by matching, looks already closed. "
+ + "Automated operations on closed entries not supported my matching");
+ throw new WorkflowException(processStepFiendlyname,
+ "ZMR entry, which was selected by matching, is not active any more.", true);
+
+ }
+ return Arrays.asList(activeResult);
}
}
- @Nonnull
+ /**
+ * Process a single Person data-set from ZMR.
+ *
+ * @param personEl Person data-set from ZMR
+ * @param citizenCountryCode Country-Code of the citizen
+ * @return Simplified register result, or null
if the person data-set is not active anymore
+ * @throws EaafAuthenticationException In case of a validation error
+ */
+ @Nullable
private RegisterResult processPersonResult(
@Nonnull PersonErgebnisSatzType personEl, @Nonnull String citizenCountryCode)
+ throws EaafAuthenticationException {
+ PersonErgebnisType latestPersonResult = extractLatestPersonResult(personEl);
+
+ // check if person was not closed already
+ if (checkIfPersonIsActive(latestPersonResult)) {
+ return mapZmrResponseToRegisterResult(latestPersonResult, citizenCountryCode);
+
+ } else {
+ log.debug("Entity is not valid anymore. Skip it ... ");
+ return null;
+
+ }
+ }
+
+ @Nonnull
+ private PersonErgebnisType extractLatestPersonResult(PersonErgebnisSatzType personEl)
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 PersoenErgebnis in Personendaten.");
+ log.debug("Find #{} data sets in person information", personEl.getPersonendaten().getPersonErgebnis().size());
+ if (personEl.getPersonendaten().getPersonErgebnis().size() == 0) {
+ log.error("Find no PersoenErgebnis in Personendaten from ZMR.");
+ throw new EaafAuthenticationException(ERROR_MATCHING_04, null);
+
+ } else if (personEl.getPersonendaten().getPersonErgebnis().size() > 1) {
+ log.error("Find more than on PersoenErgebnis in Personendaten from ZMR.");
+ //TODO: select latest entry in case of historic information
throw new EaafAuthenticationException(ERROR_MATCHING_02, null);
+ } else {
+ return personEl.getPersonendaten().getPersonErgebnis().get(0);
+
+ }
+ }
+
+ private boolean checkIfPersonIsActive(PersonErgebnisType latestPersonResult) {
+ EntityErgebnisReferenzType entityRef = latestPersonResult.getEntityErgebnisReferenz();
+ if (entityRef.getBis() != null) {
+ LocalDateTime validTo = entityRef.getBis().toGregorianCalendar().toZonedDateTime().toLocalDateTime();
+ LocalDateTime now = LocalDateTime.now();
+ if (validTo.isBefore(now)) {
+ log.warn("Enity was valid to: {}, but now its: {}. Ignore that entry", validTo, now);
+ return false;
+
+ } else {
+ log.debug("Entity has a 'validTo' element, but it is in the future.");
+
+ }
+
} else {
- return mapZmrResponseToRegisterResult(
- personEl.getPersonendaten().getPersonErgebnis().get(0), citizenCountryCode);
-
+ log.trace("Entity has no 'validTo' element. Therefore it should be valid");
+
}
-
+
+ return true;
}
@Nonnull
diff --git a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java
index 8716f80d..61d5ded2 100644
--- a/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java
+++ b/modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/handler/AbstractEidProcessor.java
@@ -253,7 +253,7 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor {
}
} else {
- log.debug("Map " + spConfig.getAreaSpecificTargetIdentifier() + " to 'PrivateSector'");
+ log.debug("Map {} to 'PrivateSector'", spConfig.getAreaSpecificTargetIdentifier());
authnRequestBuilder.spType(SpType.PRIVATE.getValue());
// TODO: switch to RequesterId in further version
--
cgit v1.2.3