mirror of
https://github.com/corda/corda.git
synced 2025-06-17 22:58:19 +00:00
Converted FullNodeConfiguration into a data class and added ability to parse Configs into data classes
This commit is contained in:
@ -1,10 +1,11 @@
|
||||
package net.corda.node
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import joptsimple.OptionParser
|
||||
import joptsimple.util.EnumConverter
|
||||
import net.corda.core.div
|
||||
import net.corda.node.services.config.ConfigHelper
|
||||
import net.corda.node.services.config.FullNodeConfiguration
|
||||
import net.corda.nodeapi.config.parseAs
|
||||
import org.slf4j.event.Level
|
||||
import java.io.PrintStream
|
||||
import java.nio.file.Path
|
||||
@ -64,7 +65,9 @@ data class CmdLineOptions(val baseDirectory: Path,
|
||||
val isVersion: Boolean,
|
||||
val noLocalShell: Boolean,
|
||||
val sshdServer: Boolean) {
|
||||
fun loadConfig(allowMissingConfig: Boolean = false, configOverrides: Map<String, Any?> = emptyMap()): Config {
|
||||
return ConfigHelper.loadConfig(baseDirectory, configFile, allowMissingConfig, configOverrides)
|
||||
fun loadConfig(allowMissingConfig: Boolean = false, configOverrides: Map<String, Any?> = emptyMap()): FullNodeConfiguration {
|
||||
return ConfigHelper
|
||||
.loadConfig(baseDirectory, configFile, allowMissingConfig, configOverrides)
|
||||
.parseAs<FullNodeConfiguration>()
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
package net.corda.node
|
||||
|
||||
import com.jcabi.manifests.Manifests
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigException
|
||||
import joptsimple.OptionException
|
||||
import net.corda.core.*
|
||||
@ -86,9 +85,7 @@ fun main(args: Array<String>) {
|
||||
printBasicNodeInfo("Logs can be found in", System.getProperty("log-path"))
|
||||
|
||||
val conf = try {
|
||||
val conf = cmdlineOptions.loadConfig()
|
||||
checkConfigVersion(conf)
|
||||
FullNodeConfiguration(cmdlineOptions.baseDirectory, conf)
|
||||
cmdlineOptions.loadConfig()
|
||||
} catch (e: ConfigException) {
|
||||
println("Unable to load the configuration file: ${e.rootCause.message}")
|
||||
exitProcess(2)
|
||||
@ -157,16 +154,6 @@ fun main(args: Array<String>) {
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
private fun checkConfigVersion(conf: Config) {
|
||||
// TODO: Remove this check in future milestone.
|
||||
if (conf.hasPath("artemisAddress")) {
|
||||
// artemisAddress has been renamed to p2pAddress in M10.
|
||||
println("artemisAddress has been renamed to p2pAddress in M10, please upgrade your configuration file and start Corda node again.")
|
||||
println("Corda will now exit...")
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkJavaVersion() {
|
||||
// Check we're not running a version of Java with a known bug: https://github.com/corda/corda/issues/83
|
||||
try {
|
||||
|
@ -27,6 +27,7 @@ import net.corda.node.utilities.ServiceIdentityGenerator
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import net.corda.nodeapi.config.parseAs
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.slf4j.Logger
|
||||
@ -115,6 +116,7 @@ data class NodeHandle(
|
||||
val nodeInfo: NodeInfo,
|
||||
val rpc: CordaRPCOps,
|
||||
val configuration: FullNodeConfiguration,
|
||||
val webAddress: HostAndPort,
|
||||
val process: Process
|
||||
) {
|
||||
fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.rpcAddress!!)
|
||||
@ -421,7 +423,7 @@ class DriverDSL(
|
||||
"useTestClock" to useTestClock,
|
||||
"rpcUsers" to rpcUsers.map {
|
||||
mapOf(
|
||||
"user" to it.username,
|
||||
"username" to it.username,
|
||||
"password" to it.password,
|
||||
"permissions" to it.permissions
|
||||
)
|
||||
@ -429,22 +431,19 @@ class DriverDSL(
|
||||
"verifierType" to verifierType.name
|
||||
) + customOverrides
|
||||
|
||||
val configuration = FullNodeConfiguration(
|
||||
baseDirectory,
|
||||
ConfigHelper.loadConfig(
|
||||
val config = ConfigHelper.loadConfig(
|
||||
baseDirectory = baseDirectory,
|
||||
allowMissingConfig = true,
|
||||
configOverrides = configOverrides
|
||||
)
|
||||
)
|
||||
configOverrides = configOverrides)
|
||||
val configuration = config.parseAs<FullNodeConfiguration>()
|
||||
|
||||
val processFuture = startNode(executorService, configuration, quasarJarPath, debugPort, systemProperties)
|
||||
val processFuture = startNode(executorService, configuration, config, quasarJarPath, debugPort, systemProperties)
|
||||
registerProcess(processFuture)
|
||||
return processFuture.flatMap { process ->
|
||||
// We continue to use SSL enabled port for RPC when its for node user.
|
||||
establishRpc(p2pAddress, configuration).flatMap { rpc ->
|
||||
rpc.waitUntilRegisteredWithNetworkMap().map {
|
||||
NodeHandle(rpc.nodeIdentity(), rpc, configuration, process)
|
||||
NodeHandle(rpc.nodeIdentity(), rpc, configuration, webAddress, process)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -482,34 +481,29 @@ class DriverDSL(
|
||||
}
|
||||
}
|
||||
|
||||
private fun queryWebserver(configuration: FullNodeConfiguration, process: Process): HostAndPort? {
|
||||
val protocol = if (configuration.useHTTPS) {
|
||||
"https://"
|
||||
} else {
|
||||
"http://"
|
||||
}
|
||||
val url = URL(protocol + configuration.webAddress.toString() + "/api/status")
|
||||
val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).build()
|
||||
private fun queryWebserver(handle: NodeHandle, process: Process): HostAndPort {
|
||||
val protocol = if (handle.configuration.useHTTPS) "https://" else "http://"
|
||||
val url = URL("$protocol${handle.webAddress}/api/status")
|
||||
val client = OkHttpClient.Builder().connectTimeout(5, SECONDS).readTimeout(60, SECONDS).build()
|
||||
|
||||
while (process.isAlive) try {
|
||||
val response = client.newCall(Request.Builder().url(url).build()).execute()
|
||||
if (response.isSuccessful && (response.body().string() == "started")) {
|
||||
return configuration.webAddress
|
||||
return handle.webAddress
|
||||
}
|
||||
} catch(e: ConnectException) {
|
||||
log.debug("Retrying webserver info at ${configuration.webAddress}")
|
||||
log.debug("Retrying webserver info at ${handle.webAddress}")
|
||||
}
|
||||
|
||||
log.error("Webserver at ${configuration.webAddress} has died")
|
||||
return null
|
||||
throw IllegalStateException("Webserver at ${handle.webAddress} has died")
|
||||
}
|
||||
|
||||
override fun startWebserver(handle: NodeHandle): ListenableFuture<HostAndPort> {
|
||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||
val process = DriverDSL.startWebserver(executorService, handle.configuration, debugPort)
|
||||
val process = DriverDSL.startWebserver(executorService, handle, debugPort)
|
||||
registerProcess(process)
|
||||
return process.map {
|
||||
queryWebserver(handle.configuration, it)!!
|
||||
queryWebserver(handle, it)
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,7 +531,7 @@ class DriverDSL(
|
||||
)
|
||||
|
||||
log.info("Starting network-map-service")
|
||||
val startNode = startNode(executorService, FullNodeConfiguration(baseDirectory, config), quasarJarPath, debugPort, systemProperties)
|
||||
val startNode = startNode(executorService, config.parseAs<FullNodeConfiguration>(), config, quasarJarPath, debugPort, systemProperties)
|
||||
registerProcess(startNode)
|
||||
}
|
||||
|
||||
@ -553,13 +547,14 @@ class DriverDSL(
|
||||
private fun startNode(
|
||||
executorService: ListeningScheduledExecutorService,
|
||||
nodeConf: FullNodeConfiguration,
|
||||
config: Config,
|
||||
quasarJarPath: String,
|
||||
debugPort: Int?,
|
||||
overriddenSystemProperties: Map<String, String>
|
||||
): ListenableFuture<Process> {
|
||||
return executorService.submit<Process> {
|
||||
// Write node.conf
|
||||
writeConfig(nodeConf.baseDirectory, "node.conf", nodeConf.config)
|
||||
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
||||
|
||||
val systemProperties = mapOf(
|
||||
"name" to nodeConf.myLegalName,
|
||||
@ -586,19 +581,19 @@ class DriverDSL(
|
||||
|
||||
private fun startWebserver(
|
||||
executorService: ListeningScheduledExecutorService,
|
||||
nodeConf: FullNodeConfiguration,
|
||||
handle: NodeHandle,
|
||||
debugPort: Int?
|
||||
): ListenableFuture<Process> {
|
||||
return executorService.submit<Process> {
|
||||
val className = "net.corda.webserver.WebServer"
|
||||
ProcessUtilities.startJavaProcess(
|
||||
className = className, // cannot directly get class for this, so just use string
|
||||
arguments = listOf("--base-directory", nodeConf.baseDirectory.toString()),
|
||||
arguments = listOf("--base-directory", handle.configuration.baseDirectory.toString()),
|
||||
jdwpPort = debugPort,
|
||||
extraJvmArguments = listOf("-Dname=node-${nodeConf.p2pAddress}-webserver"),
|
||||
extraJvmArguments = listOf("-Dname=node-${handle.configuration.p2pAddress}-webserver"),
|
||||
errorLogPath = Paths.get("error.$className.log")
|
||||
)
|
||||
}.flatMap { process -> addressMustBeBound(executorService, nodeConf.webAddress).map { process } }
|
||||
}.flatMap { process -> addressMustBeBound(executorService, handle.webAddress).map { process } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.services.transactions.RaftUniquenessProvider
|
||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||
import net.corda.node.services.transactions.RaftNonValidatingNotaryService
|
||||
import net.corda.node.services.transactions.*
|
||||
import net.corda.node.utilities.AddressUtils
|
||||
import net.corda.node.utilities.AffinityExecutor
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.NetworkMapAddress
|
||||
@ -128,7 +129,7 @@ class Node(override val configuration: FullNodeConfiguration,
|
||||
}
|
||||
|
||||
override fun makeMessagingService(): MessagingServiceInternal {
|
||||
userService = RPCUserServiceImpl(configuration)
|
||||
userService = RPCUserServiceImpl(configuration.rpcUsers)
|
||||
val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker()
|
||||
val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null
|
||||
return NodeMessagingClient(
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.User
|
||||
|
||||
/**
|
||||
@ -16,13 +15,8 @@ interface RPCUserService {
|
||||
|
||||
// TODO Store passwords as salted hashes
|
||||
// TODO Or ditch this and consider something like Apache Shiro
|
||||
class RPCUserServiceImpl(config: NodeConfiguration) : RPCUserService {
|
||||
|
||||
private val _users = config.rpcUsers.associateBy(User::username)
|
||||
|
||||
override fun getUser(username: String): User? = _users[username]
|
||||
|
||||
override val users: List<User> get() = _users.values.toList()
|
||||
class RPCUserServiceImpl(override val users: List<User>) : RPCUserService {
|
||||
override fun getUser(username: String): User? = users.find { it.username == username }
|
||||
}
|
||||
|
||||
fun startFlowPermission(className: String) = "StartFlow.$className"
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.node.services.config
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import com.typesafe.config.Config
|
||||
import net.corda.core.div
|
||||
import net.corda.core.node.NodeVersionInfo
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
@ -12,19 +11,13 @@ import net.corda.node.services.messaging.CertificateChainCheckPolicy
|
||||
import net.corda.node.services.network.NetworkMapService
|
||||
import net.corda.node.utilities.TestClock
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.getListOrElse
|
||||
import net.corda.nodeapi.config.getOrElse
|
||||
import net.corda.nodeapi.config.getValue
|
||||
import net.corda.nodeapi.config.OldConfig
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
enum class VerifierType {
|
||||
InMemory,
|
||||
OutOfProcess
|
||||
}
|
||||
|
||||
interface NodeConfiguration : net.corda.nodeapi.config.SSLConfiguration {
|
||||
interface NodeConfiguration : SSLConfiguration {
|
||||
val baseDirectory: Path
|
||||
override val certificatesDirectory: Path get() = baseDirectory / "certificates"
|
||||
val myLegalName: String
|
||||
@ -32,83 +25,89 @@ interface NodeConfiguration : net.corda.nodeapi.config.SSLConfiguration {
|
||||
val nearestCity: String
|
||||
val emailAddress: String
|
||||
val exportJMXto: String
|
||||
val dataSourceProperties: Properties get() = Properties()
|
||||
val rpcUsers: List<User> get() = emptyList()
|
||||
val dataSourceProperties: Properties
|
||||
val rpcUsers: List<User>
|
||||
val devMode: Boolean
|
||||
val certificateSigningService: URL
|
||||
val certificateChainCheckPolicies: Map<String, CertificateChainCheckPolicy>
|
||||
val certificateChainCheckPolicies: List<CertChainPolicyConfig>
|
||||
val verifierType: VerifierType
|
||||
}
|
||||
|
||||
/**
|
||||
* [baseDirectory] is not retrieved from the config file but rather from a command line argument.
|
||||
*/
|
||||
class FullNodeConfiguration(override val baseDirectory: Path, val config: Config) : NodeConfiguration {
|
||||
override val myLegalName: String by config
|
||||
override val nearestCity: String by config
|
||||
override val emailAddress: String by config
|
||||
data class FullNodeConfiguration(
|
||||
// TODO Remove this subsitution value and use baseDirectory as the subsitution instead
|
||||
@Deprecated(
|
||||
"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 nearestCity: String,
|
||||
override val emailAddress: String,
|
||||
override val keyStorePassword: String,
|
||||
override val trustStorePassword: String,
|
||||
override val dataSourceProperties: Properties,
|
||||
override val certificateSigningService: URL,
|
||||
override val networkMapService: NetworkMapInfo?,
|
||||
override val rpcUsers: List<User>,
|
||||
override val verifierType: VerifierType,
|
||||
val useHTTPS: Boolean,
|
||||
@OldConfig("artemisAddress")
|
||||
val p2pAddress: HostAndPort,
|
||||
val rpcAddress: HostAndPort?,
|
||||
// TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker.
|
||||
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
|
||||
val messagingServerAddress: HostAndPort?,
|
||||
val extraAdvertisedServiceIds: List<String>,
|
||||
val notaryNodeId: Int?,
|
||||
val notaryNodeAddress: HostAndPort?,
|
||||
val notaryClusterAddresses: List<HostAndPort>,
|
||||
override val certificateChainCheckPolicies: List<CertChainPolicyConfig>,
|
||||
override val devMode: Boolean = false,
|
||||
val useTestClock: Boolean = false
|
||||
) : NodeConfiguration {
|
||||
/** This is not retrieved from the config file but rather from a command line argument. */
|
||||
@Suppress("DEPRECATION")
|
||||
override val baseDirectory: Path get() = basedir
|
||||
override val exportJMXto: String get() = "http"
|
||||
override val keyStorePassword: String by config
|
||||
override val trustStorePassword: String by config
|
||||
override val dataSourceProperties: Properties by config
|
||||
override val devMode: Boolean by config.getOrElse { false }
|
||||
override val certificateSigningService: URL by config
|
||||
override val networkMapService: NetworkMapInfo? = config.getOptionalConfig("networkMapService")?.run {
|
||||
NetworkMapInfo(
|
||||
HostAndPort.fromString(getString("address")),
|
||||
getString("legalName"))
|
||||
}
|
||||
override val rpcUsers: List<User> = config
|
||||
.getListOrElse<Config>("rpcUsers") { emptyList() }
|
||||
.map {
|
||||
val username = it.getString("user")
|
||||
require(username.matches("\\w+".toRegex())) { "Username $username contains invalid characters" }
|
||||
val password = it.getString("password")
|
||||
val permissions = it.getListOrElse<String>("permissions") { emptyList() }.toSet()
|
||||
User(username, password, permissions)
|
||||
}
|
||||
override val certificateChainCheckPolicies = config.getOptionalConfig("certificateChainCheckPolicies")?.run {
|
||||
entrySet().associateByTo(HashMap(), { it.key }, { parseCertificateChainCheckPolicy(getConfig(it.key)) })
|
||||
} ?: emptyMap<String, CertificateChainCheckPolicy>()
|
||||
override val verifierType: VerifierType by config
|
||||
val useHTTPS: Boolean by config
|
||||
val p2pAddress: HostAndPort by config
|
||||
val rpcAddress: HostAndPort? by config
|
||||
val webAddress: HostAndPort by config
|
||||
// TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker.
|
||||
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
|
||||
val messagingServerAddress: HostAndPort? by config
|
||||
val extraAdvertisedServiceIds: List<String> = config.getListOrElse<String>("extraAdvertisedServiceIds") { emptyList() }
|
||||
val useTestClock: Boolean by config.getOrElse { false }
|
||||
val notaryNodeId: Int? by config
|
||||
val notaryNodeAddress: HostAndPort? by config
|
||||
val notaryClusterAddresses: List<HostAndPort> = config
|
||||
.getListOrElse<String>("notaryClusterAddresses") { emptyList() }
|
||||
.map { HostAndPort.fromString(it) }
|
||||
|
||||
fun createNode(nodeVersionInfo: NodeVersionInfo): Node {
|
||||
init {
|
||||
// This is a sanity feature do not remove.
|
||||
require(!useTestClock || devMode) { "Cannot use test clock outside of dev mode" }
|
||||
// TODO Move this to ArtemisMessagingServer
|
||||
rpcUsers.forEach {
|
||||
require(it.username.matches("\\w+".toRegex())) { "Username ${it.username} contains invalid characters" }
|
||||
}
|
||||
}
|
||||
|
||||
fun createNode(nodeVersionInfo: NodeVersionInfo): Node {
|
||||
val advertisedServices = extraAdvertisedServiceIds
|
||||
.filter(String::isNotBlank)
|
||||
.map { ServiceInfo.parse(it) }
|
||||
.toMutableSet()
|
||||
if (networkMapService == null) advertisedServices.add(ServiceInfo(NetworkMapService.type))
|
||||
if (networkMapService == null) advertisedServices += ServiceInfo(NetworkMapService.type)
|
||||
|
||||
return Node(this, advertisedServices, nodeVersionInfo, if (useTestClock) TestClock() else NodeClock())
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseCertificateChainCheckPolicy(config: Config): CertificateChainCheckPolicy {
|
||||
val policy = config.getString("policy")
|
||||
return when (policy) {
|
||||
"Any" -> CertificateChainCheckPolicy.Any
|
||||
"RootMustMatch" -> CertificateChainCheckPolicy.RootMustMatch
|
||||
"LeafMustMatch" -> CertificateChainCheckPolicy.LeafMustMatch
|
||||
"MustContainOneOf" -> CertificateChainCheckPolicy.MustContainOneOf(config.getStringList("trustedAliases").toSet())
|
||||
else -> throw IllegalArgumentException("Invalid certificate chain check policy $policy")
|
||||
}
|
||||
enum class VerifierType {
|
||||
InMemory,
|
||||
OutOfProcess
|
||||
}
|
||||
|
||||
private fun Config.getOptionalConfig(path: String): Config? = if (hasPath(path)) getConfig(path) else null
|
||||
enum class CertChainPolicyType {
|
||||
Any,
|
||||
RootMustMatch,
|
||||
LeafMustMatch,
|
||||
MustContainOneOf
|
||||
}
|
||||
|
||||
data class CertChainPolicyConfig(val role: String, val policy: CertChainPolicyType, val trustedAliases: Set<String>) {
|
||||
val certificateChainCheckPolicy: CertificateChainCheckPolicy get() {
|
||||
return when (policy) {
|
||||
CertChainPolicyType.Any -> CertificateChainCheckPolicy.Any
|
||||
CertChainPolicyType.RootMustMatch -> CertificateChainCheckPolicy.RootMustMatch
|
||||
CertChainPolicyType.LeafMustMatch -> CertificateChainCheckPolicy.LeafMustMatch
|
||||
CertChainPolicyType.MustContainOneOf -> CertificateChainCheckPolicy.MustContainOneOf(trustedAliases)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,13 @@ import com.google.common.net.HostAndPort
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import io.netty.handler.ssl.SslHandler
|
||||
import net.corda.core.ThreadBox
|
||||
import net.corda.core.*
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_CA
|
||||
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
|
||||
import net.corda.core.div
|
||||
import net.corda.core.minutes
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||
import net.corda.core.seconds
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.printBasicNodeInfo
|
||||
@ -251,8 +248,9 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
|
||||
)
|
||||
val keyStore = X509Utilities.loadKeyStore(config.keyStoreFile, config.keyStorePassword)
|
||||
val trustStore = X509Utilities.loadKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
val certChecks = defaultCertPolicies.mapValues {
|
||||
(config.certificateChainCheckPolicies[it.key] ?: it.value).createCheck(keyStore, trustStore)
|
||||
val certChecks = defaultCertPolicies.mapValues { (role, defaultPolicy) ->
|
||||
val configPolicy = config.certificateChainCheckPolicies.noneOrSingle { it.role == role }?.certificateChainCheckPolicy
|
||||
(configPolicy ?: defaultPolicy).createCheck(keyStore, trustStore)
|
||||
}
|
||||
val securityConfig = object : SecurityConfiguration() {
|
||||
// Override to make it work with our login module
|
||||
|
@ -15,9 +15,9 @@ import net.corda.core.messaging.RPCReturnsObservables
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.services.RPCUserService
|
||||
import net.corda.node.utilities.AffinityExecutor
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.nodeapi.*
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
import org.apache.activemq.artemis.api.core.Message
|
||||
|
@ -1,76 +0,0 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import net.corda.node.services.config.FullNodeConfiguration
|
||||
import net.corda.nodeapi.User
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
||||
|
||||
class RPCUserServiceImplTest {
|
||||
|
||||
@Test
|
||||
fun `missing config`() {
|
||||
val service = loadWithContents("{}")
|
||||
assertThat(service.getUser("user")).isNull()
|
||||
assertThat(service.users).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no users`() {
|
||||
val service = loadWithContents("rpcUsers : []")
|
||||
assertThat(service.getUser("user")).isNull()
|
||||
assertThat(service.users).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no permissions`() {
|
||||
val service = loadWithContents("rpcUsers : [{ user=user1, password=letmein }]")
|
||||
val expectedUser = User("user1", "letmein", permissions = emptySet())
|
||||
assertThat(service.getUser("user1")).isEqualTo(expectedUser)
|
||||
assertThat(service.users).containsOnly(expectedUser)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `single permission, which is in lower case`() {
|
||||
val service = loadWithContents("rpcUsers : [{ user=user1, password=letmein, permissions=[cash] }]")
|
||||
assertThat(service.getUser("user1")?.permissions).containsOnly("cash")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `two permissions, which are upper case`() {
|
||||
val service = loadWithContents("rpcUsers : [{ user=user1, password=letmein, permissions=[CASH, ADMIN] }]")
|
||||
assertThat(service.getUser("user1")?.permissions).containsOnly("CASH", "ADMIN")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `two users`() {
|
||||
val service = loadWithContents("""rpcUsers : [
|
||||
{ user=user, password=password, permissions=[ADMIN] }
|
||||
{ user=user2, password=password2, permissions=[] }
|
||||
]""")
|
||||
val user1 = User("user", "password", permissions = setOf("ADMIN"))
|
||||
val user2 = User("user2", "password2", permissions = emptySet())
|
||||
assertThat(service.getUser("user")).isEqualTo(user1)
|
||||
assertThat(service.getUser("user2")).isEqualTo(user2)
|
||||
assertThat(service.users).containsOnly(user1, user2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unknown user`() {
|
||||
val service = loadWithContents("rpcUsers : [{ user=user1, password=letmein }]")
|
||||
assertThat(service.getUser("test")).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Artemis special characters not permitted in usernames`() {
|
||||
assertThatThrownBy { loadWithContents("rpcUsers : [{ user=user.1, password=letmein }]") }.hasMessageContaining(".")
|
||||
assertThatThrownBy { loadWithContents("rpcUsers : [{ user=user*1, password=letmein }]") }.hasMessageContaining("*")
|
||||
assertThatThrownBy { loadWithContents("""rpcUsers : [{ user="user#1", password=letmein }]""") }.hasMessageContaining("#")
|
||||
}
|
||||
|
||||
private fun loadWithContents(configString: String): RPCUserServiceImpl {
|
||||
return RPCUserServiceImpl(FullNodeConfiguration(Paths.get("."), ConfigFactory.parseString(configString)))
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package net.corda.node.services.config
|
||||
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.testConfiguration
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
||||
|
||||
class FullNodeConfigurationTest {
|
||||
@Test
|
||||
fun `Artemis special characters not permitted in RPC usernames`() {
|
||||
fun configWithRPCUsername(username: String): FullNodeConfiguration {
|
||||
return testConfiguration(Paths.get("."), "NodeA", 0).copy(
|
||||
rpcUsers = listOf(User(username, "pass", emptySet())))
|
||||
}
|
||||
|
||||
assertThatThrownBy { configWithRPCUsername("user.1") }.hasMessageContaining(".")
|
||||
assertThatThrownBy { configWithRPCUsername("user*1") }.hasMessageContaining("*")
|
||||
assertThatThrownBy { configWithRPCUsername("user#1") }.hasMessageContaining("#")
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ import com.google.common.net.HostAndPort
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import com.typesafe.config.ConfigFactory.empty
|
||||
import net.corda.core.crypto.composite
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.messaging.Message
|
||||
@ -72,7 +71,7 @@ class ArtemisMessagingTests {
|
||||
@Before
|
||||
fun setUp() {
|
||||
val baseDirectory = temporaryFolder.root.toPath()
|
||||
userService = RPCUserServiceImpl(FullNodeConfiguration(baseDirectory, empty()))
|
||||
userService = RPCUserServiceImpl(emptyList())
|
||||
config = TestNodeConfiguration(
|
||||
baseDirectory = baseDirectory,
|
||||
myLegalName = "me",
|
||||
|
Reference in New Issue
Block a user