mirror of
https://github.com/corda/corda.git
synced 2025-06-19 23:53:52 +00:00
ENT-992: Introducing the CRR submission tool (#633)
* Introducing the CRR submission tool * Addressing review comments * Addressing review comments - round 2 - Redesign of the tool. * Fixing messages
This commit is contained in:
5
docs/source/crr-submission-tool.rst
Normal file
5
docs/source/crr-submission-tool.rst
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Certificate Revocation Request Submission Tool
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
The purpose of the Certificate Revocation Request (CRR) Submission Tool is to facilitate the process of creating a CRR.
|
||||||
|
The tool is designed with the support line in mind, and assumes it is for internal (i.e. within the doorman service managing company) usage.
|
12
docs/source/running-crr-submission-tool.rst
Normal file
12
docs/source/running-crr-submission-tool.rst
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Running the Certificate Revocation Request Submission Tool
|
||||||
|
==========================================================
|
||||||
|
|
||||||
|
The purpose of this tool is to facilitate the certificate revocation request submission process.
|
||||||
|
See :doc:`crr-submission-tool` for more details.
|
||||||
|
|
||||||
|
See the Readme under ``network-management`` for detailed building instructions.
|
||||||
|
|
||||||
|
Command line argument
|
||||||
|
----------------------
|
||||||
|
At startup, the Certificate Revocation Request Submission Tool takes only one command line argument: ``--submission-url``,
|
||||||
|
that should be followed by the url to the certificate revocation request submission endpoint.
|
@ -15,9 +15,6 @@ At startup, the HSM Certificate Generation Tool reads a configuration file, pass
|
|||||||
This is an example of what a tool configuration file might look like:
|
This is an example of what a tool configuration file might look like:
|
||||||
.. literalinclude:: ../../network-management/generator.conf
|
.. literalinclude:: ../../network-management/generator.conf
|
||||||
|
|
||||||
Invoke doorman with ``-?`` for a full list of supported command-line arguments.
|
|
||||||
|
|
||||||
|
|
||||||
General configuration parameters
|
General configuration parameters
|
||||||
--------------------------------
|
--------------------------------
|
||||||
Allowed parameters are:
|
Allowed parameters are:
|
||||||
|
@ -48,6 +48,19 @@ The built file will appear in
|
|||||||
network-management/capsule-hsm-cert-generator/build/libs/hsm-cert-generator-<VERSION>.jar
|
network-management/capsule-hsm-cert-generator/build/libs/hsm-cert-generator-<VERSION>.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Certificate Revocation Request Submission Tool
|
||||||
|
|
||||||
|
To build a fat jar containing all the CRR submission tool code you can simply invoke
|
||||||
|
```
|
||||||
|
./gradlew network-management:capsule-crr-submission:buildCrrSubmissionJAR
|
||||||
|
```
|
||||||
|
|
||||||
|
The built file will appear in
|
||||||
|
```
|
||||||
|
network-management/capsule-crr-submission/build/libs/crr-submission-<VERSION>.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
In order to set the desired logging level the system properties need to be used.
|
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.
|
Appropriate system properties can be set at the execution time.
|
||||||
|
49
network-management/capsule-crr-submission/build.gradle
Normal file
49
network-management/capsule-crr-submission/build.gradle
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
|
apply plugin: 'us.kirchmeier.capsule'
|
||||||
|
|
||||||
|
description 'HSM Certificate Generator'
|
||||||
|
|
||||||
|
version project(':network-management').version
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
runtimeArtifacts.extendsFrom runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
task buildCrrSubmissionJAR(type: FatCapsule, dependsOn: 'jar') {
|
||||||
|
applicationClass 'com.r3.corda.networkmanage.tools.crr.submission.MainKt'
|
||||||
|
archiveName "crr-submission-${version}.jar"
|
||||||
|
capsuleManifest {
|
||||||
|
applicationVersion = corda_release_version
|
||||||
|
systemProperties['visualvm.display.name'] = 'CRR Submission Tool'
|
||||||
|
minJavaVersion = '1.8.0'
|
||||||
|
jvmArgs = ['-XX:+UseG1GC']
|
||||||
|
}
|
||||||
|
applicationSource = files(
|
||||||
|
project(':network-management').configurations.runtime,
|
||||||
|
project(':network-management').jar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
runtimeArtifacts buildCrrSubmissionJAR
|
||||||
|
publish buildCrrSubmissionJAR
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
classifier "ignore"
|
||||||
|
}
|
||||||
|
|
||||||
|
publish {
|
||||||
|
name 'crr-submission'
|
||||||
|
disableDefaultJar = true
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
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()
|
||||||
|
}
|
@ -10,16 +10,12 @@
|
|||||||
|
|
||||||
package com.r3.corda.networkmanage.dev
|
package com.r3.corda.networkmanage.dev
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
|
||||||
import com.r3.corda.networkmanage.hsm.generator.CommandLineOptions
|
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
import com.typesafe.config.ConfigParseOptions
|
||||||
import joptsimple.OptionParser
|
|
||||||
import net.corda.nodeapi.internal.*
|
import net.corda.nodeapi.internal.*
|
||||||
import net.corda.nodeapi.internal.config.parseAs
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds configuration necessary for generating DEV key store and trust store.
|
* Holds configuration necessary for generating DEV key store and trust store.
|
||||||
@ -35,29 +31,6 @@ data class GeneratorConfiguration(val privateKeyPass: String = DEV_CA_PRIVATE_KE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses dev generator command line options.
|
|
||||||
*/
|
|
||||||
fun parseCommandLine(vararg args: String): CommandLineOptions? {
|
|
||||||
val optionParser = OptionParser()
|
|
||||||
val configFileArg = optionParser
|
|
||||||
.accepts("config-file", "The path to the config file")
|
|
||||||
.withRequiredArg()
|
|
||||||
.describedAs("filepath")
|
|
||||||
val helpOption = optionParser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp()
|
|
||||||
|
|
||||||
val optionSet = optionParser.parse(*args)
|
|
||||||
// Print help and exit on help option.
|
|
||||||
if (optionSet.has(helpOption)) {
|
|
||||||
throw ShowHelpException(optionParser)
|
|
||||||
}
|
|
||||||
return if (optionSet.has(configFileArg)) {
|
|
||||||
CommandLineOptions(Paths.get(optionSet.valueOf(configFileArg)).toAbsolutePath())
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a configuration file, which contains all the configuration - i.e. for the key store generator.
|
* Parses a configuration file, which contains all the configuration - i.e. for the key store generator.
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +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.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
|
||||||
@ -33,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)?.configFile))
|
run(parseParameters(parseCommandLine(*args)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run(configuration: GeneratorConfiguration) {
|
fun run(configuration: GeneratorConfiguration) {
|
||||||
|
@ -10,15 +10,11 @@
|
|||||||
|
|
||||||
package com.r3.corda.networkmanage.hsm.generator
|
package com.r3.corda.networkmanage.hsm.generator
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigParseOptions
|
import com.typesafe.config.ConfigParseOptions
|
||||||
import joptsimple.OptionParser
|
|
||||||
import net.corda.core.internal.isRegularFile
|
|
||||||
import net.corda.nodeapi.internal.config.parseAs
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds configuration necessary for user's authentication against HSM.
|
* Holds configuration necessary for user's authentication against HSM.
|
||||||
@ -62,37 +58,6 @@ data class CertificateConfiguration(val keyGroup: String,
|
|||||||
val keyCurve: String, // we use "NIST-P256", check Utimaco docs for other options
|
val keyCurve: String, // we use "NIST-P256", check Utimaco docs for other options
|
||||||
val keyGenMechanism: Int) // MECH_KEYGEN_UNCOMP = 4 or MECH_RND_REAL = 0
|
val keyGenMechanism: Int) // MECH_KEYGEN_UNCOMP = 4 or MECH_RND_REAL = 0
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds arguments for command line options.
|
|
||||||
*/
|
|
||||||
data class CommandLineOptions(val configFile: Path) {
|
|
||||||
init {
|
|
||||||
check(configFile.isRegularFile()) { "Config file $configFile does not exist" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses key generator command line options.
|
|
||||||
*/
|
|
||||||
fun parseCommandLine(vararg args: String): CommandLineOptions {
|
|
||||||
val optionParser = OptionParser()
|
|
||||||
val configFileArg = optionParser
|
|
||||||
.accepts("config-file", "The path to the config file")
|
|
||||||
.withRequiredArg()
|
|
||||||
.describedAs("filepath")
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
val configFile = Paths.get(optionSet.valueOf(configFileArg)).toAbsolutePath()
|
|
||||||
|
|
||||||
return CommandLineOptions(configFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a configuration file, which contains all the configuration - i.e. for user and certificate parameters.
|
* Parses a configuration file, which contains all the configuration - i.e. for user and certificate parameters.
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +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.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
|
||||||
@ -18,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)?.configFile))
|
run(parseParameters(parseCommandLine(*args)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run(parameters: GeneratorParameters) {
|
fun run(parameters: GeneratorParameters) {
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
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))
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.r3.corda.networkmanage.tools.crr.submission
|
||||||
|
|
||||||
|
import com.r3.corda.networkmanage.common.utils.initialiseSerialization
|
||||||
|
import com.r3.corda.networkmanage.hsm.authentication.ConsoleInputReader
|
||||||
|
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.internal.post
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
|
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.net.URL
|
||||||
|
import java.security.cert.CRLReason
|
||||||
|
|
||||||
|
private val logger = LogManager.getLogger("com.r3.corda.networkmanage.common.tools.crr.Main")
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
initialiseSerialization()
|
||||||
|
try {
|
||||||
|
submit(parseSubmissionUrl(*args))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("Error when submitting a certificate revocation request.", e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submit(url: URL, inputReader: InputReader = ConsoleInputReader()) {
|
||||||
|
val certificateSerialNumber = inputReader.getOptionalInput("certificate serial number")?.let { BigInteger(it) }
|
||||||
|
val csrRequestId = inputReader.getOptionalInput("certificate signing request ID")
|
||||||
|
val legalName = inputReader.getOptionalInput("node X.500 legal name")?.let { CordaX500Name.parse(it) }
|
||||||
|
CertificateRevocationRequest.validateOptional(certificateSerialNumber, csrRequestId, legalName)
|
||||||
|
val reason = inputReader.getRequiredInput("revocation reason").let { CRLReason.valueOf(it) }
|
||||||
|
val reporter = inputReader.getRequiredInput("reporter of the revocation request")
|
||||||
|
val request = CertificateRevocationRequest(certificateSerialNumber, csrRequestId, legalName, reason, reporter)
|
||||||
|
logger.debug("POST to $url request: $request")
|
||||||
|
val requestId = String(url.post(request.serialize()))
|
||||||
|
logger.debug("Certificate revocation request successfully submitted. Request ID: $requestId")
|
||||||
|
println("Successfully submitted certificate revocation request. Generated request ID: $requestId")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InputReader.getOptionalInput(attributeName: String): String? {
|
||||||
|
print("Type in $attributeName (press enter if not available):")
|
||||||
|
return this.readLine()?.let {
|
||||||
|
if (it.isBlank()) null else it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InputReader.getRequiredInput(attributeName: String): String {
|
||||||
|
print("Type in $attributeName:")
|
||||||
|
val line = this.readLine()
|
||||||
|
return if (line == null || line.isNullOrBlank()) {
|
||||||
|
throw IllegalArgumentException("The $attributeName needs to be specified.")
|
||||||
|
} else {
|
||||||
|
line
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
package com.r3.corda.networkmanage.dev
|
package com.r3.corda.networkmanage.dev
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.hsm.generator.parseCommandLine
|
import com.r3.corda.networkmanage.common.configuration.parseCommandLine
|
||||||
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).configFile)
|
val config = parseParameters(parseCommandLine("--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)
|
||||||
|
@ -10,8 +10,10 @@
|
|||||||
|
|
||||||
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.utils.ShowHelpException
|
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||||
import com.typesafe.config.ConfigException
|
import com.typesafe.config.ConfigException
|
||||||
|
import joptsimple.OptionException
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import org.assertj.core.api.Assertions
|
import org.assertj.core.api.Assertions
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -28,23 +30,23 @@ class GeneratorParametersTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should fail when config file is missing`() {
|
fun `should fail when config file is missing`() {
|
||||||
val message = assertFailsWith<IllegalStateException> {
|
val message = assertFailsWith<OptionException> {
|
||||||
parseCommandLine("--config-file", "not-existing-file")
|
parseCommandLine("--config-file", "not-existing-file")
|
||||||
}.message
|
}.message
|
||||||
Assertions.assertThat(message).contains("Config file ")
|
Assertions.assertThat(message).contains("not-existing-file")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should throw ShowHelpException when help option is passed on the command line`() {
|
fun `should throw ShowHelpException when help option is passed on the command line`() {
|
||||||
assertFailsWith<ShowHelpException> {
|
assertFailsWith<ShowHelpException> {
|
||||||
parseCommandLine("-?")
|
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).configFile)
|
parseParameters(parseCommandLine("--config-file", invalidConfigPath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +71,6 @@ class GeneratorParametersTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseCommandLineAndGetParameters(): GeneratorParameters {
|
private fun parseCommandLineAndGetParameters(): GeneratorParameters {
|
||||||
return parseParameters(parseCommandLine(*validArgs).configFile)
|
return parseParameters(parseCommandLine(*validArgs))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* 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.tools.crr.submission
|
||||||
|
|
||||||
|
import com.nhaarman.mockito_kotlin.eq
|
||||||
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
|
import com.nhaarman.mockito_kotlin.verify
|
||||||
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
|
import com.r3.corda.networkmanage.doorman.NetworkManagementWebServer
|
||||||
|
import com.r3.corda.networkmanage.doorman.signer.CrrHandler
|
||||||
|
import com.r3.corda.networkmanage.doorman.webservice.CertificateRevocationRequestWebService
|
||||||
|
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||||
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
|
import net.corda.testing.core.freeLocalHostAndPort
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.net.URL
|
||||||
|
import java.security.cert.CRLReason
|
||||||
|
|
||||||
|
class CertificateRevocationRequestSubmissionToolTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
|
|
||||||
|
private val hostAndPort = freeLocalHostAndPort()
|
||||||
|
private lateinit var webServer: NetworkManagementWebServer
|
||||||
|
private lateinit var inputReader: InputReader
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
inputReader = mock()
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun close() {
|
||||||
|
webServer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `submit request succeeds`() {
|
||||||
|
// given
|
||||||
|
val request = CertificateRevocationRequest(
|
||||||
|
certificateSerialNumber = BigInteger.TEN,
|
||||||
|
csrRequestId = "TestCSRId",
|
||||||
|
legalName = CordaX500Name.parse("O=TestOrg, C=GB, L=London"),
|
||||||
|
reason = CRLReason.KEY_COMPROMISE,
|
||||||
|
reporter = "TestReporter"
|
||||||
|
)
|
||||||
|
|
||||||
|
givenUserConsoleSequentialInputOnReadLine(request.certificateSerialNumber.toString(),
|
||||||
|
request.csrRequestId!!,
|
||||||
|
request.legalName.toString(),
|
||||||
|
request.reason.name,
|
||||||
|
request.reporter)
|
||||||
|
|
||||||
|
val requestId = SecureHash.randomSHA256().toString()
|
||||||
|
val requestProcessor = mock<CrrHandler> {
|
||||||
|
on { saveRevocationRequest(eq(request)) }.then { requestId }
|
||||||
|
}
|
||||||
|
startSigningServer(requestProcessor)
|
||||||
|
|
||||||
|
// when
|
||||||
|
submit(URL("http://$hostAndPort/certificate-revocation-request"), inputReader)
|
||||||
|
|
||||||
|
// then
|
||||||
|
verify(requestProcessor).saveRevocationRequest(eq(request))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenUserConsoleSequentialInputOnReadLine(vararg inputs: String) {
|
||||||
|
var sequence = whenever(inputReader.readLine()).thenReturn(inputs.first())
|
||||||
|
inputs.drop(1).forEach {
|
||||||
|
sequence = sequence.thenReturn(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startSigningServer(handler: CrrHandler) {
|
||||||
|
webServer = NetworkManagementWebServer(hostAndPort, CertificateRevocationRequestWebService(handler))
|
||||||
|
webServer.start()
|
||||||
|
}
|
||||||
|
}
|
@ -15,9 +15,15 @@ data class CertificateRevocationRequest(val certificateSerialNumber: BigInteger?
|
|||||||
val legalName: CordaX500Name? = null,
|
val legalName: CordaX500Name? = null,
|
||||||
val reason: CRLReason,
|
val reason: CRLReason,
|
||||||
val reporter: String) {
|
val reporter: String) {
|
||||||
init {
|
companion object {
|
||||||
|
fun validateOptional(certificateSerialNumber: BigInteger?, csrRequestId: String?, legalName: CordaX500Name?) {
|
||||||
require(certificateSerialNumber != null || csrRequestId != null || legalName != null) {
|
require(certificateSerialNumber != null || csrRequestId != null || legalName != null) {
|
||||||
"At least one of the following needs to be specified: certificateSerialNumber, csrRequestId, legalName."
|
"At least one of the following needs to be specified: certificateSerialNumber, csrRequestId, legalName."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
validateOptional(certificateSerialNumber, csrRequestId, legalName)
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,7 @@ include 'network-management'
|
|||||||
include 'network-management:capsule'
|
include 'network-management:capsule'
|
||||||
include 'network-management:capsule-hsm'
|
include 'network-management:capsule-hsm'
|
||||||
include 'network-management:capsule-hsm-cert-generator'
|
include 'network-management:capsule-hsm-cert-generator'
|
||||||
|
include 'network-management:capsule-crr-submission'
|
||||||
include 'network-management:registration-tool'
|
include 'network-management:registration-tool'
|
||||||
include 'tools:jmeter'
|
include 'tools:jmeter'
|
||||||
include 'tools:explorer'
|
include 'tools:explorer'
|
||||||
|
Reference in New Issue
Block a user