Split up the parsing of the command line arguments from the parsing of the config files. (#547)

Also, the "config-file" command line argument for the doorman jar is required - it no longer defaults to network-management.conf
This commit is contained in:
Shams Asari 2018-03-14 07:01:53 +00:00 committed by GitHub
parent 4b1e0a6ffd
commit 34800ab527
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 453 additions and 459 deletions

View File

@ -27,11 +27,7 @@ Allowed parameters are:
:rootPrivateKeyPassword: Password for the root private key
:host: host on which doorman runs
:port: port on which doorman runs
:mode: must be one of: DOORMAN (default), CA_KEYGEN, ROOT_KEYGEN.
:address: The host and port on which doorman runs
:database: database properties. The same (including its default value) as for node configuration (see :doc:`corda-configuration-file`).

View File

@ -71,8 +71,8 @@ Doorman service can be started with the following options :
The doorman service can use JIRA to manage the certificate signing request approval workflow. This can be turned on by providing JIRA connection configuration in the config file.
```
doormanConfig {
jiraConfig {
doorman {
jira {
address = "https://doorman-jira-host.com/"
projectCode = "TD"
username = "username"
@ -93,7 +93,7 @@ The doorman service can use JIRA to manage the certificate signing request appro
### Network map service
Network map service can be enabled by providing the following config:
```
networkMapConfig {
networkMap {
cacheTimeout = 600000
signInterval = 10000
}
@ -104,15 +104,13 @@ The doorman service can use JIRA to manage the certificate signing request appro
##Example config file
```
basedir = "."
host = localhost
port = 0
address = "localhost:0"
rootStorePath = ${basedir}"/certificates/rootstore.jks"
keystorePath = ${basedir}"/certificates/caKeystore.jks"
#keystorePassword = "password" #Optional if not specified, user will be prompted on the console.
#caPrivateKeyPassword = "password" #Optional if not specified, user will be prompted on the console.
#rootPrivateKeyPassword = "password" #Optional if not specified, user will be prompted on the console.
#rootKeystorePassword = "password" #Optional if not specified, user will be prompted on the console.
#trustStorePassword = "password" #Optional if not specified, user will be prompted on the console. Applicable only if, in the ROOT_KEYGEN execution mode.
dataSourceProperties {
dataSourceClassName = org.h2.jdbcx.JdbcDataSource
@ -153,31 +151,35 @@ networkMap {
If local signer is enabled, the server will look for key stores in the certificate folder on start up.
The key stores can be created using `--mode` flag.
```
java -jar doorman-<version>.jar --mode ROOT_KEYGEN
java -jar doorman-<version>.jar --config-file <config file> --mode ROOT_KEYGEN
```
and
```
java -jar doorman-<version>.jar --mode CA_KEYGEN
java -jar doorman-<version>.jar --config-file <config file> --mode CA_KEYGEN
```
A trust store file containing the root trust certificate will be produced in the location `distribute-nodes / truststore.jks`
(relative to `rootStorePath`). `truststore.jks` must be copied to the `certificates` directory of each node before
they attempt to register. The trust store password is `trustpass`.
A trust store containing the root certificate will be created in the location `distribute-nodes / network-root-truststore.jks`
(relative to `rootStorePath`). The trust store's password can be set using command line argument `--trust-store-password`,
or the doorman's keygen utility will ask for password input if trust store password is not provided using this flag.
Path to the network root trust store and password must be provided to corda node via command line arguments before
they attempt to register.
This trust store file is to be distributed to every node that wishes to register with the doorman. The node cannot
register without it.
### 2. Start Doorman service for notary registration
Start the network management server with the doorman service for initial bootstrapping. Network map service (`networkMapConfig`) should be **disabled** at this point.
**Comment out** network map config in the config file and start the server by running :
Start the network management server with the doorman service for initial bootstrapping. Network map service (`networkMap`)
should be **disabled** at this point. **Comment out** network map config in the config file and start the server by running :
```
java -jar doorman-<version>.jar
java -jar doorman-<version>.jar --config-file <config file>
```
### 3. Create notary node and register with the doorman
After the doorman service is started, start the notary node for the `initial-registration` process.
After the doorman service is started, start the notary node for registration.
```
java -jar corda.jar --initial-registration --network-root-truststore-password <trust store password>
```
By default it will expect trust store file received from the doorman to be in the location ``certificates/network-root-truststore.jks``.
This can be overridden with the additional `--network-root-truststore` flag.
NOTE: This step applies to all nodes that wish to register with the doorman.
### 4. Generate node info files for notary nodes
Once notary nodes are registered, run the notary nodes with the `just-generate-node-info` flag.
@ -187,16 +189,19 @@ networkMap {
The network parameters should contain reference to the notaries node info files.
Example network parameters file:
notaries : [{
notaryNodeInfoFile: "/Path/To/NodeInfo/File1"
validating: true
}, {
notaryNodeInfoFile: "/Path/To/NodeInfo/File2"
validating: false
}]
notaries : [
{
notaryNodeInfoFile: "/Path/To/NodeInfo/File1"
validating: true
},
{
notaryNodeInfoFile: "/Path/To/NodeInfo/File2"
validating: false
}
]
minimumPlatformVersion = 1
maxMessageSize = 10485760
maxTransactionSize = 40000
maxTransactionSize = 10485760
Save the parameters to `network-parameters.conf`
@ -204,7 +209,7 @@ networkMap {
A network parameters file is required to start the network map service for the first time. The initial network parameters file can be loaded using the `--update-network-parameters` flag.
We can now restart the network management server with both doorman and network map service.
```
java -jar doorman-<version>.jar --update-network-parameters network-parameters.conf
java -jar doorman-<version>.jar --config-file <config file> --update-network-parameters network-parameters.conf
```
### 7. Logs
@ -212,5 +217,5 @@ In order to set the desired logging level the system properties need to be used.
Appropriate system properties can be set at the execution time.
Example:
```
java -DdefaultLogLevel=TRACE -DconsoleLogLevel=TRACE -jar doorman-<version>.jar
java -DdefaultLogLevel=TRACE -DconsoleLogLevel=TRACE -jar doorman-<version>.jar --config-file <config file>
```

View File

@ -13,14 +13,14 @@ h2port = 0
# Doorman config
# Comment out this section if running without doorman service
doormanConfig{
doorman {
approveInterval = 10000
approveAll = false
}
# Network map config
# Comment out this section if running without network map service
networkMapConfig{
networkMap {
cacheTimeout = 600000
signInterval = 10000
}

View File

@ -13,10 +13,10 @@ h2port = 0
# Doorman config
# Comment out this section if running without doorman service
doormanConfig{
doorman {
approveInterval = 10000
approveAll = false
jiraConfig{
jira {
address = "https://doorman-jira-host.com/"
projectCode = "TD"
username = "username"
@ -27,7 +27,7 @@ doormanConfig{
# Network map config
# Comment out this section if running without network map service
networkMapConfig{
networkMap {
cacheTimeout = 600000
signInterval = 10000
}

View File

@ -19,14 +19,14 @@ h2port = 0
# Doorman config
# Comment out this section if running without doorman service
doormanConfig{
doorman {
approveInterval = 10000
approveAll = false
}
# Network map config
# Comment out this section if running without network map service
networkMapConfig{
networkMap {
cacheTimeout = 600000
signInterval = 10000
}

View File

@ -1,13 +1,11 @@
basedir = "."
host = localhost
port = 0
address = "localhost:0"
rootStorePath = ${basedir}"/certificates/rootstore.jks"
keystorePath = ${basedir}"/certificates/caKeystore.jks"
#keystorePassword = "password" #Optional if not specified, user will be prompted on the console.
#caPrivateKeyPassword = "password" #Optional if not specified, user will be prompted on the console.
#rootPrivateKeyPassword = "password" #Optional if not specified, user will be prompted on the console.
#rootKeystorePassword = "password" #Optional if not specified, user will be prompted on the console.
#trustStorePassword = "password" #Optional if not specified, user will be prompted on the console. Applicable only if, in the ROOT_KEYGEN execution mode.
dataSourceProperties {
dataSourceClassName = org.h2.jdbcx.JdbcDataSource

View File

@ -15,17 +15,6 @@ doorman {
}
}
networkMap {
username = "TEST_USERNAME",
keyGroup = "DEV.CORDACONNECT.OPS.NETMAP"
authParameters {
mode = KEY_FILE
password = "PASSWORD"
keyFilePath = "./Administrator.KEY"
threshold = 2
}
}
h2port = 0
dataSourceProperties {
"dataSourceClassName" = org.h2.jdbcx.JdbcDataSource

View File

@ -0,0 +1,22 @@
basedir = "."
device = "3001@192.168.0.1"
keySpecifier = -1
networkMap {
username = "TEST_USERNAME",
keyGroup = "DEV.CORDACONNECT.OPS.NETMAP"
authParameters {
mode = KEY_FILE
password = "PASSWORD"
keyFilePath = "./Administrator.KEY"
threshold = 2
}
}
h2port = 0
dataSourceProperties {
"dataSourceClassName" = org.h2.jdbcx.JdbcDataSource
"dataSource.url" = "jdbc:h2:file:"${basedir}"/persistence;DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=0;AUTO_SERVER_PORT="${h2port}
"dataSource.user" = sa
"dataSource.password" = ""
}

View File

@ -15,10 +15,10 @@ 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.InputReader
import com.r3.corda.networkmanage.hsm.configuration.AuthenticationParameters
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateParameters
import com.r3.corda.networkmanage.hsm.configuration.NetworkMapCertificateParameters
import com.r3.corda.networkmanage.hsm.configuration.Parameters
import com.r3.corda.networkmanage.hsm.configuration.AuthParametersConfig
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateConfig
import com.r3.corda.networkmanage.hsm.configuration.NetworkMapCertificateConfig
import com.r3.corda.networkmanage.hsm.configuration.SigningServiceConfig
import com.r3.corda.networkmanage.hsm.generator.CertificateConfiguration
import com.r3.corda.networkmanage.hsm.generator.GeneratorParameters
import com.r3.corda.networkmanage.hsm.generator.UserAuthenticationParameters
@ -127,26 +127,26 @@ abstract class HsmBaseTest {
), hsmUserConfigs)
}
protected fun createHsmSigningServiceConfig(): Parameters {
return Parameters(
protected fun createHsmSigningServiceConfig(): SigningServiceConfig {
return SigningServiceConfig(
dataSourceProperties = mock(),
device = "${hsmSimulator.port}@${hsmSimulator.host}",
keySpecifier = 1,
doorman = DoormanCertificateParameters(
doorman = DoormanCertificateConfig(
rootKeyStoreFile = rootKeyStoreFile,
keyGroup = DOORMAN_CERT_KEY_GROUP,
validDays = 3650,
rootKeyStorePassword = TRUSTSTORE_PASSWORD,
crlDistributionPoint = "http://test.com/revoked.crl",
authParameters = AuthenticationParameters(
authParameters = AuthParametersConfig(
mode = SigningServiceAuthMode.PASSWORD,
threshold = 2
)
),
networkMap = NetworkMapCertificateParameters(
networkMap = NetworkMapCertificateConfig(
username = "INTEGRATION_TEST",
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
authParameters = AuthenticationParameters(
authParameters = AuthParametersConfig(
mode = SigningServiceAuthMode.PASSWORD,
password = "INTEGRATION_TEST",
threshold = 2

View File

@ -10,10 +10,9 @@
package com.r3.corda.networkmanage.common.utils
import com.google.common.base.CaseFormat
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import joptsimple.ArgumentAcceptingOptionSpec
import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigRenderOptions
import joptsimple.OptionParser
import net.corda.core.CordaOID
import net.corda.core.crypto.sha256
@ -22,6 +21,7 @@ import net.corda.core.internal.SignedDataWithCert
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.network.NetworkMap
@ -32,6 +32,9 @@ import org.bouncycastle.asn1.ASN1Encodable
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.nio.file.Path
import java.security.KeyPair
import java.security.PrivateKey
import java.security.PublicKey
@ -40,6 +43,8 @@ import java.security.cert.X509Certificate
const val CORDA_NETWORK_MAP = "cordanetworkmap"
val logger: Logger = LoggerFactory.getLogger("com.r3.corda.networkmanage.common.utils")
// TODO These should be defined in node-api
typealias SignedNetworkParameters = SignedDataWithCert<NetworkParameters>
typealias SignedNetworkMap = SignedDataWithCert<NetworkMap>
@ -54,20 +59,10 @@ data class CertPathAndKey(val certPath: List<X509Certificate>, val key: PrivateK
*/
fun PublicKey.hashString() = encoded.sha256().toString()
fun Array<out String>.toConfigWithOptions(registerOptions: OptionParser.() -> Unit): Config {
val parser = OptionParser()
val helpOption = parser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp()
registerOptions(parser)
val optionSet = parser.parse(*this)
// Print help and exit on help option.
if (optionSet.has(helpOption)) {
throw ShowHelpException(parser)
}
// Convert all command line options to Config.
return ConfigFactory.parseMap(parser.recognizedOptions().mapValues {
val optionSpec = it.value
if (optionSpec is ArgumentAcceptingOptionSpec<*> && !optionSpec.requiresArgument() && optionSet.has(optionSpec)) true else optionSpec.value(optionSet)
}.mapKeys { it.key.toCamelcase() }.filterValues { it != null })
inline fun <reified T : Any> parseConfig(file: Path): T {
val config = ConfigFactory.parseFile(file.toFile(), ConfigParseOptions.defaults().setAllowMissing(true)).resolve()
logger.info(config.root().render(ConfigRenderOptions.defaults()))
return config.parseAs(strict = false)
}
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception()
@ -87,12 +82,6 @@ fun initialiseSerialization() {
context)
}
private fun String.toCamelcase(): String {
return if (contains('_') || contains('-')) {
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, this.replace("-", "_"))
} else this
}
private fun PKCS10CertificationRequest.firstAttributeValue(identifier: ASN1ObjectIdentifier): ASN1Encodable? {
return getAttributes(identifier).firstOrNull()?.attrValues?.firstOrNull()
}

View File

@ -0,0 +1,55 @@
package com.r3.corda.networkmanage.doorman
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigRenderOptions
import joptsimple.OptionParser
import joptsimple.util.EnumConverter
import joptsimple.util.PathConverter
import joptsimple.util.PathProperties
import net.corda.nodeapi.internal.config.parseAs
import java.nio.file.Path
class DoormanArgsParser {
private val optionParser = OptionParser()
private val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
private val configFileArg = optionParser
.accepts("config-file", "The path to the config file")
.withRequiredArg()
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
.required()
private val modeArg = optionParser
.accepts("mode", "Set the mode of this application")
.withRequiredArg()
.withValuesConvertedBy(object : EnumConverter<Mode>(Mode::class.java) {})
.defaultsTo(Mode.DOORMAN)
private val updateNetworkParametersArg = optionParser
.accepts("update-network-parameters", "Update network parameters file. Currently only network parameters initialisation is supported.")
.withRequiredArg()
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
private val trustStorePasswordArg = optionParser
.accepts("trust-store-password", "Password for the generated network root trust store. Only required when operating in ${Mode.ROOT_KEYGEN} mode.")
.withRequiredArg()
fun parse(vararg args: String): DoormanCmdLineOptions {
val optionSet = optionParser.parse(*args)
if (optionSet.has(helpOption)) {
throw ShowHelpException(optionParser)
}
val configFile = optionSet.valueOf(configFileArg)
val mode = optionSet.valueOf(modeArg)
val networkParametersFile = optionSet.valueOf(updateNetworkParametersArg)
val trustStorePassword = optionSet.valueOf(trustStorePasswordArg)
return DoormanCmdLineOptions(configFile, mode, networkParametersFile, trustStorePassword)
}
}
data class DoormanCmdLineOptions(val configFile: Path, val mode: Mode, val networkParametersFile: Path?, val trustStorePassword: String?) {
init {
// Make sure trust store password is only specified in root keygen mode.
if (mode != Mode.ROOT_KEYGEN) {
require(trustStorePassword == null) { "Trust store password should not be specified in this mode." }
}
}
}

View File

@ -10,31 +10,21 @@
package com.r3.corda.networkmanage.doorman
import com.r3.corda.networkmanage.common.utils.toConfigWithOptions
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import net.corda.core.internal.div
import net.corda.core.internal.isRegularFile
import com.google.common.primitives.Booleans
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
data class NetworkManagementServerConfig( // TODO: Move local signing to signing server.
val host: String,
val port: Int,
val address: NetworkHostAndPort,
val dataSourceProperties: Properties,
val database: DatabaseConfig = DatabaseConfig(),
val mode: Mode,
val doorman: DoormanConfig?,
val networkMap: NetworkMapConfig?,
val updateNetworkParameters: Path?,
val trustStorePassword: String?,
// TODO Should be part of a localSigning sub-config
val keystorePath: Path? = null,
// TODO Should be part of a localSigning sub-config
@ -52,20 +42,20 @@ data class NetworkManagementServerConfig( // TODO: Move local signing to signing
val DEFAULT_APPROVE_INTERVAL = 5.seconds
val DEFAULT_SIGN_INTERVAL = 5.seconds
}
init {
if (updateNetworkParameters != null) {
check(updateNetworkParameters.isRegularFile()) { "Update network parameters file $updateNetworkParameters does not exist" }
}
}
}
data class DoormanConfig(val approveAll: Boolean = false,
val jira: JiraConfig? = null,
val approveInterval: Long = NetworkManagementServerConfig.DEFAULT_APPROVE_INTERVAL.toMillis())
val approveInterval: Long = NetworkManagementServerConfig.DEFAULT_APPROVE_INTERVAL.toMillis()) {
init {
require(Booleans.countTrue(approveAll, jira != null) == 1) {
"Either 'approveAll' or 'jira' config settings need to be specified but not both"
}
}
}
data class NetworkMapConfig(val cacheTimeout: Long,
// TODO: Move signing to signing server.
// TODO: Move signing to signing server.
val signInterval: Long = NetworkManagementServerConfig.DEFAULT_SIGN_INTERVAL.toMillis())
enum class Mode {
@ -81,43 +71,3 @@ data class JiraConfig(
val username: String,
val password: String
)
/**
* Parses the doorman command line options.
*/
fun parseParameters(vararg args: String): NetworkManagementServerConfig {
val argConfig = args.toConfigWithOptions {
accepts("config-file", "The path to the config file")
.withRequiredArg()
.describedAs("filepath")
accepts("update-network-parameters", "Update network parameters filepath. Currently only network parameters initialisation is supported.")
.withRequiredArg()
.describedAs("filepath")
accepts("mode", "Set the mode of this application")
.withRequiredArg()
.defaultsTo(Mode.DOORMAN.name)
accepts("trust-store-password", "Password for generated network root trust store. Only required when operating in root keygen mode.")
.withRequiredArg()
.describedAs("password")
}
// The config-file option is changed to configFile
val configFile = if (argConfig.hasPath("configFile")) {
Paths.get(argConfig.getString("configFile"))
} else {
// TODO Remove this default
Paths.get(".") / "network-management.conf"
}
require(configFile.isRegularFile()) { "Config file $configFile does not exist" }
val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true)))
.resolve()
.parseAs<NetworkManagementServerConfig>(false)
// Make sure trust store password is only specified in root keygen mode.
if (config.mode != Mode.ROOT_KEYGEN) {
require(config.trustStorePassword == null) { "Trust store password should not be specified in this mode." }
}
return config
}

View File

@ -15,32 +15,35 @@ import com.r3.corda.networkmanage.common.persistence.configureDatabase
import com.r3.corda.networkmanage.common.utils.*
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
import net.corda.core.node.NetworkParameters
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities
import org.slf4j.LoggerFactory
import java.time.Instant
import kotlin.concurrent.thread
import kotlin.system.exitProcess
private val logger = LoggerFactory.getLogger("com.r3.corda.networkmanage.doorman")
fun main(args: Array<String>) {
if (Manifests.exists("Doorman-Version")) {
println("Version: ${Manifests.read("Doorman-Version")}")
}
val parameters = try {
parseParameters(*args)
val cmdLineOptions = try {
DoormanArgsParser().parse(*args)
} catch (e: ShowHelpException) {
e.errorMessage?.let(::println)
e.parser.printHelpOn(System.out)
exitProcess(0)
}
// TODO Use the logger for this and elsewhere in this file.
println("Running in ${parameters.mode} mode")
when (parameters.mode) {
Mode.ROOT_KEYGEN -> parameters.rootKeyGenMode()
Mode.CA_KEYGEN -> parameters.caKeyGenMode()
Mode.DOORMAN -> parameters.doormanMode()
val config = parseConfig<NetworkManagementServerConfig>(cmdLineOptions.configFile)
logger.info("Running in ${cmdLineOptions.mode} mode")
when (cmdLineOptions.mode) {
Mode.ROOT_KEYGEN -> rootKeyGenMode(cmdLineOptions, config)
Mode.CA_KEYGEN -> caKeyGenMode(config)
Mode.DOORMAN -> doormanMode(cmdLineOptions, config)
}
}
@ -60,48 +63,48 @@ private fun processKeyStore(config: NetworkManagementServerConfig): Pair<CertPat
return Pair(csrCertPathAndKey, networkMapSigner)
}
private fun NetworkManagementServerConfig.rootKeyGenMode() {
private fun rootKeyGenMode(cmdLineOptions: DoormanCmdLineOptions, config: NetworkManagementServerConfig) {
generateRootKeyPair(
rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
rootKeystorePassword,
rootPrivateKeyPassword,
trustStorePassword
config.rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
config.rootKeystorePassword,
config.rootPrivateKeyPassword,
cmdLineOptions.trustStorePassword
)
}
private fun NetworkManagementServerConfig.caKeyGenMode() {
private fun caKeyGenMode(config: NetworkManagementServerConfig) {
generateSigningKeyPairs(
keystorePath ?: throw IllegalArgumentException("The 'keystorePath' parameter must be specified when generating keys!"),
rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
rootKeystorePassword,
rootPrivateKeyPassword,
keystorePassword,
caPrivateKeyPassword
config.keystorePath ?: throw IllegalArgumentException("The 'keystorePath' parameter must be specified when generating keys!"),
config.rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
config.rootKeystorePassword,
config.rootPrivateKeyPassword,
config.keystorePassword,
config.caPrivateKeyPassword
)
}
private fun NetworkManagementServerConfig.doormanMode() {
private fun doormanMode(cmdLineOptions: DoormanCmdLineOptions, config: NetworkManagementServerConfig) {
initialiseSerialization()
val persistence = configureDatabase(dataSourceProperties, database)
val persistence = configureDatabase(config.dataSourceProperties, config.database)
// TODO: move signing to signing server.
val csrAndNetworkMap = processKeyStore(this)
val csrAndNetworkMap = processKeyStore(config)
if (csrAndNetworkMap != null) {
println("Starting network management services with local signing")
logger.info("Starting network management services with local signing")
}
val networkManagementServer = NetworkManagementServer()
val networkParameters = updateNetworkParameters?.let {
val networkParameters = cmdLineOptions.networkParametersFile?.let {
// TODO This check shouldn't be needed. Fix up the config design.
requireNotNull(networkMap) { "'networkMap' config is required for applying network parameters" }
println("Parsing network parameters from '${it.toAbsolutePath()}'...")
requireNotNull(config.networkMap) { "'networkMap' config is required for applying network parameters" }
logger.info("Parsing network parameters from '${it.toAbsolutePath()}'...")
parseNetworkParametersConfig(it).toNetworkParameters(modifiedTime = Instant.now(), epoch = 1)
}
val networkMapStartParams = networkMap?.let {
val networkMapStartParams = config.networkMap?.let {
NetworkMapStartParams(csrAndNetworkMap?.second, networkParameters, it)
}
networkManagementServer.start(NetworkHostAndPort(host, port), persistence, csrAndNetworkMap?.first, doorman, networkMapStartParams)
networkManagementServer.start(config.address, persistence, csrAndNetworkMap?.first, config.doorman, networkMapStartParams)
Runtime.getRuntime().addShutdownHook(thread(start = false) {
networkManagementServer.close()

View File

@ -43,6 +43,7 @@ class NetworkManagementServer : Closeable {
lateinit var hostAndPort: NetworkHostAndPort
override fun close() {
logger.info("Closing server...")
for (closeAction in closeActions) {
try {
closeAction()
@ -94,7 +95,6 @@ class NetworkManagementServer : Closeable {
serverStatus: NetworkManagementServerStatus): RegistrationWebService {
logger.info("Starting Doorman server.")
val requestService = if (config.approveAll) {
require(config.jira == null) { "Jira configuration cannot be specified when the approveAll parameter is set to true." }
logger.warn("Doorman server is in 'Approve All' mode, this will approve all incoming certificate signing requests.")
ApproveAllCertificateSigningRequestStorage(PersistentCertificateSigningRequestStorage(database))
} else {

View File

@ -10,63 +10,56 @@
package com.r3.corda.networkmanage.hsm
import com.google.common.primitives.Booleans
import com.jcabi.manifests.Manifests
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.hsm.configuration.parseParameters
import com.r3.corda.networkmanage.common.utils.parseConfig
import com.r3.corda.networkmanage.hsm.configuration.SigningServiceArgsParser
import com.r3.corda.networkmanage.hsm.configuration.SigningServiceConfig
import com.r3.corda.networkmanage.hsm.processor.CsrProcessor
import com.r3.corda.networkmanage.hsm.processor.NetworkMapProcessor
import org.apache.logging.log4j.LogManager
import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.Security
import javax.crypto.Cipher
import kotlin.system.exitProcess
private val logger = LogManager.getLogger("com.r3.corda.networkmanage.hsm.Main")
private fun logServiceVersion() {
fun main(args: Array<String>) {
if (Manifests.exists("Signing-Service-Version")) {
println("Signing Service Version: ${Manifests.read("Signing-Service-Version")}")
}
}
fun main(args: Array<String>) {
logServiceVersion()
parseParameters(*args).run {
try {
// Validate
// Grabbed from https://stackoverflow.com/questions/7953567/checking-if-unlimited-cryptography-is-available
require(Cipher.getMaxAllowedKeyLength("AES") >= 256) {
"Unlimited Strength Jurisdiction Policy Files must be installed, see http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html"
}
require(Booleans.countTrue(doorman != null, networkMap != null) == 1) {
"Exactly one networkMap or doorman configuration needs to be specified."
}
requireNotNull(dataSourceProperties)
val cmdLineOptions = try {
SigningServiceArgsParser().parse(*args)
} catch (e: ShowHelpException) {
e.errorMessage?.let(::println)
e.parser.printHelpOn(System.out)
exitProcess(0)
}
// Ensure the BouncyCastle provider is installed
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(BouncyCastleProvider())
}
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)
initialiseSerialization()
// Create DB connection.
val persistence = configureDatabase(dataSourceProperties, database)
if (networkMap != null) {
NetworkMapProcessor(networkMap, device, keySpecifier, persistence).run()
} else {
try {
CsrProcessor(doorman!!, device, keySpecifier, persistence).showMenu()
} catch (e: ShowHelpException) {
e.errorMessage?.let(::println)
e.parser.printHelpOn(System.out)
}
}
} catch (e: Exception) {
logger.error("Error while starting the HSM Signing service.", e)
}
// Validate
// Grabbed from https://stackoverflow.com/questions/7953567/checking-if-unlimited-cryptography-is-available
require(Cipher.getMaxAllowedKeyLength("AES") >= 256) {
"Unlimited Strength Jurisdiction Policy Files must be installed, see http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html"
}
// Ensure the BouncyCastle provider is installed
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(BouncyCastleProvider())
}
initialiseSerialization()
// Create DB connection.
val persistence = configureDatabase(config.dataSourceProperties, config.database)
if (config.networkMap != null) {
NetworkMapProcessor(config.networkMap, config.device, config.keySpecifier, persistence).run()
} else {
CsrProcessor(config.doorman!!, config.device, config.keySpecifier, persistence).showMenu()
}
}

View File

@ -1,87 +0,0 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package com.r3.corda.networkmanage.hsm.configuration
import com.r3.corda.networkmanage.common.utils.toConfigWithOptions
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import net.corda.core.internal.div
import net.corda.core.internal.isRegularFile
import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
/**
* Configuration parameters. Those are general configuration parameters shared with both
* network map and certificate signing requests processes.
*/
data class Parameters(val dataSourceProperties: Properties,
val database: DatabaseConfig = DatabaseConfig(),
val device: String,
val keySpecifier: Int,
val networkMap: NetworkMapCertificateParameters? = null,
val doorman: DoormanCertificateParameters? = null)
/**
* Network map signing process specific parameters.
*/
data class NetworkMapCertificateParameters(val username: String,
val keyGroup: String,
val authParameters: AuthenticationParameters)
/**
* Certificate signing requests process specific parameters.
*/
data class DoormanCertificateParameters(val crlDistributionPoint: String,
val keyGroup:String,
val validDays: Int,
val rootKeyStoreFile: Path,
val rootKeyStorePassword: String,
val authParameters: AuthenticationParameters) {
fun loadRootKeyStore(createNew: Boolean = false): X509KeyStore {
return X509KeyStore.fromFile(rootKeyStoreFile, rootKeyStorePassword, createNew)
}
}
/**
* Authentication related parameters.
*/
data class AuthenticationParameters(val mode: AuthMode,
val password: String? = null, // This is either HSM password or key file password, depending on the mode.
val keyFilePath: Path? = null,
val threshold: Int)
/**
* Parses the list of arguments and produces an instance of [Parameters].
* @param args list of strings corresponding to program arguments
* @return instance of Parameters produced from [args]
*/
fun parseParameters(vararg args: String): Parameters {
val argConfig = args.toConfigWithOptions {
accepts("basedir", "Overriding configuration filepath, default to current directory.").withRequiredArg().defaultsTo(".").describedAs("filepath")
accepts("config-file", "Overriding configuration file.").withRequiredArg().describedAs("filepath")
}
// The config-file option is changed to configFile
val configFile = if (argConfig.hasPath("configFile")) {
Paths.get(argConfig.getString("configFile"))
} else {
Paths.get(argConfig.getString("basedir")) / "signing_service.conf"
}
require(configFile.isRegularFile()) { "Config file $configFile does not exist" }
val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))).resolve()
return config.parseAs(false)
}

View File

@ -0,0 +1,100 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package com.r3.corda.networkmanage.hsm.configuration
import com.google.common.primitives.Booleans
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import com.typesafe.config.ConfigRenderOptions
import joptsimple.OptionParser
import joptsimple.util.PathConverter
import joptsimple.util.PathProperties
import net.corda.core.internal.div
import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
/**
* Configuration parameters. Those are general configuration parameters shared with both
* network map and certificate signing requests processes.
*/
data class SigningServiceConfig(val dataSourceProperties: Properties,
val database: DatabaseConfig = DatabaseConfig(),
val device: String,
val keySpecifier: Int,
val networkMap: NetworkMapCertificateConfig? = null,
val doorman: DoormanCertificateConfig? = null) {
init {
require(Booleans.countTrue(doorman != null, networkMap != null) == 1) {
"Exactly one networkMap or doorman configuration needs to be specified."
}
}
}
/**
* Network map signing process specific parameters.
*/
data class NetworkMapCertificateConfig(val username: String,
val keyGroup: String,
val authParameters: AuthParametersConfig)
/**
* Certificate signing requests process specific parameters.
*/
data class DoormanCertificateConfig(val crlDistributionPoint: String,
val keyGroup:String,
val validDays: Int,
val rootKeyStoreFile: Path,
val rootKeyStorePassword: String,
val authParameters: AuthParametersConfig) {
fun loadRootKeyStore(createNew: Boolean = false): X509KeyStore {
return X509KeyStore.fromFile(rootKeyStoreFile, rootKeyStorePassword, createNew)
}
}
/**
* Authentication related parameters.
*/
data class AuthParametersConfig(val mode: AuthMode,
val password: String? = null, // This is either HSM password or key file password, depending on the mode.
val keyFilePath: Path? = null,
val threshold: Int)
class SigningServiceArgsParser {
private val optionParser = OptionParser()
private val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
private val baseDirArg = optionParser
.accepts("basedir", "Overriding configuration filepath, default to current directory.")
.withRequiredArg()
.withValuesConvertedBy(PathConverter(PathProperties.DIRECTORY_EXISTING))
.defaultsTo(Paths.get("."))
private val configFileArg = optionParser
.accepts("config-file", "The path to the config file")
.withRequiredArg()
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
fun parse(vararg args: String): SigningServiceCmdLineOptions {
val optionSet = optionParser.parse(*args)
if (optionSet.has(helpOption)) {
throw ShowHelpException(optionParser)
}
val baseDir = optionSet.valueOf(baseDirArg)
val configFile = optionSet.valueOf(configFileArg) ?: baseDir / "signing_service.conf"
return SigningServiceCmdLineOptions(baseDir, configFile)
}
}
data class SigningServiceCmdLineOptions(val baseDir: Path, val configFile: Path)

View File

@ -12,7 +12,7 @@ package com.r3.corda.networkmanage.hsm.processor
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
import com.r3.corda.networkmanage.hsm.authentication.createProvider
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateParameters
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateConfig
import com.r3.corda.networkmanage.hsm.menu.Menu
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
@ -21,7 +21,7 @@ import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.persistence.CordaPersistence
class CsrProcessor(private val parameters: DoormanCertificateParameters,
class CsrProcessor(private val config: DoormanCertificateConfig,
private val device: String,
private val keySpecifier: Int,
private val database: CordaPersistence) {
@ -29,12 +29,12 @@ class CsrProcessor(private val parameters: DoormanCertificateParameters,
val logger = contextLogger()
}
private val auth = parameters.authParameters
private val auth = config.authParameters
fun showMenu() {
val csrStorage = DBSignedCertificateRequestStorage(database)
val sign: (List<ApprovedCertificateRequestData>) -> Unit = {
val signer = parameters.run {
val signer = config.run {
HsmCsrSigner(
csrStorage,
loadRootKeyStore(),
@ -42,7 +42,7 @@ class CsrProcessor(private val parameters: DoormanCertificateParameters,
null,
validDays,
Authenticator(
provider = createProvider(parameters.keyGroup, keySpecifier, device),
provider = createProvider(config.keyGroup, keySpecifier, device),
mode = auth.mode,
authStrengthThreshold = auth.threshold))
}

View File

@ -15,12 +15,12 @@ import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
import com.r3.corda.networkmanage.hsm.authentication.createProvider
import com.r3.corda.networkmanage.hsm.configuration.NetworkMapCertificateParameters
import com.r3.corda.networkmanage.hsm.configuration.NetworkMapCertificateConfig
import com.r3.corda.networkmanage.hsm.signer.HsmSigner
import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.persistence.CordaPersistence
class NetworkMapProcessor(private val parameters: NetworkMapCertificateParameters,
class NetworkMapProcessor(private val config: NetworkMapCertificateConfig,
private val device: String,
private val keySpecifier: Int,
private val database: CordaPersistence) {
@ -29,7 +29,7 @@ class NetworkMapProcessor(private val parameters: NetworkMapCertificateParameter
}
init {
parameters.authParameters.run {
config.authParameters.run {
requireNotNull(password)
require(mode != AuthMode.CARD_READER)
if (mode == AuthMode.KEY_FILE) {
@ -40,7 +40,7 @@ class NetworkMapProcessor(private val parameters: NetworkMapCertificateParameter
fun run() {
logger.info("Starting network map processor.")
parameters.run {
config.run {
val networkMapStorage = PersistentNetworkMapStorage(database)
val signer = HsmSigner(
Authenticator(

View File

@ -0,0 +1,64 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package com.r3.corda.networkmanage.doorman
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import joptsimple.OptionException
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
class DoormanArgsParserTest {
private val validConfigPath = File("doorman.conf").absolutePath
private val argsParser = DoormanArgsParser()
@Test
fun `should fail when network parameters file is missing`() {
assertThatThrownBy {
argsParser.parse("--config-file", validConfigPath, "--update-network-parameters", "not-here")
}.hasMessageContaining("not-here")
}
@Test
fun `should fail when config file is missing`() {
assertThatThrownBy {
argsParser.parse("--config-file", "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
fun `should parse trust store password correctly`() {
val parameter = argsParser.parse("--config-file", validConfigPath, "--mode", "ROOT_KEYGEN", "--trust-store-password", "testPassword")
assertEquals("testPassword", parameter.trustStorePassword)
assertFailsWith<OptionException> {
argsParser.parse("--trust-store-password")
}
// Should fail if password is provided in mode other then root keygen.
assertFailsWith<IllegalArgumentException> {
argsParser.parse("--config-file", validConfigPath, "--trust-store-password", "testPassword")
}
// trust store password is optional.
assertNull(argsParser.parse("--config-file", validConfigPath).trustStorePassword)
}
}

View File

@ -1,84 +0,0 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package com.r3.corda.networkmanage.doorman
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import com.typesafe.config.ConfigException
import joptsimple.OptionException
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.io.File
import java.lang.reflect.InvocationTargetException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue
class DoormanParametersTest {
private val validOverrideNetworkConfigPath = File("network-parameters.conf").absolutePath
private val validConfigPath = File("doorman.conf").absolutePath
private val invalidConfigPath = File(javaClass.getResource("/doorman_fail.conf").toURI()).absolutePath
private val validArgs = arrayOf("--config-file", validConfigPath, "--update-network-parameters", validOverrideNetworkConfigPath)
@Test
fun `should fail when initial network parameters file is missing`() {
val message = assertFailsWith<InvocationTargetException> {
parseParameters("--config-file", validConfigPath, "--update-network-parameters", "not-here")
}.targetException.message
assertThat(message).contains("Update network parameters file ")
}
@Test
fun `should fail when config file is missing`() {
val message = assertFailsWith<IllegalArgumentException> {
parseParameters("--config-file", "not-existing-file")
}.message
assertThat(message).contains("Config file ")
}
@Test
fun `should throw ShowHelpException when help option is passed on the command line`() {
assertFailsWith<ShowHelpException> {
parseParameters("-?")
}
}
@Test
fun `should fail when config missing`() {
assertFailsWith<ConfigException.Missing> {
parseParameters("--config-file", invalidConfigPath)
}
}
@Test
fun `should parse database config correctly`() {
val parameter = parseParameters(*validArgs).database
assertTrue(parameter.runMigration)
}
@Test
fun `should parse trust store password correctly`() {
val parameter = parseParameters("--config-file", validConfigPath, "--mode", "ROOT_KEYGEN", "--trust-store-password", "testPassword")
assertEquals("testPassword", parameter.trustStorePassword)
assertFailsWith<OptionException> {
parseParameters("--trust-store-password")
}
// Should fail if password is provided in mode other then root keygen.
assertFailsWith<IllegalArgumentException> {
parseParameters("--config-file", validConfigPath, "--trust-store-password", "testPassword")
}
// trust store password is optional.
assertNull(parseParameters("--config-file", validConfigPath).trustStorePassword)
}
}

View File

@ -1,58 +0,0 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package com.r3.corda.networkmanage.hsm.configuration
import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
import com.typesafe.config.ConfigException
import org.assertj.core.api.Assertions
import org.junit.Test
import java.io.File
import java.nio.file.Paths
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class ConfigurationTest : TestBase() {
private val validConfigPath = File("./hsm.conf").absolutePath
private val invalidConfigPath = File(javaClass.getResource("/hsm_fail.conf").toURI()).absolutePath
@Test
fun `config file is parsed correctly`() {
val parameters = parseParameters("--config-file", validConfigPath)
assertEquals("3001@192.168.0.1", parameters.device)
val doormanCertParameters = parameters.doorman!!
assertEquals(AuthMode.PASSWORD, doormanCertParameters.authParameters.mode)
assertEquals(2, doormanCertParameters.authParameters.threshold)
assertEquals(3650, doormanCertParameters.validDays)
val nmParams = parameters.networkMap!!
assertEquals(AuthMode.KEY_FILE, nmParams.authParameters.mode)
assertEquals(Paths.get("./Administrator.KEY"), nmParams.authParameters.keyFilePath)
assertEquals(2, nmParams.authParameters.threshold)
assertEquals("PASSWORD", nmParams.authParameters.password)
assertEquals("TEST_USERNAME", nmParams.username)
}
@Test
fun `should fail when config missing database source properties`() {
// dataSourceProperties is missing from node_fail.conf and it should fail during parsing, and shouldn't use default from reference.conf.
assertFailsWith<ConfigException.Missing> {
parseParameters("--config-file", invalidConfigPath)
}
}
@Test
fun `should fail when config file is missing`() {
val message = assertFailsWith<IllegalArgumentException> {
com.r3.corda.networkmanage.doorman.parseParameters("--config-file", "not-existing-file")
}.message
Assertions.assertThat(message).contains("Config file ")
}
}

View File

@ -0,0 +1,57 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package com.r3.corda.networkmanage.hsm.configuration
import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.utils.parseConfig
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import java.io.File
import java.nio.file.Paths
import kotlin.test.assertEquals
class SigningServiceArgsParserTest : TestBase() {
private val doormanConfigPath = File("./hsm-for-doorman.conf").absolutePath
private val networkMapConfigPath = File("./hsm-for-networkmap.conf").absolutePath
private val argsParser = SigningServiceArgsParser()
@Test
fun `doorman-based config file is parsed correctly`() {
val cmdLineOptions = argsParser.parse("--config-file", doormanConfigPath)
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)
assertEquals("3001@192.168.0.1", config.device)
val doormanCertParameters = config.doorman!!
assertEquals(AuthMode.PASSWORD, doormanCertParameters.authParameters.mode)
assertEquals(2, doormanCertParameters.authParameters.threshold)
assertEquals(3650, doormanCertParameters.validDays)
}
@Test
fun `networkmap-based config file is parsed correctly`() {
val cmdLineOptions = argsParser.parse("--config-file", networkMapConfigPath)
val config = parseConfig<SigningServiceConfig>(cmdLineOptions.configFile)
assertEquals("3001@192.168.0.1", config.device)
val networkMapConfig = config.networkMap!!
assertEquals(AuthMode.KEY_FILE, networkMapConfig.authParameters.mode)
assertEquals(Paths.get("./Administrator.KEY"), networkMapConfig.authParameters.keyFilePath)
assertEquals(2, networkMapConfig.authParameters.threshold)
assertEquals("PASSWORD", networkMapConfig.authParameters.password)
assertEquals("TEST_USERNAME", networkMapConfig.username)
}
@Test
fun `should fail when config file is missing`() {
assertThatThrownBy {
argsParser.parse("--config-file", "not-existing-file")
}.hasMessageContaining("not-existing-file")
}
}

View File

@ -19,6 +19,7 @@ import net.corda.core.internal.uncheckedCast
import net.corda.core.utilities.NetworkHostAndPort
import org.slf4j.LoggerFactory
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Modifier.isStatic
import java.lang.reflect.ParameterizedType
import java.net.Proxy
@ -74,19 +75,20 @@ fun <T : Any> Config.parseAs(clazz: KClass<T>, strict: Boolean = true): T {
val path = defaultToOldPath(property)
getValueInternal<Any>(path, param.type, strict)
}
return constructor.callBy(args)
try {
return constructor.callBy(args)
} catch (e: InvocationTargetException) {
throw e.cause!!
}
}
class UnknownConfigurationKeysException private constructor(val unknownKeys: Set<String>) : IllegalArgumentException(message(unknownKeys)) {
init {
require(unknownKeys.isNotEmpty()) { "Absence of unknown keys should not raise UnknownConfigurationKeysException." }
}
companion object {
fun of(offendingKeys: Set<String>): UnknownConfigurationKeysException = UnknownConfigurationKeysException(offendingKeys)
private fun message(offendingKeys: Set<String>) = "Unknown configuration keys: ${offendingKeys.joinToString(", ", "[", "]")}."
}
}