From 6fb6c473ea98375ce965aca9f34c431d722c1c04 Mon Sep 17 00:00:00 2001 From: pkaras Date: Tue, 6 Nov 2018 15:23:28 +0100 Subject: SSL setup for dmaap publisher Change-Id: I5dbfc551e515a5f3ce23ec9ffc766ae3012a057a Issue-ID: DCAEGEN2-952 Signed-off-by: piotr.karas --- .../services/prh/tasks/DmaapPublisherTaskImpl.java | 3 +- .../prh/tasks/DmaapPublisherTaskImplTest.java | 1 - prh-dmaap-client/pom.xml | 8 ++ .../ConsumerReactiveHttpClientFactory.java | 8 +- .../producer/DMaaPPublisherReactiveHttpClient.java | 13 ++- .../service/producer/DmaaPRestTemplateFactory.java | 115 +++++++++++++++++++++ .../PublisherReactiveHttpClientFactory.java | 11 +- .../DMaaPPublisherReactiveHttpClientTest.java | 3 +- .../producer/DmaaPRestTemplateFactoryTest.java | 62 +++++++++++ .../PublisherReactiveHttpClientFactoryTest.java | 4 +- .../src/test/resources/keystore.password | 1 + .../src/test/resources/org.onap.dcae.jks | Bin 0 -> 4512 bytes .../src/test/resources/org.onap.dcae.trust.jks | Bin 0 -> 1413 bytes .../src/test/resources/truststore.password | 1 + 14 files changed, 213 insertions(+), 17 deletions(-) create mode 100644 prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/DmaaPRestTemplateFactory.java create mode 100644 prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/DmaaPRestTemplateFactoryTest.java create mode 100644 prh-dmaap-client/src/test/resources/keystore.password create mode 100644 prh-dmaap-client/src/test/resources/org.onap.dcae.jks create mode 100644 prh-dmaap-client/src/test/resources/org.onap.dcae.trust.jks create mode 100644 prh-dmaap-client/src/test/resources/truststore.password diff --git a/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/DmaapPublisherTaskImpl.java b/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/DmaapPublisherTaskImpl.java index 76c1bb56..d6ad540b 100644 --- a/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/DmaapPublisherTaskImpl.java +++ b/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/DmaapPublisherTaskImpl.java @@ -24,6 +24,7 @@ import org.onap.dcaegen2.services.prh.configuration.Config; import org.onap.dcaegen2.services.prh.exceptions.DmaapNotFoundException; import org.onap.dcaegen2.services.prh.model.ConsumerDmaapModel; import org.onap.dcaegen2.services.prh.service.producer.DMaaPPublisherReactiveHttpClient; +import org.onap.dcaegen2.services.prh.service.producer.DmaaPRestTemplateFactory; import org.onap.dcaegen2.services.prh.service.producer.PublisherReactiveHttpClientFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +45,7 @@ public class DmaapPublisherTaskImpl implements DmaapPublisherTask { @Autowired public DmaapPublisherTaskImpl(Config config) { - this(config, new PublisherReactiveHttpClientFactory()); + this(config, new PublisherReactiveHttpClientFactory(new DmaaPRestTemplateFactory())); } DmaapPublisherTaskImpl(Config config, PublisherReactiveHttpClientFactory httpClientFactory) { diff --git a/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/DmaapPublisherTaskImplTest.java b/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/DmaapPublisherTaskImplTest.java index 5bdcba52..538c197b 100644 --- a/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/DmaapPublisherTaskImplTest.java +++ b/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/DmaapPublisherTaskImplTest.java @@ -24,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; diff --git a/prh-dmaap-client/pom.xml b/prh-dmaap-client/pom.xml index 751a4f00..ebc2b992 100644 --- a/prh-dmaap-client/pom.xml +++ b/prh-dmaap-client/pom.xml @@ -115,5 +115,13 @@ reactor-test test + + org.springframework.boot + spring-boot-starter-web + + + org.apache.httpcomponents + httpclient + diff --git a/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/consumer/ConsumerReactiveHttpClientFactory.java b/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/consumer/ConsumerReactiveHttpClientFactory.java index a80f1346..ece7c67b 100644 --- a/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/consumer/ConsumerReactiveHttpClientFactory.java +++ b/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/consumer/ConsumerReactiveHttpClientFactory.java @@ -25,16 +25,16 @@ import org.onap.dcaegen2.services.prh.config.DmaapConsumerConfiguration; public class ConsumerReactiveHttpClientFactory { - private final DMaaPReactiveWebClientFactory reactiveWebClient; + private final DMaaPReactiveWebClientFactory reactiveWebClientFactory; - public ConsumerReactiveHttpClientFactory(DMaaPReactiveWebClientFactory reactiveWebClient) { - this.reactiveWebClient = reactiveWebClient; + public ConsumerReactiveHttpClientFactory(DMaaPReactiveWebClientFactory reactiveWebClientFactory) { + this.reactiveWebClientFactory = reactiveWebClientFactory; } public DMaaPConsumerReactiveHttpClient create(DmaapConsumerConfiguration consumerConfiguration) throws SSLException { return new DMaaPConsumerReactiveHttpClient(consumerConfiguration, - reactiveWebClient.build(consumerConfiguration)); + reactiveWebClientFactory.build(consumerConfiguration)); } } diff --git a/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/DMaaPPublisherReactiveHttpClient.java b/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/DMaaPPublisherReactiveHttpClient.java index b262e6e9..2b339775 100644 --- a/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/DMaaPPublisherReactiveHttpClient.java +++ b/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/DMaaPPublisherReactiveHttpClient.java @@ -41,7 +41,6 @@ import org.springframework.web.util.DefaultUriBuilderFactory; import reactor.core.publisher.Mono; - /** * @author Przemysław Wąsala on 7/4/18 */ @@ -53,7 +52,7 @@ public class DMaaPPublisherReactiveHttpClient { private final String dmaapProtocol; private final String dmaapTopicName; private final String dmaapContentType; - private final RestTemplate restTemplate; + private final Mono restTemplateMono; /** * Constructor DMaaPPublisherReactiveHttpClient. @@ -61,13 +60,13 @@ public class DMaaPPublisherReactiveHttpClient { * @param dmaapPublisherConfiguration - DMaaP producer configuration object */ DMaaPPublisherReactiveHttpClient(DmaapPublisherConfiguration dmaapPublisherConfiguration, - RestTemplate restTemplate) { + Mono restTemplateMono) { this.dmaapHostName = dmaapPublisherConfiguration.dmaapHostName(); this.dmaapProtocol = dmaapPublisherConfiguration.dmaapProtocol(); this.dmaapPortNumber = dmaapPublisherConfiguration.dmaapPortNumber(); this.dmaapTopicName = dmaapPublisherConfiguration.dmaapTopicName(); this.dmaapContentType = dmaapPublisherConfiguration.dmaapContentType(); - this.restTemplate = restTemplate; + this.restTemplateMono = restTemplateMono; } /** @@ -81,8 +80,8 @@ public class DMaaPPublisherReactiveHttpClient { return Mono.defer(() -> { HttpEntity request = new HttpEntity<>(createJsonBody(consumerDmaapModelMono), getAllHeaders()); logger.info("Request: {} {}", getUri(), request); - return Mono.just(restTemplate.exchange(getUri(), HttpMethod.POST, request, String.class)); - + return restTemplateMono.map( + restTemplate -> restTemplate.exchange(getUri(), HttpMethod.POST, request, String.class)); }); } @@ -97,7 +96,7 @@ public class DMaaPPublisherReactiveHttpClient { URI getUri() { return new DefaultUriBuilderFactory().builder().scheme(dmaapProtocol).host(dmaapHostName).port(dmaapPortNumber) - .path(dmaapTopicName).build(); + .path(dmaapTopicName).build(); } } diff --git a/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/DmaaPRestTemplateFactory.java b/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/DmaaPRestTemplateFactory.java new file mode 100644 index 00000000..6c1005d6 --- /dev/null +++ b/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/DmaaPRestTemplateFactory.java @@ -0,0 +1,115 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.dcaegen2.services.prh.service.producer; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import javax.net.ssl.SSLContext; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.onap.dcaegen2.services.prh.config.DmaapPublisherConfiguration; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; +import reactor.core.publisher.Mono; + +public class DmaaPRestTemplateFactory { + + /** + * Function for creating RestTemplate object. + * + * @param publisherConfiguration - DMaaP publisher configuration object + * @return RestTemplate with correct ssl configuration + */ + public Mono build(DmaapPublisherConfiguration publisherConfiguration) { + if (publisherConfiguration.enableDmaapCertAuth()) { + return createRestTemplateWithSslSetup(publisherConfiguration); + } + + return Mono.just(new RestTemplate()); + } + + private Mono createRestTemplateWithSslSetup(DmaapPublisherConfiguration publisherConfiguration) { + try { + RestTemplateBuilder builder = new RestTemplateBuilder(); + + SSLContext sslContext = createSslContext(publisherConfiguration, + loadPasswordFromFile(publisherConfiguration.keyStorePasswordPath()), + loadPasswordFromFile(publisherConfiguration.trustStorePasswordPath())); + + return Mono.just(builder + .requestFactory(() -> createRequestFactory(sslContext)).build()); + + } catch (GeneralSecurityException | IOException e) { + return Mono.error(e); + } + } + + private SSLContext createSslContext(DmaapPublisherConfiguration publisherConfiguration, + String keyStorePassword, String trustStorePassword) + throws IOException, GeneralSecurityException { + return new SSLContextBuilder() + .loadKeyMaterial( + keyStore(publisherConfiguration.keyStorePath(), keyStorePassword), + keyStorePassword.toCharArray()) + .loadTrustMaterial( + getFile(publisherConfiguration.trustStorePath()), trustStorePassword.toCharArray()) + .build(); + } + + private HttpComponentsClientHttpRequestFactory createRequestFactory(SSLContext sslContext) { + SSLConnectionSocketFactory socketFactory = + new SSLConnectionSocketFactory(sslContext); + HttpClient httpClient = HttpClients.custom() + .setSSLSocketFactory(socketFactory).build(); + + return new HttpComponentsClientHttpRequestFactory(httpClient); + } + + private KeyStore keyStore(String keyStoreFile, String keyStorePassword) + throws GeneralSecurityException, IOException { + KeyStore ks = KeyStore.getInstance("jks"); + ks.load(getResource(keyStoreFile), keyStorePassword.toCharArray()); + return ks; + } + + private File getFile(String fileName) { + return new File(fileName); + } + + private InputStream getResource(String fileName) throws FileNotFoundException { + return new FileInputStream(fileName); + } + + private String loadPasswordFromFile(String path) throws IOException { + return new String(Files.readAllBytes(Paths.get(path))); + } + +} diff --git a/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/PublisherReactiveHttpClientFactory.java b/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/PublisherReactiveHttpClientFactory.java index 0fc8f16a..7f97f903 100644 --- a/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/PublisherReactiveHttpClientFactory.java +++ b/prh-dmaap-client/src/main/java/org/onap/dcaegen2/services/prh/service/producer/PublisherReactiveHttpClientFactory.java @@ -21,12 +21,19 @@ package org.onap.dcaegen2.services.prh.service.producer; import org.onap.dcaegen2.services.prh.config.DmaapPublisherConfiguration; -import org.springframework.web.client.RestTemplate; + public class PublisherReactiveHttpClientFactory { + private final DmaaPRestTemplateFactory restTemplateFactory; + + public PublisherReactiveHttpClientFactory(DmaaPRestTemplateFactory restTemplateFactory) { + this.restTemplateFactory = restTemplateFactory; + } + public DMaaPPublisherReactiveHttpClient create(DmaapPublisherConfiguration publisherConfiguration) { - return new DMaaPPublisherReactiveHttpClient(publisherConfiguration, new RestTemplate()); + return new DMaaPPublisherReactiveHttpClient(publisherConfiguration, + restTemplateFactory.build(publisherConfiguration)); } } diff --git a/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/DMaaPPublisherReactiveHttpClientTest.java b/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/DMaaPPublisherReactiveHttpClientTest.java index ce2f7f36..a163fb74 100644 --- a/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/DMaaPPublisherReactiveHttpClientTest.java +++ b/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/DMaaPPublisherReactiveHttpClientTest.java @@ -37,6 +37,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; +import reactor.core.publisher.Mono; import reactor.test.StepVerifier; /** @@ -62,7 +63,7 @@ class DMaaPPublisherReactiveHttpClientTest { when(dmaapPublisherConfigurationMock.dmaapContentType()).thenReturn("application/json"); when(dmaapPublisherConfigurationMock.dmaapTopicName()).thenReturn("unauthenticated.PNF_READY"); dmaapPublisherReactiveHttpClient = - new DMaaPPublisherReactiveHttpClient(dmaapPublisherConfigurationMock, restTemplate); + new DMaaPPublisherReactiveHttpClient(dmaapPublisherConfigurationMock, Mono.just(restTemplate)); } diff --git a/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/DmaaPRestTemplateFactoryTest.java b/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/DmaaPRestTemplateFactoryTest.java new file mode 100644 index 00000000..97303b35 --- /dev/null +++ b/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/DmaaPRestTemplateFactoryTest.java @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.dcaegen2.services.prh.service.producer; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import javax.net.ssl.SSLException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.onap.dcaegen2.services.prh.config.DmaapPublisherConfiguration; + + +class DmaaPRestTemplateFactoryTest { + + private static final String KEY_STORE = "org.onap.dcae.jks"; + private static final String KEYSTORE_PASSWORD = "keystore.password"; + private static final String TRUSTSTORE_PASSWORD = "truststore.password"; + private static final String TRUST_STORE = "org.onap.dcae.trust.jks"; + private DmaapPublisherConfiguration publisherConfiguration = mock(DmaapPublisherConfiguration.class); + private DmaaPRestTemplateFactory factory = new DmaaPRestTemplateFactory(); + + @Test + void build_shouldCreateRestTemplateWithoutSslConfiguration() { + when(publisherConfiguration.enableDmaapCertAuth()).thenReturn(false); + + Assertions.assertNotNull(factory.build(publisherConfiguration).block()); + } + + @Test + void build_shouldCreateRestTemplateWithSslConfiguration() { + when(publisherConfiguration.enableDmaapCertAuth()).thenReturn(true); + when(publisherConfiguration.keyStorePath()).thenReturn(getPath(KEY_STORE)); + when(publisherConfiguration.keyStorePasswordPath()).thenReturn(getPath(KEYSTORE_PASSWORD)); + when(publisherConfiguration.trustStorePath()).thenReturn(getPath(TRUST_STORE)); + when(publisherConfiguration.trustStorePasswordPath()).thenReturn(getPath(TRUSTSTORE_PASSWORD)); + + Assertions.assertNotNull(factory.build(publisherConfiguration).block()); + } + + private String getPath(String fileName) { + return this.getClass().getClassLoader().getResource(fileName).getPath(); + } +} \ No newline at end of file diff --git a/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/PublisherReactiveHttpClientFactoryTest.java b/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/PublisherReactiveHttpClientFactoryTest.java index 3acfde9d..764d5788 100644 --- a/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/PublisherReactiveHttpClientFactoryTest.java +++ b/prh-dmaap-client/src/test/java/org/onap/dcaegen2/services/prh/service/producer/PublisherReactiveHttpClientFactoryTest.java @@ -29,8 +29,10 @@ import org.onap.dcaegen2.services.prh.config.DmaapPublisherConfiguration; class PublisherReactiveHttpClientFactoryTest { + private DmaaPRestTemplateFactory restTemplateFactory = mock(DmaaPRestTemplateFactory.class); private DmaapPublisherConfiguration dmaapPublisherConfiguration = mock(DmaapPublisherConfiguration.class); - private PublisherReactiveHttpClientFactory httpClientFactory = new PublisherReactiveHttpClientFactory(); + private PublisherReactiveHttpClientFactory httpClientFactory = + new PublisherReactiveHttpClientFactory(restTemplateFactory); @Test void create_shouldReturnNotNullFactoryInstance() { diff --git a/prh-dmaap-client/src/test/resources/keystore.password b/prh-dmaap-client/src/test/resources/keystore.password new file mode 100644 index 00000000..39823872 --- /dev/null +++ b/prh-dmaap-client/src/test/resources/keystore.password @@ -0,0 +1 @@ +mYHC98!qX}7h?W}jRv}MIXTJ \ No newline at end of file diff --git a/prh-dmaap-client/src/test/resources/org.onap.dcae.jks b/prh-dmaap-client/src/test/resources/org.onap.dcae.jks new file mode 100644 index 00000000..e74ce64f Binary files /dev/null and b/prh-dmaap-client/src/test/resources/org.onap.dcae.jks differ diff --git a/prh-dmaap-client/src/test/resources/org.onap.dcae.trust.jks b/prh-dmaap-client/src/test/resources/org.onap.dcae.trust.jks new file mode 100644 index 00000000..10103cfb Binary files /dev/null and b/prh-dmaap-client/src/test/resources/org.onap.dcae.trust.jks differ diff --git a/prh-dmaap-client/src/test/resources/truststore.password b/prh-dmaap-client/src/test/resources/truststore.password new file mode 100644 index 00000000..168e64bd --- /dev/null +++ b/prh-dmaap-client/src/test/resources/truststore.password @@ -0,0 +1 @@ +*TQH?Lnszprs4LmlAj38yds( \ No newline at end of file -- cgit 1.2.3-korg