ENT-1661 Doorman crashes ungracefully when started with incorrect or … (#747)

* ENT-1661 Doorman crashes ungracefully when started with incorrect or no program arguments. Should display a meaningful message instead.
This commit is contained in:
Patrick Kuo 2018-04-19 21:22:04 +01:00 committed by GitHub
parent b3a4e3907f
commit 57291c435c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 141 additions and 180 deletions

View File

@ -68,6 +68,8 @@ publish {
dependencies { dependencies {
compile project(':node') compile project(':node')
// TODO: remove this when ArgsParser is moved into corda.
compile project(':network-management')
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile "org.assertj:assertj-core:${assertj_version}" testCompile "org.assertj:assertj-core:${assertj_version}"
} }

View File

@ -1,75 +1,61 @@
package com.r3.corda.networkmanage.registration package com.r3.corda.networkmanage.registration
import com.r3.corda.networkmanage.common.utils.ArgsParser
import com.r3.corda.networkmanage.registration.ToolOption.KeyCopierOption import com.r3.corda.networkmanage.registration.ToolOption.KeyCopierOption
import com.r3.corda.networkmanage.registration.ToolOption.RegistrationOption import com.r3.corda.networkmanage.registration.ToolOption.RegistrationOption
import joptsimple.OptionParser import joptsimple.OptionSet
import joptsimple.OptionSpecBuilder import joptsimple.OptionSpecBuilder
import joptsimple.util.PathConverter import joptsimple.util.PathConverter
import joptsimple.util.PathProperties import joptsimple.util.PathProperties
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import java.nio.file.Path import java.nio.file.Path
import kotlin.system.exitProcess
fun main(args: Array<String>) { fun main(args: Array<String>) {
Crypto.registerProviders() // Required to register Providers first thing on boot. Crypto.registerProviders() // Required to register Providers first thing on boot.
val options = try { val options = ToolArgsParser().parseOrExit(*args, printHelpOn = System.out)
parseOptions(*args)
} catch (e: ShowHelpException) {
e.errorMessage?.let(::println)
e.parser.printHelpOn(System.out)
exitProcess(0)
}
when (options) { when (options) {
is RegistrationOption -> options.runRegistration() is RegistrationOption -> options.runRegistration()
is KeyCopierOption -> options.copyKeystore() is KeyCopierOption -> options.copyKeystore()
} }
} }
fun parseOptions(vararg args: String): ToolOption { class ToolArgsParser : ArgsParser<ToolOption>() {
val optionParser = OptionParser() private val importKeyStoreArg = optionParser.accepts("importkeystore")
val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
val importKeyStoreArg = optionParser.accepts("importkeystore") private val configFileArg = optionParser
val configFileArg = optionParser
.accepts("config-file", "Path to the registration config file") .accepts("config-file", "Path to the registration config file")
.availableUnless(importKeyStoreArg) .availableUnless(importKeyStoreArg)
.requiredUnless(importKeyStoreArg) .requiredUnless(importKeyStoreArg)
.withRequiredArg() .withRequiredArg()
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING)) .withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
// key copy tool args // key copy tool args
val destKeystorePathArg = optionParser.accepts("destkeystore", "Path to the destination keystore which the private key should be copied to") private val destKeystorePathArg = optionParser.accepts("destkeystore", "Path to the destination keystore which the private key should be copied to")
.requireOnlyIf(importKeyStoreArg) .requireOnlyIf(importKeyStoreArg)
.withRequiredArg() .withRequiredArg()
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING)) .withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
val srcKeystorePathArg = optionParser.accepts("srckeystore", "Path to the source keystore containing the private key") private val srcKeystorePathArg = optionParser.accepts("srckeystore", "Path to the source keystore containing the private key")
.requireOnlyIf(importKeyStoreArg) .requireOnlyIf(importKeyStoreArg)
.withRequiredArg() .withRequiredArg()
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING)) .withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
val destPasswordArg = optionParser.accepts("deststorepass", "Source keystore password. Read in from the console if not specified.") private val destPasswordArg = optionParser.accepts("deststorepass", "Source keystore password. Read in from the console if not specified.")
.availableIf(importKeyStoreArg) .availableIf(importKeyStoreArg)
.withRequiredArg() .withRequiredArg()
val srcPasswordArg = optionParser.accepts("srcstorepass", "Destination keystore password. Read in from the console if not specified.") private val srcPasswordArg = optionParser.accepts("srcstorepass", "Destination keystore password. Read in from the console if not specified.")
.availableIf(importKeyStoreArg) .availableIf(importKeyStoreArg)
.withRequiredArg() .withRequiredArg()
val destAliasArg = optionParser.accepts("destalias", "The alias under which the private key will be stored in the destination key store. If not provided then [srcalias] is used.") private val destAliasArg = optionParser.accepts("destalias", "The alias under which the private key will be stored in the destination key store. If not provided then [srcalias] is used.")
.availableIf(importKeyStoreArg) .availableIf(importKeyStoreArg)
.withRequiredArg() .withRequiredArg()
val srcAliasArg = optionParser.accepts("srcalias", "The alias under which the private key resides in the source key store") private val srcAliasArg = optionParser.accepts("srcalias", "The alias under which the private key resides in the source key store")
.requireOnlyIf(importKeyStoreArg) .requireOnlyIf(importKeyStoreArg)
.withRequiredArg() .withRequiredArg()
val optionSet = optionParser.parse(*args) override fun parse(optionSet: OptionSet): ToolOption {
if (optionSet.has(helpOption)) {
throw ShowHelpException(optionParser)
}
val isCopyKey = optionSet.has(importKeyStoreArg) val isCopyKey = optionSet.has(importKeyStoreArg)
return if (isCopyKey) { return if (isCopyKey) {
val srcKeystorePath = optionSet.valueOf(srcKeystorePathArg) val srcKeystorePath = optionSet.valueOf(srcKeystorePathArg)
@ -84,6 +70,7 @@ fun parseOptions(vararg args: String): ToolOption {
RegistrationOption(configFile) RegistrationOption(configFile)
} }
} }
}
private fun OptionSpecBuilder.requireOnlyIf(option: OptionSpecBuilder): OptionSpecBuilder = requiredIf(option).availableIf(option) private fun OptionSpecBuilder.requireOnlyIf(option: OptionSpecBuilder): OptionSpecBuilder = requiredIf(option).availableIf(option)
@ -97,8 +84,6 @@ sealed class ToolOption {
val destinationAlias: String?) : ToolOption() val destinationAlias: String?) : ToolOption()
} }
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception()
fun readPassword(fmt: String): String { fun readPassword(fmt: String): String {
return if (System.console() != null) { return if (System.console() != null) {
String(System.console().readPassword(fmt)) String(System.console().readPassword(fmt))

View File

@ -27,7 +27,7 @@ class OptionParserTest {
@Test @Test
fun `parse registration args correctly`() { fun `parse registration args correctly`() {
val options = parseOptions("--config-file", "${tempDir / "test.file"}") as ToolOption.RegistrationOption val options = ToolArgsParser().parseOrExit("--config-file", "${tempDir / "test.file"}") as ToolOption.RegistrationOption
assertThat(options.configFile).isEqualTo(tempDir / "test.file") assertThat(options.configFile).isEqualTo(tempDir / "test.file")
} }
@ -40,7 +40,7 @@ class OptionParserTest {
"--destkeystore", "${tempDir / "target.jks"}", "--destkeystore", "${tempDir / "target.jks"}",
"--deststorepass", "password2", "--deststorepass", "password2",
"--srcalias", "testalias") "--srcalias", "testalias")
assertThatThrownBy { parseOptions(*keyCopyArgs, "--config-file", "test.file") } assertThatThrownBy { ToolArgsParser().parseOrExit(*keyCopyArgs, "--config-file", "test.file", printHelpOn = null) }
.isInstanceOf(OptionException::class.java) .isInstanceOf(OptionException::class.java)
.hasMessageContaining("Option(s) [config-file] are unavailable given other options on the command line") .hasMessageContaining("Option(s) [config-file] are unavailable given other options on the command line")
} }
@ -48,7 +48,7 @@ class OptionParserTest {
@Test @Test
fun `key copy args should be unavailable in registration mode`() { fun `key copy args should be unavailable in registration mode`() {
assertThatThrownBy { assertThatThrownBy {
parseOptions("--config-file", "${tempDir / "test.file"}", "--srckeystore", "${tempDir / "source.jks"}") ToolArgsParser().parseOrExit("--config-file", "${tempDir / "test.file"}", "--srckeystore", "${tempDir / "source.jks"}", printHelpOn = null)
}.isInstanceOf(OptionException::class.java) }.isInstanceOf(OptionException::class.java)
.hasMessageContaining("Option(s) [srckeystore] are unavailable given other options on the command line") .hasMessageContaining("Option(s) [srckeystore] are unavailable given other options on the command line")
} }
@ -63,7 +63,7 @@ class OptionParserTest {
"--deststorepass", "password2", "--deststorepass", "password2",
"--srcalias", "testalias", "--srcalias", "testalias",
"--destalias", "testalias2") "--destalias", "testalias2")
assertThat(parseOptions(*keyCopyArgs)).isEqualTo(ToolOption.KeyCopierOption( assertThat(ToolArgsParser().parseOrExit(*keyCopyArgs)).isEqualTo(ToolOption.KeyCopierOption(
sourceFile = tempDir / "source.jks", sourceFile = tempDir / "source.jks",
destinationFile = tempDir / "target.jks", destinationFile = tempDir / "target.jks",
sourcePassword = "password1", sourcePassword = "password1",
@ -80,7 +80,7 @@ class OptionParserTest {
"--srckeystore", "${tempDir / "source.jks"}", "--srckeystore", "${tempDir / "source.jks"}",
"--destkeystore", "${tempDir / "target.jks"}", "--destkeystore", "${tempDir / "target.jks"}",
"--srcalias", "testalias") "--srcalias", "testalias")
assertThat(parseOptions(*keyCopyArgs)).isEqualTo(ToolOption.KeyCopierOption( assertThat(ToolArgsParser().parseOrExit(*keyCopyArgs)).isEqualTo(ToolOption.KeyCopierOption(
sourceFile = tempDir / "source.jks", sourceFile = tempDir / "source.jks",
destinationFile = tempDir / "target.jks", destinationFile = tempDir / "target.jks",
sourcePassword = null, sourcePassword = null,

View File

@ -0,0 +1,23 @@
package com.r3.corda.networkmanage.common.configuration
import com.r3.corda.networkmanage.common.utils.ArgsParser
import joptsimple.OptionSet
import joptsimple.util.PathConverter
import joptsimple.util.PathProperties
import java.nio.file.Path
/**
* Parses key generator command line options.
*/
class ConfigFilePathArgsParser : ArgsParser<Path>() {
private val configFileArg = optionParser
.accepts("config-file", "The path to the config file")
.withRequiredArg()
.required()
.describedAs("filepath")
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
override fun parse(optionSet: OptionSet): Path {
return optionSet.valueOf(configFileArg).toAbsolutePath()
}
}

View File

@ -1,28 +0,0 @@
package com.r3.corda.networkmanage.common.configuration
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import joptsimple.OptionParser
import joptsimple.util.PathConverter
import joptsimple.util.PathProperties
import java.nio.file.Path
/**
* Parses key generator command line options.
*/
fun parseCommandLine(vararg args: String): Path {
val optionParser = OptionParser()
val configFileArg = optionParser
.accepts("config-file", "The path to the config file")
.withRequiredArg()
.required()
.describedAs("filepath")
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
val optionSet = optionParser.parse(*args)
// Print help and exit on help option or if there are missing options.
if (optionSet.has(helpOption) || !optionSet.has(configFileArg)) {
throw ShowHelpException(optionParser)
}
return optionSet.valueOf(configFileArg).toAbsolutePath()
}

View File

@ -0,0 +1,37 @@
package com.r3.corda.networkmanage.common.utils
import joptsimple.OptionException
import joptsimple.OptionParser
import joptsimple.OptionSet
import java.io.PrintStream
import kotlin.system.exitProcess
// TODO: This class could be useful for the rest of the codebase.
abstract class ArgsParser<out T : Any> {
protected val optionParser = OptionParser()
private val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
/**
If [printHelpOn] output stream is not null, this method will print help message and exit process
when encountered any error during args parsing, or when help flag is set.
*/
fun parseOrExit(vararg args: String, printHelpOn: PrintStream? = System.out): T {
val optionSet = try {
optionParser.parse(*args)
} catch (e: OptionException) {
printHelpOn?.let {
it.println(e.message ?: "Unable to parse arguments.")
optionParser.printHelpOn(it)
exitProcess(-1)
}
throw e
}
if (optionSet.has(helpOption)) {
printHelpOn?.let(optionParser::printHelpOn)
exitProcess(0)
}
return parse(optionSet)
}
protected abstract fun parse(optionSet: OptionSet): T
}

View File

@ -13,9 +13,7 @@ package com.r3.corda.networkmanage.common.utils
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigRenderOptions
import joptsimple.OptionParser
import net.corda.core.CordaOID import net.corda.core.CordaOID
import net.corda.core.crypto.sha256
import net.corda.core.internal.CertRole import net.corda.core.internal.CertRole
import net.corda.core.serialization.internal.SerializationEnvironmentImpl import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.serialization.internal.nodeSerializationEnv
@ -34,7 +32,6 @@ import org.slf4j.LoggerFactory
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -52,8 +49,6 @@ inline fun <reified T : Any> parseConfig(file: Path): T {
return config.parseAs(strict = false) return config.parseAs(strict = false)
} }
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception()
fun buildCertPath(certPathBytes: ByteArray): CertPath = X509CertificateFactory().delegate.generateCertPath(certPathBytes.inputStream()) fun buildCertPath(certPathBytes: ByteArray): CertPath = X509CertificateFactory().delegate.generateCertPath(certPathBytes.inputStream())
fun X509KeyStore.getCertPathAndKey(alias: String, privateKeyPassword: String): CertPathAndKey { fun X509KeyStore.getCertPathAndKey(alias: String, privateKeyPassword: String): CertPathAndKey {

View File

@ -38,8 +38,7 @@ fun parseParameters(configFile: Path?): GeneratorConfiguration {
return if (configFile == null) { return if (configFile == null) {
GeneratorConfiguration() GeneratorConfiguration()
} else { } else {
ConfigFactory ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))
.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))
.resolve() .resolve()
.parseAs() .parseAs()
} }

View File

@ -10,7 +10,7 @@
package com.r3.corda.networkmanage.dev package com.r3.corda.networkmanage.dev
import com.r3.corda.networkmanage.common.configuration.parseCommandLine import com.r3.corda.networkmanage.common.configuration.ConfigFilePathArgsParser
import com.r3.corda.networkmanage.doorman.CORDA_X500_BASE import com.r3.corda.networkmanage.doorman.CORDA_X500_BASE
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
@ -34,7 +34,7 @@ private val logger = LogManager.getLogger("com.r3.corda.networkmanage.dev.Main")
* Look for the 'certificates' directory. * Look for the 'certificates' directory.
*/ */
fun main(args: Array<String>) { fun main(args: Array<String>) {
run(parseParameters(parseCommandLine(*args))) run(parseParameters(ConfigFilePathArgsParser().parseOrExit(*args)))
} }
fun run(configuration: GeneratorConfiguration) { fun run(configuration: GeneratorConfiguration) {

View File

@ -1,10 +1,10 @@
package com.r3.corda.networkmanage.doorman package com.r3.corda.networkmanage.doorman
import com.google.common.primitives.Booleans import com.google.common.primitives.Booleans
import com.r3.corda.networkmanage.common.utils.ShowHelpException import com.r3.corda.networkmanage.common.utils.ArgsParser
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
import joptsimple.OptionParser import joptsimple.OptionSet
import joptsimple.util.EnumConverter import joptsimple.util.EnumConverter
import joptsimple.util.PathConverter import joptsimple.util.PathConverter
import joptsimple.util.PathProperties import joptsimple.util.PathProperties
@ -14,9 +14,7 @@ import net.corda.nodeapi.internal.config.parseAs
import java.nio.file.Path import java.nio.file.Path
import java.time.Instant import java.time.Instant
class DoormanArgsParser { class DoormanArgsParser : ArgsParser<DoormanCmdLineOptions>() {
private val optionParser = OptionParser()
private val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
private val configFileArg = optionParser private val configFileArg = optionParser
.accepts("config-file", "The path to the config file") .accepts("config-file", "The path to the config file")
.withRequiredArg() .withRequiredArg()
@ -37,11 +35,7 @@ class DoormanArgsParser {
.accepts("trust-store-password", "Password for the generated network root trust store. Only applicable when operating in ${Mode.ROOT_KEYGEN} mode.") .accepts("trust-store-password", "Password for the generated network root trust store. Only applicable when operating in ${Mode.ROOT_KEYGEN} mode.")
.withRequiredArg() .withRequiredArg()
fun parse(vararg args: String): DoormanCmdLineOptions { override fun parse(optionSet: OptionSet): DoormanCmdLineOptions {
val optionSet = optionParser.parse(*args)
if (optionSet.has(helpOption)) {
throw ShowHelpException(optionParser)
}
val configFile = optionSet.valueOf(configFileArg) val configFile = optionSet.valueOf(configFileArg)
val mode = optionSet.valueOf(modeArg) val mode = optionSet.valueOf(modeArg)
val setNetworkParametersFile = optionSet.valueOf(setNetworkParametersArg) val setNetworkParametersFile = optionSet.valueOf(setNetworkParametersArg)

View File

@ -31,13 +31,7 @@ fun main(args: Array<String>) {
} }
initialiseSerialization() initialiseSerialization()
val cmdLineOptions = try { val cmdLineOptions = DoormanArgsParser().parseOrExit(*args)
DoormanArgsParser().parse(*args)
} catch (e: ShowHelpException) {
e.errorMessage?.let(::println)
e.parser.printHelpOn(System.out)
exitProcess(0)
}
val config = parseConfig<NetworkManagementServerConfig>(cmdLineOptions.configFile) val config = parseConfig<NetworkManagementServerConfig>(cmdLineOptions.configFile)

View File

@ -12,7 +12,6 @@ package com.r3.corda.networkmanage.hsm
import com.jcabi.manifests.Manifests import com.jcabi.manifests.Manifests
import com.r3.corda.networkmanage.common.persistence.configureDatabase import com.r3.corda.networkmanage.common.persistence.configureDatabase
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import com.r3.corda.networkmanage.common.utils.initialiseSerialization import com.r3.corda.networkmanage.common.utils.initialiseSerialization
import com.r3.corda.networkmanage.common.utils.parseConfig import com.r3.corda.networkmanage.common.utils.parseConfig
import com.r3.corda.networkmanage.hsm.configuration.ManualMode import com.r3.corda.networkmanage.hsm.configuration.ManualMode
@ -25,7 +24,6 @@ import org.apache.logging.log4j.LogManager
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.Security import java.security.Security
import javax.crypto.Cipher import javax.crypto.Cipher
import kotlin.system.exitProcess
private val logger = LogManager.getLogger("com.r3.corda.networkmanage.hsm.Main") private val logger = LogManager.getLogger("com.r3.corda.networkmanage.hsm.Main")
@ -34,13 +32,7 @@ fun main(args: Array<String>) {
println("Signing Service Version: ${Manifests.read("Signing-Service-Version")}") println("Signing Service Version: ${Manifests.read("Signing-Service-Version")}")
} }
val cmdLineOptions = try { val cmdLineOptions = SigningServiceArgsParser().parseOrExit(*args)
SigningServiceArgsParser().parse(*args)
} catch (e: ShowHelpException) {
e.errorMessage?.let(::println)
e.parser.printHelpOn(System.out)
exitProcess(0)
}
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile) val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)

View File

@ -11,20 +11,15 @@
package com.r3.corda.networkmanage.hsm.configuration package com.r3.corda.networkmanage.hsm.configuration
import com.google.common.primitives.Booleans import com.google.common.primitives.Booleans
import com.r3.corda.networkmanage.common.utils.ShowHelpException import com.r3.corda.networkmanage.common.utils.ArgsParser
import com.r3.corda.networkmanage.hsm.authentication.AuthMode import com.r3.corda.networkmanage.hsm.authentication.AuthMode
import com.typesafe.config.ConfigFactory import joptsimple.OptionSet
import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigRenderOptions
import joptsimple.OptionParser
import joptsimple.util.PathConverter import joptsimple.util.PathConverter
import joptsimple.util.PathProperties import joptsimple.util.PathProperties
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import java.net.InetAddress
import java.net.URL import java.net.URL
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
@ -84,9 +79,7 @@ data class AuthParametersConfig(val mode: AuthMode,
val keyFilePath: Path? = null, val keyFilePath: Path? = null,
val threshold: Int) val threshold: Int)
class SigningServiceArgsParser { class SigningServiceArgsParser : ArgsParser<SigningServiceCmdLineOptions>() {
private val optionParser = OptionParser()
private val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
private val baseDirArg = optionParser private val baseDirArg = optionParser
.accepts("basedir", "Overriding configuration filepath, default to current directory.") .accepts("basedir", "Overriding configuration filepath, default to current directory.")
.withRequiredArg() .withRequiredArg()
@ -97,11 +90,7 @@ class SigningServiceArgsParser {
.withRequiredArg() .withRequiredArg()
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING)) .withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
fun parse(vararg args: String): SigningServiceCmdLineOptions { override fun parse(optionSet: OptionSet): SigningServiceCmdLineOptions {
val optionSet = optionParser.parse(*args)
if (optionSet.has(helpOption)) {
throw ShowHelpException(optionParser)
}
val baseDir = optionSet.valueOf(baseDirArg) val baseDir = optionSet.valueOf(baseDirArg)
val configFile = optionSet.valueOf(configFileArg) ?: baseDir / "signing_service.conf" val configFile = optionSet.valueOf(configFileArg) ?: baseDir / "signing_service.conf"
return SigningServiceCmdLineOptions(baseDir, configFile) return SigningServiceCmdLineOptions(baseDir, configFile)

View File

@ -10,7 +10,7 @@
package com.r3.corda.networkmanage.hsm.generator package com.r3.corda.networkmanage.hsm.generator
import com.r3.corda.networkmanage.common.configuration.parseCommandLine import com.r3.corda.networkmanage.common.configuration.ConfigFilePathArgsParser
import com.r3.corda.networkmanage.hsm.authentication.CryptoServerProviderConfig import com.r3.corda.networkmanage.hsm.authentication.CryptoServerProviderConfig
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
import net.corda.nodeapi.internal.crypto.CertificateType.ROOT_CA import net.corda.nodeapi.internal.crypto.CertificateType.ROOT_CA
@ -19,7 +19,7 @@ import org.apache.logging.log4j.LogManager
private val logger = LogManager.getLogger("com.r3.corda.networkmanage.hsm.generator.Main") private val logger = LogManager.getLogger("com.r3.corda.networkmanage.hsm.generator.Main")
fun main(args: Array<String>) { fun main(args: Array<String>) {
run(parseParameters(parseCommandLine(*args))) run(parseParameters(ConfigFilePathArgsParser().parseOrExit(*args)))
} }
fun run(parameters: GeneratorParameters) { fun run(parameters: GeneratorParameters) {

View File

@ -0,0 +1,16 @@
package com.r3.corda.networkmanage.tools.crr.submission
import com.r3.corda.networkmanage.common.utils.ArgsParser
import joptsimple.OptionSet
import java.net.URL
class CRRToolArgsParser : ArgsParser<URL>() {
private val submissionUrlArg = optionParser
.accepts("submission-url", "CRR submission endpoint.")
.withRequiredArg()
.required()
override fun parse(optionSet: OptionSet): URL {
return URL(optionSet.valueOf(submissionUrlArg))
}
}

View File

@ -1,21 +0,0 @@
package com.r3.corda.networkmanage.tools.crr.submission
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import joptsimple.OptionParser
import java.net.URL
fun parseSubmissionUrl(vararg args: String): URL {
val optionParser = OptionParser()
val submissionUrlArg = optionParser
.accepts("submission-url", "CRR submission endpoint.")
.withRequiredArg()
.required()
val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
val optionSet = optionParser.parse(*args)
// Print help and exit on help option or if there are missing options.
if (optionSet.has(helpOption) || !optionSet.has(submissionUrlArg)) {
throw ShowHelpException(optionParser)
}
return URL(optionSet.valueOf(submissionUrlArg))
}

View File

@ -17,7 +17,7 @@ private val logger = LogManager.getLogger("com.r3.corda.networkmanage.common.too
fun main(args: Array<String>) { fun main(args: Array<String>) {
initialiseSerialization() initialiseSerialization()
try { try {
submit(parseSubmissionUrl(*args)) submit(CRRToolArgsParser().parseOrExit(*args))
} catch (e: Exception) { } catch (e: Exception) {
logger.error("Error when submitting a certificate revocation request.", e) logger.error("Error when submitting a certificate revocation request.", e)
throw e throw e

View File

@ -10,7 +10,7 @@
package com.r3.corda.networkmanage.dev package com.r3.corda.networkmanage.dev
import com.r3.corda.networkmanage.common.configuration.parseCommandLine import com.r3.corda.networkmanage.common.configuration.ConfigFilePathArgsParser
import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_FILE import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_FILE
import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_PASS import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_PASS
import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_FILE import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_FILE
@ -31,7 +31,7 @@ class GeneratorConfigurationTest {
@Test @Test
fun `config file is parsed correctly`() { fun `config file is parsed correctly`() {
val config = parseParameters(parseCommandLine("--config-file", configPath)) val config = parseParameters(ConfigFilePathArgsParser().parseOrExit("--config-file", configPath))
assertEquals(GeneratorConfiguration.DEFAULT_DIRECTORY, config.directory) assertEquals(GeneratorConfiguration.DEFAULT_DIRECTORY, config.directory)
assertEquals(DEV_CA_KEY_STORE_FILE, config.keyStoreFileName) assertEquals(DEV_CA_KEY_STORE_FILE, config.keyStoreFileName)
assertEquals(DEV_CA_KEY_STORE_PASS, config.keyStorePass) assertEquals(DEV_CA_KEY_STORE_PASS, config.keyStorePass)

View File

@ -10,7 +10,6 @@
package com.r3.corda.networkmanage.doorman package com.r3.corda.networkmanage.doorman
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import joptsimple.OptionException import joptsimple.OptionException
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test
@ -26,39 +25,32 @@ class DoormanArgsParserTest {
@Test @Test
fun `should fail when network parameters file is missing`() { fun `should fail when network parameters file is missing`() {
assertThatThrownBy { assertThatThrownBy {
argsParser.parse("--config-file", validConfigPath, "--set-network-parameters", "not-here") argsParser.parseOrExit("--config-file", validConfigPath, "--set-network-parameters", "not-here")
}.hasMessageContaining("not-here") }.hasMessageContaining("not-here")
} }
@Test @Test
fun `should fail when config file is missing`() { fun `should fail when config file is missing`() {
assertThatThrownBy { assertThatThrownBy {
argsParser.parse("--config-file", "not-existing-file") argsParser.parseOrExit("--config-file", "not-existing-file")
}.hasMessageContaining("not-existing-file") }.hasMessageContaining("not-existing-file")
} }
@Test
fun `should throw ShowHelpException when help option is passed on the command line`() {
assertFailsWith<ShowHelpException> {
argsParser.parse("-h")
}
}
@Test @Test
fun `should parse trust store password correctly`() { fun `should parse trust store password correctly`() {
val parameter = argsParser.parse("--config-file", validConfigPath, "--mode", "ROOT_KEYGEN", "--trust-store-password", "testPassword") val parameter = argsParser.parseOrExit("--config-file", validConfigPath, "--mode", "ROOT_KEYGEN", "--trust-store-password", "testPassword")
assertEquals("testPassword", parameter.trustStorePassword) assertEquals("testPassword", parameter.trustStorePassword)
assertFailsWith<OptionException> { assertFailsWith<OptionException> {
argsParser.parse("--trust-store-password") argsParser.parseOrExit("--trust-store-password", printHelpOn = null)
} }
// Should fail if password is provided in mode other then root keygen. // Should fail if password is provided in mode other then root keygen.
assertFailsWith<IllegalArgumentException> { assertFailsWith<IllegalArgumentException> {
argsParser.parse("--config-file", validConfigPath, "--trust-store-password", "testPassword") argsParser.parseOrExit("--config-file", validConfigPath, "--trust-store-password", "testPassword")
} }
// trust store password is optional. // trust store password is optional.
assertNull(argsParser.parse("--config-file", validConfigPath).trustStorePassword) assertNull(argsParser.parseOrExit("--config-file", validConfigPath).trustStorePassword)
} }
} }

View File

@ -26,7 +26,7 @@ class SigningServiceArgsParserTest : TestBase() {
@Test @Test
fun `doorman-based config file is parsed correctly`() { fun `doorman-based config file is parsed correctly`() {
val cmdLineOptions = argsParser.parse("--config-file", doormanConfigPath) val cmdLineOptions = argsParser.parseOrExit("--config-file", doormanConfigPath)
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile) val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)
assertEquals("3001@192.168.0.1", config.device) assertEquals("3001@192.168.0.1", config.device)
val doormanCertParameters = config.doorman!! val doormanCertParameters = config.doorman!!
@ -37,7 +37,7 @@ class SigningServiceArgsParserTest : TestBase() {
@Test @Test
fun `networkmap-based config file is parsed correctly`() { fun `networkmap-based config file is parsed correctly`() {
val cmdLineOptions = argsParser.parse("--config-file", networkMapConfigPath) val cmdLineOptions = argsParser.parseOrExit("--config-file", networkMapConfigPath)
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile) val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)
assertEquals("3001@192.168.0.1", config.device) assertEquals("3001@192.168.0.1", config.device)
val networkMapConfig = config.networkMap!! val networkMapConfig = config.networkMap!!
@ -51,7 +51,7 @@ class SigningServiceArgsParserTest : TestBase() {
@Test @Test
fun `should fail when config file is missing`() { fun `should fail when config file is missing`() {
assertThatThrownBy { assertThatThrownBy {
argsParser.parse("--config-file", "not-existing-file") argsParser.parseOrExit("--config-file", "not-existing-file")
}.hasMessageContaining("not-existing-file") }.hasMessageContaining("not-existing-file")
} }
} }

View File

@ -10,8 +10,7 @@
package com.r3.corda.networkmanage.hsm.generator package com.r3.corda.networkmanage.hsm.generator
import com.r3.corda.networkmanage.common.configuration.parseCommandLine import com.r3.corda.networkmanage.common.configuration.ConfigFilePathArgsParser
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import com.typesafe.config.ConfigException import com.typesafe.config.ConfigException
import joptsimple.OptionException import joptsimple.OptionException
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
@ -31,22 +30,15 @@ class GeneratorParametersTest {
@Test @Test
fun `should fail when config file is missing`() { fun `should fail when config file is missing`() {
val message = assertFailsWith<OptionException> { val message = assertFailsWith<OptionException> {
parseCommandLine("--config-file", "not-existing-file") ConfigFilePathArgsParser().parseOrExit("--config-file", "not-existing-file")
}.message }.message
Assertions.assertThat(message).contains("not-existing-file") Assertions.assertThat(message).contains("not-existing-file")
} }
@Test
fun `should throw ShowHelpException when help option is passed on the command line`() {
assertFailsWith<ShowHelpException> {
parseCommandLine("-h")
}
}
@Test @Test
fun `should fail when config is invalid`() { fun `should fail when config is invalid`() {
assertFailsWith<ConfigException.Missing> { assertFailsWith<ConfigException.Missing> {
parseParameters(parseCommandLine("--config-file", invalidConfigPath)) parseParameters(ConfigFilePathArgsParser().parseOrExit("--config-file", invalidConfigPath))
} }
} }
@ -71,6 +63,6 @@ class GeneratorParametersTest {
} }
private fun parseCommandLineAndGetParameters(): GeneratorParameters { private fun parseCommandLineAndGetParameters(): GeneratorParameters {
return parseParameters(parseCommandLine(*validArgs)) return parseParameters(ConfigFilePathArgsParser().parseOrExit(*validArgs))
} }
} }