Make security keys optional with SSL disabled 05/82805/5
authorFilip Krzywka <filip.krzywka@nokia.com>
Wed, 20 Mar 2019 13:53:57 +0000 (14:53 +0100)
committerFilip Krzywka <filip.krzywka@nokia.com>
Thu, 21 Mar 2019 12:33:27 +0000 (13:33 +0100)
Change-Id: I0b0dd63cb3609cddf6aa5e63cf0a0f147a0aca91
Issue-ID: DCAEGEN2-1340
Signed-off-by: Filip Krzywka <filip.krzywka@nokia.com>
sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/ConfigurationValidator.kt
sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/FileConfigurationReader.kt
sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/AddressAdapter.kt
sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityAdapter.kt [new file with mode: 0644]
sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityKeysAdapter.kt [deleted file]
sources/hv-collector-configuration/src/test/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/AddressAdapterTest.kt [new file with mode: 0644]
sources/hv-collector-configuration/src/test/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityAdapterTest.kt [new file with mode: 0644]

index 83f5692..90be3db 100644 (file)
@@ -52,8 +52,7 @@ internal class ConfigurationValidator {
         val cbsConfiguration = partialConfig.cbs.bind()
                 .let { createCbsConfiguration(it).bind() }
 
-        val securityConfiguration = partialConfig.security.bind()
-                .let { createSecurityConfiguration(it) }
+        val securityConfiguration = SecurityConfiguration(partialConfig.security.bind().keys)
 
         val collectorConfiguration = partialConfig.collector.bind()
                 .let { createCollectorConfig(it).bind() }
@@ -93,13 +92,6 @@ internal class ConfigurationValidator {
                 )
             }
 
-    private fun createSecurityConfiguration(partial: PartialSecurityConfig) =
-            partial.keys
-                    .fold(
-                            { SecurityConfiguration(None) },
-                            { SecurityConfiguration(Some(it)) }
-                    )
-
     private fun createCollectorConfig(partial: PartialCollectorConfig) =
             partial.mapBinding {
                 CollectorConfiguration(
index 452b921..1e77dde 100644 (file)
@@ -27,7 +27,7 @@ import org.onap.dcae.collectors.veshv.config.impl.gsonadapters.AddressAdapter
 import org.onap.dcae.collectors.veshv.config.impl.gsonadapters.OptionAdapter
 import org.onap.dcae.collectors.veshv.config.impl.gsonadapters.RouteAdapter
 import org.onap.dcae.collectors.veshv.config.impl.gsonadapters.RoutingAdapter
-import org.onap.dcae.collectors.veshv.config.impl.gsonadapters.SecurityKeysAdapter
+import org.onap.dcae.collectors.veshv.config.impl.gsonadapters.SecurityAdapter
 import org.onap.dcaegen2.services.sdk.security.ssl.SecurityKeys
 import java.io.Reader
 import java.net.InetSocketAddress
@@ -42,7 +42,7 @@ internal class FileConfigurationReader {
             .registerTypeAdapter(Route::class.java, RouteAdapter())
             .registerTypeAdapter(Routing::class.java, RoutingAdapter())
             .registerTypeAdapter(Option::class.java, OptionAdapter())
-            .registerTypeAdapter(SecurityKeys::class.java, SecurityKeysAdapter())
+            .registerTypeAdapter(PartialSecurityConfig::class.java, SecurityAdapter())
             .create()
 
     fun loadConfig(input: Reader): PartialConfiguration =
index 6d20da4..255be03 100644 (file)
@@ -22,10 +22,10 @@ package org.onap.dcae.collectors.veshv.config.impl.gsonadapters
 import com.google.gson.JsonDeserializationContext
 import com.google.gson.JsonDeserializer
 import com.google.gson.JsonElement
+import com.google.gson.JsonParseException
 import java.lang.reflect.Type
 import java.net.InetSocketAddress
 
-
 /**
  * @author Pawel Biniek <pawel.biniek@nokia.com>
  * @since February 2019
@@ -34,17 +34,15 @@ internal class AddressAdapter : JsonDeserializer<InetSocketAddress> {
     override fun deserialize(
             json: JsonElement,
             typeOfT: Type,
-            context: JsonDeserializationContext?): InetSocketAddress
-        {
-            val portStart = json.asString.lastIndexOf(":")
-            if (portStart > 0) {
-                val address = json.asString.substring(0, portStart)
-                val port = json.asString.substring(portStart + 1)
-                return InetSocketAddress(address, port.toInt())
-            } else throw InvalidAddressException("Cannot parse '" + json.asString + "' to address")
-        }
+            context: JsonDeserializationContext): InetSocketAddress {
+        val portStart = json.asString.lastIndexOf(":")
+        if (portStart > 0) {
+            val address = json.asString.substring(0, portStart)
+            val port = json.asString.substring(portStart + 1)
+            return InetSocketAddress(address, port.toInt())
+        } else throw InvalidAddressException("Cannot parse '" + json.asString + "' to address")
+    }
 
-    class InvalidAddressException(reason:String) : RuntimeException(reason)
+    class InvalidAddressException(reason: String) : RuntimeException(reason)
 }
 
-
diff --git a/sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityAdapter.kt b/sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityAdapter.kt
new file mode 100644 (file)
index 0000000..859fd70
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * ============LICENSE_START=======================================================
+ * dcaegen2-collectors-veshv
+ * ================================================================================
+ * Copyright (C) 2019 NOKIA
+ * ================================================================================
+ * 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.dcae.collectors.veshv.config.impl.gsonadapters
+
+import arrow.core.Option
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonObject
+import org.onap.dcae.collectors.veshv.config.impl.PartialSecurityConfig
+import org.onap.dcaegen2.services.sdk.security.ssl.ImmutableSecurityKeys
+import org.onap.dcaegen2.services.sdk.security.ssl.ImmutableSecurityKeysStore
+import org.onap.dcaegen2.services.sdk.security.ssl.Passwords
+import org.onap.dcaegen2.services.sdk.security.ssl.SecurityKeys
+import java.io.File
+import java.lang.reflect.Type
+
+/**
+ * @author Pawel Biniek <pawel.biniek@nokia.com>
+ * @since March 2019
+ */
+internal class SecurityAdapter : JsonDeserializer<PartialSecurityConfig> {
+
+    override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext?) =
+            json.asJsonObject.let { security ->
+                if (security.entrySet().isEmpty() || hasSslDisableSet(security)) {
+                    PartialSecurityConfig(Option.empty())
+                } else {
+                    PartialSecurityConfig(Option.just(security.securityKeys(::asImmutableSecurityKeys)))
+                }
+            }
+
+    private fun hasSslDisableSet(security: JsonObject) =
+            security.has(SSL_DISABLE_KEY) && security[SSL_DISABLE_KEY].asBoolean
+
+    private fun JsonObject.securityKeys(f: (JsonObject) -> SecurityKeys) = f(getAsJsonObject(KEYS_OBJECT_KEY))
+
+    private fun asImmutableSecurityKeys(keys: JsonObject) = ImmutableSecurityKeys.builder()
+            .keyStore(ImmutableSecurityKeysStore.of(
+                    File(keys[KEY_STORE_FILE_KEY].asString).toPath()))
+            .keyStorePassword(
+                    Passwords.fromString(keys[KEY_STORE_PASSWORD_KEY].asString))
+            .trustStore(ImmutableSecurityKeysStore.of(
+                    File(keys[TRUST_STORE_FILE_KEY].asString).toPath()))
+            .trustStorePassword(
+                    Passwords.fromString(keys[TRUST_STORE_PASSWORD_KEY].asString))
+            .build()
+
+    companion object {
+        private val SSL_DISABLE_KEY = "sslDisable"
+        private val KEYS_OBJECT_KEY = "keys"
+        private val KEY_STORE_FILE_KEY = "keyStoreFile"
+        private val KEY_STORE_PASSWORD_KEY = "keyStorePassword"
+        private val TRUST_STORE_FILE_KEY = "trustStoreFile"
+        private val TRUST_STORE_PASSWORD_KEY = "trustStorePassword"
+    }
+}
+
diff --git a/sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityKeysAdapter.kt b/sources/hv-collector-configuration/src/main/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityKeysAdapter.kt
deleted file mode 100644 (file)
index f9c0b4d..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * dcaegen2-collectors-veshv
- * ================================================================================
- * Copyright (C) 2019 NOKIA
- * ================================================================================
- * 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.dcae.collectors.veshv.config.impl.gsonadapters
-
-import com.google.gson.JsonDeserializationContext
-import com.google.gson.JsonDeserializer
-import com.google.gson.JsonElement
-import org.onap.dcaegen2.services.sdk.security.ssl.ImmutableSecurityKeys
-import org.onap.dcaegen2.services.sdk.security.ssl.ImmutableSecurityKeysStore
-import org.onap.dcaegen2.services.sdk.security.ssl.Passwords
-import org.onap.dcaegen2.services.sdk.security.ssl.SecurityKeys
-import java.io.File
-import java.lang.reflect.Type
-
-/**
- * @author Pawel Biniek <pawel.biniek@nokia.com>
- * @since March 2019
- */
-internal class SecurityKeysAdapter : JsonDeserializer<SecurityKeys> {
-    override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext?): SecurityKeys {
-        val obj = json.asJsonObject
-        return ImmutableSecurityKeys.builder()
-                .keyStore(ImmutableSecurityKeysStore.of(
-                        File(obj["keyStoreFile"].asString).toPath()))
-                .keyStorePassword(
-                        Passwords.fromString(obj["keyStorePassword"].asString))
-                .trustStore(ImmutableSecurityKeysStore.of(
-                        File(obj["trustStoreFile"].asString).toPath()))
-                .trustStorePassword(
-                        Passwords.fromString(obj["trustStorePassword"].asString))
-                .build()
-    }
-
-}
diff --git a/sources/hv-collector-configuration/src/test/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/AddressAdapterTest.kt b/sources/hv-collector-configuration/src/test/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/AddressAdapterTest.kt
new file mode 100644 (file)
index 0000000..f70c433
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * ============LICENSE_START=======================================================
+ * dcaegen2-collectors-veshv
+ * ================================================================================
+ * Copyright (C) 2019 NOKIA
+ * ================================================================================
+ * 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.dcae.collectors.veshv.config.impl.gsonadapters
+
+import com.google.gson.Gson
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonParseException
+import com.google.gson.reflect.TypeToken
+import com.nhaarman.mockitokotlin2.mock
+import org.assertj.core.api.Assertions.assertThat
+import org.jetbrains.spek.api.Spek
+import org.jetbrains.spek.api.dsl.describe
+import org.jetbrains.spek.api.dsl.given
+import org.jetbrains.spek.api.dsl.it
+import org.onap.dcae.collectors.veshv.config.impl.gsonadapters.AddressAdapter.InvalidAddressException
+import java.lang.NumberFormatException
+import kotlin.test.assertFailsWith
+
+
+internal object AddressAdapterTest : Spek({
+
+    describe("deserialization") {
+        val gson = Gson()
+        val context = mock<JsonDeserializationContext>()
+        val addressAdapterType = TypeToken.get(AddressAdapter::class.java).type
+
+        val cut = AddressAdapter()
+
+        given("valid string") {
+            val address = "hostname:9000"
+            val json = gson.toJsonTree(address)
+
+            it("should return address") {
+                val deserialized = cut.deserialize(json, addressAdapterType, context)
+
+                assertThat(deserialized.hostName).isEqualTo("hostname")
+                assertThat(deserialized.port).isEqualTo(9000)
+            }
+        }
+
+        val invalidAddresses = mapOf(
+                Pair("missingPort", InvalidAddressException::class),
+                Pair("NaNPort:Hey", NumberFormatException::class),
+                Pair(":6036", InvalidAddressException::class))
+
+        invalidAddresses.forEach { address, exception ->
+            given("invalid address string: $address") {
+
+                val json = gson.toJsonTree(address)
+                it("should throw exception") {
+                    assertFailsWith(exception) {
+                        cut.deserialize(json, addressAdapterType, context)
+                    }
+                }
+            }
+        }
+    }
+})
+
+
diff --git a/sources/hv-collector-configuration/src/test/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityAdapterTest.kt b/sources/hv-collector-configuration/src/test/kotlin/org/onap/dcae/collectors/veshv/config/impl/gsonadapters/SecurityAdapterTest.kt
new file mode 100644 (file)
index 0000000..a466896
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * ============LICENSE_START=======================================================
+ * dcaegen2-collectors-veshv
+ * ================================================================================
+ * Copyright (C) 2019 NOKIA
+ * ================================================================================
+ * 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.dcae.collectors.veshv.config.impl.gsonadapters
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonParser
+import com.google.gson.reflect.TypeToken
+import com.nhaarman.mockitokotlin2.mock
+import org.assertj.core.api.Assertions
+import org.jetbrains.spek.api.Spek
+import org.jetbrains.spek.api.dsl.describe
+import org.jetbrains.spek.api.dsl.given
+import org.jetbrains.spek.api.dsl.it
+
+internal object SecurityAdapterTest : Spek({
+
+    describe("deserialization") {
+        val gson = JsonParser()
+        val context = mock<JsonDeserializationContext>()
+        val someType = TypeToken.get(SecurityAdapter::class.java).type
+
+        val cut = SecurityAdapter()
+
+        given("empty security object") {
+            val json = gson.parse("{}")
+
+            it("should parse json to security configuration without keys") {
+                val deserialized = cut.deserialize(json, someType, context)
+
+                Assertions.assertThat(deserialized.keys.isEmpty()).isTrue()
+            }
+        }
+
+        given("valid security object with ssl disabled") {
+
+            given("security keys missing") {
+                val json = gson.parse(SECURITY_WITH_SSL_DISABLED_AND_KEYS_MISSING)
+
+                it("should parse json to security configuration without keys") {
+                    val deserialized = cut.deserialize(json, someType, context)
+
+                    Assertions.assertThat(deserialized.keys.isEmpty()).isTrue()
+                }
+            }
+
+            given("security keys provided") {
+                val json = gson.parse(SECURITY_WITH_SSL_DISABLED_AND_KEYS_PROVIDED)
+
+                it("should parse json to security configuration without keys") {
+                    val deserialized = cut.deserialize(json, someType, context)
+
+                    Assertions.assertThat(deserialized.keys.isEmpty()).isTrue()
+                }
+            }
+        }
+
+        given("valid security object with missing sslDisable key") {
+            val json = gson.parse(MISSING_SSL_DISABLE_ENTRY)
+
+            it("should return parse json to security configuration") {
+                val deserialized = cut.deserialize(json, someType, context)
+
+                Assertions.assertThat(deserialized.keys.isDefined()).isTrue()
+            }
+        }
+
+        given("valid security object with ssl enabled") {
+            val json = gson.parse(VALID_SECURITY_WITH_SSL_ENABLED)
+
+            it("should return parse json to security configuration") {
+                val deserialized = cut.deserialize(json, someType, context)
+
+                Assertions.assertThat(deserialized.keys.isDefined()).isTrue()
+            }
+        }
+    }
+})
+
+val SECURITY_WITH_SSL_DISABLED_AND_KEYS_MISSING = """
+{
+    "sslDisable": true
+}
+"""
+
+val SECURITY_WITH_SSL_DISABLED_AND_KEYS_PROVIDED = """
+{
+    "sslDisable": true,
+    "keys": {
+      "keyStoreFile": "test.ks.pkcs12",
+      "keyStorePassword": "changeMe",
+      "trustStoreFile": "trust.ks.pkcs12",
+      "trustStorePassword": "changeMeToo"
+    }
+}
+"""
+
+val MISSING_SSL_DISABLE_ENTRY = """
+{
+    "keys": {
+      "keyStoreFile": "test.ks.pkcs12",
+      "keyStorePassword": "changeMe",
+      "trustStoreFile": "trust.ks.pkcs12",
+      "trustStorePassword": "changeMeToo"
+    }
+}
+"""
+
+val VALID_SECURITY_WITH_SSL_ENABLED = """
+{
+    "sslDisable": false,
+    "keys": {
+      "keyStoreFile": "test.ks.pkcs12",
+      "keyStorePassword": "changeMe",
+      "trustStoreFile": "trust.ks.pkcs12",
+      "trustStorePassword": "changeMeToo"
+    }
+}
+"""