From b64e7f51f6ac53b3939cb65ef45623769bb9e8a9 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 27 Apr 2017 18:39:46 +0100 Subject: [PATCH] Enforce X.500 distinguished names in configuration --- .../corda/client/rpc/AbstractClientRPCTest.kt | 5 +- .../net/corda/core/crypto/X509Utilities.kt | 2 + .../corda/nodeapi/config/ConfigUtilities.kt | 2 + .../net/corda/node/driver/DriverTests.kt | 3 +- .../services/messaging/P2PMessagingTest.kt | 27 ++++++---- .../services/messaging/P2PSecurityTest.kt | 14 +++-- .../net/corda/node/internal/AbstractNode.kt | 2 +- .../node/services/config/ConfigUtilities.kt | 3 +- .../node/services/config/NodeConfiguration.kt | 5 +- .../messaging/ArtemisMessagingServer.kt | 19 +++---- .../services/messaging/NodeMessagingClient.kt | 2 +- .../node/services/messaging/RPCDispatcher.kt | 11 ++-- .../registration/NetworkRegistrationHelper.kt | 2 +- .../net/corda/node/InteractiveShellTest.kt | 2 +- .../config/FullNodeConfigurationTest.kt | 3 +- .../messaging/ArtemisMessagingTests.kt | 3 +- .../NetworkisRegistrationHelperTest.kt | 2 +- .../kotlin/net/corda/simulation/Simulation.kt | 15 ++---- .../net/corda/netmap/VisualiserViewModel.kt | 4 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 12 +++-- .../corda/testing/messaging/SimpleMQClient.kt | 6 ++- .../testing/node/InMemoryMessagingNetwork.kt | 10 ++-- .../kotlin/net/corda/testing/node/MockNode.kt | 5 +- .../net/corda/testing/node/MockServices.kt | 2 +- .../net/corda/testing/node/SimpleNode.kt | 6 ++- .../corda/demobench/model/InstallFactory.kt | 7 +-- .../corda/demobench/model/NetworkMapConfig.kt | 7 ++- .../net/corda/demobench/model/NodeConfig.kt | 13 ++--- .../corda/demobench/model/NodeController.kt | 4 +- .../kotlin/net/corda/demobench/pty/R3Pty.kt | 6 ++- .../net/corda/demobench/views/NodeTabView.kt | 4 +- .../corda/demobench/views/NodeTerminalView.kt | 2 +- .../demobench/model/NetworkMapConfigTest.kt | 3 +- .../corda/demobench/model/NodeConfigTest.kt | 53 ++++++++----------- .../demobench/model/NodeControllerTest.kt | 30 ++++++----- 35 files changed, 163 insertions(+), 133 deletions(-) diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractClientRPCTest.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractClientRPCTest.kt index ae3420686e..3c61e86dd4 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractClientRPCTest.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractClientRPCTest.kt @@ -20,6 +20,7 @@ import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl import org.apache.activemq.artemis.core.remoting.impl.invm.InVMAcceptorFactory import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ +import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Before import java.util.* @@ -74,7 +75,7 @@ abstract class AbstractClientRPCTest { override val users: List get() = listOf(rpcUser) } - val dispatcher = object : RPCDispatcher(rpcImpl, userService, ALICE.name) { + val dispatcher = object : RPCDispatcher(rpcImpl, userService, X500Name(ALICE.name)) { override fun send(data: SerializedBytes<*>, toAddress: String) { val msg = serverSession.createMessage(false).apply { writeBodyBufferBytes(data.bytes) @@ -99,4 +100,4 @@ abstract class AbstractClientRPCTest { } catch (e: Exception) { } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt b/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt index 778428bb76..7cd04628ff 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt @@ -110,6 +110,8 @@ object X509Utilities { * Return a bogus X509 for dev purposes. Use [getX509Name] for something more real. */ fun getDevX509Name(commonName: String): X500Name { + // Check that we haven't been accidentally given a full X500 distinguished name + require(!commonName.startsWith("CN=")) { "Provided common name must not start \"CN=\"" } val nameBuilder = X500NameBuilder(BCStyle.INSTANCE) nameBuilder.addRDN(BCStyle.CN, commonName) nameBuilder.addRDN(BCStyle.O, "R3") diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt index 5db04682a4..0d12757186 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt @@ -4,6 +4,7 @@ import com.google.common.net.HostAndPort import com.typesafe.config.Config import com.typesafe.config.ConfigUtil import net.corda.core.noneOrSingle +import org.bouncycastle.asn1.x500.X500Name import org.slf4j.LoggerFactory import java.net.Proxy import java.net.URL @@ -70,6 +71,7 @@ private fun Config.getSingleValue(path: String, type: KType): Any? { Path::class -> Paths.get(getString(path)) URL::class -> URL(getString(path)) Properties::class -> getConfig(path).toProperties() + X500Name::class -> X500Name(getString(path)) else -> if (typeClass.java.isEnum) { parseEnum(typeClass.java, getString(path)) } else { diff --git a/node/src/integration-test/kotlin/net/corda/node/driver/DriverTests.kt b/node/src/integration-test/kotlin/net/corda/node/driver/DriverTests.kt index 9d4fa62463..cd39098c83 100644 --- a/node/src/integration-test/kotlin/net/corda/node/driver/DriverTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/driver/DriverTests.kt @@ -8,6 +8,7 @@ import net.corda.core.node.services.ServiceInfo import net.corda.core.readLines import net.corda.core.utilities.DUMMY_BANK_A import net.corda.core.utilities.DUMMY_NOTARY +import net.corda.core.utilities.DUMMY_REGULATOR import net.corda.node.LOGS_DIRECTORY_NAME import net.corda.node.services.api.RegulatorService import net.corda.node.services.transactions.SimpleNotaryService @@ -42,7 +43,7 @@ class DriverTests { fun `simple node startup and shutdown`() { val handles = driver { val notary = startNode(DUMMY_NOTARY.name, setOf(ServiceInfo(SimpleNotaryService.type))) - val regulator = startNode("CN=Regulator,O=R3,OU=corda,L=London,C=UK", setOf(ServiceInfo(RegulatorService.type))) + val regulator = startNode(DUMMY_REGULATOR.name, setOf(ServiceInfo(RegulatorService.type))) listOf(nodeMustBeUp(notary), nodeMustBeUp(regulator)) } handles.map { nodeMustBeDown(it) } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 221b334f84..56096c4f5f 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -3,6 +3,7 @@ package net.corda.services.messaging import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import net.corda.core.* +import net.corda.core.crypto.X509Utilities import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.services.DEFAULT_SESSION_ID @@ -19,6 +20,7 @@ import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.freeLocalHostAndPort import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat +import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.util.* import java.util.concurrent.CountDownLatch @@ -27,6 +29,11 @@ import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger class P2PMessagingTest : NodeBasedTest() { + private companion object { + val DISTRIBUTED_SERVICE_NAME = X509Utilities.getDevX509Name("DistributedService") + val SERVICE_2_NAME = X509Utilities.getDevX509Name("Service Node 2") + } + @Test fun `network map will work after restart`() { val identities = listOf(DUMMY_BANK_A, DUMMY_BANK_B, DUMMY_NOTARY) @@ -55,15 +62,14 @@ class P2PMessagingTest : NodeBasedTest() { // TODO Use a dummy distributed service @Test fun `communicating with a distributed service which the network map node is part of`() { - val serviceName = "DistributedService" val root = tempFolder.root.toPath() ServiceIdentityGenerator.generateToDisk( - listOf(root / DUMMY_MAP.name, root / "Service Node 2"), + listOf(root / DUMMY_MAP.name.toString(), root / SERVICE_2_NAME.toString()), RaftValidatingNotaryService.type.id, - serviceName) + DISTRIBUTED_SERVICE_NAME) - val distributedService = ServiceInfo(RaftValidatingNotaryService.type, serviceName) + val distributedService = ServiceInfo(RaftValidatingNotaryService.type, DISTRIBUTED_SERVICE_NAME) val notaryClusterAddress = freeLocalHostAndPort() startNetworkMapNode( DUMMY_MAP.name, @@ -71,7 +77,7 @@ class P2PMessagingTest : NodeBasedTest() { configOverrides = mapOf("notaryNodeAddress" to notaryClusterAddress.toString())) val (serviceNode2, alice) = Futures.allAsList( startNode( - "Service Node 2", + SERVICE_2_NAME, advertisedServices = setOf(distributedService), configOverrides = mapOf( "notaryNodeAddress" to freeLocalHostAndPort().toString(), @@ -79,14 +85,13 @@ class P2PMessagingTest : NodeBasedTest() { startNode(ALICE.name) ).getOrThrow() - assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), serviceName, alice) + assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), DISTRIBUTED_SERVICE_NAME, alice) } @Test fun `communicating with a distributed service which we're part of`() { - val serviceName = "Distributed Service" - val distributedService = startNotaryCluster(serviceName, 2).getOrThrow() - assertAllNodesAreUsed(distributedService, serviceName, distributedService[0]) + val distributedService = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow() + assertAllNodesAreUsed(distributedService, DISTRIBUTED_SERVICE_NAME, distributedService[0]) } @Test @@ -183,13 +188,13 @@ class P2PMessagingTest : NodeBasedTest() { return Pair(firstRequestReceived, requestsReceived) } - private fun assertAllNodesAreUsed(participatingServiceNodes: List, serviceName: String, originatingNode: Node) { + private fun assertAllNodesAreUsed(participatingServiceNodes: List, serviceName: X500Name, originatingNode: Node) { // Setup each node in the distributed service to return back it's NodeInfo so that we can know which node is being used participatingServiceNodes.forEach { node -> node.respondWith(node.info) } val serviceAddress = originatingNode.services.networkMapCache.run { - originatingNode.net.getAddressOfParty(getPartyInfo(getNotary(serviceName)!!)!!) + originatingNode.net.getAddressOfParty(getPartyInfo(getNotary(serviceName.toString())!!)!!) } val participatingNodes = HashSet() // Try several times so that we can be fairly sure that any node not participating is not due to Artemis' selection diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index df615e7165..6235dfd508 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -2,12 +2,15 @@ package net.corda.services.messaging import com.google.common.util.concurrent.ListenableFuture import net.corda.core.crypto.Party +import net.corda.core.crypto.commonName import net.corda.core.div import net.corda.core.getOrThrow import net.corda.core.node.NodeInfo import net.corda.core.random63BitValue import net.corda.core.seconds import net.corda.core.utilities.BOB +import net.corda.core.utilities.DUMMY_BANK_A +import net.corda.core.utilities.DUMMY_BANK_B import net.corda.node.internal.NetworkMapInfo import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.messaging.sendRequest @@ -21,6 +24,7 @@ import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.SimpleNode import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatThrownBy +import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.time.Instant import java.util.concurrent.TimeoutException @@ -42,9 +46,9 @@ class P2PSecurityTest : NodeBasedTest() { @Test fun `register with the network map service using a legal name different from the TLS CN`() { - startSimpleNode("CN=Attacker,O=R3,OU=corda,L=London,C=UK").use { + startSimpleNode(X500Name(DUMMY_BANK_A.name)).use { // Register with the network map using a different legal name - val response = it.registerWithNetworkMap("CN=Legit Business,O=R3,OU=corda,L=London,C=UK") + val response = it.registerWithNetworkMap(X500Name(DUMMY_BANK_B.name)) // We don't expect a response because the network map's host verification will prevent a connection back // to the attacker as the TLS CN will not match the legal name it has just provided assertThatExceptionOfType(TimeoutException::class.java).isThrownBy { @@ -53,16 +57,16 @@ class P2PSecurityTest : NodeBasedTest() { } } - private fun startSimpleNode(legalName: String): SimpleNode { + private fun startSimpleNode(legalName: X500Name): SimpleNode { val config = TestNodeConfiguration( - baseDirectory = tempFolder.root.toPath() / legalName, + baseDirectory = tempFolder.root.toPath() / legalName.commonName, myLegalName = legalName, networkMapService = NetworkMapInfo(networkMapNode.configuration.p2pAddress, networkMapNode.info.legalIdentity.name)) config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name return SimpleNode(config).apply { start() } } - private fun SimpleNode.registerWithNetworkMap(registrationName: String): ListenableFuture { + private fun SimpleNode.registerWithNetworkMap(registrationName: X500Name): ListenableFuture { val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public), MOCK_VERSION_INFO.platformVersion) val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX) val request = RegistrationRequest(registration.toWire(identity.private), net.myAddress) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index bde1cbe50d..2b4f65119d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -566,7 +566,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, // the legal name is actually validated in some way. val privKeyFile = dir / privateKeyFileName val pubIdentityFile = dir / publicKeyFileName - val identityName = serviceName ?: configuration.myLegalName + val identityName = serviceName ?: configuration.myLegalName.toString() val identityAndKey = if (!privKeyFile.exists()) { log.info("Identity key not found, generating fresh key!") diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index 17616796fa..8f7616656b 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -14,6 +14,7 @@ import net.corda.core.div import net.corda.core.exists import net.corda.core.utilities.loggerFor import net.corda.nodeapi.config.SSLConfiguration +import org.bouncycastle.asn1.x500.X500Name import java.nio.file.Path object ConfigHelper { @@ -45,7 +46,7 @@ object ConfigHelper { */ fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName) -fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: String) { +fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: X500Name) { certificatesDirectory.createDirectories() if (!trustStoreFile.exists()) { javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStoreFile) diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index d5156309e0..f0db112828 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -13,6 +13,7 @@ import net.corda.node.utilities.TestClock import net.corda.nodeapi.User import net.corda.nodeapi.config.OldConfig import net.corda.nodeapi.config.SSLConfiguration +import org.bouncycastle.asn1.x500.X500Name import java.net.URL import java.nio.file.Path import java.util.* @@ -20,7 +21,7 @@ import java.util.* interface NodeConfiguration : SSLConfiguration { val baseDirectory: Path override val certificatesDirectory: Path get() = baseDirectory / "certificates" - val myLegalName: String + val myLegalName: X500Name val networkMapService: NetworkMapInfo? val minimumPlatformVersion: Int val nearestCity: String @@ -41,7 +42,7 @@ data class FullNodeConfiguration( "This is a subsitution value which points to the baseDirectory and is manually added into the config before parsing", ReplaceWith("baseDirectory")) val basedir: Path, - override val myLegalName: String, + override val myLegalName: X500Name, override val nearestCity: String, override val emailAddress: String, override val keyStorePassword: String, diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index cb74e856f1..74c7d7f8a6 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -238,7 +238,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, .loadCertificateFromKeyStore(config.keyStoreFile, config.keyStorePassword, CORDA_CLIENT_CA) val ourSubjectDN = X500Name(ourCertificate.subjectDN.name) // This is a sanity check and should not fail unless things have been misconfigured - require(ourSubjectDN.commonName == config.myLegalName) { + require(ourSubjectDN.commonName == config.myLegalName.commonName) { "Legal name does not match with our subject CN: $ourSubjectDN" } val defaultCertPolicies = mapOf( @@ -398,18 +398,18 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, private fun getBridgeName(queueName: String, hostAndPort: HostAndPort): String = "$queueName -> $hostAndPort" // This is called on one of Artemis' background threads - internal fun hostVerificationFail(peerLegalName: String, expectedCommonName: String) { - log.error("Peer has wrong CN - expected $expectedCommonName but got $peerLegalName. This is either a fatal " + + internal fun hostVerificationFail(peerLegalName: X500Name, expectedLegalName: X500Name) { + log.error("Peer has wrong CN - expected $expectedLegalName but got $peerLegalName. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!") - if (expectedCommonName == config.networkMapService?.legalName) { + if (expectedLegalName.toString() == config.networkMapService?.legalName) { // If the peer that failed host verification was the network map node then we're in big trouble and need to bail! _networkMapConnectionFuture!!.setException(IOException("${config.networkMapService} failed host verification check")) } } // This is called on one of Artemis' background threads - internal fun onTcpConnection(peerLegalName: String) { - if (peerLegalName == config.networkMapService?.legalName) { + internal fun onTcpConnection(peerLegalName: X500Name) { + if (peerLegalName.toString() == config.networkMapService?.legalName) { _networkMapConnectionFuture!!.set(Unit) } } @@ -437,7 +437,9 @@ private class VerifyingNettyConnector(configuration: MutableMap?, protocolManager: ClientProtocolManager?) : NettyConnector(configuration, handler, listener, closeExecutor, threadPool, scheduledThreadPool, protocolManager) { private val server = configuration?.get(ArtemisMessagingServer::class.java.name) as? ArtemisMessagingServer - private val expectedCommonName = configuration?.get(ArtemisTcpTransport.VERIFY_PEER_COMMON_NAME) as? String + private val expectedCommonName = (configuration?.get(ArtemisTcpTransport.VERIFY_PEER_COMMON_NAME) as? String)?.let { + X500Name(it) + } override fun createConnection(): Connection? { val connection = super.createConnection() as NettyConnection? @@ -451,9 +453,8 @@ private class VerifyingNettyConnector(configuration: MutableMap?, .peerPrincipal .name .let(::X500Name) - .commonName // TODO Verify on the entire principle (subject) - if (peerLegalName != expectedCommonName) { + if (peerLegalName.commonName != expectedCommonName.commonName) { connection.close() server!!.hostVerificationFail(peerLegalName, expectedCommonName) return null // Artemis will keep trying to reconnect until it's told otherwise diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt index f2db4f1a40..ef4ca7668d 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt @@ -547,7 +547,7 @@ class NodeMessagingClient(override val config: NodeConfiguration, } } - private fun createRPCDispatcher(ops: RPCOps, userService: RPCUserService, nodeLegalName: String) = + private fun createRPCDispatcher(ops: RPCOps, userService: RPCUserService, nodeLegalName: X500Name): RPCDispatcher = object : RPCDispatcher(ops, userService, nodeLegalName) { override fun send(data: SerializedBytes<*>, toAddress: String) { messagingExecutor.fetchFrom { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCDispatcher.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCDispatcher.kt index 7acd066da6..4e8e7de62a 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCDispatcher.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCDispatcher.kt @@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicInteger * are handled, this is probably the wrong system. */ // TODO remove the nodeLegalName parameter once the webserver doesn't need special privileges -abstract class RPCDispatcher(val ops: RPCOps, val userService: RPCUserService, val nodeLegalName: String) { +abstract class RPCDispatcher(val ops: RPCOps, val userService: RPCUserService, val nodeLegalName: X500Name) { // Throw an exception if there are overloaded methods private val methodTable = ops.javaClass.declaredMethods.groupBy { it.name }.mapValues { it.value.single() } @@ -184,9 +184,14 @@ abstract class RPCDispatcher(val ops: RPCOps, val userService: RPCUserService, v val rpcUser = userService.getUser(validatedUser) if (rpcUser != null) { return rpcUser - } else if (X500Name(validatedUser).commonName == nodeLegalName) { - return nodeUser } else { + try { + if (X500Name(validatedUser) == nodeLegalName) { + return nodeUser + } + } catch (ex: IllegalArgumentException) { + // Just means the two can't be compared, treat as no match + } throw IllegalArgumentException("Validated user '$validatedUser' is not an RPC user nor the NODE user") } } diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 7a466ea6ce..17af4b6667 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -100,7 +100,7 @@ class NetworkRegistrationHelper(val config: NodeConfiguration, val certService: private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String { // Retrieve request id from file if exists, else post a request to server. return if (!requestIdStore.exists()) { - val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.nearestCity, config.emailAddress, keyPair) + val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, keyPair) val writer = StringWriter() JcaPEMWriter(writer).use { it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded)) diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt index 67c22343ac..39a12f7cc9 100644 --- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt +++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt @@ -34,7 +34,7 @@ class InteractiveShellTest { } private val someCorpLegalName = MEGA_CORP.name - private val ids = InMemoryIdentityService().apply { registerIdentity(Party(MEGA_CORP.name, DUMMY_PUBKEY_1)) } + private val ids = InMemoryIdentityService().apply { registerIdentity(Party(someCorpLegalName, DUMMY_PUBKEY_1)) } private val om = JacksonSupport.createInMemoryMapper(ids, YAMLFactory()) private fun check(input: String, expected: String) { diff --git a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt index fca4598ebf..7600ebe97c 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt @@ -4,6 +4,7 @@ import net.corda.core.utilities.ALICE import net.corda.nodeapi.User import net.corda.testing.testConfiguration import org.assertj.core.api.Assertions.assertThatThrownBy +import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.nio.file.Paths @@ -11,7 +12,7 @@ class FullNodeConfigurationTest { @Test fun `Artemis special characters not permitted in RPC usernames`() { fun configWithRPCUsername(username: String): FullNodeConfiguration { - return testConfiguration(Paths.get("."), ALICE.name, 0).copy( + return testConfiguration(Paths.get("."), X500Name(ALICE.name), 0).copy( rpcUsers = listOf(User(username, "pass", emptySet()))) } diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index 8365e53de6..1c3e90a59f 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -27,6 +27,7 @@ import net.corda.testing.freeLocalHostAndPort import net.corda.testing.node.makeTestDataSourceProperties import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy +import org.bouncycastle.asn1.x500.X500Name import org.jetbrains.exposed.sql.Database import org.junit.After import org.junit.Before @@ -71,7 +72,7 @@ class ArtemisMessagingTests { userService = RPCUserServiceImpl(emptyList()) config = TestNodeConfiguration( baseDirectory = baseDirectory, - myLegalName = ALICE.name, + myLegalName = X500Name(ALICE.name), networkMapService = null) LogHelper.setLevel(PersistentUniquenessProvider::class) val dataSourceAndDatabase = configureDatabase(makeTestDataSourceProperties()) diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt index 8ea2144518..506f03b22e 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt @@ -39,7 +39,7 @@ class NetworkRegistrationHelperTest { val config = TestNodeConfiguration( baseDirectory = tempFolder.root.toPath(), - myLegalName = ALICE.name, + myLegalName = X500Name(ALICE.name), networkMapService = null) assertFalse(config.keyStoreFile.exists()) diff --git a/samples/irs-demo/src/main/kotlin/net/corda/simulation/Simulation.kt b/samples/irs-demo/src/main/kotlin/net/corda/simulation/Simulation.kt index 333d5d8bfc..2977af1b55 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/simulation/Simulation.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/simulation/Simulation.kt @@ -72,8 +72,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val cfg = TestNodeConfiguration( baseDirectory = config.baseDirectory, - myLegalName = "CN=Bank $letter,O=Bank $letter,L=$city,C=$country", - nearestCity = city, + myLegalName = X500Name("CN=Bank $letter,O=Bank $letter,L=$city,C=$country"), networkMapService = null) return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) } @@ -95,8 +94,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, require(advertisedServices.containsType(NetworkMapService.type)) val cfg = TestNodeConfiguration( baseDirectory = config.baseDirectory, - myLegalName = DUMMY_MAP.name, - nearestCity = X500Name(DUMMY_MAP.name).location, + myLegalName = X500Name(DUMMY_MAP.name), networkMapService = null) return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {} } @@ -109,8 +107,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, require(advertisedServices.containsType(SimpleNotaryService.type)) val cfg = TestNodeConfiguration( baseDirectory = config.baseDirectory, - myLegalName = DUMMY_NOTARY.name, - nearestCity = X500Name(DUMMY_NOTARY.name).location, + myLegalName = X500Name(DUMMY_NOTARY.name), networkMapService = null) return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) } @@ -118,7 +115,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, object RatesOracleFactory : MockNetwork.Factory { // TODO: Make a more realistic legal name - val RATES_SERVICE_NAME = "CN=Rates Service Provider,O=R3,OU=corda,L=Madrid,C=ES" + val RATES_SERVICE_NAME = X500Name("CN=Rates Service Provider,O=R3,OU=corda,L=Madrid,C=ES") override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, advertisedServices: Set, id: Int, overrideServices: Map?, @@ -127,7 +124,6 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val cfg = TestNodeConfiguration( baseDirectory = config.baseDirectory, myLegalName = RATES_SERVICE_NAME, - nearestCity = X500Name(RATES_SERVICE_NAME).location, networkMapService = null) return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { override fun start(): MockNetwork.MockNode { @@ -149,8 +145,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, entropyRoot: BigInteger): MockNetwork.MockNode { val cfg = TestNodeConfiguration( baseDirectory = config.baseDirectory, - myLegalName = DUMMY_REGULATOR.name, - nearestCity = X500Name(DUMMY_REGULATOR.name).location, + myLegalName = X500Name(DUMMY_REGULATOR.name), networkMapService = null) return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { // TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request. diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt index 920a44c22a..a87c1a1cf5 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt @@ -127,7 +127,7 @@ class VisualiserViewModel { } } - fun makeNodeWidget(forNode: MockNetwork.MockNode, type: String, label: String = "Bank of Bologna", + fun makeNodeWidget(forNode: MockNetwork.MockNode, type: String, label: X500Name = X500Name("CN=Bank of Bologna,OU=Corda QA Department,O=R3 CEV,L=Bologna,C=IT"), nodeType: NetworkMapVisualiser.NodeType, index: Int): NodeWidget { fun emitRadarPulse(initialRadius: Double, targetRadius: Double, duration: Double): Pair { val pulse = Circle(initialRadius).apply { @@ -157,7 +157,7 @@ class VisualiserViewModel { view.root.children += longPulseOuterDot view.root.children += innerDot - val nameLabel = Label(label) + val nameLabel = Label(label.toString()) val nameLabelRect = StackPane(nameLabel).apply { styleClass += "node-label" alignment = Pos.CENTER_RIGHT diff --git a/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 6df145eac2..352fdd6909 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -28,6 +28,7 @@ import net.corda.testing.node.MockIdentityService import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.style.BCStyle import java.net.ServerSocket import java.net.URL import java.nio.file.Files @@ -156,23 +157,24 @@ inline fun > AbstractNode.initiateSingleShotFlow( // TODO Replace this with testConfiguration data class TestNodeConfiguration( override val baseDirectory: Path, - override val myLegalName: String, + override val myLegalName: X500Name, override val networkMapService: NetworkMapInfo?, override val minimumPlatformVersion: Int = 1, override val keyStorePassword: String = "cordacadevpass", override val trustStorePassword: String = "trustpass", override val rpcUsers: List = emptyList(), override val dataSourceProperties: Properties = makeTestDataSourceProperties(myLegalName), - override val nearestCity: String = "Null Island", override val emailAddress: String = "", override val exportJMXto: String = "", override val devMode: Boolean = true, override val certificateSigningService: URL = URL("http://localhost"), override val certificateChainCheckPolicies: List = emptyList(), override val verifierType: VerifierType = VerifierType.InMemory, - override val messageRedeliveryDelaySeconds: Int = 5) : NodeConfiguration + override val messageRedeliveryDelaySeconds: Int = 5) : NodeConfiguration { + override val nearestCity = myLegalName.getRDNs(BCStyle.L).single().typesAndValues.single().value.toString() +} -fun testConfiguration(baseDirectory: Path, legalName: String, basePort: Int): FullNodeConfiguration { +fun testConfiguration(baseDirectory: Path, legalName: X500Name, basePort: Int): FullNodeConfiguration { return FullNodeConfiguration( basedir = baseDirectory, myLegalName = legalName, @@ -198,7 +200,7 @@ fun testConfiguration(baseDirectory: Path, legalName: String, basePort: Int): Fu } @JvmOverloads -fun configureTestSSL(legalName: String = "Mega Corp."): SSLConfiguration = object : SSLConfiguration { +fun configureTestSSL(legalName: X500Name = X500Name(MEGA_CORP.name)): SSLConfiguration = object : SSLConfiguration { override val certificatesDirectory = Files.createTempDirectory("certs") override val keyStorePassword: String get() = "cordacadevpass" override val trustStorePassword: String get() = "trustpass" diff --git a/test-utils/src/main/kotlin/net/corda/testing/messaging/SimpleMQClient.kt b/test-utils/src/main/kotlin/net/corda/testing/messaging/SimpleMQClient.kt index f3bbc770cf..db15251845 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/messaging/SimpleMQClient.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/messaging/SimpleMQClient.kt @@ -7,12 +7,16 @@ import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.config.SSLConfiguration import net.corda.testing.configureTestSSL import org.apache.activemq.artemis.api.core.client.* +import org.bouncycastle.asn1.x500.X500Name /** * As the name suggests this is a simple client for connecting to MQ brokers. */ class SimpleMQClient(val target: HostAndPort, - override val config: SSLConfiguration? = configureTestSSL("SimpleMQClient")) : ArtemisMessagingComponent() { + override val config: SSLConfiguration? = configureTestSSL(DEFAULT_MQ_LEGAL_NAME)) : ArtemisMessagingComponent() { + companion object { + val DEFAULT_MQ_LEGAL_NAME = X500Name("CN=SimpleMQClient,O=R3,OU=corda,L=London,C=UK") + } lateinit var sessionFactory: ClientSessionFactory lateinit var session: ClientSession lateinit var producer: ClientProducer diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index b67c51d47b..c2d1cf310b 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -127,10 +127,10 @@ class InMemoryMessagingNetwork( id: Int, executor: AffinityExecutor, advertisedServices: List, - description: String? = null, + description: X500Name? = null, database: Database) : MessagingServiceBuilder { - return Builder(manuallyPumped, PeerHandle(id, description ?: "In memory node $id"), advertisedServices.map(::ServiceHandle), executor, database = database) + return Builder(manuallyPumped, PeerHandle(id, description ?: X509Utilities.getDevX509Name("In memory node $id")), advertisedServices.map(::ServiceHandle), executor, database = database) } interface LatencyCalculator { @@ -198,8 +198,8 @@ class InMemoryMessagingNetwork( } @CordaSerializable - data class PeerHandle(val id: Int, val description: String) : SingleMessageRecipient { - override fun toString() = description + data class PeerHandle(val id: Int, val description: X500Name) : SingleMessageRecipient { + override fun toString() = description.toString() override fun equals(other: Any?) = other is PeerHandle && other.id == id override fun hashCode() = id.hashCode() } @@ -468,6 +468,6 @@ class InMemoryMessagingNetwork( 1, message.uniqueMessageId, message.debugTimestamp, - X509Utilities.getDevX509Name(sender.description)) + sender.description) } } diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockNode.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockNode.kt index f868df14b3..106caea67d 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -35,6 +35,7 @@ import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.TestNodeConfiguration import org.apache.activemq.artemis.utils.ReusableLatch +import org.bouncycastle.asn1.x500.X500Name import org.slf4j.Logger import java.math.BigInteger import java.nio.file.FileSystem @@ -287,9 +288,9 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, val config = TestNodeConfiguration( baseDirectory = path, - myLegalName = legalName ?: "Mock Company $id", + myLegalName = X500Name(legalName ?: "CN=Mock Company $id,OU=Corda QA Department,O=R3 CEV,L=New York,C=US"), networkMapService = null, - dataSourceProperties = makeTestDataSourceProperties("node_${id}_net_$networkId")) + dataSourceProperties = makeTestDataSourceProperties(X500Name("CN=node_${id}_net_$networkId,OU=Corda QA Department,O=R3 CEV,L=New York,C=US"))) val node = nodeFactory.create(config, this, networkMapAddress, advertisedServices.toSet(), id, overrideServices, entropyRoot) if (start) { node.setup().start() diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt index 5085afe132..40f22742e8 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -176,7 +176,7 @@ class MockStorageService(override val attachments: AttachmentStorage = MockAttac * * @param nodeName Reflects the "instance" of the in-memory database. Defaults to a random string. */ -fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties { +fun makeTestDataSourceProperties(nodeName: X500Name = X509Utilities.getDevX509Name(SecureHash.randomSHA256().toString())): Properties { val props = Properties() props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource") props.setProperty("dataSource.url", "jdbc:h2:mem:${nodeName}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE") diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt b/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt index 6e3fa38534..2631249c14 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/SimpleNode.kt @@ -3,6 +3,7 @@ package net.corda.testing.node import com.codahale.metrics.MetricRegistry import com.google.common.net.HostAndPort import com.google.common.util.concurrent.SettableFuture +import net.corda.core.crypto.commonName import net.corda.core.crypto.generateKeyPair import net.corda.core.messaging.RPCOps import net.corda.testing.MOCK_VERSION_INFO @@ -16,6 +17,7 @@ import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.transaction import net.corda.testing.freeLocalHostAndPort +import org.bouncycastle.asn1.x500.X500Name import org.jetbrains.exposed.sql.Database import java.io.Closeable import java.security.KeyPair @@ -32,7 +34,7 @@ class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeL val userService = RPCUserServiceImpl(config.rpcUsers) val monitoringService = MonitoringService(MetricRegistry()) val identity: KeyPair = generateKeyPair() - val executor = ServiceAffinityExecutor(config.myLegalName, 1) + val executor = ServiceAffinityExecutor(config.myLegalName.commonName, 1) val broker = ArtemisMessagingServer(config, address, rpcAddress, InMemoryNetworkMapCache(), userService) val networkMapRegistrationFuture: SettableFuture = SettableFuture.create() val net = database.transaction { @@ -54,7 +56,7 @@ class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeL override val protocolVersion = 0 }, userService) - thread(name = config.myLegalName) { + thread(name = config.myLegalName.commonName) { net.run() } } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt index 42589e194b..9a10e04652 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt @@ -2,6 +2,8 @@ package net.corda.demobench.model import com.google.common.net.HostAndPort import com.typesafe.config.Config +import org.bouncycastle.asn1.x500.X500Name + import tornadofx.* import java.io.IOException import java.nio.file.Files @@ -23,10 +25,9 @@ class InstallFactory : Controller() { val nodeConfig = NodeConfig( tempDir, - config.getString("myLegalName"), + X500Name(config.getString("myLegalName")), p2pPort, rpcPort, - config.getString("nearestCity"), webPort, h2Port, extraServices, @@ -35,7 +36,7 @@ class InstallFactory : Controller() { if (config.hasPath("networkMapService")) { val nmap = config.getConfig("networkMapService") - nodeConfig.networkMap = NetworkMapConfig(nmap.getString("legalName"), nmap.parsePort("address")) + nodeConfig.networkMap = NetworkMapConfig(X500Name(nmap.getString("legalName")), nmap.parsePort("address")) } else { log.info("Node '${nodeConfig.legalName}' is the network map") } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NetworkMapConfig.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NetworkMapConfig.kt index cdfdf77522..bd08931e76 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NetworkMapConfig.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NetworkMapConfig.kt @@ -1,8 +1,11 @@ package net.corda.demobench.model -open class NetworkMapConfig(val legalName: String, val p2pPort: Int) { +import net.corda.core.crypto.commonName +import org.bouncycastle.asn1.x500.X500Name - val key: String = legalName.toKey() +open class NetworkMapConfig(val legalName: X500Name, val p2pPort: Int) { + + val key: String = legalName.commonName.toKey() } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt index ea2bf4ecfe..745373e329 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt @@ -2,6 +2,8 @@ package net.corda.demobench.model import com.typesafe.config.* import net.corda.nodeapi.User +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.style.BCStyle import java.io.File import java.nio.file.Files import java.nio.file.Path @@ -9,10 +11,9 @@ import java.nio.file.StandardCopyOption class NodeConfig( baseDir: Path, - legalName: String, + legalName: X500Name, p2pPort: Int, val rpcPort: Int, - val nearestCity: String, val webPort: Int, val h2Port: Int, val extraServices: List, @@ -25,6 +26,7 @@ class NodeConfig( val defaultUser = user("guest") } + val nearestCity: String? = legalName.getRDNs(BCStyle.L).singleOrNull()?.typesAndValues?.singleOrNull()?.value?.toString() val nodeDir: Path = baseDir.resolve(key) override val pluginDir: Path = nodeDir.resolve("plugins") val explorerDir: Path = baseDir.resolve("$key-explorer") @@ -42,14 +44,13 @@ class NodeConfig( * which is mutable. */ fun toFileConfig(): Config = ConfigFactory.empty() - .withValue("myLegalName", valueFor(legalName)) + .withValue("myLegalName", valueFor(legalName.toString())) .withValue("p2pAddress", addressValueFor(p2pPort)) - .withValue("nearestCity", valueFor(nearestCity)) .withValue("extraAdvertisedServiceIds", valueFor(extraServices)) .withFallback(optional("networkMapService", networkMap, { c, n -> c.withValue("address", addressValueFor(n.p2pPort)) - .withValue("legalName", valueFor(n.legalName)) + .withValue("legalName", valueFor(n.legalName.toString())) })) .withValue("webAddress", addressValueFor(webPort)) .withValue("rpcAddress", addressValueFor(rpcPort)) @@ -60,7 +61,7 @@ class NodeConfig( fun toText(): String = toFileConfig().root().render(renderOptions) fun moveTo(baseDir: Path) = NodeConfig( - baseDir, legalName, p2pPort, rpcPort, nearestCity, webPort, h2Port, extraServices, users, networkMap + baseDir, legalName, p2pPort, rpcPort, webPort, h2Port, extraServices, users, networkMap ) fun install(plugins: Collection) { diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 810039af4b..fe46a5acda 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -2,6 +2,7 @@ package net.corda.demobench.model import net.corda.demobench.plugin.PluginController import net.corda.demobench.pty.R3Pty +import org.bouncycastle.asn1.x500.X500Name import tornadofx.* import java.io.IOException import java.lang.management.ManagementFactory @@ -51,10 +52,9 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { fun validate(nodeData: NodeData): NodeConfig? { val config = NodeConfig( baseDir, - nodeData.legalName.value.trim(), + X500Name(nodeData.legalName.value.trim()), nodeData.p2pPort.value, nodeData.rpcPort.value, - nodeData.nearestCity.value.description.trim(), nodeData.webPort.value, nodeData.h2Port.value, nodeData.extraServices.value diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt index f38c1f1e0c..feaa423299 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt @@ -4,14 +4,16 @@ import com.jediterm.terminal.ui.JediTermWidget import com.jediterm.terminal.ui.UIUtil import com.jediterm.terminal.ui.settings.SettingsProvider import com.pty4j.PtyProcess +import net.corda.core.crypto.commonName import net.corda.core.utilities.loggerFor +import org.bouncycastle.asn1.x500.X500Name import java.awt.Dimension import java.io.IOException import java.nio.charset.StandardCharsets.UTF_8 import java.util.concurrent.Executors import java.util.concurrent.TimeUnit -class R3Pty(val name: String, settings: SettingsProvider, dimension: Dimension, val onExit: (Int) -> Unit) : AutoCloseable { +class R3Pty(val name: X500Name, settings: SettingsProvider, dimension: Dimension, val onExit: (Int) -> Unit) : AutoCloseable { private companion object { val log = loggerFor() } @@ -32,7 +34,7 @@ class R3Pty(val name: String, settings: SettingsProvider, dimension: Dimension, val process = PtyProcess.exec(command, environment, workingDir) try { - return PtyProcessTtyConnector(name, process, UTF_8) + return PtyProcessTtyConnector(name.commonName, process, UTF_8) } catch (e: Exception) { process.destroyForcibly() process.waitFor(30, TimeUnit.SECONDS) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt index 6c62beca98..2c87ab73c1 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt @@ -263,11 +263,11 @@ class NodeTabView : Fragment() { } private fun launchNode(config: NodeConfig) { - val countryCode = CityDatabase.cityMap[config.nearestCity]?.countryCode + val countryCode = CityDatabase.cityMap[config.nearestCity ?: "Nowhere"]?.countryCode if (countryCode != null) { nodeTab.graphic = ImageView(flags.get()[countryCode]).apply { fitWidth = 24.0; isPreserveRatio = true } } - nodeTab.text = config.legalName + nodeTab.text = config.legalName.toString() nodeTerminalView.open(config) { exitCode -> Platform.runLater { if (exitCode == 0) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt index aecfc1053c..cd37b5f48c 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt @@ -64,7 +64,7 @@ class NodeTerminalView : Fragment() { private lateinit var swingTerminal: SwingNode fun open(config: NodeConfig, onExit: (Int) -> Unit) { - nodeName.text = config.legalName + nodeName.text = config.legalName.toString() swingTerminal = SwingNode() swingTerminal.setOnMouseClicked { diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NetworkMapConfigTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NetworkMapConfigTest.kt index fcb444c9ec..62ad3d41a6 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NetworkMapConfigTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NetworkMapConfigTest.kt @@ -1,5 +1,6 @@ package net.corda.demobench.model +import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import kotlin.test.assertEquals @@ -7,7 +8,7 @@ class NetworkMapConfigTest { @Test fun keyValue() { - val config = NetworkMapConfig("My\tNasty Little\rLabel\n", 10000) + val config = NetworkMapConfig(X500Name("CN=My\tNasty Little\rLabel\n"), 10000) assertEquals("mynastylittlelabel", config.key) } diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt index eb5ed3bcdb..168e029933 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt @@ -13,6 +13,7 @@ import net.corda.node.services.config.FullNodeConfiguration import net.corda.nodeapi.User import net.corda.nodeapi.config.parseAs import net.corda.webserver.WebServerConfig +import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.io.StringWriter import java.nio.file.Path @@ -22,41 +23,39 @@ import kotlin.test.assertFalse import kotlin.test.assertNull import kotlin.test.assertTrue + class NodeConfigTest { - private val baseDir: Path = Paths.get(".").toAbsolutePath() + companion object { + private val baseDir: Path = Paths.get(".").toAbsolutePath() + private val myLegalName = X500Name("CN=My Name,OU=Corda QA Department,O=R3 CEV,L=New York,C=US") + } @Test fun `test name`() { - val config = createConfig(legalName = "My Name") - assertEquals("My Name", config.legalName) + val config = createConfig(legalName = myLegalName) + assertEquals(myLegalName, config.legalName) assertEquals("myname", config.key) } @Test fun `test node directory`() { - val config = createConfig(legalName = "My Name") + val config = createConfig(legalName = myLegalName) assertEquals(baseDir / "myname", config.nodeDir) } @Test fun `test explorer directory`() { - val config = createConfig(legalName = "My Name") + val config = createConfig(legalName = myLegalName) assertEquals(baseDir / "myname-explorer", config.explorerDir) } @Test fun `test plugin directory`() { - val config = createConfig(legalName = "My Name") + val config = createConfig(legalName = myLegalName) assertEquals(baseDir / "myname" / "plugins", config.pluginDir) } - @Test - fun `test nearest city`() { - val config = createConfig(nearestCity = "Leicester") - assertEquals("Leicester", config.nearestCity) - } - @Test fun `test P2P port`() { val config = createConfig(p2pPort = 10001) @@ -134,8 +133,7 @@ class NodeConfigTest { @Test fun `test config text`() { val config = createConfig( - legalName = "My Name", - nearestCity = "Stockholm", + legalName = myLegalName, p2pPort = 10001, rpcPort = 40002, webPort = 20001, @@ -147,7 +145,6 @@ class NodeConfigTest { + "\"extraAdvertisedServiceIds\":[\"my.service\"]," + "\"h2port\":30001," + "\"myLegalName\":\"My Name\"," - + "\"nearestCity\":\"Stockholm\"," + "\"p2pAddress\":\"localhost:10001\"," + "\"rpcAddress\":\"localhost:40002\"," + "\"rpcUsers\":[" @@ -161,8 +158,7 @@ class NodeConfigTest { @Test fun `test config text with network map`() { val config = createConfig( - legalName = "My Name", - nearestCity = "Stockholm", + legalName = myLegalName, p2pPort = 10001, rpcPort = 40002, webPort = 20001, @@ -170,13 +166,12 @@ class NodeConfigTest { services = listOf("my.service"), users = listOf(user("jenny")) ) - config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345) + config.networkMap = NetworkMapConfig(X500Name(DUMMY_NOTARY.name), 12345) assertEquals(prettyPrint("{" + "\"extraAdvertisedServiceIds\":[\"my.service\"]," + "\"h2port\":30001," + "\"myLegalName\":\"My Name\"," - + "\"nearestCity\":\"Stockholm\"," + "\"networkMapService\":{\"address\":\"localhost:12345\",\"legalName\":\"CN=Notary Service,O=R3,OU=corda,L=Zurich,C=CH\"}," + "\"p2pAddress\":\"localhost:10001\"," + "\"rpcAddress\":\"localhost:40002\"," @@ -191,8 +186,7 @@ class NodeConfigTest { @Test fun `reading node configuration`() { val config = createConfig( - legalName = "My Name", - nearestCity = "Stockholm", + legalName = myLegalName, p2pPort = 10001, rpcPort = 40002, webPort = 20001, @@ -200,7 +194,7 @@ class NodeConfigTest { services = listOf("my.service"), users = listOf(user("jenny")) ) - config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345) + config.networkMap = NetworkMapConfig(X500Name(DUMMY_NOTARY.name), 12345) val nodeConfig = config.toFileConfig() .withValue("basedir", ConfigValueFactory.fromAnyRef(baseDir.toString())) @@ -208,8 +202,8 @@ class NodeConfigTest { .resolve() val fullConfig = nodeConfig.parseAs() - assertEquals("My Name", fullConfig.myLegalName) - assertEquals("Stockholm", fullConfig.nearestCity) + assertEquals(myLegalName, fullConfig.myLegalName) + assertEquals("London", fullConfig.nearestCity) assertEquals(localPort(40002), fullConfig.rpcAddress) assertEquals(localPort(10001), fullConfig.p2pAddress) assertEquals(listOf("my.service"), fullConfig.extraAdvertisedServiceIds) @@ -222,8 +216,7 @@ class NodeConfigTest { @Test fun `reading webserver configuration`() { val config = createConfig( - legalName = "My Name", - nearestCity = "Stockholm", + legalName = myLegalName, p2pPort = 10001, rpcPort = 40002, webPort = 20001, @@ -231,7 +224,7 @@ class NodeConfigTest { services = listOf("my.service"), users = listOf(user("jenny")) ) - config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345) + config.networkMap = NetworkMapConfig(X500Name(DUMMY_NOTARY.name), 12345) val nodeConfig = config.toFileConfig() .withValue("basedir", ConfigValueFactory.fromAnyRef(baseDir.toString())) @@ -247,7 +240,7 @@ class NodeConfigTest { @Test fun `test moving`() { - val config = createConfig(legalName = "My Name") + val config = createConfig(legalName = myLegalName) val elsewhere = baseDir / "elsewhere" val moved = config.moveTo(elsewhere) @@ -257,8 +250,7 @@ class NodeConfigTest { } private fun createConfig( - legalName: String = "Unknown", - nearestCity: String = "Nowhere", + legalName: X500Name = X500Name("CN=Unknown,O=R3,OU=corda,L=Nowhere,C=UK"), p2pPort: Int = -1, rpcPort: Int = -1, webPort: Int = -1, @@ -268,7 +260,6 @@ class NodeConfigTest { ) = NodeConfig( baseDir, legalName = legalName, - nearestCity = nearestCity, p2pPort = p2pPort, rpcPort = rpcPort, webPort = webPort, diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt index 33decb8ce1..ef2ed88680 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt @@ -1,7 +1,9 @@ package net.corda.demobench.model +import net.corda.core.crypto.X509Utilities import net.corda.core.utilities.DUMMY_NOTARY import net.corda.nodeapi.User +import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.nio.file.Path import java.nio.file.Paths @@ -11,11 +13,13 @@ class NodeControllerTest { private val baseDir: Path = Paths.get(".").toAbsolutePath() private val controller = NodeController({ _, _ -> }) + private val node1Name = X500Name("CN=Node 1,OU=Corda QA Department,O=R3 CEV,L=New York,C=US") + private val node2Name = X500Name("CN=Node 2,OU=Corda QA Department,O=R3 CEV,L=New York,C=US") @Test fun `test unique nodes after validate`() { val data = NodeData() - data.legalName.value = "Node 1" + data.legalName.value = node1Name.toString() assertNotNull(controller.validate(data)) assertNull(controller.validate(data)) } @@ -23,7 +27,7 @@ class NodeControllerTest { @Test fun `test unique key after validate`() { val data = NodeData() - data.legalName.value = "Node 1" + data.legalName.value = node1Name.toString() assertFalse(controller.keyExists("node1")) controller.validate(data) @@ -33,7 +37,7 @@ class NodeControllerTest { @Test fun `test matching name after validate`() { val data = NodeData() - data.legalName.value = "Node 1" + data.legalName.value = node1Name.toString() assertFalse(controller.nameExists("Node 1")) assertFalse(controller.nameExists("Node1")) @@ -47,7 +51,7 @@ class NodeControllerTest { @Test fun `test first validated node becomes network map`() { val data = NodeData() - data.legalName.value = "Node 1" + data.legalName.value = node1Name.toString() data.p2pPort.value = 100000 assertFalse(controller.hasNetworkMap()) @@ -57,14 +61,14 @@ class NodeControllerTest { @Test fun `test register unique nodes`() { - val config = createConfig(legalName = "Node 2") + val config = createConfig(legalName = node2Name) assertTrue(controller.register(config)) assertFalse(controller.register(config)) } @Test fun `test unique key after register`() { - val config = createConfig(legalName = "Node 2") + val config = createConfig(legalName = node2Name) assertFalse(controller.keyExists("node2")) controller.register(config) @@ -73,7 +77,7 @@ class NodeControllerTest { @Test fun `test matching name after register`() { - val config = createConfig(legalName = "Node 2") + val config = createConfig(legalName = node2Name) assertFalse(controller.nameExists("Node 2")) assertFalse(controller.nameExists("Node2")) @@ -86,7 +90,7 @@ class NodeControllerTest { @Test fun `test register network map node`() { - val config = createConfig(legalName = "Node is Network Map") + val config = createConfig(legalName = X500Name("CN=Node is Network Map,OU=Corda QA Department,O=R3 CEV,L=New York,C=US")) assertTrue(config.isNetworkMap()) assertFalse(controller.hasNetworkMap()) @@ -96,8 +100,8 @@ class NodeControllerTest { @Test fun `test register non-network-map node`() { - val config = createConfig(legalName = "Node is not Network Map") - config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 10000) + val config = createConfig(legalName = X500Name("CN=Node is not Network Map,OU=Corda QA Department,O=R3 CEV,L=New York,C=US")) + config.networkMap = NetworkMapConfig(X500Name(DUMMY_NOTARY.name), 10000) assertFalse(config.isNetworkMap()) assertFalse(controller.hasNetworkMap()) @@ -151,7 +155,7 @@ class NodeControllerTest { @Test fun `dispose node`() { - val config = createConfig(legalName = "MyName") + val config = createConfig(legalName = X500Name("CN=MyName,OU=Corda QA Department,O=R3 CEV,L=New York,C=US")) controller.register(config) assertEquals(NodeState.STARTING, config.state) @@ -162,8 +166,7 @@ class NodeControllerTest { } private fun createConfig( - legalName: String = "Unknown", - nearestCity: String = "Nowhere", + legalName: X500Name = X509Utilities.getDevX509Name("Unknown"), p2pPort: Int = -1, rpcPort: Int = -1, webPort: Int = -1, @@ -173,7 +176,6 @@ class NodeControllerTest { ) = NodeConfig( baseDir, legalName = legalName, - nearestCity = nearestCity, p2pPort = p2pPort, rpcPort = rpcPort, webPort = webPort,