From 7853cfe0034cc769358193f26afa3021c38a0554 Mon Sep 17 00:00:00 2001 From: Anthony Keenan <anthony.keenan@r3.com> Date: Sat, 21 Jul 2018 11:54:02 +0100 Subject: [PATCH] [CORDA-1482] Make boolean config variables case insensitive (#3622) * Make boolean config variables case insensitive * Address review comments --- .../internal/config/ConfigUtilities.kt | 15 ++++++++++- .../internal/network/NetworkBootstrapper.kt | 3 ++- .../internal/config/ConfigParsingTest.kt | 12 ++++++++- .../internal/cordapp/TypesafeCordappConfig.kt | 3 ++- .../config/NodeConfigurationImplTest.kt | 25 ++++++++++--------- .../net/corda/bootstrapper/volumes/Volume.kt | 3 ++- 6 files changed, 44 insertions(+), 17 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt index de1311ef78..cf3245837c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt @@ -121,7 +121,7 @@ private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set Int::class -> getInt(path) Long::class -> getLong(path) Double::class -> getDouble(path) - Boolean::class -> getBoolean(path) + Boolean::class -> getBooleanCaseInsensitive(path) LocalDate::class -> LocalDate.parse(getString(path)) Duration::class -> getDuration(path) Instant::class -> Instant.parse(getString(path)) @@ -276,6 +276,19 @@ private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> { } } +// The typesafe .getBoolean function is case sensitive, this is a case insensitive version +fun Config.getBooleanCaseInsensitive(path: String): Boolean { + try { + return getBoolean(path) + } catch(e:Exception) { + val stringVal = getString(path).toLowerCase() + if (stringVal == "true" || stringVal == "false") { + return stringVal.toBoolean() + } + throw e + } +} + private val logger = LoggerFactory.getLogger("net.corda.nodeapi.internal.config") enum class UnknownConfigKeysPolicy(private val handle: (Set<String>, logger: Logger) -> Unit) { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index 393e905c6e..2945421170 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -21,6 +21,7 @@ import net.corda.core.utilities.days import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.nodeapi.internal.* +import net.corda.nodeapi.internal.config.getBooleanCaseInsensitive import net.corda.nodeapi.internal.network.NodeInfoFilesCopier.Companion.NODE_INFO_FILE_NAME_PREFIX import net.corda.serialization.internal.AMQP_P2P_CONTEXT import net.corda.serialization.internal.CordaSerializationMagic @@ -289,7 +290,7 @@ class NetworkBootstrapper // The config contains the notary type val nodeConfig = configs[nodeInfoFile.parent]!! if (nodeConfig.hasPath("notary")) { - val validating = nodeConfig.getBoolean("notary.validating") + val validating = nodeConfig.getBooleanCaseInsensitive("notary.validating") // And the node-info file contains the notary's identity val nodeInfo = nodeInfoFile.readObject<SignedNodeInfo>().verified() NotaryInfo(nodeInfo.notaryIdentity(), validating) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/config/ConfigParsingTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/config/ConfigParsingTest.kt index 07de31c51d..cb92e9431b 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/config/ConfigParsingTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/config/ConfigParsingTest.kt @@ -1,6 +1,7 @@ package net.corda.nodeapi.internal.config import com.typesafe.config.Config +import com.typesafe.config.ConfigException import com.typesafe.config.ConfigFactory.empty import com.typesafe.config.ConfigRenderOptions.defaults import com.typesafe.config.ConfigValueFactory @@ -8,6 +9,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort import org.assertj.core.api.Assertions.* +import org.hibernate.exception.DataException import org.junit.Test import java.net.URL import java.nio.file.Path @@ -41,8 +43,16 @@ class ConfigParsingTest { @Test fun Boolean() { testPropertyType<BooleanData, BooleanListData, Boolean>(true, false) + assertThat(config(Pair("value", "false")).parseAs<BooleanData>().value).isEqualTo(false) + assertThat(config(Pair("value", "False")).parseAs<BooleanData>().value).isEqualTo(false) + assertThat(config(Pair("value", "FALSE")).parseAs<BooleanData>().value).isEqualTo(false) + assertThat(config(Pair("value", "true")).parseAs<BooleanData>().value).isEqualTo(true) + assertThat(config(Pair("value", "True")).parseAs<BooleanData>().value).isEqualTo(true) + assertThat(config(Pair("value", "TRUE")).parseAs<BooleanData>().value).isEqualTo(true) + assertThatThrownBy { config(Pair("value", "stilton")).parseAs<BooleanData>().value } + .isInstanceOf(ConfigException.WrongType::class.java) + .hasMessageContaining("hardcoded value: value has type STRING rather than BOOLEAN") } - @Test fun Enum() { testPropertyType<EnumData, EnumListData, TestEnum>(TestEnum.Value2, TestEnum.Value1, valuesToString = true) diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/TypesafeCordappConfig.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/TypesafeCordappConfig.kt index 73f5633350..31d9ed54b7 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/TypesafeCordappConfig.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/TypesafeCordappConfig.kt @@ -4,6 +4,7 @@ import com.typesafe.config.Config import com.typesafe.config.ConfigException import net.corda.core.cordapp.CordappConfig import net.corda.core.cordapp.CordappConfigException +import net.corda.nodeapi.internal.config.getBooleanCaseInsensitive /** * Provides configuration from a typesafe config source @@ -71,7 +72,7 @@ class TypesafeCordappConfig(private val cordappConfig: Config) : CordappConfig { override fun getBoolean(path: String): Boolean { try { - return cordappConfig.getBoolean(path) + return cordappConfig.getBooleanCaseInsensitive(path) } catch (e: ConfigException) { throw CordappConfigException("Cordapp configuration is incorrect due to exception", e) } diff --git a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt index 22d68fab17..93c45b2c5b 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt @@ -4,6 +4,7 @@ import com.typesafe.config.* import net.corda.core.internal.toPath import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.seconds +import net.corda.nodeapi.internal.config.getBooleanCaseInsensitive import net.corda.testing.core.ALICE_NAME import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.tools.shell.SSHDConfiguration @@ -67,16 +68,16 @@ class NodeConfigurationImplTest { val os = System.getProperty("os.name") setSystemOs("Windows 98") - assertTrue(getConfig("test-config-empty.conf").getBoolean("devMode")) + assertTrue(getConfig("test-config-empty.conf").getBooleanCaseInsensitive("devMode")) setSystemOs("Mac Sierra") - assertTrue(getConfig("test-config-empty.conf").getBoolean("devMode")) + assertTrue(getConfig("test-config-empty.conf").getBooleanCaseInsensitive("devMode")) setSystemOs("Windows server 2008") - assertFalse(getConfig("test-config-empty.conf").getBoolean("devMode")) + assertFalse(getConfig("test-config-empty.conf").getBooleanCaseInsensitive("devMode")) setSystemOs("Linux") - assertFalse(getConfig("test-config-empty.conf").getBoolean("devMode")) + assertFalse(getConfig("test-config-empty.conf").getBooleanCaseInsensitive("devMode")) setSystemOs(os) } @@ -87,22 +88,22 @@ class NodeConfigurationImplTest { @Test fun `Dev mode is read from the config over the autodetect logic`() { - assertTrue(getConfig("test-config-DevMode.conf").getBoolean("devMode")) - assertFalse(getConfig("test-config-noDevMode.conf").getBoolean("devMode")) + assertTrue(getConfig("test-config-DevMode.conf").getBooleanCaseInsensitive("devMode")) + assertFalse(getConfig("test-config-noDevMode.conf").getBooleanCaseInsensitive("devMode")) } @Test fun `Dev mode is true if overriden`() { - assertTrue(getConfig("test-config-DevMode.conf", ConfigFactory.parseMap(mapOf("devMode" to true))).getBoolean("devMode")) - assertTrue(getConfig("test-config-noDevMode.conf", ConfigFactory.parseMap(mapOf("devMode" to true))).getBoolean("devMode")) - assertTrue(getConfig("test-config-empty.conf", ConfigFactory.parseMap(mapOf("devMode" to true))).getBoolean("devMode")) + assertTrue(getConfig("test-config-DevMode.conf", ConfigFactory.parseMap(mapOf("devMode" to true))).getBooleanCaseInsensitive("devMode")) + assertTrue(getConfig("test-config-noDevMode.conf", ConfigFactory.parseMap(mapOf("devMode" to true))).getBooleanCaseInsensitive("devMode")) + assertTrue(getConfig("test-config-empty.conf", ConfigFactory.parseMap(mapOf("devMode" to true))).getBooleanCaseInsensitive("devMode")) } @Test fun `Dev mode is false if overriden`() { - assertFalse(getConfig("test-config-DevMode.conf", ConfigFactory.parseMap(mapOf("devMode" to false))).getBoolean("devMode")) - assertFalse(getConfig("test-config-noDevMode.conf", ConfigFactory.parseMap(mapOf("devMode" to false))).getBoolean("devMode")) - assertFalse(getConfig("test-config-empty.conf", ConfigFactory.parseMap(mapOf("devMode" to false))).getBoolean("devMode")) + assertFalse(getConfig("test-config-DevMode.conf", ConfigFactory.parseMap(mapOf("devMode" to false))).getBooleanCaseInsensitive("devMode")) + assertFalse(getConfig("test-config-noDevMode.conf", ConfigFactory.parseMap(mapOf("devMode" to false))).getBooleanCaseInsensitive("devMode")) + assertFalse(getConfig("test-config-empty.conf", ConfigFactory.parseMap(mapOf("devMode" to false))).getBooleanCaseInsensitive("devMode")) } private fun getConfig(cfgName: String, overrides: Config = ConfigFactory.empty()): Config { diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/Volume.kt b/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/Volume.kt index fa514dd168..4ec5d47f02 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/Volume.kt +++ b/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/Volume.kt @@ -9,6 +9,7 @@ import net.corda.core.node.NotaryInfo import net.corda.core.serialization.deserialize import net.corda.nodeapi.internal.DEV_ROOT_CA import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.config.getBooleanCaseInsensitive import net.corda.nodeapi.internal.createDevNetworkMapCa import java.io.File import java.security.cert.X509Certificate @@ -36,7 +37,7 @@ interface Volume { fun convertNodeIntoToNetworkParams(notaryFiles: List<Pair<File, File>>): NetworkParameters { val notaryInfos = notaryFiles.map { (configFile, nodeInfoFile) -> - val validating = ConfigFactory.parseFile(configFile).getConfig("notary").getBoolean("validating") + val validating = ConfigFactory.parseFile(configFile).getConfig("notary").getBooleanCaseInsensitive("validating") nodeInfoFile.readBytes().deserialize<SignedNodeInfo>().verified().let { NotaryInfo(it.legalIdentities.first(), validating) } }