diff options
Diffstat (limited to 'eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus')
31 files changed, 1902 insertions, 790 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 3a267d29..3e20a132 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,6 +27,9 @@ 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"; @@ -91,6 +94,42 @@ 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"; +   +   +  // 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"; @@ -112,11 +151,23 @@ 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";    public static final String CONIG_PROPS_EIDAS_SZRCLIENT_PARAMS_VKZ = CONIG_PROPS_EIDAS_SZRCLIENT @@ -153,7 +204,7 @@ public class Constants {    // eIDAS request parameters    public static final String eIDAS_REQ_NAMEID_FORMAT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; -  // eIDAS attribute names +  // eIDAS attribute names        public static final String eIDAS_ATTR_PERSONALIDENTIFIER = "PersonIdentifier";    public static final String eIDAS_ATTR_DATEOFBIRTH = "DateOfBirth";    public static final String eIDAS_ATTR_CURRENTGIVENNAME = "FirstName"; @@ -166,6 +217,15 @@ public class Constants {    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_REQ_PARAM_SECTOR_PUBLIC = "public";    public static final String eIDAS_REQ_PARAM_SECTOR_PRIVATE = "private"; 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..bfdf3991 --- /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,197 @@ +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))) { +      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 { +      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/szr/SzrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/clients/szr/SzrClient.java index 2d612f0c..2230f30a 100644 --- 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/clients/szr/SzrClient.java @@ -21,31 +21,18 @@   * 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.io.ByteArrayInputStream;  import java.io.ByteArrayOutputStream; -import java.io.IOException;  import java.net.URL;  import java.nio.charset.StandardCharsets; -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.Collections;  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; @@ -58,23 +45,11 @@ 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 at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; -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 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; @@ -83,14 +58,17 @@ 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.SimpleEidasData;  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.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.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.exceptions.EaafConfigurationException;  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; @@ -108,7 +86,7 @@ import szrservices.TravelDocumentType;  @Service("SZRClientForeIDAS") -public class SzrClient { +public class SzrClient extends AbstractSoapClient {    private static final Logger log = LoggerFactory.getLogger(SzrClient.class);    private static final String CLIENT_DEFAULT = "DefaultClient"; @@ -121,11 +99,7 @@ public class SzrClient {    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"; - -  @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") -  @Autowired -  private IConfiguration basicConfig; - +      // client for anything, without identitylink    private SZR szr = null; @@ -378,9 +352,9 @@ public class SzrClient {    }    @PostConstruct -  private void initialize() { +  private void initialize() throws EaafConfigurationException {      log.info("Starting SZR-Client initialization .... "); -    final URL url = SzrClient.class.getResource("/szr_client/SZR_v4.0.wsdl"); +    final URL url = SzrClient.class.getResource("/wsdl/szr_client/SZR_v4.0.wsdl");      final boolean useTestSzr = basicConfig.getBasicConfigurationBoolean(          Constants.CONIG_PROPS_EIDAS_SZRCLIENT_USETESTSERVICE, @@ -428,150 +402,44 @@ public class SzrClient {      // inject handler      log.info("Use SZR service-URL: " + szrUrl); -    injectBindingProvider((BindingProvider) szr, CLIENT_DEFAULT, szrUrl); -    injectBindingProvider(dispatch, CLIENT_RAW, 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 ... "); -    injectHttpClient(szr, CLIENT_DEFAULT, szrUrl); -    injectHttpClient(dispatch, CLIENT_RAW, szrUrl); +    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 injectHttpClient(Object raw, String clientType, String szrUrl) { -    // 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(); -    String connectionTimeout = basicConfig.getBasicConfiguration( -        Constants.CONIG_PROPS_EIDAS_SZRCLIENT_TIMEOUT_CONNECTION, Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_CONNECTION); -    httpClientPolicy.setConnectionTimeout(Integer.parseInt(connectionTimeout) * 1000L); -    String responseTimeout = basicConfig.getBasicConfiguration( -        Constants.CONIG_PROPS_EIDAS_SZRCLIENT_TIMEOUT_RESPONSE, Constants.HTTP_CLIENT_DEFAULT_TIMEOUT_RESPONSE); -    httpClientPolicy.setReceiveTimeout(Integer.parseInt(responseTimeout) * 1000L); -    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, String szrUrl) { -    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, SimpleEidasData eidData) {      if (basicConfig.getBasicConfigurationBoolean(          Constants.CONIG_PROPS_EIDAS_SZRCLIENT_SET_MDS_TO_EIDASBIND, false)) { 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..18bcbacc --- /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,89 @@ +/* + * 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 +   * @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)  +      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; + + +  void update(@Nullable BigInteger zmrProzessId, RegisterResult registerResult, SimpleEidasData eidData); + +  ZmrRegisterResult searchWithBpkZp(@Nullable BigInteger zmrProzessId, String bpkzp); + +  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..014d202b --- /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,560 @@ +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.Collections; +import java.util.List; +import java.util.Objects; +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.ZmrCommunicationException; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils; +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.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.PersonErgebnisSatzType; +import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonErgebnisType; +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.NatuerlichePersonTyp; +import at.gv.e_government.reference.namespace.persondata.de._20040201.PersonenNameTyp; +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_00 = "module.eidasauth.matching.00"; +  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 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 personIdentifier) +      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.setEidasSuchdaten(eidasInfos); +      eidasInfos.setEidasArt(Constants.eIDAS_ATTRURN_PERSONALIDENTIFIER); +      eidasInfos.setEidasNummer(personIdentifier); + +      // 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, EidasResponseUtils.parseEidasPersonalIdentifier(personIdentifier) +          .getFirst(), +          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 (final EaafAuthenticationException 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 (final EaafAuthenticationException 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 (final EaafAuthenticationException e) { +      log.warn(LOGMSG_ZMR_RESP_PROCESS, friendlyMsg, e.getMessage()); +      throw new EidasSAuthenticationException(ERROR_MATCHING_99, new Object[] { e.getMessage() }, e); + +    } +  } + +  @Override +  public void update(BigInteger zmrProzessId, RegisterResult registerResult, SimpleEidasData eidData) { +    // TODO Auto-generated method stub + +  } + +  @Override +  public ZmrRegisterResult searchWithBpkZp(BigInteger zmrProzessId, String bpkzp) { +    // TODO Auto-generated method stub +    return null; +  } + +  @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); +         +  } +   +  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), +            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) throws EaafAuthenticationException { +    if (personErgebnisSatz.size() > 1) { +      log.error("Find more than on person with eIDAS personalIdentifier."); +      throw new EaafAuthenticationException(ERROR_MATCHING_00, null); + +    } 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_PERSONALIDENTIFIER)) +        .birthName(selectSingleEidasDocument(person, citizenCountryCode, +            Constants.eIDAS_ATTRURN_PERSONALIDENTIFIER))         +        .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.getStaatscode3().equals(citizenCountryCode)) +        .map(el -> el.getDokumentNummer()) +        .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.getStaatscode3().equals(citizenCountryCode)) +        .findFirst() +        .map(el -> el.getDokumentNummer()) +        .orElse(null); + +  } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MergedRegisterSearchResult.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MergedRegisterSearchResult.java deleted file mode 100644 index 0c977016..00000000 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/MergedRegisterSearchResult.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2020 A-SIT Plus GmbH - * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, - * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "License"); - * You may not use this work except in compliance with the License. - * You may obtain a copy of the License at: - * https://joinup.ec.europa.eu/news/understanding-eupl-v12 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - */ - -package at.asitplus.eidas.specific.modules.auth.eidas.v2.dao; - -import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException; -import lombok.Data; - -import java.util.List; - -@Data -public class MergedRegisterSearchResult { - -  private final List<RegisterResult> resultsZmr; -  private final List<RegisterResult> resultsErnp; - -  public MergedRegisterSearchResult(List<RegisterResult> resultsZmr, List<RegisterResult> resultsErnp) { -    this.resultsZmr = resultsZmr; -    this.resultsErnp = resultsErnp; -  } - -  public int getResultCount() { -    return resultsZmr.size() + resultsErnp.size(); -  } - -  /** -   * Verifies that there is only one match and returns the bpk. -   * -   * @return bpk bpk of the match -   * @throws WorkflowException if multiple results have been found -   */ -  public String getBpk() throws WorkflowException { -    if (getResultCount() != 1) { -      throw new WorkflowException("getResultCount() != 1"); -    } -    return getResult().getBpk(); -  } - -  /** -   * Returns the results, if there is exactly one, throws exception otherwise. -   * -   * @return The result -   * @throws WorkflowException Results does not contain exactly one result -   */ -  public RegisterResult getResult() throws WorkflowException { -    if (getResultCount() != 1) { -      throw new WorkflowException("getResultCount() != 1"); -    } -    if (resultsZmr.size() == 1) { -      return resultsZmr.get(0); -    } else { -      return resultsErnp.get(0); -    } -  } -} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/RegisterResult.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/RegisterResult.java index 369a4e31..4959d72f 100644 --- 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 @@ -23,70 +23,29 @@  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.Data; +import lombok.Builder; +import lombok.Getter; -@Data +@Builder +@Getter  public class RegisterResult {    // MDS -  private final String pseudonym; +  private final List<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 String taxNumber; -  private final PostalAddressType address; - -  private final String bpk; +  private String placeOfBirth; +  private String birthName; +  private String taxNumber; +  private PostalAddressType address; -  /** -   * Register search result. -   * -   * @param bpk         The bpk -   * @param pseudonym   The pseudonym -   * @param givenName   The givenName -   * @param familyName  The familyName -   * @param dateOfBirth The dateOfBirth -   */ -  public RegisterResult(String bpk, String pseudonym, String givenName, String familyName, String dateOfBirth) { -    this.bpk = bpk; -    this.pseudonym = pseudonym; -    this.givenName = givenName; -    this.familyName = familyName; -    this.dateOfBirth = dateOfBirth; -    this.placeOfBirth = null; -    this.birthName = null; -    this.taxNumber = null; -    this.address = null; -  } +  private String bpk; -  /** -   * Register search result. -   * -   * @param bpk          The bpk -   * @param pseudonym    The pseudonym -   * @param givenName    The givenName -   * @param familyName   The familyName -   * @param dateOfBirth  The dateOfBirth -   * @param placeOfBirth The placeOfBirth -   * @param birthName    The birthName -   * @param taxNumber    The taxNumber -   * @param address      The address -   */ -  public RegisterResult(String bpk, String pseudonym, String givenName, String familyName, String dateOfBirth, -                        String placeOfBirth, String birthName, String taxNumber, PostalAddressType address) { -    this.bpk = bpk; -    this.pseudonym = pseudonym; -    this.givenName = givenName; -    this.familyName = familyName; -    this.dateOfBirth = dateOfBirth; -    this.placeOfBirth = placeOfBirth; -    this.birthName = birthName; -    this.taxNumber = taxNumber; -    this.address = address; -  } +      } diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleEidasData.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/dao/SimpleEidasData.java index ecf5007a..ab84a45f 100644 --- 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 @@ -23,20 +23,32 @@  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.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterSearchResult;  import at.gv.e_government.reference.namespace.persondata._20020228.PostalAddressType;  import lombok.Builder;  import lombok.Data; -import org.apache.commons.lang3.builder.EqualsBuilder;  @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; @@ -55,16 +67,24 @@ public class SimpleEidasData {     * @return true or false depending of the data matches     * @throws WorkflowException if multiple results have been found     */ -  public boolean equalsRegisterData(MergedRegisterSearchResult result) throws WorkflowException { +  public boolean equalsRegisterData(RegisterSearchResult 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.getResult().getPseudonym(), pseudonym)          .append(result.getResult().getGivenName(), givenName)          .append(result.getResult().getFamilyName(), familyName)          .append(result.getResult().getDateOfBirth(), dateOfBirth)          .append(result.getResult().getPlaceOfBirth(), placeOfBirth)          .append(result.getResult().getBirthName(), birthName)          .append(result.getResult().getTaxNumber(), taxNumber) -        .isEquals(); +        .isEquals() && result.getResult().getPseudonym().stream() +            .filter(el -> el.equals(pseudonym)) +            .findFirst() +            .isPresent(); +        }  } 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 index 4a27e60e..92e727ea 100644 --- 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 @@ -23,15 +23,15 @@  package at.asitplus.eidas.specific.modules.auth.eidas.v2.dao; +import org.apache.commons.lang3.builder.EqualsBuilder; +  import lombok.Builder;  import lombok.Data; -import org.apache.commons.lang3.builder.EqualsBuilder;  @Data  @Builder  public class SimpleMobileSignatureData { -  private final String citizenCountryCode;    private final String bpk;    private final String givenName;    private final String familyName; 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 index 3536b0dc..065b17a2 100644 --- 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 @@ -23,13 +23,14 @@  package at.asitplus.eidas.specific.modules.auth.eidas.v2.ernp; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; -import org.springframework.stereotype.Service; -  import java.util.Collections;  import java.util.List; +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 { 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 index 218a9f41..b3b0c033 100644 --- 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 @@ -23,11 +23,11 @@  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; -import java.util.List; -  public interface IErnpClient {    List<RegisterResult> searchWithPersonIdentifier(String personIdentifier); 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 index f28d8afa..c7df56d0 100644 --- 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 @@ -26,8 +26,9 @@ package at.asitplus.eidas.specific.modules.auth.eidas.v2.exception;  public class InvalidUserInputException extends EidasSAuthenticationException {    private static final long serialVersionUID = 1L; -  public InvalidUserInputException() { -    super("eidas.10", null); +  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 index 2fecaa6b..cf69bd2c 100644 --- 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 @@ -28,11 +28,17 @@ 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("eidas.09", new Object[] { personIdentifier }); +    super("module.eidasauth.matching.04", new Object[] { personIdentifier });    }    public ManualFixNecessaryException(SimpleEidasData eidData) { -    super("eidas.09", new Object[] { eidData.getPseudonym() });//TODO what info to pass??? +    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 index b6f3309b..795b4386 100644 --- 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 @@ -23,11 +23,72 @@  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; -  public WorkflowException(String data) { -    super("eidas.08", new Object[]{data}); +  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/zmr/IZmrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ZmrCommunicationException.java index 5ca69d3d..a6978458 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/IZmrClient.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/exception/ZmrCommunicationException.java @@ -1,5 +1,5 @@  /* - * Copyright 2020 A-SIT Plus GmbH + * 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.   * @@ -19,30 +19,20 @@   * file for details on the various modules and licenses.   * The "NOTICE" text file is part of the distribution. Any derivative works   * that you distribute must include a readable copy of the "NOTICE" text file. - */ +*/ -package at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr; +package at.asitplus.eidas.specific.modules.auth.eidas.v2.exception; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; +public class ZmrCommunicationException extends EidasSAuthenticationException { -import java.util.List; +  private static final long serialVersionUID = 1L; -public interface IZmrClient { +  public ZmrCommunicationException(String internalMsgId, Object[] params) { +    super(internalMsgId, params); +  } -  List<RegisterResult> searchWithPersonIdentifier(String personIdentifier); - -  List<RegisterResult> searchWithMds(String givenName, String familyName, String dateOfBirth); - -  List<RegisterResult> searchDeSpecific(String givenName, String familyName, String dateOfBirth, -                                             String birthPlace, String birthName); - -  List<RegisterResult> searchItSpecific(String taxNumber); - -  void update(RegisterResult registerResult, SimpleEidasData eidData); - -  List<RegisterResult> searchWithBpkZp(String bpkzp); - -  List<RegisterResult> searchWithResidenceData(String zipcode, String city, String street); +  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 05254fe2..1050f8d9 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 @@ -24,11 +24,13 @@  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.util.Map;  import java.util.regex.Matcher;  import java.util.regex.Pattern; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;  import org.apache.commons.lang3.StringUtils;  import org.joda.time.DateTime;  import org.slf4j.Logger; @@ -37,7 +39,9 @@ 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.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; @@ -47,15 +51,11 @@ 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 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 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; -  public abstract class AbstractEidProcessor implements INationalEidProcessor {    private static final Logger log = LoggerFactory.getLogger(AbstractEidProcessor.class); @@ -77,19 +77,26 @@ public abstract class AbstractEidProcessor implements INationalEidProcessor {    public final SimpleEidasData postProcess(Map<String, Object> eidasAttrMap) throws EidPostProcessingException,        EidasAttributeException {      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();    } 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 index 6e8f7fce..c2a62f5c 100644 --- 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 @@ -23,8 +23,10 @@  package at.asitplus.eidas.specific.modules.auth.eidas.v2.handler; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MergedRegisterSearchResult; +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 { @@ -37,11 +39,18 @@ public interface CountrySpecificDetailSearchProcessor {     * Check if this postProcessor is sensitive for a specific country.     *     * @param countryCode of the eID data that should be processed -   * @param eidData     eID data +   * @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); -  MergedRegisterSearchResult search(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 index 904c41a1..802fde14 100644 --- 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 @@ -23,40 +23,39 @@  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.MergedRegisterSearchResult;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService; +import at.gv.bmi.namespace.zmr_su.zmr._20040201.EidasSuchdatenType; +import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;  public class DeSpecificDetailSearchProcessor implements CountrySpecificDetailSearchProcessor { -  private final RegisterSearchService registerSearchService; - -  public DeSpecificDetailSearchProcessor(RegisterSearchService registerSearchService) { -    this.registerSearchService = registerSearchService; -  } -    @Override    public String getName() { -    return this.getClass().getName(); +    return this.getClass().getSimpleName();    }    @Override    public boolean canHandle(String countryCode, SimpleEidasData eidData) { -    if (!countryCode.equalsIgnoreCase(Constants.COUNTRY_CODE_DE)) { -      return false; -    } -    if (eidData.getBirthName() == null || eidData.getBirthName().isEmpty()) { -      return false; -    } -    if (eidData.getPlaceOfBirth() == null || eidData.getPlaceOfBirth().isEmpty()) { -      return false; -    } -    return true; +    return countryCode.equalsIgnoreCase(Constants.COUNTRY_CODE_DE)  +        && StringUtils.isNotEmpty(eidData.getBirthName())  +        && StringUtils.isNotEmpty(eidData.getPlaceOfBirth()); +        }    @Override -  public MergedRegisterSearchResult search(SimpleEidasData eidData) { -    return registerSearchService.searchDeSpecific(eidData); +  public PersonSuchenRequest generateSearchRequest(SimpleEidasData eidData) { +     +    PersonSuchenRequest req = new PersonSuchenRequest();     +    EidasSuchdatenType eidasInfos = new EidasSuchdatenType(); +    req.setEidasSuchdaten(eidasInfos); +     +     +    //TODO: how we can search for more than one eIDAS attribute as a Set +     +     +    return req;    }  } 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 index 7e74a85c..b49c355d 100644 --- 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 @@ -23,37 +23,31 @@  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.MergedRegisterSearchResult;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService; +import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;  public class ItSpecificDetailSearchProcessor implements CountrySpecificDetailSearchProcessor { -  private final RegisterSearchService registerSearchService; - -  public ItSpecificDetailSearchProcessor(RegisterSearchService registerSearchService) { -    this.registerSearchService = registerSearchService; -  } -    @Override    public String getName() { -    return this.getClass().getName(); +    return this.getClass().getSimpleName();    }    @Override    public boolean canHandle(String countryCode, SimpleEidasData eidData) { -    if (!countryCode.equalsIgnoreCase(Constants.COUNTRY_CODE_IT)) { -      return false; -    } -    if (eidData.getTaxNumber() == null || eidData.getTaxNumber().isEmpty()) { -      return false; -    } -    return true; +    return countryCode.equalsIgnoreCase(Constants.COUNTRY_CODE_IT)  +        &&  StringUtils.isNotEmpty(eidData.getTaxNumber()); +    }    @Override -  public MergedRegisterSearchResult search(SimpleEidasData eidData) { -    return registerSearchService.searchItSpecific(eidData); +  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/service/RegisterSearchService.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/service/RegisterSearchService.java index 6b524e36..166ffafb 100644 --- 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 @@ -1,18 +1,28 @@  package at.asitplus.eidas.specific.modules.auth.eidas.v2.service; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.MergedRegisterSearchResult; +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; + +import javax.annotation.Nonnull; + +import org.jetbrains.annotations.Nullable; +import org.springframework.stereotype.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.zmr.IZmrClient; -import at.gv.egiz.eaaf.core.api.IRequest; -import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; +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.Setter;  import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.Collections; -import java.util.List;  @Slf4j  @Service("registerSearchService") @@ -21,99 +31,277 @@ public class RegisterSearchService {    private final IZmrClient zmrClient;    private final IErnpClient ernpClient; -  public RegisterSearchService(IZmrClient zmrClient, 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()); +    }    /** -   * Automatic process to fix the register entries. +   * Search with Person Identifier (eIDAS Pseudonym) in ZMR and ERnP.     * -   * @param initialSearchResult Result of initial register search -   * @param specificSearchResult Result of last register search -   * @param eidasData Received eidas data -   * @param pendingReq Pending request -   * @return The bpk -   * @throws TaskExecutionException if an error occurs during the register update +   * @param eidasData            Received eIDAS data +   * @throws WorkflowException In case of a register interaction error     */ -  public String step7aKittProcess(MergedRegisterSearchResult initialSearchResult, -                                  MergedRegisterSearchResult specificSearchResult, -                                  SimpleEidasData eidasData, -                                  IRequest pendingReq) throws TaskExecutionException { -    log.trace("Starting step7aKittProcess"); -    // TODO verify with which data this method gets called +  @Nonnull +  public RegisterSearchResult searchWithPersonIdentifier(SimpleEidasData eidasData) +      throws WorkflowException {      try { -      if (initialSearchResult.getResultCount() != 0) { -        throw new WorkflowException("initialSearchResult.getResultCount() != 0"); -      } -      if (specificSearchResult.getResultCount() != 1) { -        throw new WorkflowException("specificSearchResult.getResultCount() != 1"); -      } -      if (specificSearchResult.getResultsZmr().size() == 1) { -        zmrClient.update(specificSearchResult.getResultsZmr().get(0), eidasData); -      } -      if (specificSearchResult.getResultsErnp().size() == 1) { -        ernpClient.update(specificSearchResult.getResultsErnp().get(0), eidasData); -      } -      return specificSearchResult.getBpk(); -    } catch (WorkflowException e) { -      throw new TaskExecutionException(pendingReq, "Step7a failed.", e); +      final ZmrRegisterResult resultsZmr = zmrClient.searchWithPersonIdentifier( +          null, eidasData.getPersonalIdentifier());             +      final List<RegisterResult> resultsErnp = ernpClient.searchWithPersonIdentifier( +          eidasData.getPersonalIdentifier()); +             +      return new RegisterSearchResult(new RegisterOperationStatus(resultsZmr.getProcessId()),  +          resultsZmr.getPersonResult(), 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     */ -  public MergedRegisterSearchResult searchWithMds(SimpleEidasData eidasData) { -    List<RegisterResult> resultsZmr = -        zmrClient.searchWithMds(eidasData.getGivenName(), eidasData.getFamilyName(), eidasData.getDateOfBirth()); -    List<RegisterResult> resultsErnp = -        ernpClient.searchWithMds(eidasData.getGivenName(), eidasData.getFamilyName(), eidasData.getDateOfBirth()); -    return new MergedRegisterSearchResult(resultsZmr, resultsErnp); +  @Nonnull +  public RegisterSearchResult 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 new RegisterSearchResult(new RegisterOperationStatus(resultsZmr.getProcessId()),  +          resultsZmr.getPersonResult(), resultsErnp); + +    } catch (final EidasSAuthenticationException e) { +      throw new WorkflowException("searchWithMDSOnly", e.getMessage(),  +          !(e instanceof ZmrCommunicationException), e); + +    }    }    /** -   * Search with Person Identifier (eIDAS Pseudonym) in ZMR and ERnP. +   * 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     */ -  public MergedRegisterSearchResult searchWithPersonIdentifier(SimpleEidasData eidasData) { -    List<RegisterResult> resultsZmr = zmrClient.searchWithPersonIdentifier(eidasData.getPseudonym()); -    List<RegisterResult> resultsErnp = ernpClient.searchWithPersonIdentifier(eidasData.getPseudonym()); -    return new MergedRegisterSearchResult(resultsZmr, resultsErnp); +  @Nonnull +  public RegisterSearchResult 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()); + +        // TODO: add search procesfor for ERnP searching +        return new RegisterSearchResult(operationStatus, resultsZmr.getPersonResult(), Collections.emptyList()); + +      } else { +        return new RegisterSearchResult(operationStatus, Collections.emptyList(), Collections.emptyList()); + +      } + +    } catch (final EidasSAuthenticationException e) { +      throw new WorkflowException("searchWithCountrySpecifics", e.getMessage(),  +          !(e instanceof ZmrCommunicationException), e); + +    }    }    /** -   * Search with Tax Number in ZMR and ERnP. +   * Search with BPK-ZP in BMR and ERnP.     */ -  public MergedRegisterSearchResult searchItSpecific(SimpleEidasData eidasData) { -    List<RegisterResult> resultsZmr = zmrClient.searchItSpecific(eidasData.getTaxNumber()); -    List<RegisterResult> resultsErnb = ernpClient.searchItSpecific(eidasData.getTaxNumber()); -    return new MergedRegisterSearchResult(resultsZmr, resultsErnb); +  public RegisterSearchResult searchWithBpkZp(RegisterOperationStatus operationStatus, String bpkZp) { +    final ZmrRegisterResult resultsZmr = zmrClient.searchWithBpkZp( +        operationStatus.getZmrProcessId(), bpkZp); +    final List<RegisterResult> resultsErnp = ernpClient.searchWithBpkZp(bpkZp);  +    return new RegisterSearchResult(operationStatus, resultsZmr.getPersonResult(), resultsErnp); +    }    /** -   * Search with Given Name, Family Name, Date of Birth, Place of Birth and Birth Name in ZMR and ERnP. +   * 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 MergedRegisterSearchResult searchDeSpecific(SimpleEidasData eidasData) { -    List<RegisterResult> resultsZmr = -        zmrClient.searchDeSpecific(eidasData.getGivenName(), eidasData.getFamilyName(), eidasData.getDateOfBirth(), -            eidasData.getPlaceOfBirth(), eidasData.getBirthName()); -    List<RegisterResult> resultsErnb = -        ernpClient.searchDeSpecific(eidasData.getGivenName(), eidasData.getFamilyName(), eidasData.getDateOfBirth(), -            eidasData.getPlaceOfBirth(), eidasData.getBirthName()); -    return new MergedRegisterSearchResult(resultsZmr, resultsErnb); +  public RegisterSearchResult 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 new RegisterSearchResult(operationStatus, resultsZmr.getPersonResult(), Collections.emptyList()); +    }    /** -   * Search with BPK-ZP in BMR and ERnP. +   * Automatic process to fix the register entries. +   * +   * @param specificSearchResult Result of last register search +   * @param eidasData            Received eidas data +   */ +  public void step7aKittProcess(RegisterSearchResult specificSearchResult, +      SimpleEidasData eidasData) throws WorkflowException { +    log.trace("Starting step7aKittProcess"); +    // TODO verify with which data this method gets called +    if (specificSearchResult.getResultCount() != 1) { +      throw new WorkflowException("step7aKittProcess", "getResultCount() != 1"); + +    } +     +    if (specificSearchResult.getResultsZmr().size() == 1) { +      zmrClient.update(specificSearchResult.getOperationStatus().getZmrProcessId(), +           +          specificSearchResult.getResultsZmr().get(0), eidasData); +    } +     +    if (specificSearchResult.getResultsErnp().size() == 1) { +      ernpClient.update(specificSearchResult.getResultsErnp().get(0), eidasData); +       +    } +         +  } +   +  @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 +   *     */ -  public MergedRegisterSearchResult searchWithBpkZp(String bpkZp) { -    List<RegisterResult> resultsZmr = zmrClient.searchWithBpkZp(bpkZp); -    List<RegisterResult> resultsErnp = ernpClient.searchWithBpkZp(bpkZp); -    return new MergedRegisterSearchResult(resultsZmr, resultsErnp); +  @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}. +   *  +   * @author tlenz +   * +   */ +  @Getter +  @RequiredArgsConstructor +  public static class RegisterSearchResult { +     +    /** +     * Mark the register result finished. +     */ +    @Setter +    private boolean matchingFinished = false; +     +    /** +     * 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 or matching is not marked as finished +     */ +    public String getBpk() throws WorkflowException { +      if (getResultCount() != 1 || !matchingFinished) { +        throw new WorkflowException("readRegisterResults",  +            matchingFinished ? "getResultCount() != 1" : "matching prozess not finished yet"); +         +      } +      return getResult().getBpk(); +    } -  public MergedRegisterSearchResult searchWithResidence(String zipcode, String city, String street) { -    List<RegisterResult> resultsZmr = zmrClient.searchWithResidenceData(zipcode, city, street); -    return new MergedRegisterSearchResult(resultsZmr, Collections.emptyList()); +    /** +     * 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); +         +      } +    } +        } +  } 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 97769cc2..41bf4409 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 @@ -23,15 +23,40 @@  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.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joda.time.DateTime; +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.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +  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.clients.szr.SzrClient;  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.egiz.eaaf.core.api.data.EaafConstants;  import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions; @@ -48,36 +73,14 @@ 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 com.fasterxml.jackson.core.JsonProcessingException; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet;  import eu.eidas.auth.commons.attribute.AttributeDefinition;  import eu.eidas.auth.commons.attribute.AttributeValue;  import eu.eidas.auth.commons.light.ILightResponse;  import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress;  import lombok.Data;  import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.joda.time.DateTime; -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 szrservices.IdentityLinkType; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -  /**   * Task that creates the IdentityLink for an eIDAS authenticated person.    * Input: @@ -127,10 +130,14 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {    public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)        throws TaskExecutionException {      try { +       +      /*TODO: needs refactoring because we has to be operate on national identifiers  +       *      because matching and insert ERnP was already done!!         +       */        final ILightResponse eidasResponse = getAuthProcessDataWrapper()            .getGenericDataFromSession(Constants.DATA_FULL_EIDAS_RESPONSE, ILightResponse.class);        final Map<String, Object> eidasAttributes = convertEidasAttrToSimpleMap( -          eidasResponse.getAttributes().getAttributeMap()); +          eidasResponse.getAttributes().getAttributeMap());                          final SimpleEidasData eidData = eidPostProcessor.postProcess(eidasAttributes);        //final SimpleEidasData eidData =        //    getAuthProcessDataWrapper().getGenericDataFromSession(Constants.DATA_SIMPLE_EIDAS, SimpleEidasData.class); @@ -192,7 +199,7 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask {    private void executeEidMode(SimpleEidasData eidData, String personalIdentifier)        throws JsonProcessingException, EaafException, JoseException { -    // get encrypted baseId +    // get encrypted baseId              String vsz = szrClient.getEncryptedStammzahl(eidData);      //write revision-Log entry and extended infos personal-identifier mapping 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 index 025f3475..69b127d8 100644 --- 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 @@ -23,18 +23,19 @@  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.asitplus.eidas.specific.modules.auth.eidas.v2.clients.szr.SzrClient;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.szr.SzrClient; +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.TaskExecutionException; -import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper;  import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;  import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse;  /**   * Task that searches ERnP and ZMR before adding person to SZR. @@ -69,9 +70,11 @@ public class CreateNewErnpEntryTask extends AbstractAuthServletTask {    public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)        throws TaskExecutionException {      try { -      SimpleEidasData simpleEidasData = getInitialEidasData(); +      SimpleEidasData simpleEidasData = MatchingTaskUtils.getInitialEidasData(pendingReq);    +                  // TODO When to do eidPostProcessor.postProcess on the eidas attributes?        String vsz = szrClient.createNewErnpEntry(simpleEidasData); +        // TODO what to do with the VSZ now        log.info("VSZ: {}", vsz);      } catch (final Exception e) { @@ -80,12 +83,4 @@ public class CreateNewErnpEntryTask extends AbstractAuthServletTask {      }    } -  private SimpleEidasData getInitialEidasData() { -    return getAuthProcessDataWrapper().getGenericDataFromSession(Constants.DATA_SIMPLE_EIDAS, SimpleEidasData.class); -  } - -  private AuthProcessDataWrapper getAuthProcessDataWrapper() { -    return pendingReq.getSessionData(AuthProcessDataWrapper.class); -  } -  } 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 index a6e0bca8..1563d6df 100644 --- 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 @@ -23,43 +23,44 @@  package at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks; +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; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.joda.time.DateTime; +import org.springframework.stereotype.Component; + +import com.google.common.collect.ImmutableMap; +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.dao.MergedRegisterSearchResult;  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.ManualFixNecessaryException;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.WorkflowException; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.handler.CountrySpecificDetailSearchProcessor;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.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.RegisterSearchResult;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.EidasResponseUtils; +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.data.AuthProcessDataWrapper;  import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet;  import eu.eidas.auth.commons.attribute.AttributeDefinition;  import eu.eidas.auth.commons.attribute.AttributeValue;  import eu.eidas.auth.commons.light.ILightResponse;  import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress;  import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.joda.time.DateTime; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -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;  /**   * Task that searches registers (ERnP and ZMR) before adding person to SZR. @@ -91,23 +92,19 @@ import static at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants.TRANSIT  @SuppressWarnings("PMD.TooManyStaticImports")  public class InitialSearchTask extends AbstractAuthServletTask { -  private final List<CountrySpecificDetailSearchProcessor> handlers;    private final RegisterSearchService registerSearchService;    private final ICcSpecificEidProcessingService eidPostProcessor;    /**     * Constructor. -   *  @param handlers              List of countrySpecificSearchProcessors     * @param registerSearchService Service for register search access     * @param eidPostProcessor      Country-Specific post processing of attributes     */ -  public InitialSearchTask(List<CountrySpecificDetailSearchProcessor> handlers, -                           RegisterSearchService registerSearchService, +  public InitialSearchTask(RegisterSearchService registerSearchService,                             ICcSpecificEidProcessingService eidPostProcessor) {      this.registerSearchService = registerSearchService; -    this.handlers = handlers;      this.eidPostProcessor = eidPostProcessor; -    log.info("Init with {} country specific detail search services", handlers.size()); +        }    @Override @@ -115,126 +112,124 @@ public class InitialSearchTask extends AbstractAuthServletTask {        throws TaskExecutionException {      try {        final SimpleEidasData eidasData = convertEidasAttrToSimpleData(); -      storeInitialEidasData(eidasData); +      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", e); +      throw new TaskExecutionException(pendingReq, "Initial search failed with a generic error", e); +            }    }    private void step2RegisterSearchWithPersonIdentifier( -      ExecutionContext executionContext, SimpleEidasData eidasData) -      throws TaskExecutionException, EaafStorageException, ManualFixNecessaryException { -    log.trace("Starting step2RegisterSearchWithPersonIdentifier"); -    MergedRegisterSearchResult initialSearchResult = registerSearchService.searchWithPersonIdentifier(eidasData); -    storeInitialRegisterResult(initialSearchResult); -    int resultCount = initialSearchResult.getResultCount(); -    if (resultCount == 0) { -      step5CountrySpecificSearchCheck(executionContext, initialSearchResult, eidasData); -    } else if (resultCount == 1) { -      step3CheckRegisterUpdateNecessary(initialSearchResult, eidasData); -    } else { -      throw new ManualFixNecessaryException(eidasData); -    } -  } - -  private void step3CheckRegisterUpdateNecessary( -      MergedRegisterSearchResult initialSearchResult, SimpleEidasData eidasData) -      throws ManualFixNecessaryException { -    log.trace("Starting step3CheckRegisterUpdateNecessary"); -    try { -      if (eidasData.equalsRegisterData(initialSearchResult)) { -        storeMatchingBpk(initialSearchResult.getBpk()); +      ExecutionContext executionContext, SimpleEidasData eidasData) throws WorkflowException, EaafStorageException { +    try {   +      log.trace("Starting step2RegisterSearchWithPersonIdentifier"); +      RegisterSearchResult initialSearchResult = registerSearchService.searchWithPersonIdentifier(eidasData);     +      int resultCount = initialSearchResult.getResultCount(); +      if (resultCount == 0) { +        step6CountrySpecificSearch(executionContext, initialSearchResult.getOperationStatus(), eidasData); +       +      } else if (resultCount == 1) {       +        // find person by PersonalIdentifier --> finalize first matching task              +        initialSearchResult.setMatchingFinished(true);                 +        foundMatchFinializeTask(initialSearchResult, eidasData); +                    } else { -        // TODO Update "initialSearchResult" in register with "eidasData" from login not possible for now -        log.info("Update in Register"); -        storeMatchingBpk(initialSearchResult.getBpk()); +        throw new WorkflowException("step2RegisterSearchWithPersonIdentifier",  +            "More than one entry with unique personal-identifier", true); +              } -    } catch (WorkflowException | EaafStorageException e) { -      throw new ManualFixNecessaryException(eidasData); +       +    } 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 step5CountrySpecificSearchCheck( -      ExecutionContext executionContext, MergedRegisterSearchResult initialSearchResult, SimpleEidasData eidasData) -      throws TaskExecutionException, ManualFixNecessaryException, EaafStorageException { -    log.trace("Starting step5CheckAndPerformCountrySpecificSearchIfPossible"); -    CountrySpecificDetailSearchProcessor specificHandler = findSpecificProcessor(eidasData); -    if (specificHandler != null) { -      step6CountrySpecificSearch(executionContext, specificHandler, initialSearchResult, eidasData); +   +  private void step6CountrySpecificSearch( +      ExecutionContext executionContext, RegisterOperationStatus registerOperationStatus, SimpleEidasData eidasData) +      throws EaafStorageException, WorkflowException { +     +    log.trace("Starting 'step6CountrySpecificSearch' ... "); +    RegisterSearchResult countrySpecificResult = registerSearchService.searchWithCountrySpecifics( +        registerOperationStatus, eidasData); +    if (countrySpecificResult.getResultCount() == 0) { +      log.trace("'step6CountrySpecificSearch' ends with no result. Forward to next matching step ... "); +      step8RegisterSearchWithMds(executionContext, countrySpecificResult.getOperationStatus(), eidasData); +       +    } else if (countrySpecificResult.getResultCount() == 1) { +      log.trace("'step6CountrySpecificSearch' finds a person. Forward to 'step7aKittProcess' step ... "); +      registerSearchService.step7aKittProcess(countrySpecificResult, eidasData); +       +      // find person by country-specific information --> finalize first matching task +      countrySpecificResult.setMatchingFinished(true);         +      foundMatchFinializeTask(countrySpecificResult, eidasData);       +            } else { -      step8RegisterSearchWithMds(executionContext, eidasData); -    } +      throw new WorkflowException("step6CountrySpecificSearch",  +          "More than one entry with unique country-specific informations", true); +       +    }        } -  @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; -  } - -  private void step6CountrySpecificSearch(ExecutionContext executionContext, -                                          CountrySpecificDetailSearchProcessor processor, -                                          MergedRegisterSearchResult initialSearchResult, -                                          SimpleEidasData eidasData) -      throws TaskExecutionException, ManualFixNecessaryException, EaafStorageException { -    log.trace("Starting step6CountrySpecificSearch"); -    MergedRegisterSearchResult countrySearchResult = processor.search(eidasData); -    int resultCount = countrySearchResult.getResultCount(); -    if (resultCount == 0) { -      step8RegisterSearchWithMds(executionContext, eidasData); -    } else if (resultCount == 1) { -      String bpk = registerSearchService -          .step7aKittProcess(initialSearchResult, countrySearchResult, eidasData, pendingReq); -      storeMatchingBpk(bpk); -    } else { -      throw new ManualFixNecessaryException(eidasData); -    } -  } - -  private void step8RegisterSearchWithMds(ExecutionContext executionContext, SimpleEidasData eidasData) -      throws EaafStorageException { +  private void step8RegisterSearchWithMds(ExecutionContext executionContext,  +      RegisterOperationStatus registerOperationStatus, SimpleEidasData eidasData) +      throws EaafStorageException, WorkflowException {      log.trace("Starting step8RegisterSearchWithMds"); -    MergedRegisterSearchResult registerData = registerSearchService.searchWithMds(eidasData); +    RegisterSearchResult 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 { -      storeFurtherRegisterResults(registerData); +      log.debug("Matching step: 'step8RegisterSearchWithMds' has #{} results. " +          + "Forward to GUI based matching steps ... ", registerData.getResultCount()); +       +      MatchingTaskUtils.storeInitialRegisterResult(pendingReq, registerData);        executionContext.put(TRANSITION_TO_GENERATE_OTHER_LOGIN_METHOD_GUI_TASK, true); +            }    } -  private void storeInitialRegisterResult(MergedRegisterSearchResult registerData) throws EaafStorageException { -    getAuthProcessDataWrapper().setGenericDataToSession(Constants.DATA_INITIAL_REGISTER_RESULT, registerData); -  } - -  private void storeFurtherRegisterResults(MergedRegisterSearchResult registerData) throws EaafStorageException { -    getAuthProcessDataWrapper().setGenericDataToSession(Constants.DATA_FURTHER_REGISTER_RESULT, registerData); -  } -  private void storeInitialEidasData(SimpleEidasData eidasData) throws EaafStorageException { -    getAuthProcessDataWrapper().setGenericDataToSession(Constants.DATA_SIMPLE_EIDAS, eidasData); -  } -  private void storeMatchingBpk(String bpk) throws EaafStorageException { -    getAuthProcessDataWrapper().setGenericDataToSession(Constants.DATA_RESULT_MATCHING_BPK, bpk); +  private void foundMatchFinializeTask(RegisterSearchResult searchResult, SimpleEidasData eidasData)  +      throws WorkflowException, EaafStorageException { +    // check if register update is required +    step3CheckRegisterUpdateNecessary(searchResult, eidasData); +     +    // store search result +    MatchingTaskUtils.storeInitialRegisterResult(pendingReq, searchResult); +            } - -  private AuthProcessDataWrapper getAuthProcessDataWrapper() { -    return pendingReq.getSessionData(AuthProcessDataWrapper.class); +   +  private void step3CheckRegisterUpdateNecessary(RegisterSearchResult initialSearchResult,  +      SimpleEidasData eidasData) throws WorkflowException { +    log.trace("Starting step3CheckRegisterUpdateNecessary"); +    if (!eidasData.equalsRegisterData(initialSearchResult)) { +      // TODO Update "initialSearchResult" in register with "eidasData" from login not possible for now +      log.info("Skipping update-register-information step, because it's not supported yet"); +         +    } else {         +      log.debug("Register information match to eIDAS information. No update requird");       +       +    } +        } - +      @NotNull    private SimpleEidasData convertEidasAttrToSimpleData()        throws EidasAttributeException, EidPostProcessingException { -    final ILightResponse eidasResponse = getAuthProcessDataWrapper() +    final ILightResponse eidasResponse = MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq)          .getGenericDataFromSession(Constants.DATA_FULL_EIDAS_RESPONSE, ILightResponse.class);      Map<String, Object> simpleMap = convertEidasAttrToSimpleMap(eidasResponse.getAttributes().getAttributeMap());      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 index 3b7f361c..b18104fa 100644 --- 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 @@ -23,29 +23,31 @@  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.MergedRegisterSearchResult;  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.RegisterSearchResult; +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.data.AuthProcessDataWrapper;  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; -import org.apache.commons.lang.StringEscapeUtils; -import org.jetbrains.annotations.NotNull; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Enumeration;  /** @@ -69,6 +71,7 @@ import java.util.Enumeration;   *   * @author amarsalek   * @author ckollmann + * @author tlenz   */  @Slf4j  @Component("ReceiveAustrianResidenceGuiResponseTask") @@ -98,72 +101,88 @@ public class ReceiveAustrianResidenceGuiResponseTask extends AbstractAuthServlet    public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)        throws TaskExecutionException {      log.trace("Starting ReceiveAustrianResidenceGuiResponseTask"); -    UserInput input = parseHtmlInput(request); +         +    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()); +      throw new TaskExecutionException(pendingReq, "Invalid user input",  +          new InvalidUserInputException("module.eidasauth.matching.06")); +            } -    // TODO Also search with MDS? But MDS Search has already happened? +     +     +              try { -      SimpleEidasData eidasData = getInitialEidasData(); -      MergedRegisterSearchResult residencyResult = registerSearchService -          .searchWithResidence(input.zipcode, input.city, input.street); +      SimpleEidasData eidasData = MatchingTaskUtils.getInitialEidasData(pendingReq); +      RegisterSearchResult initialSearchResult = MatchingTaskUtils.getInitialRegisterResult(pendingReq); +       +      RegisterSearchResult 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, -                                                  MergedRegisterSearchResult residencyResult, SimpleEidasData eidasData) +  private void compareSearchResultWithInitialData(ExecutionContext executionContext,  +      RegisterSearchResult residencyResult, SimpleEidasData eidasData)        throws TaskExecutionException, EaafStorageException { -    try { -      MergedRegisterSearchResult initialSearchResult = getInitialRegisterResult(); -      // TODO search "residencyResult" in "initialSearchResult"!? +    try {             +      /*TODO: check 'equalsRegisterData' because this method maybe this method evaluate to an invalid result. +       *      See TODO in methods body +       */              if (eidasData.equalsRegisterData(residencyResult)) { -        String bpk = registerSearchService -            .step7aKittProcess(initialSearchResult, residencyResult, eidasData, pendingReq); -        storeMatchingBpk(bpk); +        // update register information +        registerSearchService.step7aKittProcess(residencyResult, eidasData); +                                 +        // store search result to re-used in CreateIdentityLink step, because there we need bPK and MDS +        residencyResult.setMatchingFinished(true); +        MatchingTaskUtils.storeInitialRegisterResult(pendingReq, residencyResult); +                } else {          moveToNextTask(executionContext); +                } +            } catch (WorkflowException e) {        throw new TaskExecutionException(pendingReq, "Search failed", new ManualFixNecessaryException(eidasData)); +            }    } -  private SimpleEidasData getInitialEidasData() { -    return getAuthProcessDataWrapper().getGenericDataFromSession(Constants.DATA_SIMPLE_EIDAS, SimpleEidasData.class); -  } - -  private MergedRegisterSearchResult getInitialRegisterResult() { -    return getAuthProcessDataWrapper().getGenericDataFromSession(Constants.DATA_INITIAL_REGISTER_RESULT, -        MergedRegisterSearchResult.class); -  } - -  private void storeMatchingBpk(String bpk) throws EaafStorageException { -    getAuthProcessDataWrapper().setGenericDataToSession(Constants.DATA_RESULT_MATCHING_BPK, bpk); -  } - -  private AuthProcessDataWrapper getAuthProcessDataWrapper() { -    return pendingReq.getSessionData(AuthProcessDataWrapper.class); -  } -    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) { @@ -174,15 +193,20 @@ public class ReceiveAustrianResidenceGuiResponseTask extends AbstractAuthServlet        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/ReceiveMobilePhoneSignatureResponseTask.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/tasks/ReceiveMobilePhoneSignatureResponseTask.java index a6ff345d..fd469f49 100644 --- 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 @@ -23,24 +23,45 @@  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.MergedRegisterSearchResult; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData;  import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleMobileSignatureData;  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.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.RegisterSearchResult; +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.EaafStorageException;  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.data.AuthProcessDataWrapper;  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; @@ -58,23 +79,6 @@ 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; -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 javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.xml.transform.TransformerException; -import java.io.IOException; -import java.util.List; -import java.util.Set; - -import static at.asitplus.eidas.specific.modules.auth.eidas.v2.idaustriaclient.IdAustriaClientAuthConstants.MODULE_NAME_FOR_LOGGING;  /**   * Task that receives the SAML2 response from ID Austria system. @@ -144,95 +148,84 @@ public class ReceiveMobilePhoneSignatureResponseTask extends AbstractAuthServlet        InboundMessage inboundMessage = decodeAndVerifyMessage(request, response, decoder, comparator);        Pair<PvpSProfileResponse, Boolean> processedMsg = validateAssertion((PvpSProfileResponse) inboundMessage);        if (processedMsg.getSecond()) { -        stopProcessFromUserDecision(executionContext, request, response); +        // 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); +      RegisterSearchResult initialSearchResult = MatchingTaskUtils.getInitialRegisterResult(pendingReq); +       +      // extract user information from ID Austria authentication        AssertionAttributeExtractor extractor = new AssertionAttributeExtractor(processedMsg.getFirst().getResponse()); - -      /* -       * SAML2 response ist bereits vollständig validiert und die Attribute können aus dem -       * <AssertionAttributeExtractor extractor> ausgelesen werden. -       * Die AttributeNamen sind entsprechend PVP Spezifikation, z.B. PvpAttributeDefinitions.GIVEN_NAME_NAME -       * -       * --------------------------------------------------------------------------------------------- -       * -       * TODO: ab hier müssen wir wohl was anpassen -       * -       */ - -      MergedRegisterSearchResult initialSearchResult = getInitialRegisterResult(); -      SimpleEidasData eidasData = getInitialEidasData(); -      String bpkZp = extractBpkZp(extractor, eidasData); - -      // TODO Hier ist wohl keine Register-Suche notwendig, denn das ergibt sicher einen Treffer -      // TODO Soll: In den Ergebnissen aus Step8 matchen! Über BPK matchen, und dann schauen, ob zumindest -      //  Geburtsdatum passt -      MergedRegisterSearchResult registerResult = registerSearchService.searchWithBpkZp(bpkZp); -      if (registerResult.getResultCount() == 0) { -        executionContext.put(Constants.TRANSITION_TO_GENERATE_GUI_QUERY_AUSTRIAN_RESIDENCE_TASK, true); -        return; -      } else if (registerResult.getResultCount() == 1) { -        String bpk = registerSearchService -            .step7aKittProcess(initialSearchResult, registerResult, eidasData, pendingReq); -        storeMatchingBpk(bpk); -        return; -      } else if (registerResult.getResultCount() > 1) { -        throw new ManualFixNecessaryException(eidasData); +      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");  +                       } - -      // set NeedConsent to false, because user gives consent during authentication -      pendingReq.setNeedUserConsent(false); -      log.info("Receive a valid assertion from IDP " + inboundMessage.getEntityID()); +             +      // search entry in initial search result from steps before and build new RegisterSearchResult +      RegisterSearchResult registerResult = new RegisterSearchResult(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 +        registerResult.setMatchingFinished(true); +        MatchingTaskUtils.storeInitialRegisterResult(pendingReq, registerResult); +         +      }             +            } 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 String extractBpkZp(AssertionAttributeExtractor extractor, -                              SimpleEidasData eidasData) throws EaafBuilderException, InvalidUserInputException { -    SimpleMobileSignatureData simpleMobileSignatureData = getAuthDataFromInterfederation(extractor); -    if (!simpleMobileSignatureData.equalsSimpleEidasData(eidasData)) { -      throw new InvalidUserInputException(); // user has cheated!? -    } -    return simpleMobileSignatureData.getBpk(); -  } +  private List<RegisterResult> extractEntriesByBpk(Stream<RegisterResult> stream, String bpk) { +    return stream.filter(el -> bpk.equals(el.getBpk())).collect(Collectors.toList());        -  private SimpleEidasData getInitialEidasData() { -    return getAuthProcessDataWrapper().getGenericDataFromSession(Constants.DATA_SIMPLE_EIDAS, SimpleEidasData.class); -  } - -  private MergedRegisterSearchResult getInitialRegisterResult() { -    return getAuthProcessDataWrapper().getGenericDataFromSession(Constants.DATA_INITIAL_REGISTER_RESULT, -        MergedRegisterSearchResult.class); -  } - -  private void storeMatchingBpk(String bpk) throws EaafStorageException { -    getAuthProcessDataWrapper().setGenericDataToSession(Constants.DATA_RESULT_MATCHING_BPK, bpk); -  } - -  private AuthProcessDataWrapper getAuthProcessDataWrapper() { -    return pendingReq.getSessionData(AuthProcessDataWrapper.class);    }    @NotNull @@ -359,13 +352,15 @@ public class ReceiveMobilePhoneSignatureResponseTask extends AbstractAuthServlet        }        if (PvpAttributeDefinitions.BIRTHDATE_NAME.equals(attrName)) {          builder.dateOfBirth(extractor.getSingleAttributeValue(attrName)); -      } +      }              if (PvpAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME.equals(attrName)) { -        getAuthProcessDataWrapper().setQaaLevel(extractor.getSingleAttributeValue(attrName)); +        MatchingTaskUtils.getAuthProcessDataWrapper(pendingReq).setQaaLevel( +            extractor.getSingleAttributeValue(attrName));        }      } -    getAuthProcessDataWrapper().setIssueInstant(extractor.getAssertionIssuingDate()); +    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 index e139b280..59a6886a 100644 --- 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 @@ -23,6 +23,14 @@  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; @@ -30,12 +38,6 @@ 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.apache.commons.lang.StringEscapeUtils; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Enumeration;  /**   * Handles user's selection from {@link GenerateOtherLoginMethodGuiTask}. @@ -69,13 +71,16 @@ public class ReceiveOtherLoginMethodGuiResponseTask extends AbstractAuthServletT        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()); +          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); +            }    } @@ -85,9 +90,12 @@ public class ReceiveOtherLoginMethodGuiResponseTask extends AbstractAuthServletT        String paramName = paramNames.nextElement();        if (Constants.REQ_SELECTED_LOGIN_METHOD_PARAMETER.equalsIgnoreCase(paramName)) {          return StringEscapeUtils.escapeHtml(request.getParameter(paramName)); +                }      } -    throw new InvalidUserInputException(); +     +    throw new InvalidUserInputException("module.eidasauth.matching.98"); +        }    private void transitionToNextTask(ExecutionContext executionContext, SelectedLoginMethod selection) @@ -96,14 +104,18 @@ public class ReceiveOtherLoginMethodGuiResponseTask extends AbstractAuthServletT        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(); +        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 3023bc0a..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 @@ -23,30 +23,33 @@  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; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableList; +import 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 com.google.common.collect.ImmutableList; -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.attribute.AttributeValueMarshaller;  import eu.eidas.auth.commons.attribute.AttributeValueMarshallingException;  import eu.eidas.auth.commons.attribute.AttributeValueTransliterator;  import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress; -import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern;  public class EidasResponseUtils {    private static final Logger log = LoggerFactory.getLogger(EidasResponseUtils.class); @@ -324,6 +327,21 @@ public class EidasResponseUtils {    }    /** +   * 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 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..5625a30d --- /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,88 @@ +package at.asitplus.eidas.specific.modules.auth.eidas.v2.utils; + +import javax.annotation.Nullable; + +import org.springframework.lang.NonNull; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.RegisterSearchService.RegisterSearchResult; +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; + +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 Matching result from session. +   *  +   * @param pendingReq Current pendingRequest +   * @return Matching result or <code>null</code> if not exist +   */ +  @Nullable +  public static RegisterSearchResult getInitialRegisterResult(IRequest pendingReq) { +    return getAuthProcessDataWrapper(pendingReq).getGenericDataFromSession(Constants.DATA_INITIAL_REGISTER_RESULT, +        RegisterSearchResult.class); +     +  } +   +  /** +   * Store matching result into session. +   *  +   * @param pendingReq Current pendingRequest +   * @param registerData Matching result information +   * @throws EaafStorageException In case of data can not be add into session +   */ +  @Nullable +  public static void storeInitialRegisterResult(IRequest pendingReq, RegisterSearchResult registerData)  +      throws EaafStorageException { +    getAuthProcessDataWrapper(pendingReq).setGenericDataToSession( +        Constants.DATA_INITIAL_REGISTER_RESULT, registerData); +         +  } + +  /** +   * 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); +     +  } +   +  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; +     +  } +} diff --git a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/DummyZmrClient.java b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/DummyZmrClient.java index 80e61451..30a801a4 100644 --- a/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/DummyZmrClient.java +++ b/eidas_modules/authmodule-eIDAS-v2/src/main/java/at/asitplus/eidas/specific/modules/auth/eidas/v2/zmr/DummyZmrClient.java @@ -23,55 +23,59 @@  package at.asitplus.eidas.specific.modules.auth.eidas.v2.zmr; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.RegisterResult; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.dao.SimpleEidasData; +import java.math.BigInteger; +import java.util.Collections; +  import org.springframework.stereotype.Service; -import java.util.Collections; -import java.util.List; +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.exception.EidasSAuthenticationException; +import at.gv.bmi.namespace.zmr_su.zmr._20040201.PersonSuchenRequest;  @Service("ZmrClientForeIDAS")  public class DummyZmrClient implements IZmrClient {    @Override -  public List<RegisterResult> searchWithPersonIdentifier(String personIdentifier) { -    return Collections.emptyList(); +  public ZmrRegisterResult searchWithPersonIdentifier(BigInteger zmrProzessId, String personIdentifier) { +    return new ZmrRegisterResult(Collections.emptyList(), null); +        }    @Override -  public List<RegisterResult> searchWithMds(String givenName, String familyName, String dateOfBirth) { +  public ZmrRegisterResult searchWithMds(BigInteger zmrProzessId, String givenName, String familyName,  +      String dateOfBirth,  String countryCode) {      //TODO will I only receive matches where all three values match perfectly? -    return Collections.emptyList(); +    return new ZmrRegisterResult(Collections.emptyList(), null); +        }    @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(); +  public ZmrRegisterResult searchCountrySpecific(BigInteger zmrProzessId, PersonSuchenRequest personSearchDao, +      String citizenCountryCode) throws EidasSAuthenticationException { +    return new ZmrRegisterResult(Collections.emptyList(), null); +        }    @Override -  public void update(RegisterResult registerResult, SimpleEidasData eidData) { +  public void update(BigInteger zmrProzessId, RegisterResult registerResult, SimpleEidasData eidData) {      //TODO    }    @Override -  public List<RegisterResult> searchWithBpkZp(String bpkzp) { +  public ZmrRegisterResult searchWithBpkZp(BigInteger zmrProzessId, String bpkzp) {      //TODO -    return Collections.emptyList(); +    return new ZmrRegisterResult(Collections.emptyList(), null);    }    @Override -  public List<RegisterResult> searchWithResidenceData(String zipcode, String city, String street) { +  public ZmrRegisterResult searchWithResidenceData(BigInteger zmrProzessId, String givenName, String familyName,  +      String dateOfBirth, String zipcode, String city, String street) {      // TODO -    return Collections.emptyList(); +    return new ZmrRegisterResult(Collections.emptyList(), null);    } +  } | 
