diff --git a/docs/source/crr-submission-tool.rst b/docs/source/crr-submission-tool.rst new file mode 100644 index 0000000000..51c91d6685 --- /dev/null +++ b/docs/source/crr-submission-tool.rst @@ -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. \ No newline at end of file diff --git a/docs/source/running-crr-submission-tool.rst b/docs/source/running-crr-submission-tool.rst new file mode 100644 index 0000000000..6fea7d6ca7 --- /dev/null +++ b/docs/source/running-crr-submission-tool.rst @@ -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. \ No newline at end of file diff --git a/docs/source/running-hsm-cert-generator.rst b/docs/source/running-hsm-cert-generator.rst index 8b84b1488a..f5fd9f8bef 100644 --- a/docs/source/running-hsm-cert-generator.rst +++ b/docs/source/running-hsm-cert-generator.rst @@ -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: .. literalinclude:: ../../network-management/generator.conf -Invoke doorman with ``-?`` for a full list of supported command-line arguments. - - General configuration parameters -------------------------------- Allowed parameters are: diff --git a/network-management/README.md b/network-management/README.md index b2c9be981e..383546d2f5 100644 --- a/network-management/README.md +++ b/network-management/README.md @@ -48,6 +48,19 @@ The built file will appear in network-management/capsule-hsm-cert-generator/build/libs/hsm-cert-generator-.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-.jar +``` + + # Logs 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. diff --git a/network-management/capsule-crr-submission/build.gradle b/network-management/capsule-crr-submission/build.gradle new file mode 100644 index 0000000000..9b37a407d4 --- /dev/null +++ b/network-management/capsule-crr-submission/build.gradle @@ -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 +} \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/configuration/Configuration.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/configuration/Configuration.kt new file mode 100644 index 0000000000..9167701826 --- /dev/null +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/configuration/Configuration.kt @@ -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() +} \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/dev/Configuration.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/dev/Configuration.kt index 9138ef9876..cd92e249c1 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/dev/Configuration.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/dev/Configuration.kt @@ -10,16 +10,12 @@ 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.ConfigParseOptions -import joptsimple.OptionParser import net.corda.nodeapi.internal.* import net.corda.nodeapi.internal.config.parseAs import java.io.File import java.nio.file.Path -import java.nio.file.Paths /** * 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. */ diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/dev/Main.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/dev/Main.kt index f6ba95f6ad..1de3ec8868 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/dev/Main.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/dev/Main.kt @@ -10,6 +10,7 @@ package com.r3.corda.networkmanage.dev +import com.r3.corda.networkmanage.common.configuration.parseCommandLine import com.r3.corda.networkmanage.doorman.CORDA_X500_BASE import net.corda.core.crypto.Crypto 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. */ fun main(args: Array) { - run(parseParameters(parseCommandLine(*args)?.configFile)) + run(parseParameters(parseCommandLine(*args))) } fun run(configuration: GeneratorConfiguration) { diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/generator/GeneratorParameters.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/generator/GeneratorParameters.kt index aab045f2ba..439ce17cdc 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/generator/GeneratorParameters.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/generator/GeneratorParameters.kt @@ -10,15 +10,11 @@ package com.r3.corda.networkmanage.hsm.generator -import com.r3.corda.networkmanage.common.utils.ShowHelpException import com.typesafe.config.ConfigFactory 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.crypto.CertificateType import java.nio.file.Path -import java.nio.file.Paths /** * 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 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. */ diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/generator/Main.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/generator/Main.kt index 7e38b5778f..f8153ebe0b 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/generator/Main.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/generator/Main.kt @@ -10,6 +10,7 @@ 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.utils.mapCryptoServerException 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") fun main(args: Array) { - run(parseParameters(parseCommandLine(*args)?.configFile)) + run(parseParameters(parseCommandLine(*args))) } fun run(parameters: GeneratorParameters) { diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/tools/crr/submission/Configuration.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/tools/crr/submission/Configuration.kt new file mode 100644 index 0000000000..09ab159185 --- /dev/null +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/tools/crr/submission/Configuration.kt @@ -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)) +} \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/tools/crr/submission/Main.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/tools/crr/submission/Main.kt new file mode 100644 index 0000000000..8691737f55 --- /dev/null +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/tools/crr/submission/Main.kt @@ -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) { + 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 + } +} \ No newline at end of file diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/dev/GeneratorConfigurationTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/dev/GeneratorConfigurationTest.kt index 1cb61b5cb6..beebaa367c 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/dev/GeneratorConfigurationTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/dev/GeneratorConfigurationTest.kt @@ -10,7 +10,7 @@ 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_PASS import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_FILE @@ -31,7 +31,7 @@ class GeneratorConfigurationTest { @Test 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(DEV_CA_KEY_STORE_FILE, config.keyStoreFileName) assertEquals(DEV_CA_KEY_STORE_PASS, config.keyStorePass) diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/hsm/generator/GeneratorParametersTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/hsm/generator/GeneratorParametersTest.kt index de47be0a70..1bf28557ca 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/hsm/generator/GeneratorParametersTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/hsm/generator/GeneratorParametersTest.kt @@ -10,8 +10,10 @@ 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.typesafe.config.ConfigException +import joptsimple.OptionException import net.corda.nodeapi.internal.crypto.CertificateType import org.assertj.core.api.Assertions import org.junit.Test @@ -28,23 +30,23 @@ class GeneratorParametersTest { @Test fun `should fail when config file is missing`() { - val message = assertFailsWith { + val message = assertFailsWith { parseCommandLine("--config-file", "not-existing-file") }.message - Assertions.assertThat(message).contains("Config file ") + Assertions.assertThat(message).contains("not-existing-file") } @Test fun `should throw ShowHelpException when help option is passed on the command line`() { assertFailsWith { - parseCommandLine("-?") + parseCommandLine("-h") } } @Test fun `should fail when config is invalid`() { assertFailsWith { - parseParameters(parseCommandLine("--config-file", invalidConfigPath).configFile) + parseParameters(parseCommandLine("--config-file", invalidConfigPath)) } } @@ -69,6 +71,6 @@ class GeneratorParametersTest { } private fun parseCommandLineAndGetParameters(): GeneratorParameters { - return parseParameters(parseCommandLine(*validArgs).configFile) + return parseParameters(parseCommandLine(*validArgs)) } } \ No newline at end of file diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/tools/crr/submission/CertificateRevocationRequestSubmissionToolTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/tools/crr/submission/CertificateRevocationRequestSubmissionToolTest.kt new file mode 100644 index 0000000000..f3eb5301f6 --- /dev/null +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/tools/crr/submission/CertificateRevocationRequestSubmissionToolTest.kt @@ -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 { + 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() + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/CertificateRevocationRequest.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/CertificateRevocationRequest.kt index 2001bd6a62..2b1361b107 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/CertificateRevocationRequest.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/CertificateRevocationRequest.kt @@ -15,9 +15,15 @@ data class CertificateRevocationRequest(val certificateSerialNumber: BigInteger? val legalName: CordaX500Name? = null, val reason: CRLReason, val reporter: String) { - init { - require(certificateSerialNumber != null || csrRequestId != null || legalName != null) { - "At least one of the following needs to be specified: certificateSerialNumber, csrRequestId, legalName." + companion object { + fun validateOptional(certificateSerialNumber: BigInteger?, csrRequestId: String?, legalName: CordaX500Name?) { + require(certificateSerialNumber != null || csrRequestId != null || legalName != null) { + "At least one of the following needs to be specified: certificateSerialNumber, csrRequestId, legalName." + } } } + + init { + validateOptional(certificateSerialNumber, csrRequestId, legalName) + } } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index a58d0b41a4..93c5f6b9ca 100644 --- a/settings.gradle +++ b/settings.gradle @@ -47,6 +47,7 @@ include 'network-management' include 'network-management:capsule' include 'network-management:capsule-hsm' include 'network-management:capsule-hsm-cert-generator' +include 'network-management:capsule-crr-submission' include 'network-management:registration-tool' include 'tools:jmeter' include 'tools:explorer'