From fffcdb47da78ae8089b1edaf5ea34e1632684bc1 Mon Sep 17 00:00:00 2001 From: mkit Date: Wed, 15 Nov 2017 08:36:24 +0000 Subject: [PATCH] Introducing the InputReader interface (#102) * Introducing the InputReader interface * Addressing review comments * Addressing review comments --- network-management/build.gradle | 2 +- .../com/r3/corda/networkmanage/hsm/HsmTest.kt | 12 +++--- .../hsm/authentication/Authenticator.kt | 28 ++----------- .../hsm/authentication/InputReader.kt | 40 +++++++++++++++++++ .../networkmanage/hsm/signer/HsmCsrSigner.kt | 3 +- .../hsm/authentication/AuthenticatorTest.kt | 17 ++++---- 6 files changed, 60 insertions(+), 42 deletions(-) create mode 100644 network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/authentication/InputReader.kt diff --git a/network-management/build.gradle b/network-management/build.gradle index e2fd25c93d..cc3fa459cf 100644 --- a/network-management/build.gradle +++ b/network-management/build.gradle @@ -125,7 +125,7 @@ dependencies { testCompile "com.nhaarman:mockito-kotlin:0.6.1" testRuntime "net.corda:corda-rpc:$corda_dependency_version" testCompile "com.spotify:docker-client:8.9.1" - integrationTestCompile "net.corda:corda-test-utils:$corda_dependency_version" + integrationTestCompile "net.corda:corda-test-common:$corda_dependency_version" integrationTestRuntime "net.corda:corda-rpc:$corda_dependency_version" compile('com.atlassian.jira:jira-rest-java-client-core:4.0.0') { diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/HsmTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/HsmTest.kt index 49df4fc685..55be8b4a2d 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/HsmTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/HsmTest.kt @@ -5,12 +5,12 @@ import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.whenever import com.r3.corda.networkmanage.HsmSimulator import com.r3.corda.networkmanage.hsm.authentication.Authenticator +import com.r3.corda.networkmanage.hsm.authentication.InputReader import com.r3.corda.networkmanage.hsm.authentication.createProvider import com.r3.corda.networkmanage.hsm.configuration.Parameters import org.junit.Before import org.junit.Rule import org.junit.Test -import java.io.Console import kotlin.test.assertTrue class HsmTest { @@ -19,11 +19,11 @@ class HsmTest { @JvmField val hsmSimulator: HsmSimulator = HsmSimulator() - private var console: Console? = null + private lateinit var inputReader: InputReader @Before fun setUp() { - console = mock() + inputReader = mock() } @Test @@ -35,9 +35,9 @@ class HsmTest { keySpecifier = 1, keyGroup = "*" ) - whenever(console?.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username) - whenever(console?.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password.toCharArray()) - val authenticator = Authenticator(parameters.createProvider(), console = console) + whenever(inputReader.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username) + whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password) + val authenticator = Authenticator(parameters.createProvider(), inputReader = inputReader) var executed = false // when diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/authentication/Authenticator.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/authentication/Authenticator.kt index bc9a8193f7..c41e7e1359 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/authentication/Authenticator.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/authentication/Authenticator.kt @@ -4,7 +4,6 @@ import CryptoServerJCE.CryptoServerProvider import com.r3.corda.networkmanage.hsm.configuration.Parameters import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream -import java.io.Console import java.nio.file.Path import kotlin.reflect.full.memberProperties @@ -17,7 +16,7 @@ class Authenticator(private val provider: CryptoServerProvider, private val authKeyFilePath: Path? = null, private val authKeyFilePass: String? = null, private val authStrengthThreshold: Int = 2, - val console: Console? = System.console()) { + inputReader: InputReader = ConsoleInputReader()) : InputReader by inputReader { /** * Interactively (using console) authenticates a user against the HSM. Once authentication is successful the @@ -32,7 +31,7 @@ class Authenticator(private val provider: CryptoServerProvider, loop@ while (true) { val user = if (autoUsername.isNullOrEmpty()) { print("Enter User Name (or Q to quit): ") - val input = readConsoleLine(console) + val input = readLine() if (input != null && "q" == input.toLowerCase()) { authenticated.clear() break @@ -47,7 +46,7 @@ class Authenticator(private val provider: CryptoServerProvider, AuthMode.KEY_FILE -> { println("Authenticating using preconfigured key file") val password = if (authKeyFilePass == null) { - val input = readPassword("Enter key file password (or Q to quit): ", console) + val input = readPassword("Enter key file password (or Q to quit): ") if ("q" == input.toLowerCase()) { authenticated.clear() break@loop @@ -60,7 +59,7 @@ class Authenticator(private val provider: CryptoServerProvider, provider.loginSign(user, authKeyFilePath.toString(), password) } AuthMode.PASSWORD -> { - val password = readPassword("Enter password (or Q to quit): ", console) + val password = readPassword("Enter password (or Q to quit): ") if ("q" == password.toLowerCase()) { authenticated.clear() break@loop @@ -125,23 +124,4 @@ fun Parameters.createProvider(): CryptoServerProvider { val provider = CryptoServerProvider(cfg) cfg.close() return provider -} - -/** Read password from console, do a readLine instead if console is null (e.g. when debugging in IDE). */ -internal fun readPassword(fmt: String, console: Console? = System.console()): String { - return if (console != null) { - String(console.readPassword(fmt)) - } else { - print(fmt) - readLine()!! - } -} - -/** Read console line */ -internal fun readConsoleLine(console: Console?): String? { - return if (console == null) { - readLine() - } else { - console.readLine() - } } \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/authentication/InputReader.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/authentication/InputReader.kt new file mode 100644 index 0000000000..678bb4d39d --- /dev/null +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/authentication/InputReader.kt @@ -0,0 +1,40 @@ +package com.r3.corda.networkmanage.hsm.authentication + +/** + * User input reader interface + */ +interface InputReader { + /** + * Reads a single line from user's input. + */ + fun readLine(): String? + + /** + * Reads a single line from user's input. The characters from the input are masked while being entered. + * @param format message string displayed before user's input + */ + fun readPassword(format: String): String +} + +class ConsoleInputReader : InputReader { + private val console = System.console() + + /** Read password from console, do a readLine instead if console is null (e.g. when debugging in IDE). */ + override fun readPassword(format: String): String { + return if (console != null) { + String(console.readPassword(format)) + } else { + print(format) + kotlin.io.readLine() ?: throw IllegalArgumentException("Password required") + } + } + + /** Read console line */ + override fun readLine(): String? { + return if (console == null) { + kotlin.io.readLine() + } else { + console.readLine() + } + } +} \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmCsrSigner.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmCsrSigner.kt index becc8613d6..aa7b589cf6 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmCsrSigner.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmCsrSigner.kt @@ -1,7 +1,6 @@ package com.r3.corda.networkmanage.hsm.signer import com.r3.corda.networkmanage.hsm.authentication.Authenticator -import com.r3.corda.networkmanage.hsm.authentication.readPassword import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage import com.r3.corda.networkmanage.hsm.utils.X509Utilities.buildCertPath @@ -36,7 +35,7 @@ class HsmCsrSigner(private val storage: SignedCertificateRequestStorage, // This should be changed once we allow for more certificates in the chain. Preferably we should use // keyStore.getCertificateChain(String) and assume entire chain is stored in the HSM (depending on the support). val caParentCertificate = keyStore.getCertificate(caParentCertificateName) - val caPrivateKeyPass = caPrivateKeyPass ?: readPassword("CA Private Key Password: ", authenticator.console) + val caPrivateKeyPass = caPrivateKeyPass ?: authenticator.readPassword("CA Private Key Password: ") val caCertAndKey = retrieveCertificateAndKeys(caCertificateName, caPrivateKeyPass, keyStore) toSign.forEach { it.certPath = buildCertPath(createClientCertificate(caCertAndKey, it.request, validDays, provider), caParentCertificate) diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/hsm/authentication/AuthenticatorTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/hsm/authentication/AuthenticatorTest.kt index 3351c85526..2ae412619a 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/hsm/authentication/AuthenticatorTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/hsm/authentication/AuthenticatorTest.kt @@ -5,20 +5,19 @@ import com.nhaarman.mockito_kotlin.* import com.r3.corda.networkmanage.TestBase import org.junit.Before import org.junit.Test -import java.io.Console import kotlin.test.assertFalse import kotlin.test.assertTrue class AuthenticatorTest : TestBase() { private lateinit var provider: CryptoServerProvider - private lateinit var console: Console + private lateinit var inputReader: InputReader @Before fun setUp() { provider = mock() whenever(provider.cryptoServer).thenReturn(mock()) - console = mock() + inputReader = mock() } @Test @@ -28,7 +27,7 @@ class AuthenticatorTest : TestBase() { var executed = false // when - Authenticator(provider = provider, console = console).connectAndAuthenticate { _, _ -> executed = true } + Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _ -> executed = true } // then assertFalse(executed) @@ -47,7 +46,7 @@ class AuthenticatorTest : TestBase() { var executed = false // when - Authenticator(provider = provider, console = console).connectAndAuthenticate { _, _ -> executed = true } + Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _ -> executed = true } // then verify(provider).loginPassword(username, password) @@ -64,7 +63,7 @@ class AuthenticatorTest : TestBase() { var executed = false // when - Authenticator(provider = provider, console = console, mode = AuthMode.CARD_READER).connectAndAuthenticate { _, _ -> executed = true } + Authenticator(provider = provider, inputReader = inputReader, mode = AuthMode.CARD_READER).connectAndAuthenticate { _, _ -> executed = true } // then verify(provider).loginSign(username, ":cs2:cyb:USB0", null) @@ -83,7 +82,7 @@ class AuthenticatorTest : TestBase() { var executed = false // when - Authenticator(provider = provider, console = console).connectAndAuthenticate { _, _ -> executed = true } + Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _ -> executed = true } // then verify(provider, times(3)).loginPassword(username, password) @@ -99,11 +98,11 @@ class AuthenticatorTest : TestBase() { } private fun givenUserConsoleInputOnReadPassword(input: String) { - whenever(console.readPassword(any())).thenReturn(input.toCharArray()) + whenever(inputReader.readPassword(any())).thenReturn(input) } private fun givenUserConsoleInputOnReadLine(input: String) { - whenever(console.readLine()).thenReturn(input) + whenever(inputReader.readLine()).thenReturn(input) } } \ No newline at end of file