Removed SSLConfiguration parameter from CordaRPCClient, thus removing SSL support.

The current use of SSL RPC relies on access to the node's keystore file, and further to that some uses where using the NODE_USER to login on the p2p port.
This commit is contained in:
Shams Asari 2017-09-26 09:11:00 +01:00
parent b4e674c2fe
commit 4df8b427d2
11 changed files with 52 additions and 71 deletions

View File

@ -57,7 +57,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
CordaFuture<StartedNode<Node>> nodeFuture = startNode(getALICE().getName(), 1, services, singletonList(rpcUser), emptyMap()); CordaFuture<StartedNode<Node>> nodeFuture = startNode(getALICE().getName(), 1, services, singletonList(rpcUser), emptyMap());
node = nodeFuture.get(); node = nodeFuture.get();
node.getInternals().registerCustomSchemas(Collections.singleton(CashSchemaV1.INSTANCE)); node.getInternals().registerCustomSchemas(Collections.singleton(CashSchemaV1.INSTANCE));
client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()), null, getDefault(), false); client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()), getDefault(), false);
} }
@After @After

View File

@ -7,7 +7,6 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.config.SSLConfiguration
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
import java.time.Duration import java.time.Duration
@ -34,9 +33,9 @@ data class CordaRPCClientConfiguration(
} }
/** @see RPCClient */ /** @see RPCClient */
//TODO Add SSL support
class CordaRPCClient( class CordaRPCClient(
hostAndPort: NetworkHostAndPort, hostAndPort: NetworkHostAndPort,
sslConfiguration: SSLConfiguration? = null,
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.default,
initialiseSerialization: Boolean = true initialiseSerialization: Boolean = true
) { ) {
@ -50,7 +49,7 @@ class CordaRPCClient(
} }
private val rpcClient = RPCClient<CordaRPCOps>( private val rpcClient = RPCClient<CordaRPCOps>(
tcpTransport(ConnectionDirection.Outbound(), hostAndPort, sslConfiguration), tcpTransport(ConnectionDirection.Outbound(), hostAndPort, config = null),
configuration.toRpcClientConfiguration(), configuration.toRpcClientConfiguration(),
KRYO_RPC_CLIENT_CONTEXT KRYO_RPC_CLIENT_CONTEXT
) )

View File

@ -228,6 +228,9 @@ Release 1.0
* ``@RPCSinceVersion``, ``RPCException`` and ``PermissionException`` have moved to ``net.corda.client.rpc``. * ``@RPCSinceVersion``, ``RPCException`` and ``PermissionException`` have moved to ``net.corda.client.rpc``.
* Current implementation of SSL in ``CordaRPCClient`` has been removed until we have a better solution which doesn't rely
on the node's keystore.
Milestone 14 Milestone 14
------------ ------------

View File

@ -1,17 +1,15 @@
package net.corda.nodeapi package net.corda.nodeapi
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.toBase58String
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.messaging.MessageRecipientGroup import net.corda.core.messaging.MessageRecipientGroup
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.internal.read
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.toBase58String
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.config.SSLConfiguration
import java.security.KeyStore
import java.security.PublicKey import java.security.PublicKey
/** /**
@ -85,22 +83,6 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
/** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */ /** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */
abstract val config: SSLConfiguration? abstract val config: SSLConfiguration?
/**
* Returns nothing if the keystore was opened OK or throws if not. Useful to check the password, as
* unfortunately Artemis tends to bury the exception when the password is wrong.
*/
fun checkStorePasswords() {
val config = config ?: return
arrayOf(config.sslKeystore, config.nodeKeystore).forEach {
it.read {
KeyStore.getInstance("JKS").load(it, config.keyStorePassword.toCharArray())
}
}
config.trustStoreFile.read {
KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray())
}
}
// Used for bridges creation. // Used for bridges creation.
fun getArtemisPeerAddress(party: Party, address: NetworkHostAndPort, netMapName: CordaX500Name? = null): ArtemisPeerAddress { fun getArtemisPeerAddress(party: Party, address: NetworkHostAndPort, netMapName: CordaX500Name? = null): ArtemisPeerAddress {
return if (party.name == netMapName) { return if (party.name == netMapName) {

View File

@ -1,10 +1,7 @@
package net.corda.services.messaging package net.corda.services.messaging
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.configureTestSSL
import net.corda.testing.messaging.SimpleMQClient import net.corda.testing.messaging.SimpleMQClient
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Test import org.junit.Test
/** /**
@ -26,11 +23,4 @@ class MQSecurityAsRPCTest : MQSecurityTest() {
override fun startAttacker(attacker: SimpleMQClient) { override fun startAttacker(attacker: SimpleMQClient) {
attacker.start(extraRPCUsers[0].username, extraRPCUsers[0].password, false) attacker.start(extraRPCUsers[0].username, extraRPCUsers[0].password, false)
} }
@Test
fun `login to a ssl port as a RPC user`() {
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
loginToRPC(alice.internals.configuration.p2pAddress, extraRPCUsers[0], configureTestSSL())
}
}
} }

View File

@ -153,8 +153,8 @@ abstract class MQSecurityTest : NodeBasedTest() {
return client return client
} }
fun loginToRPC(target: NetworkHostAndPort, rpcUser: User, sslConfiguration: SSLConfiguration? = null): CordaRPCOps { fun loginToRPC(target: NetworkHostAndPort, rpcUser: User): CordaRPCOps {
return CordaRPCClient(target, sslConfiguration, initialiseSerialization = false).start(rpcUser.username, rpcUser.password).proxy return CordaRPCClient(target, initialiseSerialization = false).start(rpcUser.username, rpcUser.password).proxy
} }
fun loginToRPCAndGetClientQueue(): String { fun loginToRPCAndGetClientQueue(): String {

View File

@ -20,15 +20,11 @@ import net.corda.core.utilities.seconds
import net.corda.finance.plugin.registerFinanceJSONMappers import net.corda.finance.plugin.registerFinanceJSONMappers
import net.corda.irs.contract.InterestRateSwap import net.corda.irs.contract.InterestRateSwap
import net.corda.irs.utilities.uploadFile import net.corda.irs.utilities.uploadFile
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.DUMMY_BANK_A import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.DUMMY_BANK_B import net.corda.testing.*
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.IntegrationTestCategory
import net.corda.testing.chooseIdentity
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.http.HttpApi import net.corda.testing.http.HttpApi
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
@ -45,7 +41,7 @@ class IRSDemoTest : IntegrationTestCategory {
val log = loggerFor<IRSDemoTest>() val log = loggerFor<IRSDemoTest>()
} }
private val rpcUser = User("user", "password", emptySet()) private val rpcUser = User("user", "password", setOf("ALL"))
private val currentDate: LocalDate = LocalDate.now() private val currentDate: LocalDate = LocalDate.now()
private val futureDate: LocalDate = currentDate.plusMonths(6) private val futureDate: LocalDate = currentDate.plusMonths(6)
private val maxWaitTime: Duration = 60.seconds private val maxWaitTime: Duration = 60.seconds
@ -58,15 +54,16 @@ class IRSDemoTest : IntegrationTestCategory {
providedName = DUMMY_NOTARY.name, providedName = DUMMY_NOTARY.name,
advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))), advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))),
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)), startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)),
startNode(providedName = DUMMY_BANK_B.name)).map { it.getOrThrow() } startNode(providedName = DUMMY_BANK_B.name))
.map { it.getOrThrow() }
log.info("All nodes started") log.info("All nodes started")
val controllerAddrFuture = startWebserver(controller) val (controllerAddr, nodeAAddr, nodeBAddr) = listOf(
val nodeAAddrFuture = startWebserver(nodeA) startWebserver(controller),
val nodeBAddrFuture = startWebserver(nodeB) startWebserver(nodeA),
val (controllerAddr, nodeAAddr, nodeBAddr) = startWebserver(nodeB))
listOf(controllerAddrFuture, nodeAAddrFuture, nodeBAddrFuture).map { it.getOrThrow().listenAddress } .map { it.getOrThrow().listenAddress }
log.info("All webservers started") log.info("All webservers started")

View File

@ -23,16 +23,14 @@ import net.corda.core.utilities.*
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.NodeStartup import net.corda.node.internal.NodeStartup
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.ServiceType
import net.corda.node.services.config.* import net.corda.node.services.config.*
import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.ArtemisMessagingComponent
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.config.SSLConfiguration
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.config.parseAs
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.nodeapi.internal.ServiceType
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
@ -178,6 +176,10 @@ interface DriverDSLInternalInterface : DriverDSLExposedInterface {
sealed class NodeHandle { sealed class NodeHandle {
abstract val nodeInfo: NodeInfo abstract val nodeInfo: NodeInfo
/**
* Interface to the node's RPC system. The first RPC user will be used to login if are any, otherwise a default one
* will be added and that will be used.
*/
abstract val rpc: CordaRPCOps abstract val rpc: CordaRPCOps
abstract val configuration: FullNodeConfiguration abstract val configuration: FullNodeConfiguration
abstract val webAddress: NetworkHostAndPort abstract val webAddress: NetworkHostAndPort
@ -627,20 +629,21 @@ class DriverDSL(
_executorService?.shutdownNow() _executorService?.shutdownNow()
} }
private fun establishRpc(nodeAddress: NetworkHostAndPort, sslConfig: SSLConfiguration, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> { private fun establishRpc(config: FullNodeConfiguration, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
val client = CordaRPCClient(nodeAddress, sslConfig, initialiseSerialization = false) val rpcAddress = config.rpcAddress!!
val client = CordaRPCClient(rpcAddress, initialiseSerialization = false)
val connectionFuture = poll(executorService, "RPC connection") { val connectionFuture = poll(executorService, "RPC connection") {
try { try {
client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER) client.start(config.rpcUsers[0].username, config.rpcUsers[0].password)
} catch (e: Exception) { } catch (e: Exception) {
if (processDeathFuture.isDone) throw e if (processDeathFuture.isDone) throw e
log.error("Exception $e, Retrying RPC connection at $nodeAddress") log.error("Exception $e, Retrying RPC connection at $rpcAddress")
null null
} }
} }
return firstOf(connectionFuture, processDeathFuture) { return firstOf(connectionFuture, processDeathFuture) {
if (it == processDeathFuture) { if (it == processDeathFuture) {
throw ListenProcessDeathException(nodeAddress, processDeathFuture.getOrThrow()) throw ListenProcessDeathException(rpcAddress, processDeathFuture.getOrThrow())
} }
val connection = connectionFuture.getOrThrow() val connection = connectionFuture.getOrThrow()
shutdownManager.registerShutdown(connection::close) shutdownManager.registerShutdown(connection::close)
@ -696,7 +699,7 @@ class DriverDSL(
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() }, "extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"networkMapService" to networkMapServiceConfigLookup(name), "networkMapService" to networkMapServiceConfigLookup(name),
"useTestClock" to useTestClock, "useTestClock" to useTestClock,
"rpcUsers" to rpcUsers.map { it.toMap() }, "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers.map { it.toMap() },
"verifierType" to verifierType.name "verifierType" to verifierType.name
) + customOverrides ) + customOverrides
) )
@ -709,14 +712,14 @@ class DriverDSL(
portAllocation.nextHostAndPort() // rpcAddress portAllocation.nextHostAndPort() // rpcAddress
val webAddress = portAllocation.nextHostAndPort() val webAddress = portAllocation.nextHostAndPort()
val name = CordaX500Name.parse(node.name) val name = CordaX500Name.parse(node.name)
val rpcUsers = node.rpcUsers
val config = ConfigHelper.loadConfig( val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory(name), baseDirectory = baseDirectory(name),
allowMissingConfig = true, allowMissingConfig = true,
configOverrides = node.config + mapOf( configOverrides = node.config + mapOf(
"extraAdvertisedServiceIds" to node.advertisedServices, "extraAdvertisedServiceIds" to node.advertisedServices,
"networkMapService" to networkMapServiceConfigLookup(name), "networkMapService" to networkMapServiceConfigLookup(name),
"rpcUsers" to node.rpcUsers, "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers,
"notaryClusterAddresses" to node.notaryClusterAddresses "notaryClusterAddresses" to node.notaryClusterAddresses
) )
) )
@ -808,6 +811,7 @@ class DriverDSL(
override fun startDedicatedNetworkMapService(startInProcess: Boolean?): CordaFuture<NodeHandle> { override fun startDedicatedNetworkMapService(startInProcess: Boolean?): CordaFuture<NodeHandle> {
val webAddress = portAllocation.nextHostAndPort() val webAddress = portAllocation.nextHostAndPort()
val rpcAddress = portAllocation.nextHostAndPort()
val networkMapLegalName = networkMapStartStrategy.legalName val networkMapLegalName = networkMapStartStrategy.legalName
val config = ConfigHelper.loadConfig( val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory(networkMapLegalName), baseDirectory = baseDirectory(networkMapLegalName),
@ -817,6 +821,8 @@ class DriverDSL(
// TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all // TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all
// node port numbers to be shifted, so all demos and docs need to be updated accordingly. // node port numbers to be shifted, so all demos and docs need to be updated accordingly.
"webAddress" to webAddress.toString(), "webAddress" to webAddress.toString(),
"rpcAddress" to rpcAddress.toString(),
"rpcUsers" to defaultRpcUserList,
"p2pAddress" to dedicatedNetworkMapAddress.toString(), "p2pAddress" to dedicatedNetworkMapAddress.toString(),
"useTestClock" to useTestClock, "useTestClock" to useTestClock,
"extraAdvertisedServiceIds" to listOf(ServiceInfo(NetworkMapService.type).toString()) "extraAdvertisedServiceIds" to listOf(ServiceInfo(NetworkMapService.type).toString())
@ -838,7 +844,7 @@ class DriverDSL(
} }
) )
return nodeAndThreadFuture.flatMap { (node, thread) -> return nodeAndThreadFuture.flatMap { (node, thread) ->
establishRpc(nodeConfiguration.p2pAddress, nodeConfiguration, openFuture()).flatMap { rpc -> establishRpc(nodeConfiguration, openFuture()).flatMap { rpc ->
rpc.waitUntilNetworkReady().map { rpc.waitUntilNetworkReady().map {
NodeHandle.InProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, node, thread) NodeHandle.InProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, node, thread)
} }
@ -852,8 +858,7 @@ class DriverDSL(
val processDeathFuture = poll(executorService, "process death") { val processDeathFuture = poll(executorService, "process death") {
if (process.isAlive) null else process if (process.isAlive) null else process
} }
// We continue to use SSL enabled port for RPC when its for node user. establishRpc(nodeConfiguration, processDeathFuture).flatMap { rpc ->
establishRpc(nodeConfiguration.p2pAddress, nodeConfiguration, processDeathFuture).flatMap { rpc ->
// Call waitUntilNetworkReady in background in case RPC is failing over: // Call waitUntilNetworkReady in background in case RPC is failing over:
val networkMapFuture = executorService.fork { val networkMapFuture = executorService.fork {
rpc.waitUntilNetworkReady() rpc.waitUntilNetworkReady()
@ -877,6 +882,8 @@ class DriverDSL(
} }
companion object { companion object {
private val defaultRpcUserList = listOf(User("default", "default", setOf("ALL")).toMap())
private val names = arrayOf( private val names = arrayOf(
ALICE.name, ALICE.name,
BOB.name, BOB.name,

View File

@ -235,7 +235,7 @@ class NodeConfigTest {
val webConfig = WebServerConfig(baseDir, nodeConfig) val webConfig = WebServerConfig(baseDir, nodeConfig)
assertEquals(localPort(20001), webConfig.webAddress) assertEquals(localPort(20001), webConfig.webAddress)
assertEquals(localPort(10001), webConfig.p2pAddress) assertEquals(localPort(40002), webConfig.rpcAddress)
assertEquals("trustpass", webConfig.trustStorePassword) assertEquals("trustpass", webConfig.trustStorePassword)
assertEquals("cordacadevpass", webConfig.keyStorePassword) assertEquals("cordacadevpass", webConfig.keyStorePassword)
} }

View File

@ -2,6 +2,7 @@ package net.corda.webserver
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.User
import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.config.NodeSSLConfiguration
import net.corda.nodeapi.config.getValue import net.corda.nodeapi.config.getValue
import java.nio.file.Path import java.nio.file.Path
@ -15,6 +16,7 @@ class WebServerConfig(override val baseDirectory: Path, val config: Config) : No
val exportJMXto: String get() = "http" val exportJMXto: String get() = "http"
val useHTTPS: Boolean by config val useHTTPS: Boolean by config
val myLegalName: String by config val myLegalName: String by config
val p2pAddress: NetworkHostAndPort by config // TODO: Use RPC port instead of P2P port (RPC requires authentication, P2P does not) val rpcAddress: NetworkHostAndPort by config
val webAddress: NetworkHostAndPort by config val webAddress: NetworkHostAndPort by config
val rpcUsers: List<User> by config
} }

View File

@ -5,7 +5,6 @@ import net.corda.client.jackson.JacksonSupport
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.nodeapi.ArtemisMessagingComponent
import net.corda.webserver.WebServerConfig import net.corda.webserver.WebServerConfig
import net.corda.webserver.converters.CordaConverterProvider import net.corda.webserver.converters.CordaConverterProvider
import net.corda.webserver.services.WebServerPluginRegistry import net.corda.webserver.services.WebServerPluginRegistry
@ -26,6 +25,7 @@ import org.slf4j.LoggerFactory
import java.io.IOException import java.io.IOException
import java.io.Writer import java.io.Writer
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.nio.file.NoSuchFileException
import java.util.* import java.util.*
import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletRequest
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
@ -187,12 +187,12 @@ class NodeWebServer(val config: WebServerConfig) {
try { try {
return connectLocalRpcAsNodeUser() return connectLocalRpcAsNodeUser()
} catch (e: ActiveMQNotConnectedException) { } catch (e: ActiveMQNotConnectedException) {
log.debug("Could not connect to ${config.p2pAddress} due to exception: ", e) log.debug("Could not connect to ${config.rpcAddress} due to exception: ", e)
Thread.sleep(retryDelay) Thread.sleep(retryDelay)
// This error will happen if the server has yet to create the keystore // This error will happen if the server has yet to create the keystore
// Keep the fully qualified package name due to collisions with the Kotlin stdlib // Keep the fully qualified package name due to collisions with the Kotlin stdlib
// exception of the same name // exception of the same name
} catch (e: java.nio.file.NoSuchFileException) { } catch (e: NoSuchFileException) {
log.debug("Tried to open a file that doesn't yet exist, retrying", e) log.debug("Tried to open a file that doesn't yet exist, retrying", e)
Thread.sleep(retryDelay) Thread.sleep(retryDelay)
} catch (e: Throwable) { } catch (e: Throwable) {
@ -205,9 +205,10 @@ class NodeWebServer(val config: WebServerConfig) {
} }
private fun connectLocalRpcAsNodeUser(): CordaRPCOps { private fun connectLocalRpcAsNodeUser(): CordaRPCOps {
log.info("Connecting to node at ${config.p2pAddress} as node user") val rpcUser = config.rpcUsers.firstOrNull() ?: throw IllegalArgumentException("The node config has not specified any RPC users")
val client = CordaRPCClient(config.p2pAddress, config) log.info("Connecting to node at ${config.rpcAddress} as $rpcUser")
val connection = client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER) val client = CordaRPCClient(config.rpcAddress)
val connection = client.start(rpcUser.username, rpcUser.password)
return connection.proxy return connection.proxy
} }