From f0b3d0cda2f0f726876f7fa47357a5481c4aebb7 Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Mon, 22 Jul 2024 16:57:13 +0200 Subject: [PATCH] KOP-2139 KOP-2445 setup: Reduce and refactor code from xta-client-example-lib --- pom.xml | 17 + .../de/ozgcloud/xta/client/XtaClient.java | 28 +- .../ozgcloud/xta/client/XtaClientFactory.java | 32 ++ .../xta/client/config/HttpClientPolicy.java | 36 -- .../ozgcloud/xta/client/config/HttpProxy.java | 36 -- .../ozgcloud/xta/client/config/Keystore.java | 35 -- .../xta/client/config/XtaClientConfig.java | 159 +++----- .../xta/client/config/XtaClientSettings.java | 62 --- .../xta/client/config/XtaConfigValidator.java | 33 ++ .../EmptyNamespaceMessageOutInterceptor.java | 114 ------ .../client/core/ManagementServiceClient.java | 79 ---- .../core/ManagementServiceClientBuilder.java | 55 --- .../xta/client/core/MsgBoxServiceClient.java | 86 ---- .../core/MsgBoxServiceClientBuilder.java | 54 --- .../xta/client/core/SendServiceClient.java | 73 ---- .../client/core/SendServiceClientBuilder.java | 55 --- .../xta/client/core/ServiceBuilder.java | 176 -------- .../ozgcloud/xta/client/core/VersionInfo.java | 61 --- .../de/ozgcloud/xta/client/core/WsClient.java | 266 ------------ .../xta/client/core/WsClientConfig.java | 49 --- .../xta/client/core/XTAServiceFactory.java | 106 +++++ .../core/XtaTLSClientParametersFactory.java | 68 ++++ .../ozgcloud/xta/client/model/Identifier.java | 130 ------ .../de/ozgcloud/xta/client/util/FileUtil.java | 43 -- .../xta/client/util/KeyStoreUtil.java | 97 ----- .../client/XtaClientConfigTestFactory.java | 99 +++++ .../xta/client/XtaClientFactoryTest.java | 74 ++++ .../de/ozgcloud/xta/client/XtaClientTest.java | 43 +- .../client/config/XtaConfigValidatorTest.java | 138 +++++++ .../client/core/XTAServiceFactoryTest.java | 377 ++++++++++++++++++ .../XtaTLSClientParametersFactoryTest.java | 192 +++++++++ src/test/resources/junit-platform.properties | 1 + .../org.junit.jupiter.api.extension.Extension | 1 + 33 files changed, 1207 insertions(+), 1668 deletions(-) create mode 100644 src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/config/HttpClientPolicy.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/config/HttpProxy.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/config/Keystore.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/config/XtaClientSettings.java create mode 100644 src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/EmptyNamespaceMessageOutInterceptor.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/ManagementServiceClient.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/ManagementServiceClientBuilder.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/MsgBoxServiceClient.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/MsgBoxServiceClientBuilder.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/SendServiceClient.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/SendServiceClientBuilder.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/ServiceBuilder.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/VersionInfo.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/WsClient.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/core/WsClientConfig.java create mode 100644 src/main/java/de/ozgcloud/xta/client/core/XTAServiceFactory.java create mode 100644 src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/model/Identifier.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/util/FileUtil.java delete mode 100644 src/main/java/de/ozgcloud/xta/client/util/KeyStoreUtil.java create mode 100644 src/test/java/de/ozgcloud/xta/client/XtaClientConfigTestFactory.java create mode 100644 src/test/java/de/ozgcloud/xta/client/XtaClientFactoryTest.java create mode 100644 src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java create mode 100644 src/test/java/de/ozgcloud/xta/client/core/XTAServiceFactoryTest.java create mode 100644 src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java create mode 100644 src/test/resources/junit-platform.properties create mode 100644 src/test/resources/services/org.junit.jupiter.api.extension.Extension diff --git a/pom.xml b/pom.xml index ce70297..d46c664 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ <!-- build versions --> <cxf.version>4.0.3</cxf.version> <cxf-xjc.version>4.0.0</cxf-xjc.version> + <jsr305.version>3.0.2</jsr305.version> <!-- Build settings --> <timestamp>${maven.build.timestamp}</timestamp> @@ -79,6 +80,12 @@ <version>${cxf.version}</version> </dependency> + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>jsr305</artifactId> + <version>${jsr305.version}</version> + </dependency> + <!-- Test --> <dependency> <groupId>org.projectlombok</groupId> @@ -90,6 +97,16 @@ <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> diff --git a/src/main/java/de/ozgcloud/xta/client/XtaClient.java b/src/main/java/de/ozgcloud/xta/client/XtaClient.java index 214c195..d9cb840 100644 --- a/src/main/java/de/ozgcloud/xta/client/XtaClient.java +++ b/src/main/java/de/ozgcloud/xta/client/XtaClient.java @@ -3,28 +3,22 @@ package de.ozgcloud.xta.client; import org.apache.commons.lang3.NotImplementedException; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.core.MsgBoxServiceClient; -import de.ozgcloud.xta.client.exception.ClientInitializationException; import de.ozgcloud.xta.client.model.XtaMessage; import de.ozgcloud.xta.client.model.XtaMessageId; import de.ozgcloud.xta.client.model.XtaMessageMetaDatasAndHeader; - +import genv3.de.xoev.transport.xta.x211.XTAService; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +@Builder(access = AccessLevel.PROTECTED) +@Getter(AccessLevel.PROTECTED) public class XtaClient { - private MsgBoxServiceClient msgBoxServiceClient; - - public XtaClient(final XtaClientConfig config) throws ClientInitializationException { - // MsgPort initialisieren - if (config.getMsgBoxServiceUrl() != null) { - msgBoxServiceClient = MsgBoxServiceClient.builder(config.getMsgBoxServiceUrl()) - .setClientCertKeystore(config.getClientCertKeystore()) - .setTrustCertKeystore(config.getTrustCertKeystore()) - .setHttpProxy(config.getHttpProxy()).setSoapRequestLogging(config.isLogSoapRequest()) - .setSoapResponseLogging(config.isLogSoapResponse()).setValidation(config.isValidation()) - .setHttpClientPolicy(config.getHttpClientPolicy()) - .build(); - } - } + private final XTAService service; + private final XtaClientConfig config; public XtaMessageMetaDatasAndHeader getMessagesMetadata(String xtaIdentifier) { throw new NotImplementedException(""); diff --git a/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java b/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java new file mode 100644 index 0000000..fbdb70d --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/XtaClientFactory.java @@ -0,0 +1,32 @@ +package de.ozgcloud.xta.client; + +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.config.XtaConfigValidator; +import de.ozgcloud.xta.client.core.XTAServiceFactory; +import de.ozgcloud.xta.client.exception.ClientInitializationException; +import lombok.Builder; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Builder +public class XtaClientFactory { + + private final XtaConfigValidator configValidator; + private final XTAServiceFactory xtaServiceFactory; + private final XtaClientConfig config; + + public static XtaClientFactory from(final XtaClientConfig config) { + return XtaClientFactory.builder() + .configValidator(XtaConfigValidator.builder().build()) + .xtaServiceFactory(XTAServiceFactory.from(config)) + .build(); + } + + public XtaClient create() throws ClientInitializationException { + configValidator.validate(config); + return XtaClient.builder() + .config(config) + .service(xtaServiceFactory.create()) + .build(); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/config/HttpClientPolicy.java b/src/main/java/de/ozgcloud/xta/client/config/HttpClientPolicy.java deleted file mode 100644 index 661f99d..0000000 --- a/src/main/java/de/ozgcloud/xta/client/config/HttpClientPolicy.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.config; - -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; - -/** - * Model-Klasse für eine HttpClientPolicy - */ -@Getter -@Builder -@ToString -public class HttpClientPolicy { - - private Integer connectionTimeout; - private Boolean allowChunking; - private Integer receiveTimeOut; - -} diff --git a/src/main/java/de/ozgcloud/xta/client/config/HttpProxy.java b/src/main/java/de/ozgcloud/xta/client/config/HttpProxy.java deleted file mode 100644 index c4fb514..0000000 --- a/src/main/java/de/ozgcloud/xta/client/config/HttpProxy.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.config; - -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; - -/** - * Model-Klasse für einen HttpProxy - */ -@Getter -@Builder -@ToString(exclude = {"username", "password"}) -public class HttpProxy { - - private String host; - private Integer port; - private String username; - private String password; -} diff --git a/src/main/java/de/ozgcloud/xta/client/config/Keystore.java b/src/main/java/de/ozgcloud/xta/client/config/Keystore.java deleted file mode 100644 index 4bb2aa6..0000000 --- a/src/main/java/de/ozgcloud/xta/client/config/Keystore.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.config; - -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; - -/** - * Model-Klasse für einen Keystore - */ -@Builder -@Getter -@ToString(exclude = {"password"}) -public class Keystore { - - private String path; - private String password; - -} diff --git a/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java b/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java index 846e663..e2b93c3 100644 --- a/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java +++ b/src/main/java/de/ozgcloud/xta/client/config/XtaClientConfig.java @@ -1,130 +1,61 @@ /** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) + * XTA-Client Java Library Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. + * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ package de.ozgcloud.xta.client.config; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; import java.util.List; -import de.ozgcloud.xta.client.exception.ClientException; -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import de.ozgcloud.xta.client.model.Identifier; -import lombok.AccessLevel; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; +import lombok.ToString; -/** - * Config-Holder for the Xta-Client. - */ -@Getter @RequiredArgsConstructor -@Setter(value = AccessLevel.PROTECTED) +@Getter +@Builder +@ToString public class XtaClientConfig { - private List<Identifier> clientIdentifiers; - - private URL managementServiceUrl; - private URL sendServiceUrl; - private URL msgBoxServiceUrl; - private HttpClientPolicy httpClientPolicy; - private HttpProxy httpProxy; - private Keystore clientCertKeystore; - private Keystore trustCertKeystore; - - private boolean validation; - private boolean logSoapRequest; - private boolean logSoapResponse; - - - public static class XtaClientConfigBuilder { - - private XtaClientConfig config = new XtaClientConfig(); - - public XtaClientConfigBuilder withValidation() { - config.setValidation(true); - return this; - } - - public XtaClientConfigBuilder withSoapLogging() { - config.setLogSoapRequest(true); - config.setLogSoapResponse(true); - return this; - } - - public XtaClientConfigBuilder withHttpClientPolicy(final HttpClientPolicy policy) { - config.setHttpClientPolicy(policy); - return this; - } - - public XtaClientConfigBuilder withHttpProxy(final HttpProxy proxy) { - config.setHttpProxy(proxy); - return this; - } - - public XtaClientConfig build() { - return this.config; - } - - } - - public static XtaClientConfigBuilder create(final URL manangementServiceUrl, final URL sendServiceUrl, - final URL msgBoxServiceUrl, final List<Identifier> clientIdentifiers, final String clientCertKeystore, - final String clientCertKeyStorePassword, final String trustCertKeystore, final String trustCertKeyStorePassword) { - - final XtaClientConfigBuilder builder = new XtaClientConfigBuilder(); - - builder.config.setMsgBoxServiceUrl(msgBoxServiceUrl); - builder.config.setManagementServiceUrl(manangementServiceUrl); - builder.config.setSendServiceUrl(sendServiceUrl); - builder.config.setClientIdentifiers(clientIdentifiers); - builder.config - .setClientCertKeystore(Keystore.builder().path(clientCertKeystore).password(clientCertKeyStorePassword).build()); - builder.config.setTrustCertKeystore(Keystore.builder().path(trustCertKeystore).password(trustCertKeyStorePassword).build()); - - return builder; - } - - public static XtaClientConfigBuilder create(final XtaClientSettings settings) throws ClientException { - return create(List.of(Identifier.from(settings)), settings); - } - - public static XtaClientConfigBuilder create(final List<Identifier> clientIdentifiers, final XtaClientSettings settings) throws ClientException { - try { - // Endpoint-URLs - final URL manangementServiceUrl = URI.create(settings.get(XtaClientSettings.BROKER_MANAGEMENT_SERVICE_ENDPONIT)).toURL(); - final URL sendServiceUrl = URI.create(settings.get(XtaClientSettings.BROKER_SEND_SERVICE_ENDPOINT)).toURL(); - final URL msgBoxServiceUrl = URI.create(settings.get(XtaClientSettings.BROKER_MSGBOX_SERVICE_ENDPOINT)).toURL(); - - // Clientcert - final String clientCertKeystore = settings.get(XtaClientSettings.CLIENT_KEYSTORE_CERT_PATH); - final String clientCertKeyStorePassword = settings.get(XtaClientSettings.CLIENT_KEYSTORE_CERT_PASSPHRASE); - - // Servercert - final String trustCertKeystore = settings.get(XtaClientSettings.BROKER_KEYSTORE_TRUSTCERT_PATH); - final String trustCertKeyStorePassword = settings.get(XtaClientSettings.BROKER_KEYSTORE_TRUSTCERT_PASSPHRASE); - - return create(manangementServiceUrl, sendServiceUrl, msgBoxServiceUrl, clientIdentifiers, clientCertKeystore, - clientCertKeyStorePassword, trustCertKeystore, trustCertKeyStorePassword); - - } catch (MalformedURLException e) { - throw new ClientInitializationException("Some URLs could not be parsed: " + e.getMessage(), e); - } - } + @NotEmpty(message = "at least one client identifier is required") + private final List<String> clientIdentifiers; + + @NotBlank + private final String managementServiceUrl; + @NotBlank + private final String sendServiceUrl; + @NotBlank + private final String msgBoxServiceUrl; + + @Valid + private final KeyStore clientCertKeystore; + + private final boolean schemaValidation; + private final boolean logSoapRequests; + private final boolean logSoapResponses; + + @RequiredArgsConstructor + @Getter + @Builder + @ToString + public static class KeyStore { + @NotNull + private final byte[] content; + @NotBlank + private final String type; + @NotNull + private final char[] password; + } } diff --git a/src/main/java/de/ozgcloud/xta/client/config/XtaClientSettings.java b/src/main/java/de/ozgcloud/xta/client/config/XtaClientSettings.java deleted file mode 100644 index 159dd0d..0000000 --- a/src/main/java/de/ozgcloud/xta/client/config/XtaClientSettings.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.config; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -import de.ozgcloud.xta.client.exception.ClientInitializationException; - -/** - * Klasse zum Laden der Settings über eine Propertie-Datei. - */ -public final class XtaClientSettings { - - public final static String BROKER_MANAGEMENT_SERVICE_ENDPONIT = "broker.manangement-service.endpoint"; - public final static String BROKER_SEND_SERVICE_ENDPOINT = "broker.send-service.endpoint"; - public final static String BROKER_MSGBOX_SERVICE_ENDPOINT = "broker.msgbox-service.endpoint"; - public final static String BROKER_KEYSTORE_TRUSTCERT_PATH = "broker.keystore.trustcert.path"; - public final static String BROKER_KEYSTORE_TRUSTCERT_PASSPHRASE = "broker.keystore.trustcert.passphrase"; - - public final static String CLIENT_KEYSTORE_CERT_PATH = "client.keystore.cert.path"; - public final static String CLIENT_KEYSTORE_CERT_PASSPHRASE = "client.keystore.cert.passphrase"; - - public final static String CLIENT_IDENTIFIER_ID_KEY = "client.identifier.id"; - public final static String CLIENT_IDENTIFIER_TYPE_KEY = "client.identifier.type"; - public final static String CLIENT_IDENTIFIER_CATEGORY_KEY = "client.identifier.category"; - public final static String CLIENT_IDENTIFIER_NAME_KEY = "client.identifier.name"; - - - private Properties props = new Properties(); - - public XtaClientSettings(final InputStream is) throws ClientInitializationException { - try { - props.load(is); - if (props.size() == 0) { - throw new ClientInitializationException("Properties sind nicht initialisiert."); - } - } catch (final IOException e) { - throw new ClientInitializationException("Properties konnten nicht geladen werden."); - } - } - - public String get(String key) { - return (String) props.get(key); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java b/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java new file mode 100644 index 0000000..46b82cc --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/config/XtaConfigValidator.java @@ -0,0 +1,33 @@ +package de.ozgcloud.xta.client.config; + +import static lombok.AccessLevel.*; + +import java.util.stream.Collectors; + +import jakarta.validation.Validation; +import jakarta.validation.Validator; + +import de.ozgcloud.xta.client.exception.ClientInitializationException; +import lombok.Builder; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = PRIVATE) +@Builder +public class XtaConfigValidator { + private final static Validator VALIDATOR; + + static { + try (var factory = Validation.buildDefaultValidatorFactory()) { + VALIDATOR = factory.getValidator(); + } + } + + public void validate(final XtaClientConfig config) throws ClientInitializationException { + var violations = VALIDATOR.validate(config); + if (!violations.isEmpty()) { + throw new ClientInitializationException("Client configuration is invalid:\n" + violations.stream() + .map(v -> "'%s' %s".formatted(v.getPropertyPath().toString(), v.getMessage())) + .collect(Collectors.joining("\n"))); + } + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/core/EmptyNamespaceMessageOutInterceptor.java b/src/main/java/de/ozgcloud/xta/client/core/EmptyNamespaceMessageOutInterceptor.java deleted file mode 100644 index 40560e1..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/EmptyNamespaceMessageOutInterceptor.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import org.apache.commons.io.IOUtils; -import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor; -import org.apache.cxf.io.CachedOutputStream; -import org.apache.cxf.message.Message; -import org.apache.cxf.phase.AbstractPhaseInterceptor; -import org.apache.cxf.phase.Phase; - -import lombok.extern.slf4j.Slf4j; - -/** - * Klasse um leere Namespace-Angaben zu entfernen. - */ -@Slf4j -class EmptyNamespaceMessageOutInterceptor extends AbstractPhaseInterceptor<Message> { - - public EmptyNamespaceMessageOutInterceptor() { - super(Phase.PRE_STREAM); - addBefore(SoapPreProtocolOutInterceptor.class.getName()); - } - - public void handleMessage(Message message) { - - OutputStream os = message.getContent(OutputStream.class); - CachedStream cs = new CachedStream(); - message.setContent(OutputStream.class, cs); - message.getInterceptorChain().doIntercept(message); - - try { - cs.flush(); - closeQuietly(cs); - CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class); - - String currentEnvelopeMessage = IOUtils - .toString(csnew.getInputStream(), "UTF-8"); - - csnew.flush(); - closeQuietly(csnew); - - if (log.isDebugEnabled()) { - log.debug("Outbound message: " + currentEnvelopeMessage); - } - - String res = changeOutboundMessage(currentEnvelopeMessage); - if (res != null) { - if (log.isDebugEnabled()) { - log.debug("Outbound message has been changed: " + res); - } - } - res = res != null ? res : currentEnvelopeMessage; - - InputStream replaceInStream = IOUtils - .toInputStream(res, "UTF-8"); - - IOUtils.copy(replaceInStream, os); - replaceInStream.close(); - closeQuietly(replaceInStream); - - os.flush(); - message.setContent(OutputStream.class, os); - closeQuietly(os); - - } catch (IOException ioe) { - log.error("Unable to perform change.", ioe); - throw new RuntimeException(ioe); - } - } - - protected String changeOutboundMessage(String currentEnvelope) { - currentEnvelope = currentEnvelope.replace("xmlns=\"\"", ""); - return currentEnvelope; - } - - private static class CachedStream extends CachedOutputStream { - - public CachedStream() { - super(); - } - } - - // WA - closeQuietly aus IOUtils ist veraltet, daher hier eine eigene Iplementierung - private static void closeQuietly(final Closeable closeable) { - try { - if (closeable != null) { - closeable.close(); - } - } catch (final IOException ioe) { - // ignore - } - } -} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/xta/client/core/ManagementServiceClient.java b/src/main/java/de/ozgcloud/xta/client/core/ManagementServiceClient.java deleted file mode 100644 index 0e7dea0..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/ManagementServiceClient.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * XTA-Client Java Library Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; - -import org.apache.cxf.ws.addressing.AttributedURIType; - -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import genv3.de.xoev.transport.xta.x211.CancelDeniedException; -import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException; -import genv3.de.xoev.transport.xta.x211.LookupServiceRequest; -import genv3.de.xoev.transport.xta.x211.LookupServiceResponse; -import genv3.de.xoev.transport.xta.x211.ManagementPortType; -import genv3.de.xoev.transport.xta.x211.ParameterIsNotValidException; -import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; -import genv3.de.xoev.transport.xta.x211.TransportReport; -import genv3.de.xoev.transport.xta.x211.XTAService; -import genv3.de.xoev.transport.xta.x211.XTAWSTechnicalProblemException; -import genv3.eu.osci.ws.x2014.x10.transport.PartyType; -import lombok.extern.slf4j.Slf4j; - -/** - * gewrapte Serviceklasse für den ManagementServcice eines Brokers - */ -@Slf4j -public class ManagementServiceClient extends WsClient<ManagementPortType> { - - public static ManagementServiceClientBuilder builder(final URL serviceUrl) { - return new ManagementServiceClientBuilder(serviceUrl); - } - - public ManagementServiceClient(final WsClientConfig conf) throws ClientInitializationException { - this.clientConfiguration = conf; - ManagementServiceClient.log.info(VersionInfo.getInfo()); - ManagementServiceClient.log.info(this.clientConfiguration.toString()); - this.initializeService(); - } - - @Override - void initializePort(final URL url) { - final XTAService service = new XTAService(url); - port = service.getManagementPort(); - } - - public void checkAccountActive(final PartyType authorIdentifier) - throws PermissionDeniedException, XTAWSTechnicalProblemException { - port.checkAccountActive(authorIdentifier); - } - - public TransportReport getTransportReport(final AttributedURIType messageID, final PartyType authorIdentifier) - throws PermissionDeniedException, XTAWSTechnicalProblemException, InvalidMessageIDException { - return port.getTransportReport(messageID, authorIdentifier); - } - - public void cancelMessage(final AttributedURIType messageID, final PartyType authorIdentifier) - throws XTAWSTechnicalProblemException, PermissionDeniedException, CancelDeniedException, ParameterIsNotValidException { - port.cancelMessage(messageID, authorIdentifier); - } - - public LookupServiceResponse lookupService(final LookupServiceRequest lookupServiceRequest, final PartyType authorIdentifier) - throws ParameterIsNotValidException, PermissionDeniedException, XTAWSTechnicalProblemException { - return port.lookupService(lookupServiceRequest, authorIdentifier); - } - - public AttributedURIType createMessageId(final PartyType authorIdentifier) - throws PermissionDeniedException, XTAWSTechnicalProblemException { - return port.createMessageId(authorIdentifier); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/ManagementServiceClientBuilder.java b/src/main/java/de/ozgcloud/xta/client/core/ManagementServiceClientBuilder.java deleted file mode 100644 index 6612aa8..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/ManagementServiceClientBuilder.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; - -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Konstruiert eine Instanz des {@link WsClient}. - */ -@Getter(AccessLevel.PACKAGE) -@Slf4j -public class ManagementServiceClientBuilder extends ServiceBuilder<ManagementServiceClient> { - - /** - * Initialisiert einen {@link ServiceBuilder} mit ServiceUrl-Host und ServiceUrl-Port. - * - * @param serviceUrl URL des Endpoints - */ - ManagementServiceClientBuilder(final URL serviceUrl) { - super(serviceUrl); - } - - /** - * Erstellt eine Instanz vom Typ AnlieferungServiceClient - * - * @return ManagementServiceClient - * @throws ClientInitializationException {@link ClientInitializationException} - */ - - @Override - public ManagementServiceClient build() throws ClientInitializationException { - this.validateConfiguration(); - return new ManagementServiceClient(this.clientConfiguration); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/MsgBoxServiceClient.java b/src/main/java/de/ozgcloud/xta/client/core/MsgBoxServiceClient.java deleted file mode 100644 index 4db3dbe..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/MsgBoxServiceClient.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; - -import jakarta.xml.ws.Holder; - -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import genv3.de.xoev.transport.xta.x211.*; -import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxCloseRequestType; -import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxFetchRequest; -import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxGetNextRequestType; -import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxResponseType; -import genv3.eu.osci.ws.x2008.x05.transport.MsgBoxStatusListRequestType; -import genv3.eu.osci.ws.x2008.x05.transport.MsgStatusListType; -import genv3.eu.osci.ws.x2014.x10.transport.MessageMetaData; -import genv3.eu.osci.ws.x2014.x10.transport.PartyType; -import lombok.extern.slf4j.Slf4j; - -/** - * gewrapte Serviceklasse für den MsgBoxServcice eines Brokers - */ -@Slf4j -public class MsgBoxServiceClient extends WsClient<MsgBoxPortType> { - - public static MsgBoxServiceClientBuilder builder(final URL serviceUrl) { - return new MsgBoxServiceClientBuilder(serviceUrl); - } - - public MsgBoxServiceClient(final WsClientConfig conf) throws ClientInitializationException { - - this.clientConfiguration = conf; - MsgBoxServiceClient.log.info(VersionInfo.getInfo()); - MsgBoxServiceClient.log.info(this.clientConfiguration.toString()); - this.initializeService(); - } - - @Override - void initializePort(final URL url) { - final XTAService service = new XTAService(url); - port = service.getMsgBoxPort(); - } - - public GenericContentContainer getMessage(final MsgBoxFetchRequest fetchRequest, final PartyType authorIdentifier, - final Holder<MessageMetaData> messageMetaData, final Holder<MsgBoxResponseType> fetchResponseHeader) - throws InvalidMessageIDException, PermissionDeniedException, XTAWSTechnicalProblemException { - return port.getMessage(fetchRequest, authorIdentifier, messageMetaData, fetchResponseHeader); - } - - public MsgStatusListType getStatusList(final MsgBoxStatusListRequestType fetchRequest, final PartyType authorIdentifier, - final Holder<MsgBoxResponseType> fetchResponseHeader) throws PermissionDeniedException, XTAWSTechnicalProblemException { - return port.getStatusList(fetchRequest, authorIdentifier, fetchResponseHeader); - } - - public GenericContentContainer getNextMessage(final MsgBoxGetNextRequestType fetchRequest, final PartyType authorIdentifier, - final Holder<MessageMetaData> messageMetaData, final Holder<MsgBoxResponseType> fetchResponseHeader) - throws InvalidMessageIDException, PermissionDeniedException, XTAWSTechnicalProblemException { - return port.getNextMessage(fetchRequest, authorIdentifier, messageMetaData, fetchResponseHeader); - } - - public MsgStatusListType getNextStatusList(final MsgBoxGetNextRequestType fetchRequest, final PartyType authorIdentifier, - final Holder<MsgBoxResponseType> fetchResponseHeader) throws PermissionDeniedException, XTAWSTechnicalProblemException { - return port.getNextStatusList(fetchRequest, authorIdentifier, fetchResponseHeader); - } - - public void close(final MsgBoxCloseRequestType fetchRequest, final PartyType authorIdentifier) - throws InvalidMessageIDException, PermissionDeniedException, XTAWSTechnicalProblemException { - port.close(fetchRequest, authorIdentifier); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/MsgBoxServiceClientBuilder.java b/src/main/java/de/ozgcloud/xta/client/core/MsgBoxServiceClientBuilder.java deleted file mode 100644 index bc46414..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/MsgBoxServiceClientBuilder.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; - -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Konstruiert eine Instanz des {@link WsClient}. - */ -@Getter(AccessLevel.PACKAGE) -@Slf4j -public class MsgBoxServiceClientBuilder extends ServiceBuilder<MsgBoxServiceClient> { - - /** - * Initialisiert einen {@link ServiceBuilder} mit ServiceUrl-Host und ServiceUrl-Port. - * - * @param serviceUrl URL des Endpoints - */ - MsgBoxServiceClientBuilder(final URL serviceUrl) { - super(serviceUrl); - } - - /** - * Erstellt eine Instanz vom Typ AnlieferungServiceClient - * - * @return ManagementServiceClient - * @throws ClientInitializationException {@link ClientInitializationException} - */ - @Override - public MsgBoxServiceClient build() throws ClientInitializationException { - this.validateConfiguration(); - return new MsgBoxServiceClient(this.clientConfiguration); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/SendServiceClient.java b/src/main/java/de/ozgcloud/xta/client/core/SendServiceClient.java deleted file mode 100644 index c5c1825..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/SendServiceClient.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; - -import jakarta.xml.ws.Holder; - -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import genv3.de.xoev.transport.xta.x211.GenericContentContainer; -import genv3.de.xoev.transport.xta.x211.MessageSchemaViolationException; -import genv3.de.xoev.transport.xta.x211.MessageVirusDetectionException; -import genv3.de.xoev.transport.xta.x211.ParameterIsNotValidException; -import genv3.de.xoev.transport.xta.x211.PermissionDeniedException; -import genv3.de.xoev.transport.xta.x211.SendPortType; -import genv3.de.xoev.transport.xta.x211.SyncAsyncException; -import genv3.de.xoev.transport.xta.x211.XTAService; -import genv3.de.xoev.transport.xta.x211.XTAWSTechnicalProblemException; -import genv3.eu.osci.ws.x2008.x05.transport.X509TokenContainerType; -import genv3.eu.osci.ws.x2014.x10.transport.MessageMetaData; -import lombok.extern.slf4j.Slf4j; - -/** - * gewrapte Serviceklasse für den SendServcice eines Brokers - */ -@Slf4j -public class SendServiceClient extends WsClient<SendPortType> { - - public static SendServiceClientBuilder builder(final URL serviceUrl) { - return new SendServiceClientBuilder(serviceUrl); - } - - public SendServiceClient(final WsClientConfig conf) throws ClientInitializationException { - this.clientConfiguration = conf; - SendServiceClient.log.info(VersionInfo.getInfo()); - SendServiceClient.log.info(this.clientConfiguration.toString()); - this.initializeService(); - } - - @Override - void initializePort(final URL url) { - final XTAService service = new XTAService(url); - port = service.getSendXtaPort(); - } - - public void sendMessage(final GenericContentContainer genericContainer, final MessageMetaData messageMetaData, - final X509TokenContainerType x509TokenContainer) - throws PermissionDeniedException, XTAWSTechnicalProblemException, MessageSchemaViolationException, MessageVirusDetectionException, - ParameterIsNotValidException, SyncAsyncException { - port.sendMessage(genericContainer, messageMetaData, x509TokenContainer); - } - - public void sendMessageSync(final Holder<GenericContentContainer> genericContainer, - final Holder<MessageMetaData> messageMetaData, final Holder<X509TokenContainerType> x509TokenContainer) - throws PermissionDeniedException, XTAWSTechnicalProblemException, MessageSchemaViolationException, MessageVirusDetectionException, ParameterIsNotValidException, SyncAsyncException { - port.sendMessageSync(genericContainer, messageMetaData, x509TokenContainer); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/SendServiceClientBuilder.java b/src/main/java/de/ozgcloud/xta/client/core/SendServiceClientBuilder.java deleted file mode 100644 index 8de3e05..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/SendServiceClientBuilder.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; - -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Konstruiert eine Instanz des {@link WsClient}. - */ -@Getter(AccessLevel.PACKAGE) -@Slf4j -public class SendServiceClientBuilder extends ServiceBuilder<SendServiceClient> { - - /** - * Initialisiert einen {@link ServiceBuilder} mit ServiceUrl-Host und ServiceUrl-Port. - * - * @param serviceUrl URL des Endpoints - */ - SendServiceClientBuilder(final URL serviceUrl) { - super(serviceUrl); - } - - /** - * Erstellt eine Instanz vom Typ AnlieferungServiceClient - * - * @return ManagementServiceClient - * @throws ClientInitializationException {@link ClientInitializationException} - */ - - @Override - public SendServiceClient build() throws ClientInitializationException { - this.validateConfiguration(); - return new SendServiceClient(this.clientConfiguration); - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/ServiceBuilder.java b/src/main/java/de/ozgcloud/xta/client/core/ServiceBuilder.java deleted file mode 100644 index aa9efc3..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/ServiceBuilder.java +++ /dev/null @@ -1,176 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; - -import de.ozgcloud.xta.client.config.HttpClientPolicy; -import de.ozgcloud.xta.client.config.HttpProxy; -import de.ozgcloud.xta.client.config.Keystore; -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Konstruiert eine Instanz des {@link WsClient}. - */ -@Getter(AccessLevel.PACKAGE) -@Slf4j -public abstract class ServiceBuilder<T extends WsClient> { - - @Getter(AccessLevel.NONE) - final WsClientConfig clientConfiguration; - - /** - * Initialisiert einen {@link ServiceBuilder} mit ServiceUrl-Host und ServiceUrl-Port. - * - * @param serviceUrl URL des Endpoints - */ - ServiceBuilder(final URL serviceUrl) { - this.clientConfiguration = new WsClientConfig(); - this.clientConfiguration.setServiceUrl(serviceUrl); - } - - /** - * Sezt Trust-Keystore des {@link WsClient}s. - * - * @param keystore Trust-Keystore - * @return {@link ServiceBuilder} - */ - public ServiceBuilder<T> setTrustCertKeystore(final Keystore keystore) { - this.clientConfiguration.setTrustCertKeystore(keystore); - return this; - } - - /** - * Setzt den Client-Keystore des {@link WsClient}s. - * - * @param keystore Client-Keystore - * @return {@link ServiceBuilder} - */ - public ServiceBuilder<T> setClientCertKeystore(final Keystore keystore) { - this.clientConfiguration.setClientCertKeystore(keystore); - return this; - } - - /** - * Setzt den Proxy Host. - * - * @param proxy Proxy Host - * @return {@link ServiceBuilder} - */ - public ServiceBuilder<T> setHttpProxy(final HttpProxy proxy) { - this.clientConfiguration.setHttpProxy(proxy); - return this; - } - - /** - * Setzt XSD Validation - * - * @return {@link ServiceBuilder} - */ - public ServiceBuilder<T> setValidation(final boolean validation) { - this.clientConfiguration.setValidation(validation); - return this; - } - - public ServiceBuilder<T> enableValidation() { - setValidation(true); - return this; - } - - public ServiceBuilder<T> enableSoapLogging() { - setSoapRequestLogging(true); - setSoapResponseLogging(true); - return this; - } - - /** - * Setzt SoapRequest Logging - * - * @return {@link ServiceBuilder} - */ - public ServiceBuilder<T> setSoapRequestLogging(final boolean logging) { - this.clientConfiguration.setLogSoapRequest(logging); - return this; - } - - /** - * Setzt SoapResponse Logging - * - * @return {@link ServiceBuilder} - */ - public ServiceBuilder<T> setSoapResponseLogging(final boolean logging) { - this.clientConfiguration.setLogSoapResponse(logging); - return this; - } - - /** - * Setzt eine HttpClient-Policy - * - * @param policy HttpClientPolicy - * @return {@link ServiceBuilder} - */ - - public ServiceBuilder<T> setHttpClientPolicy(final HttpClientPolicy policy) { - this.clientConfiguration.setHttpClientPolicy(policy); - return this; - } - - void validateConfiguration() { - final List<String> errorMsgs = Lists.newArrayList(); - ServiceBuilder.checkNotNull("Service-URL", this.clientConfiguration.getServiceUrl(), errorMsgs); - - // Entweder beide Angaben leer oder beide gesetzt - if (this.clientConfiguration.getHttpProxy() != null) { - if (this.clientConfiguration.getHttpProxy().getHost() == null ^ this.clientConfiguration.getHttpProxy().getPort() == 0) { - errorMsgs.add("Angaben zum Proxy (Host, Port) müssen vollständig gesetzt sein"); - } - } - - this.checkKeystoreFile("Client-Keystore", this.clientConfiguration.getClientCertKeystore(), errorMsgs); - this.checkKeystoreFile("Trust-Keystore", this.clientConfiguration.getTrustCertKeystore(), errorMsgs); - - if (errorMsgs != null && !errorMsgs.isEmpty()) { - throw new IllegalArgumentException(Joiner.on("\n").join(errorMsgs)); - } - } - - private static void checkNotNull(final String identifier, final Object value, final List<String> errorMsgs) { - if (value == null) { - errorMsgs.add(identifier + ": Wert muss gesetzt sein"); - } - } - - private void checkKeystoreFile(final String keyStoreIdentifier, final Keystore keyStore, - final List<String> errorMsgs) { - if (StringUtils.isEmpty(keyStore.getPath())) { - errorMsgs.add(keyStoreIdentifier + ": Pfad muss gesetzt sein"); - } - ServiceBuilder.checkNotNull(keyStoreIdentifier + " Passwort", keyStore.getPassword(), errorMsgs); - } - - public abstract T build() throws ClientInitializationException; -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/VersionInfo.java b/src/main/java/de/ozgcloud/xta/client/core/VersionInfo.java deleted file mode 100644 index b711992..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/VersionInfo.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -/** - * Klasse mit VersionInfo - */ -@Slf4j -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public final class VersionInfo { - - private static String build; - - private static String buildTimestamp; - - private static String version; - - public static String getInfo() { - return String.format("VersionInfo: version: %s, build: %s, timestamp: %s", VersionInfo.version, VersionInfo.build, - VersionInfo.buildTimestamp); - } - - static { - final Properties p = new Properties(); - try (final InputStream in = VersionInfo.class.getClassLoader().getResourceAsStream("version.properties")) { - - p.load(in); - - VersionInfo.build = p.getProperty("bamboo_buildNumber"); - VersionInfo.buildTimestamp = p.getProperty("build_creation_timestamp"); - VersionInfo.version = p.getProperty("project_version"); - - } catch (final IOException e) { - log.error(e.getMessage(), e); - throw new IllegalStateException("Can not initialize version information"); - } - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/WsClient.java b/src/main/java/de/ozgcloud/xta/client/core/WsClient.java deleted file mode 100644 index 2b65bf4..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/WsClient.java +++ /dev/null @@ -1,266 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -import jakarta.xml.ws.BindingProvider; - -import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.configuration.jsse.TLSClientParameters; -import org.apache.cxf.endpoint.Client; -import org.apache.cxf.ext.logging.LoggingInInterceptor; -import org.apache.cxf.ext.logging.LoggingOutInterceptor; -import org.apache.cxf.frontend.ClientProxy; -import org.apache.cxf.transport.http.HTTPConduit; -import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; - -import de.ozgcloud.xta.client.config.HttpClientPolicy; -import de.ozgcloud.xta.client.config.HttpProxy; -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import de.ozgcloud.xta.client.util.KeyStoreUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -/** - * Basisklasse für gewrapte Serviceklassen - */ -@Slf4j -abstract class WsClient<T> { - - WsClientConfig clientConfiguration; - - T port; - - abstract void initializePort(final URL url); - - void initializeService() throws ClientInitializationException { - - final String logMessage = - "JarInfo: " + this.getClass().getName() + ",\nConfiguration: " + this.clientConfiguration + "," + "\ninitializeService"; - - try { - final String serviceUrl = this.clientConfiguration.getServiceUrl().toString(); - WsClient.log.debug("Initialize Service {}", serviceUrl); - final URL url = this.getClass().getClassLoader().getResource("wsdl/XTA.wsdl"); - WsClient.log.debug("url: {}", url); - this.initializeServiceEndpoint(url); - this.initializeTransportSecurity(); - this.initializeHttpClientPolicy(); - this.initializeProxy(); - WsClient.log.info("{} sucessfully.", logMessage); - } catch (final Exception ex) { - WsClient.log.error("{} failed. Reason: {}", logMessage, ex.getMessage()); - throw new ClientInitializationException(ex.getMessage(), ex); - } - } - - private void initializeServiceEndpoint(final URL url) { - initializePort(url); - final BindingProvider bp = (BindingProvider) this.port; - - bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, this.clientConfiguration.getServiceUrl().toString()); - - WsClient.log.debug("Set EnpointUrl to: {}", this.clientConfiguration.getServiceUrl().toString()); - - // Empty xmlns="" entfernen -> XSD-Modellierung Annonymous Inner Type - // ClientProxy.getClient(port).getOutInterceptors().add(new EmptyNamespaceMessageOutInterceptor()); - - initializeValidation(); - - if (this.clientConfiguration.isLogSoapRequest()) { - LoggingOutInterceptor loi = new LoggingOutInterceptor(); - loi.setPrettyLogging(true); - ClientProxy.getClient(port).getOutInterceptors().add(loi); - WsClient.log.debug("Set SoapReqeust-Logging: {}", clientConfiguration.isLogSoapRequest()); - } - - if (this.clientConfiguration.isLogSoapResponse()) { - LoggingInInterceptor lii = new LoggingInInterceptor(); - lii.setPrettyLogging(true); - ClientProxy.getClient(port).getInInterceptors().add(lii); - WsClient.log.debug("Set SoapResponse-Logging: {}", clientConfiguration.isLogSoapResponse()); - } - } - - private void initializeHttpClientPolicy() { - final HttpClientPolicy policy = this.clientConfiguration.getHttpClientPolicy(); - if (policy != null) { - final Client client = ClientProxy.getClient(this.port); - final HTTPConduit httpConduit = (HTTPConduit) client.getConduit(); - final HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy(); - if (policy.getConnectionTimeout() != null) { - httpClientPolicy.setConnectionTimeout(policy.getConnectionTimeout()); - } - if (policy.getAllowChunking() != null) { - httpClientPolicy.setAllowChunking(policy.getAllowChunking()); - } - if (policy.getReceiveTimeOut() != null) { - httpClientPolicy.setReceiveTimeout(policy.getReceiveTimeOut()); - } - httpConduit.setClient(httpClientPolicy); - WsClient.log.debug("Intitialize HttpClientPolicy"); - } - } - - private void initializeTransportSecurity() throws ClientInitializationException { - if (this.clientConfiguration.getClientCertKeystore() != null) { - final Client client = ClientProxy.getClient(this.port); - final HTTPConduit httpConduit = (HTTPConduit) client.getConduit(); - final TLSClientParameters parameters = new TLSClientParameters(); - parameters.setSSLSocketFactory(this.createSSLContext().getSocketFactory()); - httpConduit.setTlsClientParameters(parameters); - WsClient.log.debug("Intitialize TransportSecurity"); - } - } - - private void initializeProxy() { - final HttpProxy proxy = this.clientConfiguration.getHttpProxy(); - if (proxy != null) { - final Client client = ClientProxy.getClient(this.port); - final HTTPConduit httpConduit = (HTTPConduit) client.getConduit(); - if (!StringUtils.isEmpty(proxy.getHost())) { - httpConduit.getClient().setProxyServer(proxy.getHost()); - } - if (proxy.getPort() != null) { - httpConduit.getClient().setProxyServerPort(proxy.getPort()); - } - if (!StringUtils.isEmpty(proxy.getUsername())) { - httpConduit.getProxyAuthorization().setUserName(proxy.getUsername()); - } - if (!StringUtils.isEmpty(proxy.getPassword())) { - httpConduit.getProxyAuthorization().setPassword(proxy.getPassword()); - } - WsClient.log.debug("Intitialize Proxy"); - } - } - - private SSLContext createSSLContext() throws ClientInitializationException { - try { - - // Server - final KeyStore trustStore = KeyStoreUtil - .load(this.clientConfiguration.getTrustCertKeystore().getPath(), - this.clientConfiguration.getTrustCertKeystore().getPassword()); - - // client - final KeyStore keyStore = KeyStoreUtil - .load(this.clientConfiguration.getClientCertKeystore().getPath(), - this.clientConfiguration.getClientCertKeystore().getPassword()); - - //Client-Lib Truststore - final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(trustStore); - - //Default JDK Truststore - final TrustManagerFactory defaultTmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - defaultTmf.init((KeyStore) null); - - //Client-Lib-Keystore - final KeyManagerFactory kmf = KeyManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - kmf.init(keyStore, this.clientConfiguration.getClientCertKeystore().getPassword().toCharArray()); - - final SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - - CustomTruststoreManager customTruststoreManager = new CustomTruststoreManager(getX509TrustMananger(defaultTmf), - getX509TrustMananger(tmf)); - - sslContext.init(kmf.getKeyManagers(), new TrustManager[]{customTruststoreManager}, new SecureRandom()); - - return sslContext; - } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { - throw new ClientInitializationException("Exception in createSSLContext: " + e.getMessage(), e); - } - } - - private X509TrustManager getX509TrustMananger(TrustManagerFactory tmf) { - for (TrustManager tm : tmf.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - return (X509TrustManager) tm; - } - } - - return null; - } - - public void enableValidation() { - clientConfiguration.setValidation(true); - initializeValidation(); - } - - public void disableValidation() { - clientConfiguration.setValidation(false); - initializeValidation(); - } - - private void initializeValidation() { - final BindingProvider bp = (BindingProvider) this.port; - - if (this.clientConfiguration.isValidation()) { - bp.getRequestContext().put("schema-validation-enabled", true); - } else { - bp.getRequestContext().put("schema-validation-enabled", false); - } - - WsClient.log.debug("Set Enpoint-Validation: {}", clientConfiguration.isValidation()); - } - - @RequiredArgsConstructor - private class CustomTruststoreManager implements X509TrustManager { - - final X509TrustManager defaultTm; - final X509TrustManager myTm; - - @Override - public X509Certificate[] getAcceptedIssuers() { - // If you're planning to use client-cert auth, - // merge results from "defaultTm" and "myTm". - return defaultTm.getAcceptedIssuers(); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - try { - myTm.checkServerTrusted(chain, authType); - } catch (CertificateException e) { - defaultTm.checkServerTrusted(chain, authType); - } - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - // If you're planning to use client-cert auth, - // do the same as checking the server. - defaultTm.checkClientTrusted(chain, authType); - } - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/WsClientConfig.java b/src/main/java/de/ozgcloud/xta/client/core/WsClientConfig.java deleted file mode 100644 index cb46664..0000000 --- a/src/main/java/de/ozgcloud/xta/client/core/WsClientConfig.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.core; - -import java.net.URL; - -import de.ozgcloud.xta.client.config.HttpClientPolicy; -import de.ozgcloud.xta.client.config.HttpProxy; -import de.ozgcloud.xta.client.config.Keystore; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -/** - * Konfigurationsparameter für den {@link WsClient}. - */ - -@Setter(AccessLevel.PACKAGE) -@Getter(AccessLevel.PACKAGE) -@ToString -class WsClientConfig { - - private URL serviceUrl; - private Keystore clientCertKeystore; - private Keystore trustCertKeystore; - private HttpProxy httpProxy; - private HttpClientPolicy httpClientPolicy; - - private boolean validation = false; - private boolean logSoapRequest = false; - private boolean logSoapResponse = false; - -} diff --git a/src/main/java/de/ozgcloud/xta/client/core/XTAServiceFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XTAServiceFactory.java new file mode 100644 index 0000000..fbc3ae3 --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/core/XTAServiceFactory.java @@ -0,0 +1,106 @@ +package de.ozgcloud.xta.client.core; + +import java.util.List; +import java.util.Map; + +import jakarta.xml.ws.BindingProvider; + +import org.apache.cxf.endpoint.Client; +import org.apache.cxf.ext.logging.LoggingInInterceptor; +import org.apache.cxf.ext.logging.LoggingOutInterceptor; +import org.apache.cxf.frontend.ClientProxy; +import org.apache.cxf.interceptor.Interceptor; +import org.apache.cxf.message.Message; +import org.apache.cxf.transport.http.HTTPConduit; + +import de.ozgcloud.xta.client.XtaClient; +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.exception.ClientInitializationException; +import genv3.de.xoev.transport.xta.x211.XTAService; +import lombok.Builder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Builder +@RequiredArgsConstructor +@Slf4j +public class XTAServiceFactory { + + final static String SCHEMA_VALIDATION_ENABLED_KEY = "schema-validation-enabled"; + + private final XtaClientConfig config; + private final XtaTLSClientParametersFactory sslContextFactory; + + public static XTAServiceFactory from(final XtaClientConfig config) { + return XTAServiceFactory.builder() + .config(config) + .sslContextFactory(XtaTLSClientParametersFactory.builder() + .config(config) + .build()) + .build(); + } + + public XTAService create() throws ClientInitializationException { + log.debug("[createXtaService] Using config: {}", config); + var service = new XTAService(XtaClient.class.getResource("wsdl/XTA.wsdl")); + configureServiceBindings(service); + return service; + } + + void configureServiceBindings(final XTAService service) throws ClientInitializationException { + configureBinding(config.getManagementServiceUrl(), (BindingProvider) service.getManagementPort()); + configureBinding(config.getSendServiceUrl(), (BindingProvider) service.getSendXtaPort()); + configureBinding(config.getMsgBoxServiceUrl(), (BindingProvider) service.getMsgBoxPort()); + } + + void configureBinding(final String endpointUrl, final BindingProvider port) throws ClientInitializationException { + configureRequestContext(endpointUrl, port.getRequestContext()); + configureClient(getClientFromPort(port)); + } + + Client getClientFromPort(BindingProvider port) { + return ClientProxy.getClient(port); + } + + void configureRequestContext(final String endpointUrl, Map<String, Object> requestContext) { + requestContext.putAll(Map.of( + BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointUrl, + SCHEMA_VALIDATION_ENABLED_KEY, config.isSchemaValidation() + )); + } + + void configureClient(Client client) throws ClientInitializationException { + if (config.isLogSoapRequests()) { + configureOutInterceptors(client.getOutInterceptors()); + } + if (config.isLogSoapResponses()) { + configureInInterceptors(client.getInInterceptors()); + } + configureHttpConduit((HTTPConduit) client.getConduit()); + log.debug("[configureClient] Initialized TransportSecurity"); + } + + void configureInInterceptors(List<Interceptor<? extends Message>> inInterceptors) { + inInterceptors.add(createInInterceptor()); + } + + void configureOutInterceptors(List<Interceptor<? extends Message>> outInterceptors) { + outInterceptors.add(createOutInterceptor()); + } + + private LoggingOutInterceptor createOutInterceptor() { + var outInterceptor = new LoggingOutInterceptor(); + outInterceptor.setPrettyLogging(true); + return outInterceptor; + } + + private LoggingInInterceptor createInInterceptor() { + var inInterceptor = new LoggingInInterceptor(); + inInterceptor.setPrettyLogging(true); + return inInterceptor; + } + + void configureHttpConduit(HTTPConduit conduit) throws ClientInitializationException { + conduit.setTlsClientParameters(sslContextFactory.create()); + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java b/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java new file mode 100644 index 0000000..b9e9285 --- /dev/null +++ b/src/main/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactory.java @@ -0,0 +1,68 @@ +package de.ozgcloud.xta.client.core; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; + +import javax.net.ssl.SSLContext; + +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.hc.core5.ssl.SSLContextBuilder; + +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.exception.ClientInitializationException; +import lombok.Builder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Builder +@RequiredArgsConstructor +@Slf4j +public class XtaTLSClientParametersFactory { + + final static String TLS_PROTOCOL = "TLSv1.2"; + + private final XtaClientConfig config; + + public TLSClientParameters create() throws ClientInitializationException { + log.debug("[createXtaTLsParameters] Using config: {}", config); + var sslContext = createXtaSslContext(); + + var parameters = new TLSClientParameters(); + parameters.setSSLSocketFactory(sslContext.getSocketFactory()); + return parameters; + } + + SSLContext createXtaSslContext() throws ClientInitializationException { + try { + var sslContextBuilder = createSSLContextBuilder(); + var clientCertKeystore = config.getClientCertKeystore(); + if (clientCertKeystore != null) { + sslContextBuilder.loadKeyMaterial(loadStore(clientCertKeystore), clientCertKeystore.getPassword()); + } + return sslContextBuilder.build(); + } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException | IOException | + CertificateException e) { + throw new ClientInitializationException("Failed to create SSL context: " + e.getMessage(), e); + } + } + + SSLContextBuilder createSSLContextBuilder() { + return SSLContextBuilder.create() + .setProtocol(TLS_PROTOCOL); + } + + KeyStore loadStore(final XtaClientConfig.KeyStore keystore) + throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { + var keyStore = KeyStore.getInstance(keystore.getType()); + try (var is = new ByteArrayInputStream(keystore.getContent())) { + keyStore.load(is, keystore.getPassword()); + } + return keyStore; + } +} diff --git a/src/main/java/de/ozgcloud/xta/client/model/Identifier.java b/src/main/java/de/ozgcloud/xta/client/model/Identifier.java deleted file mode 100644 index 0f10654..0000000 --- a/src/main/java/de/ozgcloud/xta/client/model/Identifier.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.model; - -import org.apache.commons.lang3.StringUtils; - -import de.ozgcloud.xta.client.codes.IdentifierType; -import de.ozgcloud.xta.client.config.XtaClientSettings; -import de.ozgcloud.xta.client.exception.ClientException; -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; - -/** - * Modelklasse, repräsentiert einen Kommunikationspartner, bestehend aus Kennung, Typ, Kategorie (optinal) und einen - * benutzerfreundlichen Namen (optional). Diese Klasse definiert nicht die (technische) Rolle des Partners wie Leser oder Autor, - * bzw. Sender oder Empfänger, sondern kann für beides stehen. - */ -@Getter -@NoArgsConstructor(access = AccessLevel.PUBLIC) -public class Identifier { - - // Pflichtfeld: die Kennung, üblich <Prefix>:<Kennung> - private String id; - - // Pflichtfeld: Typ der Kennung, z.B.: XOEV oder JUSTIZ - private String type; - - // Optional: benutzerfreundlicher Name, "Anzeigename" - private String name; - - // Optional: Konkretesiert die fachliche Rolle eines Partners genauer (e.g. "buyer", "Meldehörde", "Standesamt"...) - private String category; - - /** - * Erzeugen eines Identifier mit seinen minimalen Pflichtfeldern. - * - * @param id die ("technische") Kennung des Kommunikationspartners, zb. xdo:11009991 - * @param type Typ des Kommunikationspartners, zb. XOEV - */ - public Identifier(final String id, String type) throws ClientInitializationException { - this.id = id; - this.type = type; - - assertIsValid(this, "Constructor"); - } - - /** - * Erzeugen eines Identifier mit seinen minimalen Pflichtfeldern. - * - * @param id die ("technische") Kennung des Kommunikationspartners, zb. xdo:11009991 - * @param type Typ des Kommunikationspartners, zb. XOEV, {@link IdentifierType} - */ - public Identifier(final String id, IdentifierType type) throws ClientInitializationException { - this(id, type.getCode()); - } - - /** - * Statische Fabrik-Methode zum Erzeugen einer Identifier-Instanz aus den Einstellungen (settings.properties). Typischerweise - * ist dies die Identität des XtaClients selbst. - * - * @return eine Identifier-Instanz gefüllt mit den Daten der Einstellungen. - * @throws ClientException wenn die erzeugte Instans nicht valide ist oder es probleme mit dem Settingfile gibt (IO) - */ - public static Identifier from(final XtaClientSettings settings) throws ClientException { - Identifier target = new Identifier(); - target.id = settings.get(XtaClientSettings.CLIENT_IDENTIFIER_ID_KEY); - target.type = settings.get(XtaClientSettings.CLIENT_IDENTIFIER_TYPE_KEY); - target.category = settings.get(XtaClientSettings.CLIENT_IDENTIFIER_CATEGORY_KEY); - target.name = settings.get(XtaClientSettings.CLIENT_IDENTIFIER_NAME_KEY); - - assertIsValid(target, "Settings"); - return target; - } - - /** - * Mit einem optionalen "Nutzerfreundlichen", zb. "Heiko Materny" - * - * @param name der Name - * @return Identifier-Instanz ("sich selbst"") gefüllt mit dem Namen. - */ - public Identifier withUserFriendlyName(String name) { - this.name = name; - return this; - } - - /** - * Mit einer optionalen Kategorie, zb. "Standesamt" - * - * @param category die Kategorie, e.g. Standesamt - * @return Identifier-Instanz ("sich selbst"") gefüllt mit der Kategorie. - */ - public Identifier withCategory(String category) { - this.name = category; - return this; - } - - private static void assertIsValid(Identifier toCheck, String errMsg) throws ClientInitializationException { - if (StringUtils.isEmpty(toCheck.id)) { - throw new ClientInitializationException("construction from " + errMsg + " failed, reason: id is empty"); - } - - if (StringUtils.isEmpty(toCheck.type)) { - throw new ClientInitializationException("construction from " + errMsg + " failed, reason: type is empty"); - } - } - - /** - * Enum zur Auswahl der Kommunikationsrolle (spielt nur eine Rolle wenn der innere Nachrichtenkopf verarbeitet wird.) - */ - public enum CommunicationRole { - AUTHOR, READER, REPLYTO - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/util/FileUtil.java b/src/main/java/de/ozgcloud/xta/client/util/FileUtil.java deleted file mode 100644 index e9b35d1..0000000 --- a/src/main/java/de/ozgcloud/xta/client/util/FileUtil.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -/** - * Utility-Klasse für File-Operationen - */ -@SuppressWarnings("checkstyle:HideUtilityClassConstructor") -@NoArgsConstructor(access = AccessLevel.NONE) -public final class FileUtil { - - public static InputStream loadByName(String name) throws IOException { - final File f = new File(name); - if (f.isFile()) { - return new FileInputStream(f); - } else { - return FileUtil.class.getClassLoader().getResourceAsStream(name); - } - } -} diff --git a/src/main/java/de/ozgcloud/xta/client/util/KeyStoreUtil.java b/src/main/java/de/ozgcloud/xta/client/util/KeyStoreUtil.java deleted file mode 100644 index 88396b0..0000000 --- a/src/main/java/de/ozgcloud/xta/client/util/KeyStoreUtil.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * XTA-Client Java Library - * Copyright (C) 2021 Koordinierungsstelle für IT-Standards (KoSIT) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package de.ozgcloud.xta.client.util; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; - -import org.apache.commons.lang3.StringUtils; - -import de.ozgcloud.xta.client.exception.ClientInitializationException; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -/** - * Hilfsklasse für KeyStore-Handling. - */ -@SuppressWarnings("checkstyle:HideUtilityClassConstructor") -@NoArgsConstructor(access = AccessLevel.NONE) -public final class KeyStoreUtil { - - private static Map<String, String> mapping = new HashMap<>(); - - static { - mapping.put("p12", "PKCS12"); - mapping.put("jceks", "JCEKS"); - mapping.put("jks", "JKS"); - } - - /** - * Läd und liefert einen KeyStrore. - * <p> - * Unterstützte Formate (bisher): .p12 (Typ: PKCS12) und .jceks (Typ: JCEKS) - * - * @param path der Dateienpfad zur Store-Datei - * @param passPhrase Passwort des Stores - * @return der durch den Pfad referenzierte KeyStore - * @throws ClientInitializationException bei zum Beispiel nicht unterstützen Typen oder wenn die KeyStore-Datei nicht gefunden - * wurde - */ - public static KeyStore load(final String path, final String passPhrase) throws ClientInitializationException { - - try { - final KeyStore keyStore = KeyStore.getInstance(getType(path)); - try (final InputStream is = new FileInputStream(path)) { - keyStore.load(is, passPhrase.toCharArray()); - } catch (FileNotFoundException e) { - // relativ zum Classpath: Fallback for testing - try (final InputStream is = KeyStoreUtil.class.getClassLoader().getResourceAsStream(path)) { - keyStore.load(is, passPhrase.toCharArray()); - } - } - return keyStore; - } catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) { - throw new ClientInitializationException("Exception in createSSLContext: " + e.getMessage(), e); - } - } - - private static String getType(final String path) throws ClientInitializationException { - if (!StringUtils.isEmpty(path)) { - String key = path.substring(path.lastIndexOf(".") + 1).toUpperCase(); - return mapping.get(key.toLowerCase()); - } - return KeyStore.getDefaultType(); - } - - public static SecretKey generateSecretKey() throws NoSuchAlgorithmException { - KeyGenerator keygen = KeyGenerator.getInstance("AES"); - keygen.init(128); - return keygen.generateKey(); - } -} diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientConfigTestFactory.java b/src/test/java/de/ozgcloud/xta/client/XtaClientConfigTestFactory.java new file mode 100644 index 0000000..24c8421 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientConfigTestFactory.java @@ -0,0 +1,99 @@ +package de.ozgcloud.xta.client; + +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.util.Date; +import java.util.List; + +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + +import de.ozgcloud.xta.client.config.XtaClientConfig; +import lombok.SneakyThrows; + +public class XtaClientConfigTestFactory { + + static final String SELF_IDENTIFIER = "selfIdentifier"; + static final String SELF_IDENTIFIER2 = "selfIdentifier2"; + + public static final String KEYSTORE_ALIAS = "selfSigned"; + + static final String MANAGEMENT_PORT_SVC = "https://../managementPort.svc"; + static final String SEND_PORT_SVC = "https://../sendPort.svc"; + static final String MSG_BOX_PORT_SVC = "https://../msgBoxPort.svc"; + + public static XtaClientConfig create() { + return createBuilder().build(); + } + + public static XtaClientConfig createWithSelfSignedClientCert() { + return createBuilder() + .clientCertKeystore(createClientCertKeyStore()) + .build(); + } + + public static XtaClientConfig.XtaClientConfigBuilder createBuilder() { + return XtaClientConfig.builder() + .managementServiceUrl(MANAGEMENT_PORT_SVC) + .sendServiceUrl(SEND_PORT_SVC) + .msgBoxServiceUrl(MSG_BOX_PORT_SVC) + .clientIdentifiers(List.of(SELF_IDENTIFIER, SELF_IDENTIFIER2)) + .logSoapRequests(true) + .logSoapResponses(true) + .schemaValidation(true); + } + + @SneakyThrows + private static XtaClientConfig.KeyStore createClientCertKeyStore() { + var password = "password".toCharArray(); + var type = KeyStore.getDefaultType(); + + // Step 1: Create a KeyStore + var keyStore = KeyStore.getInstance(type); + keyStore.load(null, null); // Initialize KeyStore + + // Step 2: Generate a Key Pair + var keyPairGenerator = KeyPairGenerator.getInstance("EC"); + keyPairGenerator.initialize(384); + var keyPair = keyPairGenerator.generateKeyPair(); + + // Step 3: Create a Self-Signed Certificate + var issuerName = new X500Name("CN=Test, OU=Development, O=MyCompany, L=MyCity, ST=MyState, C=US"); + var serialNumber = new BigInteger(64, new SecureRandom()); + var startDate = new Date(); + var endDate = new Date(startDate.getTime() + 365L * 24L * 60L * 60L * 1000L); // 1 year validity + + var contentSigner = new JcaContentSignerBuilder("SHA256withECDSA").build(keyPair.getPrivate()); + var certBuilder = new JcaX509v3CertificateBuilder( + issuerName, serialNumber, startDate, endDate, issuerName, keyPair.getPublic()); + + var certHolder = certBuilder.build(contentSigner); + var cert = new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider()).getCertificate(certHolder); + + // Step 4: Store the Key Pair and Certificate in the KeyStore + keyStore.setKeyEntry(KEYSTORE_ALIAS, keyPair.getPrivate(), password, new Certificate[] { cert }); + + try (var os = new ByteArrayOutputStream()) { + keyStore.store(os, password); + return XtaClientConfig.KeyStore.builder() + .content(os.toByteArray()) + .type(type) + .password(password) + .build(); + } + } + + public static XtaClientConfig.KeyStore.KeyStoreBuilder createKeystoreBuilder() { + return XtaClientConfig.KeyStore.builder() + .content(new byte[] { 0, 1 }) + .type("AAAA") + .password("".toCharArray()); + } +} diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/XtaClientFactoryTest.java new file mode 100644 index 0000000..2c29651 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientFactoryTest.java @@ -0,0 +1,74 @@ +package de.ozgcloud.xta.client; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.config.XtaConfigValidator; +import de.ozgcloud.xta.client.core.XTAServiceFactory; +import genv3.de.xoev.transport.xta.x211.XTAService; +import lombok.SneakyThrows; + +@ExtendWith(MockitoExtension.class) +class XtaClientFactoryTest { + + @Mock + private XtaConfigValidator configValidator; + @Mock + private XTAServiceFactory xtaServiceFactory; + @Mock + private XtaClientConfig config; + + @InjectMocks + private XtaClientFactory factory; + + @DisplayName("create") + @Nested + class TestCreate { + + @Mock + private XTAService service; + + @BeforeEach + @SneakyThrows + void mock() { + when(xtaServiceFactory.create()).thenReturn(service); + } + + @DisplayName("should have service") + @Test + @SneakyThrows + void shouldHaveService() { + var client = factory.create(); + + assertThat(client.getService()).isEqualTo(service); + } + + @DisplayName("should have config") + @Test + @SneakyThrows + void shouldHaveConfig() { + var client = factory.create(); + + assertThat(client.getConfig()).isEqualTo(config); + } + + @DisplayName("should call validate") + @Test + @SneakyThrows + void shouldCallValidate() { + factory.create(); + + verify(configValidator).validate(config); + } + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java b/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java index 3fbb317..f61eb01 100644 --- a/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java +++ b/src/test/java/de/ozgcloud/xta/client/XtaClientTest.java @@ -2,31 +2,31 @@ package de.ozgcloud.xta.client; import static org.junit.jupiter.api.Assertions.*; -import java.net.URI; -import java.util.List; - import org.apache.commons.lang3.NotImplementedException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import de.ozgcloud.xta.client.codes.IdentifierType; import de.ozgcloud.xta.client.config.XtaClientConfig; -import de.ozgcloud.xta.client.model.Identifier; +import genv3.de.xoev.transport.xta.x211.XTAService; import genv3.eu.osci.ws.x2014.x10.transport.PartyIdentifierType; import genv3.eu.osci.ws.x2014.x10.transport.PartyType; -import lombok.SneakyThrows; +@ExtendWith(MockitoExtension.class) class XtaClientTest { - final String managementServiceUrl = "https://../managementPort.svc"; - final String sendServiceUrl = "https://../sendPort.svc"; - final String msgBoxServiceUrl = "https://../msgBoxPort.svc"; + @Mock + private XTAService service; + + @Mock + private XtaClientConfig config; - final String clientCertKeystore = "certs/client.jceks"; - final String clientCertKeystorePassword = "*pass*"; - final String trustCertKeystore = "certs/trust.jceks"; - final String trustCertKeystorePassword = "*pass*"; + @InjectMocks + private XtaClient client; @DisplayName("get messages metadata") @Nested @@ -38,27 +38,10 @@ class XtaClientTest { var selfIdentifier = new PartyIdentifierType(); selfIdentifier.setValue("selfIdentifier"); p.setIdentifier(selfIdentifier); - var client = createClient(); assertThrows(NotImplementedException.class, () -> client.getMessagesMetadata("selfIdentifier")); } } - @SneakyThrows - private XtaClient createClient() { - var identifier1 = new Identifier("identifier1", IdentifierType.XOEV); - var config = XtaClientConfig.create( - URI.create(managementServiceUrl).toURL(), - URI.create(sendServiceUrl).toURL(), - null, // URI.create(msgBoxServiceUrl).toURL(), // TODO - List.of(identifier1), - clientCertKeystore, - clientCertKeystorePassword, - trustCertKeystore, - trustCertKeystorePassword - ).build(); - return new XtaClient(config); - } - } \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java b/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java new file mode 100644 index 0000000..a06f8a6 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/config/XtaConfigValidatorTest.java @@ -0,0 +1,138 @@ +package de.ozgcloud.xta.client.config; + +import static java.util.Collections.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.function.UnaryOperator; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; + +import de.ozgcloud.xta.client.XtaClientConfigTestFactory; +import de.ozgcloud.xta.client.exception.ClientInitializationException; +import lombok.SneakyThrows; + +@ExtendWith(MockitoExtension.class) +class XtaConfigValidatorTest { + + @InjectMocks + private XtaConfigValidator validator; + + @DisplayName("validate") + @Nested + class TestValidate { + + @DisplayName("should return") + @Test + @SneakyThrows + void shouldReturn() { + var config = XtaClientConfigTestFactory.create(); + + validator.validate(config); + } + + @DisplayName("should return with client keystore") + @Test + @SneakyThrows + void shouldReturnWithClientKeystore() { + var config = XtaClientConfigTestFactory.createBuilder() + .clientCertKeystore(XtaClientConfigTestFactory.createKeystoreBuilder().build()) + .build(); + + validator.validate(config); + } + + @DisplayName("should throw without client identifiers") + @Test + void shouldThrowWithoutIdentifiers() { + var config = XtaClientConfigTestFactory.createBuilder() + .clientIdentifiers(emptyList()) + .build(); + + assertThatThrownBy(() -> validator.validate(config)) + .isInstanceOf(ClientInitializationException.class) + .hasMessageContaining("clientIdentifiers"); + } + + @DisplayName("should throw without management service url") + @Test + void shouldThrowWithoutManagementServiceUrl() { + var config = XtaClientConfigTestFactory.createBuilder() + .managementServiceUrl("") + .build(); + + assertThatThrownBy(() -> validator.validate(config)) + .isInstanceOf(ClientInitializationException.class) + .hasMessageContaining("managementServiceUrl"); + } + + @DisplayName("should throw without send service url") + @Test + void shouldThrowWithoutSendServiceUrl() { + var config = XtaClientConfigTestFactory.createBuilder() + .sendServiceUrl("") + .build(); + + assertThatThrownBy(() -> validator.validate(config)) + .isInstanceOf(ClientInitializationException.class) + .hasMessageContaining("sendServiceUrl"); + } + + @DisplayName("should throw without msg box service url") + @Test + void shouldThrowWithoutMsgBoxServiceUrl() { + var config = XtaClientConfigTestFactory.createBuilder() + .msgBoxServiceUrl("") + .build(); + + assertThatThrownBy(() -> validator.validate(config)) + .isInstanceOf(ClientInitializationException.class) + .hasMessageContaining("msgBoxServiceUrl"); + } + + @DisplayName("with invalid client cert keystore") + @Nested + class TestWithInvalidClientCertKeystore { + @DisplayName("should throw without password") + @Test + void shouldThrowWithoutKeystorePassword() { + var config = createKeystoreWithClientCertKeyStore(o -> o.password(null)); + + assertThatThrownBy(() -> validator.validate(config)) + .isInstanceOf(ClientInitializationException.class) + .hasMessageContaining("password"); + } + + @DisplayName("should throw without type") + @Test + void shouldThrowWithoutKeystoreType() { + var config = createKeystoreWithClientCertKeyStore(o -> o.type("")); + + assertThatThrownBy(() -> validator.validate(config)) + .isInstanceOf(ClientInitializationException.class) + .hasMessageContaining("type"); + } + + @DisplayName("should throw without content") + @Test + void shouldThrowWithoutKeystoreContent() { + var config = createKeystoreWithClientCertKeyStore(o -> o.content(null)); + + assertThatThrownBy(() -> validator.validate(config)) + .isInstanceOf(ClientInitializationException.class) + .hasMessageContaining("content"); + } + + private XtaClientConfig createKeystoreWithClientCertKeyStore(UnaryOperator<XtaClientConfig.KeyStore.KeyStoreBuilder> operator) { + return XtaClientConfigTestFactory.createBuilder() + .clientCertKeystore(operator.apply(XtaClientConfigTestFactory.createKeystoreBuilder()).build()) + .build(); + } + } + + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/core/XTAServiceFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XTAServiceFactoryTest.java new file mode 100644 index 0000000..6d71e60 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/core/XTAServiceFactoryTest.java @@ -0,0 +1,377 @@ +package de.ozgcloud.xta.client.core; + +import static de.ozgcloud.xta.client.core.XTAServiceFactory.*; +import static org.assertj.core.api.AssertionsForClassTypes.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.xml.ws.BindingProvider; + +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.endpoint.Client; +import org.apache.cxf.ext.logging.LoggingInInterceptor; +import org.apache.cxf.ext.logging.LoggingOutInterceptor; +import org.apache.cxf.interceptor.Interceptor; +import org.apache.cxf.transport.Conduit; +import org.apache.cxf.transport.http.HTTPConduit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import de.ozgcloud.xta.client.XtaClientConfigTestFactory; +import de.ozgcloud.xta.client.config.XtaClientConfig; +import genv3.de.xoev.transport.xta.x211.ManagementPortType; +import genv3.de.xoev.transport.xta.x211.MsgBoxPortType; +import genv3.de.xoev.transport.xta.x211.SendPortType; +import genv3.de.xoev.transport.xta.x211.XTAService; +import lombok.SneakyThrows; + +@ExtendWith(MockitoExtension.class) +class XTAServiceFactoryTest { + private final String endpointUrl = "http://endpoint.test/test.svc"; + + @Mock + private XtaClientConfig config; + + @Mock + private XtaTLSClientParametersFactory sslContextFactory; + + @Spy + @InjectMocks + private XTAServiceFactory factory; + + @DisplayName("create") + @Nested + class TestCreate { + + @Captor + private ArgumentCaptor<XTAService> serviceCaptor; + + @DisplayName("should call configureServiceBindings") + @Test + @SneakyThrows + void shouldCallConfigureServiceBindings() { + doNothing().when(factory).configureServiceBindings(any()); + + factory.create(); + + verify(factory).configureServiceBindings(any()); + } + + @DisplayName("should return service") + @Test + @SneakyThrows + void shouldReturnService() { + doNothing().when(factory).configureServiceBindings(any()); + + var result = factory.create(); + + verify(factory).configureServiceBindings(serviceCaptor.capture()); + assertThat(serviceCaptor.getValue()).isNotNull().isEqualTo(result); + } + } + + @DisplayName("configure service bindings") + @Nested + class TestConfigureServiceBindings { + + private final XtaClientConfig testConfig = XtaClientConfigTestFactory.create(); + + @Mock(extraInterfaces = BindingProvider.class) + private ManagementPortType managementPort; + @Mock(extraInterfaces = BindingProvider.class) + private SendPortType sendXtaPort; + @Mock(extraInterfaces = BindingProvider.class) + private MsgBoxPortType msgBoxPort; + + @Mock + private XTAService service; + + @BeforeEach + @SneakyThrows + void mock() { + when(config.getManagementServiceUrl()).thenReturn(testConfig.getManagementServiceUrl()); + when(config.getSendServiceUrl()).thenReturn(testConfig.getSendServiceUrl()); + when(config.getMsgBoxServiceUrl()).thenReturn(testConfig.getMsgBoxServiceUrl()); + + when(service.getManagementPort()).thenReturn(managementPort); + when(service.getSendXtaPort()).thenReturn(sendXtaPort); + when(service.getMsgBoxPort()).thenReturn(msgBoxPort); + + doNothing().when(factory).configureBinding(any(), any()); + } + + @DisplayName("should configure management port") + @Test + @SneakyThrows + void shouldConfigureManagementPort() { + factory.configureServiceBindings(service); + + verify(factory).configureBinding(testConfig.getManagementServiceUrl(), (BindingProvider) managementPort); + } + + @DisplayName("should configure send port") + @Test + @SneakyThrows + void shouldConfigureSendPort() { + factory.configureServiceBindings(service); + + verify(factory).configureBinding(testConfig.getSendServiceUrl(), (BindingProvider) sendXtaPort); + } + + @DisplayName("should configure msg box port") + @Test + @SneakyThrows + void shouldConfigureMsgBoxPort() { + factory.configureServiceBindings(service); + + verify(factory).configureBinding(testConfig.getMsgBoxServiceUrl(), (BindingProvider) msgBoxPort); + } + } + + @DisplayName("configure binding") + @Nested + class TestConfigureBinding { + + @Mock + private BindingProvider port; + + @Mock + private Map<String, Object> requestContext; + + @Mock + private Client client; + + @BeforeEach + @SneakyThrows + void mock() { + when(port.getRequestContext()).thenReturn(requestContext); + doNothing().when(factory).configureClient(any()); + doReturn(client).when(factory).getClientFromPort(port); + } + + @DisplayName("should configure request context") + @Test + @SneakyThrows + void shouldConfigureRequestContext() { + factory.configureBinding(endpointUrl, port); + + verify(factory).configureRequestContext(endpointUrl, requestContext); + } + + @DisplayName("should configure client") + @Test + @SneakyThrows + void shouldConfigureClient() { + factory.configureBinding(endpointUrl, port); + + verify(factory).configureClient(client); + } + } + + @DisplayName("configure request context") + @Nested + class TestConfigureRequestContext { + + private HashMap<String, Object> requestContext; + + @BeforeEach + void mock() { + requestContext = new HashMap<>(); + requestContext.put("old", "value"); + when(config.isSchemaValidation()).thenReturn(false); + } + + @DisplayName("should keep old") + @Test + void shouldKeepOld() { + factory.configureRequestContext(endpointUrl, requestContext); + + assertThat(requestContext.get("old")).isEqualTo("value"); + } + + @DisplayName("should add endpoint url") + @Test + @SneakyThrows + void shouldAddEndpointUrl() { + factory.configureRequestContext(endpointUrl, requestContext); + + assertThat(requestContext.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY)).isEqualTo(endpointUrl); + } + + @DisplayName("should add schema validation") + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void shouldAddSchemaValidation(boolean validation) { + reset(config); + when(config.isSchemaValidation()).thenReturn(validation); + + factory.configureRequestContext(endpointUrl, requestContext); + + assertThat(requestContext.get(SCHEMA_VALIDATION_ENABLED_KEY)).isEqualTo(validation); + } + } + + @DisplayName("configure client") + @Nested + class TestConfigureClient { + + @Mock + private Client client; + + @Mock + private List<Interceptor<?>> outInterceptors; + + @Mock + private List<Interceptor<?>> inInterceptors; + + @Mock(extraInterfaces = Conduit.class) + private HTTPConduit clientConduit; + + @BeforeEach + @SneakyThrows + void mock() { + when(client.getConduit()).thenReturn(clientConduit); + doNothing().when(factory).configureHttpConduit(any()); + } + + @DisplayName("should configure out interceptors") + @Test + @SneakyThrows + void shouldConfigureOutInterceptors() { + when(config.isLogSoapRequests()).thenReturn(true); + when(config.isLogSoapResponses()).thenReturn(false); + when(client.getOutInterceptors()).thenReturn(outInterceptors); + doNothing().when(factory).configureOutInterceptors(any()); + + factory.configureClient(client); + + verify(factory).configureOutInterceptors(client.getOutInterceptors()); + } + + @DisplayName("should configure in interceptors") + @Test + @SneakyThrows + void shouldConfigureInInterceptors() { + when(config.isLogSoapRequests()).thenReturn(false); + when(config.isLogSoapResponses()).thenReturn(true); + when(client.getInInterceptors()).thenReturn(inInterceptors); + doNothing().when(factory).configureInInterceptors(any()); + + factory.configureClient(client); + + verify(factory).configureInInterceptors(client.getInInterceptors()); + } + + @DisplayName("should configure http conduit") + @Test + @SneakyThrows + void shouldConfigureHttpConduit() { + when(config.isLogSoapRequests()).thenReturn(false); + when(config.isLogSoapResponses()).thenReturn(false); + + factory.configureClient(client); + + verify(factory).configureHttpConduit((HTTPConduit) client.getConduit()); + } + } + + @DisplayName("configure in interceptors") + @Nested + class TestConfigureInInterceptors { + + private List<Interceptor<?>> inInterceptors; + + @BeforeEach + void setup() { + inInterceptors = new ArrayList<>(); + } + + @DisplayName("should add one interceptor") + @Test + void shouldAddOneInterceptor() { + factory.configureInInterceptors(inInterceptors); + + assertThat(inInterceptors.size()).isEqualTo(1); + } + + @DisplayName("should add in interceptor") + @Test + @SneakyThrows + void shouldAddInInterceptor() { + factory.configureInInterceptors(inInterceptors); + + assertThat(inInterceptors.getFirst()).isInstanceOf(LoggingInInterceptor.class); + } + } + + @DisplayName("configure out interceptors") + @Nested + class TestConfigureOutInterceptors { + + private List<Interceptor<?>> outInterceptors; + + @BeforeEach + void setup() { + outInterceptors = new ArrayList<>(); + } + + @DisplayName("should add one interceptor") + @Test + void shouldAddOneInterceptor() { + factory.configureOutInterceptors(outInterceptors); + + assertThat(outInterceptors.size()).isEqualTo(1); + } + + @DisplayName("should add out interceptor") + @Test + @SneakyThrows + void shouldAddOutInterceptor() { + factory.configureOutInterceptors(outInterceptors); + + assertThat(outInterceptors.getFirst()).isInstanceOf(LoggingOutInterceptor.class); + } + } + + @DisplayName("configure http conduit") + @Nested + class TestConfigureHttpConduit { + + @Mock + private HTTPConduit conduit; + + @Mock + private TLSClientParameters tlsClientParameters; + + @BeforeEach + @SneakyThrows + void mock() { + when(sslContextFactory.create()).thenReturn(tlsClientParameters); + } + + @DisplayName("should set tls client parameters") + @Test + @SneakyThrows + void shouldSetTlsClientParameters() { + factory.configureHttpConduit(conduit); + + verify(conduit).setTlsClientParameters(tlsClientParameters); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java new file mode 100644 index 0000000..3e27432 --- /dev/null +++ b/src/test/java/de/ozgcloud/xta/client/core/XtaTLSClientParametersFactoryTest.java @@ -0,0 +1,192 @@ +package de.ozgcloud.xta.client.core; + +import static de.ozgcloud.xta.client.XtaClientConfigTestFactory.*; +import static de.ozgcloud.xta.client.core.XtaTLSClientParametersFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.security.KeyStore; +import java.security.KeyStoreException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; + +import org.apache.hc.core5.ssl.SSLContextBuilder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import de.ozgcloud.xta.client.XtaClientConfigTestFactory; +import de.ozgcloud.xta.client.config.XtaClientConfig; +import de.ozgcloud.xta.client.exception.ClientInitializationException; +import lombok.SneakyThrows; + +@ExtendWith(MockitoExtension.class) +class XtaTLSClientParametersFactoryTest { + + @Mock + private XtaClientConfig config; + + @InjectMocks + @Spy + private XtaTLSClientParametersFactory factory; + + @DisplayName("create TLS client parameters") + @Nested + class TestCreateTlsClientParameters { + @Mock + SSLSocketFactory sslSocketFactory; + + @Mock + SSLContext sslContext; + + @BeforeEach + @SneakyThrows + void mock() { + doReturn(sslContext).when(factory).createXtaSslContext(); + when(sslContext.getSocketFactory()).thenReturn(sslSocketFactory); + } + + @DisplayName("should have SSL socket factory") + @Test + @SneakyThrows + void shouldHaveSslSocketFactory() { + var tlsClientParameters = factory.create(); + + assertThat(tlsClientParameters.getSSLSocketFactory()).isEqualTo(sslSocketFactory); + } + } + + @DisplayName("create XTA SSL context") + @Nested + class TestCreateXtaSslContext { + + private final char[] PASSWORD = "password".toCharArray(); + + @Mock + XtaClientConfig.KeyStore configKeystore; + + @Mock + KeyStore keystore; + + @Mock + SSLContextBuilder sslContextBuilder; + + @Mock + SSLContext sslContext; + + @BeforeEach + @SneakyThrows + void mock() { + doReturn(sslContextBuilder).when(factory).createSSLContextBuilder(); + when(sslContextBuilder.build()).thenReturn(sslContext); + } + + @DisplayName("with client key store") + @Nested + class TestWithClientKeyStore { + + @BeforeEach + @SneakyThrows + void mock() { + when(config.getClientCertKeystore()).thenReturn(configKeystore); + when(configKeystore.getPassword()).thenReturn(PASSWORD); + doReturn(keystore).when(factory).loadStore(configKeystore); + } + + @DisplayName("should call loadKeyMaterial") + @Test + @SneakyThrows + void shouldCallLoadKeyMaterial() { + factory.createXtaSslContext(); + + verify(sslContextBuilder).loadKeyMaterial(keystore, PASSWORD); + } + + @DisplayName("should return SSL context") + @Test + @SneakyThrows + void shouldSetProtocol() { + var sslContextResult = factory.createXtaSslContext(); + + assertThat(sslContextResult).isEqualTo(sslContext); + } + + @DisplayName("should throw client initialization exception for keystore exception") + @Test + @SneakyThrows + void shouldThrowClientInitializationExceptionForKeyStoreException() { + reset(sslContextBuilder); + doThrow(new KeyStoreException("something")).when(sslContextBuilder).loadKeyMaterial(any(), any()); + + assertThatThrownBy(() -> factory.createXtaSslContext()) + .isInstanceOf(ClientInitializationException.class); + } + } + + @DisplayName("without client key store") + @Nested + class TestWithoutClientKeyStore { + + @BeforeEach + @SneakyThrows + void mock() { + when(config.getClientCertKeystore()).thenReturn(null); + } + + @DisplayName("should return SSL context") + @Test + @SneakyThrows + void shouldSetProtocol() { + var sslContextResult = factory.createXtaSslContext(); + + assertThat(sslContextResult).isEqualTo(sslContext); + } + } + } + + @DisplayName("create SSL context builder") + @Nested + class TestCreateSslContextBuilder { + + @DisplayName("should set protocol") + @Test + @SneakyThrows + void shouldSetProtocol() { + var sslContext = factory.createSSLContextBuilder().build(); + + assertThat(sslContext.getProtocol()).isEqualTo(TLS_PROTOCOL); + } + } + + @DisplayName("load store") + @Nested + class TestLoadStore { + + private final XtaClientConfig.KeyStore configKeyStore = XtaClientConfigTestFactory.createWithSelfSignedClientCert().getClientCertKeystore(); + + @DisplayName("should have type") + @Test + @SneakyThrows + void shouldHaveType() { + var keystore = factory.loadStore(configKeyStore); + + assertThat(keystore.getType()).isEqualTo(configKeyStore.getType()); + } + + @DisplayName("should have entry") + @Test + @SneakyThrows + void shouldHaveEntry() { + var keystore = factory.loadStore(configKeyStore); + + assertThat(keystore.isKeyEntry(KEYSTORE_ALIAS)).isTrue(); + } + } +} \ No newline at end of file diff --git a/src/test/resources/junit-platform.properties b/src/test/resources/junit-platform.properties new file mode 100644 index 0000000..b059a65 --- /dev/null +++ b/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.extensions.autodetection.enabled=true \ No newline at end of file diff --git a/src/test/resources/services/org.junit.jupiter.api.extension.Extension b/src/test/resources/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 0000000..79b126e --- /dev/null +++ b/src/test/resources/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +org.mockito.junit.jupiter.MockitoExtension \ No newline at end of file -- GitLab