mirror of
https://github.com/corda/corda.git
synced 2025-06-01 23:20:54 +00:00
CORDA-1733 X500Principal configuration parsing (#3580)
CORDA-1733 X500Principal configuration parsing Change the NodeConfiguration.tlsCertCrlIssuer type from String to X500Principal
This commit is contained in:
parent
5dd85e08bf
commit
4fb7f7d3d0
@ -22,6 +22,7 @@ import java.time.Instant
|
|||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.temporal.Temporal
|
import java.time.temporal.Temporal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
@ -128,6 +129,7 @@ private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set
|
|||||||
Path::class -> Paths.get(getString(path))
|
Path::class -> Paths.get(getString(path))
|
||||||
URL::class -> URL(getString(path))
|
URL::class -> URL(getString(path))
|
||||||
UUID::class -> UUID.fromString(getString(path))
|
UUID::class -> UUID.fromString(getString(path))
|
||||||
|
X500Principal::class -> X500Principal(getString(path))
|
||||||
CordaX500Name::class -> {
|
CordaX500Name::class -> {
|
||||||
when (getValue(path).valueType()) {
|
when (getValue(path).valueType()) {
|
||||||
ConfigValueType.OBJECT -> getConfig(path).parseAs(onUnknownKeys)
|
ConfigValueType.OBJECT -> getConfig(path).parseAs(onUnknownKeys)
|
||||||
@ -173,6 +175,7 @@ private fun Config.getCollectionValue(path: String, type: KType, onUnknownKeys:
|
|||||||
NetworkHostAndPort::class -> getStringList(path).map(NetworkHostAndPort.Companion::parse)
|
NetworkHostAndPort::class -> getStringList(path).map(NetworkHostAndPort.Companion::parse)
|
||||||
Path::class -> getStringList(path).map { Paths.get(it) }
|
Path::class -> getStringList(path).map { Paths.get(it) }
|
||||||
URL::class -> getStringList(path).map(::URL)
|
URL::class -> getStringList(path).map(::URL)
|
||||||
|
X500Principal::class -> getStringList(path).map(::X500Principal)
|
||||||
UUID::class -> getStringList(path).map { UUID.fromString(it) }
|
UUID::class -> getStringList(path).map { UUID.fromString(it) }
|
||||||
CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse)
|
CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse)
|
||||||
Properties::class -> getConfigList(path).map(Config::toProperties)
|
Properties::class -> getConfigList(path).map(Config::toProperties)
|
||||||
@ -226,7 +229,7 @@ private fun Any.toConfigMap(): Map<String, Any> {
|
|||||||
val configValue = if (value is String || value is Boolean || value is Number) {
|
val configValue = if (value is String || value is Boolean || value is Number) {
|
||||||
// These types are supported by Config as use as is
|
// These types are supported by Config as use as is
|
||||||
value
|
value
|
||||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL || value is UUID) {
|
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL || value is UUID || value is X500Principal) {
|
||||||
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
||||||
value.toString()
|
value.toString()
|
||||||
} else if (value is Enum<*>) {
|
} else if (value is Enum<*>) {
|
||||||
@ -261,6 +264,7 @@ private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> {
|
|||||||
NetworkHostAndPort::class.java -> map(Any?::toString)
|
NetworkHostAndPort::class.java -> map(Any?::toString)
|
||||||
Path::class.java -> map(Any?::toString)
|
Path::class.java -> map(Any?::toString)
|
||||||
URL::class.java -> map(Any?::toString)
|
URL::class.java -> map(Any?::toString)
|
||||||
|
X500Principal::class.java -> map(Any?::toString)
|
||||||
UUID::class.java -> map(Any?::toString)
|
UUID::class.java -> map(Any?::toString)
|
||||||
CordaX500Name::class.java -> map(Any?::toString)
|
CordaX500Name::class.java -> map(Any?::toString)
|
||||||
Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() }
|
Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() }
|
||||||
|
@ -14,6 +14,7 @@ import java.nio.file.Path
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.reflect.full.primaryConstructor
|
import kotlin.reflect.full.primaryConstructor
|
||||||
|
|
||||||
class ConfigParsingTest {
|
class ConfigParsingTest {
|
||||||
@ -84,6 +85,11 @@ class ConfigParsingTest {
|
|||||||
testPropertyType<URLData, URLListData, URL>(URL("http://localhost:1234"), URL("http://localhost:1235"), valuesToString = true)
|
testPropertyType<URLData, URLListData, URL>(URL("http://localhost:1234"), URL("http://localhost:1235"), valuesToString = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun X500Principal() {
|
||||||
|
testPropertyType<X500PrincipalData, X500PrincipalListData, X500Principal>(X500Principal("C=US, L=New York, CN=Corda Root CA, OU=Corda, O=R3 HoldCo LLC"), X500Principal("O=Bank A,L=London,C=GB"), valuesToString = true)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun UUID() {
|
fun UUID() {
|
||||||
testPropertyType<UUIDData, UUIDListData, UUID>(UUID.randomUUID(), UUID.randomUUID(), valuesToString = true)
|
testPropertyType<UUIDData, UUIDListData, UUID>(UUID.randomUUID(), UUID.randomUUID(), valuesToString = true)
|
||||||
@ -324,6 +330,8 @@ class ConfigParsingTest {
|
|||||||
data class PathListData(override val values: List<Path>) : ListData<Path>
|
data class PathListData(override val values: List<Path>) : ListData<Path>
|
||||||
data class URLData(override val value: URL) : SingleData<URL>
|
data class URLData(override val value: URL) : SingleData<URL>
|
||||||
data class URLListData(override val values: List<URL>) : ListData<URL>
|
data class URLListData(override val values: List<URL>) : ListData<URL>
|
||||||
|
data class X500PrincipalData(override val value: X500Principal) : SingleData<X500Principal>
|
||||||
|
data class X500PrincipalListData(override val values: List<X500Principal>) : ListData<X500Principal>
|
||||||
data class UUIDData(override val value: UUID) : SingleData<UUID>
|
data class UUIDData(override val value: UUID) : SingleData<UUID>
|
||||||
data class UUIDListData(override val values: List<UUID>) : ListData<UUID>
|
data class UUIDListData(override val values: List<UUID>) : ListData<UUID>
|
||||||
data class CordaX500NameData(override val value: CordaX500Name) : SingleData<CordaX500Name>
|
data class CordaX500NameData(override val value: CordaX500Name) : SingleData<CordaX500Name>
|
||||||
|
@ -16,12 +16,12 @@ import net.corda.nodeapi.internal.config.User
|
|||||||
import net.corda.nodeapi.internal.config.parseAs
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.tools.shell.SSHDConfiguration
|
import net.corda.tools.shell.SSHDConfiguration
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
val Int.MB: Long get() = this * 1024L * 1024L
|
val Int.MB: Long get() = this * 1024L * 1024L
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
|||||||
val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5)
|
val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5)
|
||||||
val extraNetworkMapKeys: List<UUID>
|
val extraNetworkMapKeys: List<UUID>
|
||||||
val tlsCertCrlDistPoint: URL?
|
val tlsCertCrlDistPoint: URL?
|
||||||
val tlsCertCrlIssuer: String?
|
val tlsCertCrlIssuer: X500Principal?
|
||||||
val effectiveH2Settings: NodeH2Settings?
|
val effectiveH2Settings: NodeH2Settings?
|
||||||
val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS
|
val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS
|
||||||
val flowMonitorSuspensionLoggingThresholdMillis: Duration get() = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS
|
val flowMonitorSuspensionLoggingThresholdMillis: Duration get() = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS
|
||||||
@ -168,7 +168,7 @@ data class NodeConfigurationImpl(
|
|||||||
override val compatibilityZoneURL: URL? = null,
|
override val compatibilityZoneURL: URL? = null,
|
||||||
override var networkServices: NetworkServicesConfig? = null,
|
override var networkServices: NetworkServicesConfig? = null,
|
||||||
override val tlsCertCrlDistPoint: URL? = null,
|
override val tlsCertCrlDistPoint: URL? = null,
|
||||||
override val tlsCertCrlIssuer: String? = null,
|
override val tlsCertCrlIssuer: X500Principal? = null,
|
||||||
override val rpcUsers: List<User>,
|
override val rpcUsers: List<User>,
|
||||||
override val security: SecurityConfiguration? = null,
|
override val security: SecurityConfiguration? = null,
|
||||||
override val verifierType: VerifierType,
|
override val verifierType: VerifierType,
|
||||||
@ -237,11 +237,6 @@ data class NodeConfigurationImpl(
|
|||||||
if (tlsCertCrlDistPoint == null) {
|
if (tlsCertCrlDistPoint == null) {
|
||||||
errors += "tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL"
|
errors += "tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL"
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
X500Name(tlsCertCrlIssuer)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
errors += "Error when parsing tlsCertCrlIssuer: ${e.message}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!crlCheckSoftFail && tlsCertCrlDistPoint == null) {
|
if (!crlCheckSoftFail && tlsCertCrlDistPoint == null) {
|
||||||
errors += "tlsCertCrlDistPoint needs to be specified when crlCheckSoftFail is FALSE"
|
errors += "tlsCertCrlDistPoint needs to be specified when crlCheckSoftFail is FALSE"
|
||||||
|
@ -283,20 +283,20 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
|
override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
|
||||||
config.tlsCertCrlIssuer ?: return null
|
val tlsCertCrlIssuer = config.tlsCertCrlIssuer
|
||||||
val tlsCertCrlIssuerPrincipal = X500Principal(config.tlsCertCrlIssuer)
|
tlsCertCrlIssuer ?: return null
|
||||||
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuerPrincipal, rootCert)) {
|
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
|
||||||
return rootCert
|
return rootCert
|
||||||
}
|
}
|
||||||
return if (config.trustStoreFile.exists()) {
|
return if (config.trustStoreFile.exists()) {
|
||||||
findMatchingCertificate(tlsCertCrlIssuerPrincipal, config.loadTrustStore())
|
findMatchingCertificate(tlsCertCrlIssuer, config.loadTrustStore())
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isTlsCrlIssuerCertRequired(): Boolean {
|
override fun isTlsCrlIssuerCertRequired(): Boolean {
|
||||||
return !config.tlsCertCrlIssuer.isNullOrEmpty()
|
return config.tlsCertCrlIssuer != null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findMatchingCertificate(principal: X500Principal, trustStore: X509KeyStore): X509Certificate? {
|
private fun findMatchingCertificate(principal: X500Principal, trustStore: X509KeyStore): X509Certificate? {
|
||||||
|
@ -14,6 +14,7 @@ import org.junit.Test
|
|||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -34,13 +35,6 @@ class NodeConfigurationImplTest {
|
|||||||
assertThat(configValidationResult.first()).contains("tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL")
|
assertThat(configValidationResult.first()).contains("tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `tlsCertCrlIssuer validation fails when misconfigured`() {
|
|
||||||
val configValidationResult = configTlsCertCrlOptions(URL("http://test.com/crl"), "Corda Root CA").validate()
|
|
||||||
assertTrue { configValidationResult.isNotEmpty() }
|
|
||||||
assertThat(configValidationResult.first()).contains("Error when parsing tlsCertCrlIssuer:")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `can't have tlsCertCrlDistPoint null when crlCheckSoftFail is false`() {
|
fun `can't have tlsCertCrlDistPoint null when crlCheckSoftFail is false`() {
|
||||||
val configValidationResult = configTlsCertCrlOptions(null, null, false).validate()
|
val configValidationResult = configTlsCertCrlOptions(null, null, false).validate()
|
||||||
@ -218,7 +212,7 @@ class NodeConfigurationImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun configTlsCertCrlOptions(tlsCertCrlDistPoint: URL?, tlsCertCrlIssuer: String?, crlCheckSoftFail: Boolean = true): NodeConfiguration {
|
private fun configTlsCertCrlOptions(tlsCertCrlDistPoint: URL?, tlsCertCrlIssuer: String?, crlCheckSoftFail: Boolean = true): NodeConfiguration {
|
||||||
return testConfiguration.copy(tlsCertCrlDistPoint = tlsCertCrlDistPoint, tlsCertCrlIssuer = tlsCertCrlIssuer, crlCheckSoftFail = crlCheckSoftFail)
|
return testConfiguration.copy(tlsCertCrlDistPoint = tlsCertCrlDistPoint, tlsCertCrlIssuer = tlsCertCrlIssuer?.let { X500Principal(it) }, crlCheckSoftFail = crlCheckSoftFail)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val testConfiguration = testNodeConfiguration()
|
private val testConfiguration = testNodeConfiguration()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user